summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/.gitignore1
-rw-r--r--platform/android/CHANGELOG.md6
-rw-r--r--platform/android/CONTRIBUTING_LINUX.md49
-rw-r--r--platform/android/CONTRIBUTING_OSX.md67
-rw-r--r--platform/android/DISTRIBUTE.md47
-rw-r--r--platform/android/INSTALL_LINUX.md34
-rw-r--r--platform/android/INSTALL_OSX.md67
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle6
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java74
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java295
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java42
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java341
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java352
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java115
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java53
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java180
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java36
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java67
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java489
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java355
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java297
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java96
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java77
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java145
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java115
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java705
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java146
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java757
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java19
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java26
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java97
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java93
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java90
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/overview.html8
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_bearing.pngbin0 -> 1046 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_normal.pngbin0 -> 885 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location.pngbin2783 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_bearing.pngbin5939 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_stale.pngbin2593 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_bearing.pngbin0 -> 649 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_normal.pngbin0 -> 555 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location.pngbin2089 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_bearing.pngbin3599 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_stale.pngbin1942 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_bearing.pngbin0 -> 1345 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_normal.pngbin0 -> 1096 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location.pngbin3520 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_bearing.pngbin8155 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_stale.pngbin3287 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_bearing.pngbin0 -> 1902 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_normal.pngbin0 -> 1586 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location.pngbin5057 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_bearing.pngbin13246 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_stale.pngbin4676 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_bearing.pngbin0 -> 3022 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_normal.pngbin0 -> 2456 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location.pngbin5236 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_bearing.pngbin11688 -> 0 bytes
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_stale.pngbin5053 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable/ic_mylocationview_background.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml40
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/integers.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/README.md2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BaseTest.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CameraActivityTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DirectionsActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivityTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivityTest.java33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/GeocoderActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityScreenTest.java88
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityTest.java257
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivityTest.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivityTest.java33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PolylineActivityTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivityTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ScrollByActivityTest.java26
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivityTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/TiltActivityTest.java27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java39
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java124
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java136
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java46
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java48
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/DrawerUtils.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/GestureUtils.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ScreenshotUtil.java130
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ViewUtils.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml269
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Medium.ttfbin0 -> 127488 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Regular.ttfbin0 -> 126072 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/AnimatedMarkerActivity.java126
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivity.java186
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CameraActivity.java169
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivity.java114
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DirectionsActivity.java184
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivity.java156
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivity.java130
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/GeocoderActivity.java204
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java150
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java142
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java101
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivity.java126
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java685
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivity.java123
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivity.java88
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivity.java151
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivity.java100
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivity.java123
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivity.java213
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java360
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PolylineActivity.java173
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivity.java136
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ScrollByActivity.java153
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivity.java88
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java83
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java166
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java125
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java307
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java129
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java228
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java124
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java172
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java141
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java180
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java176
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java125
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java124
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java85
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java123
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java183
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java90
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java90
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java192
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java106
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java167
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java144
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java131
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java169
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java150
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java356
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java136
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java188
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java160
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java242
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureAdapter.java55
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java178
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarker.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarkerOptions.java68
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/layers/ExampleCustomLayer.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java69
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarker.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarkerOptions.java68
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarker.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerOptions.java75
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerView.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java104
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java60
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java60
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ApiAccess.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ItemClickSupport.java95
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/rotate_360.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_down.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_up.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_us.pngbin0 -> 418 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_a_photo_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_blur_on_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_bug_report_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_crop_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dns_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dynamic_marker.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_center_focus_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_none_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_find_replace_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_back_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_front_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_gps_fixed_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_adapter.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_concurrent.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_clear_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_map_padding.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_mapboxmap.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_menu_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_my_location_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_navigation_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_wallpaper_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_widgets_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pin_drop_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_place_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_refresh_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_tilt.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_transform_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_trending_up_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/line_divider.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animate_coordinate.xml45
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml50
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml53
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_layer.xml47
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geocoder.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml73
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapboxmap.xml24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml50
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polygon.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml48
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml202
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml221
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml163
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java91
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java45
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java210
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java85
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java25
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java80
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java75
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java138
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java299
-rw-r--r--platform/android/README.md18
-rw-r--r--platform/android/bitrise.yml59
-rw-r--r--platform/android/build.gradle7
-rw-r--r--platform/android/gradle/wrapper/gradle-wrapper.properties6
-rw-r--r--platform/android/mapboxgl-app.gypi149
-rw-r--r--platform/android/platform.gyp151
-rw-r--r--platform/android/resources/dot-arrow.svg11
-rw-r--r--platform/android/scripts/configure.sh11
-rwxr-xr-xplatform/android/scripts/debug.sh46
-rw-r--r--platform/android/scripts/defaults.mk10
-rwxr-xr-xplatform/android/scripts/install.sh13
-rwxr-xr-xplatform/android/scripts/run.sh56
-rwxr-xr-xplatform/android/scripts/toolchain.sh2
-rw-r--r--platform/android/src/asset_file_source.cpp8
-rw-r--r--platform/android/src/async_task.cpp63
-rw-r--r--platform/android/src/example_custom_layer.cpp6
-rw-r--r--platform/android/src/http_file_source.cpp180
-rw-r--r--platform/android/src/http_request_android.cpp188
-rwxr-xr-xplatform/android/src/jni.cpp384
-rw-r--r--platform/android/src/jni.hpp5
-rwxr-xr-xplatform/android/src/native_map_view.cpp182
-rwxr-xr-xplatform/android/src/native_map_view.hpp21
-rw-r--r--platform/android/src/run_loop.cpp213
-rw-r--r--platform/android/src/run_loop_impl.hpp57
-rw-r--r--platform/android/src/timer.cpp78
-rw-r--r--platform/android/tests/docs/UI_TESTS.md8
-rw-r--r--platform/android/tests/docs/UNIT_TESTS.md94
-rw-r--r--platform/darwin/README.md10
-rw-r--r--platform/darwin/include/MGLAnnotation.h48
-rw-r--r--platform/darwin/include/MGLGeometry.h98
-rw-r--r--platform/darwin/include/MGLMapCamera.h53
-rw-r--r--platform/darwin/include/MGLMultiPoint.h26
-rw-r--r--platform/darwin/include/MGLOfflinePack.h201
-rw-r--r--platform/darwin/include/MGLOverlay.h46
-rw-r--r--platform/darwin/include/MGLPointAnnotation.h18
-rw-r--r--platform/darwin/include/MGLPolygon.h26
-rw-r--r--platform/darwin/include/MGLPolyline.h26
-rw-r--r--platform/darwin/include/MGLShape.h27
-rw-r--r--platform/darwin/include/MGLStyle.h125
-rw-r--r--platform/darwin/include/MGLTypes.h62
-rw-r--r--platform/darwin/resources/Base.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/en.lproj/Foundation.stringsdict54
-rw-r--r--platform/darwin/src/MGLAccountManager.h (renamed from platform/darwin/include/MGLAccountManager.h)0
-rw-r--r--platform/darwin/src/MGLAccountManager.m10
-rw-r--r--platform/darwin/src/MGLAnnotation.h61
-rw-r--r--platform/darwin/src/MGLClockDirectionFormatter.h45
-rw-r--r--platform/darwin/src/MGLClockDirectionFormatter.m59
-rw-r--r--platform/darwin/src/MGLCompassDirectionFormatter.h42
-rw-r--r--platform/darwin/src/MGLCompassDirectionFormatter.m124
-rw-r--r--platform/darwin/src/MGLCoordinateFormatter.h55
-rw-r--r--platform/darwin/src/MGLCoordinateFormatter.m126
-rw-r--r--platform/darwin/src/MGLFeature.h137
-rw-r--r--platform/darwin/src/MGLFeature.mm269
-rw-r--r--platform/darwin/src/MGLFeature_Private.h11
-rw-r--r--platform/darwin/src/MGLGeometry.h129
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h11
-rw-r--r--platform/darwin/src/MGLMapCamera.h71
-rw-r--r--platform/darwin/src/MGLMultiPoint.h42
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm36
-rw-r--r--platform/darwin/src/MGLMultiPoint_Private.h9
-rw-r--r--platform/darwin/src/MGLOfflinePack.h190
-rw-r--r--platform/darwin/src/MGLOfflinePack.mm17
-rw-r--r--platform/darwin/src/MGLOfflineRegion.h (renamed from platform/darwin/include/MGLOfflineRegion.h)0
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h (renamed from platform/darwin/include/MGLOfflineStorage.h)0
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm15
-rw-r--r--platform/darwin/src/MGLOverlay.h67
-rw-r--r--platform/darwin/src/MGLPointAnnotation.h25
-rw-r--r--platform/darwin/src/MGLPolygon.h84
-rw-r--r--platform/darwin/src/MGLPolygon.mm96
-rw-r--r--platform/darwin/src/MGLPolyline.h60
-rw-r--r--platform/darwin/src/MGLPolyline.mm64
-rw-r--r--platform/darwin/src/MGLShape.h41
-rw-r--r--platform/darwin/src/MGLShapeCollection.h35
-rw-r--r--platform/darwin/src/MGLShapeCollection.m21
-rw-r--r--platform/darwin/src/MGLStyle.h165
-rw-r--r--platform/darwin/src/MGLTilePyramidOfflineRegion.h (renamed from platform/darwin/include/MGLTilePyramidOfflineRegion.h)0
-rw-r--r--platform/darwin/src/MGLTypes.h64
-rw-r--r--platform/darwin/src/NSBundle+MGLAdditions.h23
-rw-r--r--platform/darwin/src/NSBundle+MGLAdditions.m2
-rw-r--r--platform/darwin/src/NSProcessInfo+MGLAdditions.h2
-rw-r--r--platform/darwin/src/NSProcessInfo+MGLAdditions.m2
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.h2
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m2
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.h75
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.m49
-rw-r--r--platform/darwin/src/application_root.mm18
-rw-r--r--platform/darwin/src/async_task.cpp71
-rw-r--r--platform/darwin/src/headless_view_cgl.cpp113
-rw-r--r--platform/darwin/src/headless_view_eagl.mm103
-rw-r--r--platform/darwin/src/http_file_source.mm320
-rw-r--r--platform/darwin/src/http_request_nsurl.mm267
-rw-r--r--platform/darwin/src/run_loop.cpp51
-rw-r--r--platform/darwin/src/timer.cpp60
-rw-r--r--platform/darwin/test/MGLClockDirectionFormatterTests.m74
-rw-r--r--platform/darwin/test/MGLCompassDirectionFormatterTests.m88
-rw-r--r--platform/darwin/test/MGLCoordinateFormatterTests.m48
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm160
-rw-r--r--platform/darwin/test/MGLGeometryTests.mm102
-rw-r--r--platform/darwin/test/MGLOfflinePackTests.m40
-rw-r--r--platform/darwin/test/MGLOfflineRegionTests.m32
-rw-r--r--platform/darwin/test/MGLOfflineStorageTests.m146
-rw-r--r--platform/darwin/test/MGLStyleTests.mm105
-rw-r--r--platform/default/application_root.cpp28
-rw-r--r--platform/default/asset_file_source.cpp15
-rw-r--r--platform/default/default_file_source.cpp69
-rw-r--r--platform/default/glfw_view.cpp191
-rw-r--r--platform/default/headless_display.cpp1
-rw-r--r--platform/default/headless_view.cpp260
-rw-r--r--platform/default/headless_view_glx.cpp128
-rw-r--r--platform/default/http_file_source.cpp406
-rw-r--r--platform/default/http_request_curl.cpp549
-rw-r--r--platform/default/image.cpp16
-rw-r--r--platform/default/mbgl/storage/offline.cpp14
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp84
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp8
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp52
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp25
-rw-r--r--platform/default/online_file_source.cpp156
-rw-r--r--platform/default/png_reader.cpp4
-rw-r--r--platform/default/sqlite3.cpp66
-rw-r--r--platform/default/sqlite3.hpp24
-rw-r--r--platform/default/webp_reader.cpp7
-rw-r--r--platform/ios/CHANGELOG.md34
-rw-r--r--platform/ios/DEVELOPING.md108
-rw-r--r--platform/ios/INSTALL.md71
-rw-r--r--platform/ios/Mapbox-iOS-SDK-symbols.podspec4
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec4
-rw-r--r--platform/ios/Mapbox.playground/Contents.swift158
-rw-r--r--platform/ios/Mapbox.playground/contents.xcplayground4
-rw-r--r--platform/ios/Mapbox.playground/timeline.xctimeline6
-rw-r--r--platform/ios/WorkspaceSettings.xcsettings18
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json86
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.pngbin0 -> 1103 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.pngbin0 -> 1103 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.pngbin0 -> 1234 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40.png (renamed from platform/ios/app/img/Icon-40.png)bin951 -> 951 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.pngbin0 -> 1276 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png (renamed from platform/ios/app/img/Icon-40@2x.png)bin1528 -> 1528 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png (renamed from platform/ios/app/img/Icon-60@2x.png)bin2091 -> 2091 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png (renamed from platform/ios/app/img/Icon-60@3x.png)bin1300 -> 1300 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76.png (renamed from platform/ios/app/img/Icon-76.png)bin1447 -> 1447 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png (renamed from platform/ios/app/img/Icon-76@2x.png)bin2520 -> 2520 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.pngbin0 -> 2536 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Small.png (renamed from platform/ios/app/img/Icon-Small.png)bin684 -> 684 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png (renamed from platform/ios/app/img/Icon-Spotlight-40@3x.png)bin961 -> 961 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/Contents.json6
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/Contents.json26
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask.png (renamed from platform/ios/app/img/TrackingHeadingMask.png)bin250 -> 250 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@2x.png (renamed from platform/ios/app/img/TrackingHeadingMask@2x.png)bin467 -> 467 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@3x.png (renamed from platform/ios/app/img/TrackingHeadingMask@3x.png)bin672 -> 672 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/Contents.json26
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask.png (renamed from platform/ios/app/img/TrackingLocationMask.png)bin407 -> 407 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@2x.png (renamed from platform/ios/app/img/TrackingLocationMask@2x.png)bin680 -> 680 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@3x.png (renamed from platform/ios/app/img/TrackingLocationMask@3x.png)bin903 -> 903 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/Contents.json26
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask.png (renamed from platform/ios/app/img/TrackingLocationOffMask.png)bin493 -> 493 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@2x.png (renamed from platform/ios/app/img/TrackingLocationOffMask@2x.png)bin973 -> 973 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@3x.png (renamed from platform/ios/app/img/TrackingLocationOffMask@3x.png)bin1407 -> 1407 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/Contents.json25
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings.png (renamed from platform/ios/app/img/settings.png)bin528 -> 528 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png (renamed from platform/ios/app/img/settings@2x.png)bin1130 -> 1130 bytes
-rw-r--r--platform/ios/app/Default-568h@2x.png (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png)bin18594 -> 18594 bytes
-rw-r--r--platform/ios/app/Info.plist55
-rw-r--r--platform/ios/app/LaunchScreen.storyboard27
-rw-r--r--platform/ios/app/MBXAnnotationView.h7
-rw-r--r--platform/ios/app/MBXAnnotationView.m28
-rw-r--r--platform/ios/app/MBXAppDelegate.h2
-rw-r--r--platform/ios/app/MBXAppDelegate.m8
-rw-r--r--platform/ios/app/MBXCustomCalloutView.m10
-rw-r--r--platform/ios/app/MBXOfflinePacksTableViewController.m5
-rw-r--r--platform/ios/app/MBXViewController.m696
-rw-r--r--platform/ios/app/MBXViewController.mm641
-rw-r--r--platform/ios/app/Main.storyboard193
-rw-r--r--platform/ios/app/Settings.bundle/Root.plist33
-rw-r--r--platform/ios/app/Settings.bundle/en.lproj/Root.strings3
-rw-r--r--platform/ios/app/Storyboard.storyboard184
-rw-r--r--platform/ios/app/app-info.plist68
-rw-r--r--platform/ios/app/img/Default-568h@2x.pngbin2239 -> 0 bytes
-rw-r--r--platform/ios/app/img/Default-667h@2x.pngbin2797 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-60.pngbin1313 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-72.pngbin1418 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-72@2x.pngbin2442 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-76@3x.pngbin1741 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Small-50.pngbin1118 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Small-50@2x.pngbin1839 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Small@2x.pngbin1231 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Small@3x.pngbin807 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Spotlight-40.pngbin951 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon-Spotlight-40@2x.pngbin1528 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon.pngbin1171 -> 0 bytes
-rw-r--r--platform/ios/app/img/Icon@2x.pngbin1931 -> 0 bytes
-rw-r--r--platform/ios/app/img/iTunesArtworkbin264721 -> 0 bytes
-rw-r--r--platform/ios/app/img/iTunesArtwork.pngbin5655 -> 0 bytes
-rw-r--r--platform/ios/app/img/iTunesArtwork@2xbin661346 -> 0 bytes
-rw-r--r--platform/ios/app/mapboxgl-app.gypi81
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Contents.json98
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-1.pngbin0 -> 2296 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-2.pngbin0 -> 5596 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-3.pngbin0 -> 1640 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-4.pngbin0 -> 5073 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40.png (renamed from platform/ios/benchmark/img/Icon-40.png)bin1295 -> 1295 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png (renamed from platform/ios/benchmark/img/Icon-40@2x.png)bin2971 -> 2971 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png (renamed from platform/ios/benchmark/img/Icon-40@3x.png)bin5936 -> 5936 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76.png (renamed from platform/ios/benchmark/img/Icon-76.png)bin2841 -> 2841 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png (renamed from platform/ios/benchmark/img/Icon-76@2x.png)bin8633 -> 8633 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small.png (renamed from platform/ios/benchmark/img/Icon-Small.png)bin1009 -> 1009 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png (renamed from platform/ios/benchmark/img/Icon-Small@2x.png)bin2046 -> 2046 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png (renamed from platform/ios/benchmark/img/Icon-Small@3x.png)bin3371 -> 3371 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon.pngbin0 -> 3559 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork.png (renamed from platform/ios/benchmark/img/iTunesArtwork.png)bin113442 -> 113442 bytes
-rw-r--r--platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png (renamed from platform/ios/benchmark/img/iTunesArtwork@2x.png)bin399202 -> 399202 bytes
-rw-r--r--platform/ios/benchmark/Base.lproj/LaunchScreen.storyboard27
-rw-r--r--platform/ios/benchmark/Info.plist53
-rw-r--r--platform/ios/benchmark/MBXBenchAppDelegate.h2
-rw-r--r--platform/ios/benchmark/MBXBenchAppDelegate.m26
-rw-r--r--platform/ios/benchmark/MBXBenchViewController.mm61
-rw-r--r--platform/ios/benchmark/app-info.plist53
-rw-r--r--platform/ios/benchmark/benchmark-ios.gypi78
-rw-r--r--platform/ios/benchmark/img/Icon-60.pngbin2051 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-60@2x.pngbin5936 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-60@3x.pngbin11806 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-72.pngbin2610 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-72@2x.pngbin7894 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-Small-50.pngbin1676 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon-Small-50@2x.pngbin4490 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon.pngbin2009 -> 0 bytes
-rw-r--r--platform/ios/benchmark/img/Icon.svg6
-rw-r--r--platform/ios/benchmark/img/Icon@2x.pngbin5436 -> 0 bytes
-rw-r--r--platform/ios/benchmark/locations.hpp5
-rw-r--r--platform/ios/bitrise.yml36
-rw-r--r--platform/ios/docs/doc-README.md2
-rw-r--r--platform/ios/docs/pod-README.md60
-rw-r--r--platform/ios/framework/Info-static.plist26
-rw-r--r--platform/ios/framework/Info.plist16
-rw-r--r--platform/ios/framework/Mapbox.h30
-rw-r--r--platform/ios/framework/Mapbox.m32
-rw-r--r--platform/ios/framework/Settings.bundle/Base.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/Root.plist6
-rw-r--r--platform/ios/framework/Settings.bundle/en.lproj/Root.stringsbin552 -> 0 bytes
-rw-r--r--platform/ios/framework/framework-ios.gypi71
-rw-r--r--platform/ios/include/MGLAnnotationImage.h44
-rw-r--r--platform/ios/include/MGLCalloutView.h67
-rw-r--r--platform/ios/include/MGLMapView.h1260
-rw-r--r--platform/ios/include/Mapbox.h22
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj1972
-rw-r--r--platform/ios/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme128
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme91
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme127
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme113
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme91
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme89
-rw-r--r--platform/ios/ios.xcworkspace/contents.xcworkspacedata16
-rw-r--r--platform/ios/ios.xcworkspace/xcshareddata/ios.xcscmblueprint37
-rw-r--r--platform/ios/ios.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist25
-rw-r--r--platform/ios/jazzy.yml90
-rw-r--r--platform/ios/platform.gyp141
-rw-r--r--platform/ios/resources/Base.lproj/Localizable.strings84
-rw-r--r--platform/ios/resources/Compass.pngbin1061 -> 1139 bytes
-rw-r--r--platform/ios/resources/Compass@2x.pngbin1300 -> 1892 bytes
-rw-r--r--platform/ios/resources/Compass@3x.pngbin2514 -> 3398 bytes
-rw-r--r--platform/ios/resources/en.lproj/Localizable.stringsdict23
-rwxr-xr-xplatform/ios/scripts/after_failure.sh13
-rw-r--r--platform/ios/scripts/configure.sh13
-rw-r--r--platform/ios/scripts/defaults.mk11
-rwxr-xr-xplatform/ios/scripts/deploy-packages.sh1
-rwxr-xr-xplatform/ios/scripts/document.sh1
-rwxr-xr-xplatform/ios/scripts/install.sh20
-rwxr-xr-xplatform/ios/scripts/package.sh211
-rwxr-xr-xplatform/ios/scripts/run.sh52
-rw-r--r--platform/ios/scripts/setup.sh5
-rwxr-xr-xplatform/ios/scripts/test.sh13
-rw-r--r--platform/ios/src/MGLAPIClient.m26
-rw-r--r--platform/ios/src/MGLAnnotationContainerView.h17
-rw-r--r--platform/ios/src/MGLAnnotationContainerView.m52
-rw-r--r--platform/ios/src/MGLAnnotationImage.h44
-rw-r--r--platform/ios/src/MGLAnnotationImage.m2
-rw-r--r--platform/ios/src/MGLAnnotationImage_Private.h3
-rw-r--r--platform/ios/src/MGLAnnotationView.h61
-rw-r--r--platform/ios/src/MGLAnnotationView.mm147
-rw-r--r--platform/ios/src/MGLAnnotationView_Private.h15
-rw-r--r--platform/ios/src/MGLCalloutView.h77
-rw-r--r--platform/ios/src/MGLLocationManager.m18
-rw-r--r--platform/ios/src/MGLMapView+IBAdditions.h (renamed from platform/ios/include/MGLMapView+IBAdditions.h)0
-rw-r--r--platform/ios/src/MGLMapView+MGLCustomStyleLayerAdditions.h (renamed from platform/ios/include/MGLMapView+MGLCustomStyleLayerAdditions.h)0
-rw-r--r--platform/ios/src/MGLMapView.h1187
-rw-r--r--platform/ios/src/MGLMapView.mm1334
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h284
-rw-r--r--platform/ios/src/MGLMapView_Internal.h17
-rw-r--r--platform/ios/src/MGLMapboxEvents.m32
-rw-r--r--platform/ios/src/MGLUserLocation.h (renamed from platform/ios/include/MGLUserLocation.h)0
-rw-r--r--platform/ios/src/MGLUserLocation.m3
-rw-r--r--platform/ios/src/MGLUserLocationAnnotationView.m87
-rw-r--r--platform/ios/src/Mapbox.h38
-rw-r--r--platform/ios/test/App-Info.plist55
-rw-r--r--platform/ios/test/Images.xcassets/AppIcon.appiconset/Contents.json162
-rw-r--r--platform/ios/test/Info.plist24
m---------platform/ios/test/KIF0
-rw-r--r--platform/ios/test/MapViewTests.m554
-rw-r--r--platform/ios/test/ios-tests.xcodeproj/project.pbxproj688
-rw-r--r--platform/ios/uitest/.gitignore (renamed from platform/ios/test/.gitignore)0
-rw-r--r--platform/ios/uitest/App-Info.plist53
-rw-r--r--platform/ios/uitest/Bundle-Info.plist (renamed from platform/ios/test/Bundle-Info.plist)0
-rw-r--r--platform/ios/uitest/Images.xcassets/AppIcon.appiconset/Contents.json73
-rw-r--r--platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Contents.json (renamed from platform/ios/test/Images.xcassets/LaunchImage.launchimage/Contents.json)0
-rw-r--r--platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png (renamed from platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png)bin184 -> 184 bytes
-rw-r--r--platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default@2x.png (renamed from platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default@2x.png)bin170 -> 170 bytes
m---------platform/ios/uitest/KIF0
-rw-r--r--platform/ios/uitest/KIFTestActor+MapboxGL.h (renamed from platform/ios/test/KIFTestActor+MapboxGL.h)0
-rw-r--r--platform/ios/uitest/KIFTestActor+MapboxGL.m (renamed from platform/ios/test/KIFTestActor+MapboxGL.m)0
-rw-r--r--platform/ios/uitest/LaunchScreen.xib (renamed from platform/ios/test/LaunchScreen.xib)0
-rw-r--r--platform/ios/uitest/MGLTAppDelegate.h (renamed from platform/ios/test/MGLTAppDelegate.h)0
-rw-r--r--platform/ios/uitest/MGLTAppDelegate.m (renamed from platform/ios/test/MGLTAppDelegate.m)0
-rw-r--r--platform/ios/uitest/MGLTViewController.h (renamed from platform/ios/test/MGLTViewController.h)0
-rw-r--r--platform/ios/uitest/MGLTViewController.m (renamed from platform/ios/test/MGLTViewController.m)0
-rw-r--r--platform/ios/uitest/MapViewTests.m553
-rw-r--r--platform/ios/uitest/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h (renamed from platform/ios/test/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMArg.h (renamed from platform/ios/test/OCMock/OCMock/OCMArg.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMConstraint.h (renamed from platform/ios/test/OCMock/OCMock/OCMConstraint.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMLocation.h (renamed from platform/ios/test/OCMock/OCMock/OCMLocation.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMMacroState.h (renamed from platform/ios/test/OCMock/OCMock/OCMMacroState.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMRecorder.h (renamed from platform/ios/test/OCMock/OCMock/OCMRecorder.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMStubRecorder.h (renamed from platform/ios/test/OCMock/OCMock/OCMStubRecorder.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMock.h (renamed from platform/ios/test/OCMock/OCMock/OCMock.h)0
-rw-r--r--platform/ios/uitest/OCMock/OCMock/OCMockObject.h (renamed from platform/ios/test/OCMock/OCMock/OCMockObject.h)0
-rw-r--r--platform/ios/uitest/OCMock/libOCMock.a (renamed from platform/ios/test/OCMock/libOCMock.a)bin2071640 -> 2071640 bytes
-rw-r--r--platform/ios/uitest/OHHTTPStubs/.gitignore (renamed from platform/ios/test/OHHTTPStubs/.gitignore)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/.travis.yml (renamed from platform/ios/test/OHHTTPStubs/.travis.yml)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/CHANGELOG.md (renamed from platform/ios/test/OHHTTPStubs/CHANGELOG.md)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.m (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.xib (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.xib)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile.lock (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile.lock)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg)bin46859 -> 46859 bytes
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png)bin18594 -> 18594 bytes
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m (renamed from platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/AppDelegate.swift (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/AppDelegate.swift)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Main.storyboard (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Main.storyboard)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/MainViewController.swift (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/MainViewController.swift)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile.lock (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile.lock)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h)0
l---------platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg)bin46859 -> 46859 bytes
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.txt (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.txt)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.pngbin0 -> 18594 bytes
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib (renamed from platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/LICENSE (renamed from platform/ios/test/OHHTTPStubs/LICENSE)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs.podspec (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs.podspec)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile.lock (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile.lock)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h)0
l---------platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail)0
-rwxr-xr-xplatform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail (renamed from platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/README.md (renamed from platform/ios/test/OHHTTPStubs/README.md)0
-rw-r--r--platform/ios/uitest/OHHTTPStubs/Rakefile (renamed from platform/ios/test/OHHTTPStubs/Rakefile)0
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj574
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata (renamed from platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata)0
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout (renamed from platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout)0
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout (renamed from platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout)0
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme (renamed from platform/ios/test/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme)0
-rw-r--r--platform/ios/uitest/main.m (renamed from platform/ios/test/main.m)0
-rw-r--r--platform/linux/README.md13
-rw-r--r--platform/linux/main.cpp176
-rw-r--r--platform/linux/mapboxgl-app.gypi66
-rw-r--r--platform/linux/platform.gyp125
-rwxr-xr-xplatform/linux/scripts/after_script.sh10
-rw-r--r--platform/linux/scripts/configure.sh17
-rwxr-xr-xplatform/linux/scripts/coveralls.sh23
-rw-r--r--platform/linux/scripts/defaults.mk4
-rwxr-xr-xplatform/linux/scripts/install.sh11
-rwxr-xr-xplatform/linux/scripts/run.sh31
-rwxr-xr-xplatform/linux/scripts/setup.sh28
-rwxr-xr-xplatform/linux/scripts/tidy.sh17
-rw-r--r--platform/node/CHANGELOG.md31
-rw-r--r--platform/node/README.md130
-rw-r--r--platform/node/bitrise.yml25
-rwxr-xr-xplatform/node/scripts/after_script.sh48
-rwxr-xr-xplatform/node/scripts/install.sh24
-rwxr-xr-xplatform/node/scripts/run.sh26
-rw-r--r--platform/node/src/node_feature.cpp168
-rw-r--r--platform/node/src/node_feature.hpp18
-rw-r--r--platform/node/src/node_log.cpp1
-rw-r--r--platform/node/src/node_map.cpp84
-rw-r--r--platform/node/src/node_map.hpp3
-rw-r--r--platform/node/src/node_mapbox_gl_native.cpp32
-rw-r--r--platform/node/src/node_mapbox_gl_native.hpp9
-rw-r--r--platform/node/src/node_request.cpp43
-rw-r--r--platform/node/src/node_request.hpp9
-rw-r--r--platform/node/test/js/map.test.js24
-rw-r--r--platform/node/test/js/require.js1
-rw-r--r--platform/node/test/query.test.js12
-rw-r--r--platform/node/test/render.test.js48
-rw-r--r--platform/node/test/suite_implementation.js58
-rw-r--r--platform/osx/CHANGELOG.md14
-rw-r--r--platform/osx/DEVELOPING.md40
-rw-r--r--platform/osx/INSTALL.md42
-rw-r--r--platform/osx/README.md2
-rw-r--r--platform/osx/WorkspaceSettings.xcsettings18
-rw-r--r--platform/osx/app/AppDelegate.h2
-rw-r--r--platform/osx/app/AppDelegate.m2
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon128x128.pngbin0 -> 3668 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon16x16.pngbin0 -> 713 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256-1.pngbin0 -> 8495 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256.pngbin0 -> 8495 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32-1.pngbin0 -> 1213 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32.pngbin0 -> 1213 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512-1.pngbin0 -> 20280 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512.pngbin0 -> 20280 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/Contents.json68
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon-1.pngbin0 -> 2205 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon.png (renamed from platform/ios/app/img/iTunesArtwork@2x.png)bin9293 -> 9293 bytes
-rw-r--r--platform/osx/app/Assets.xcassets/Contents.json6
-rw-r--r--platform/osx/app/Base.lproj/MainMenu.xib845
-rw-r--r--platform/osx/app/Base.lproj/MapDocument.xib142
-rw-r--r--platform/osx/app/DroppedPinAnnotation.h2
-rw-r--r--platform/osx/app/DroppedPinAnnotation.m17
-rw-r--r--platform/osx/app/Icon.icnsbin64088 -> 0 bytes
-rw-r--r--platform/osx/app/Info.plist14
-rw-r--r--platform/osx/app/LocationCoordinate2DTransformer.m33
-rw-r--r--platform/osx/app/MainMenu.xib813
-rw-r--r--platform/osx/app/MapDocument.m113
-rw-r--r--platform/osx/app/MapDocument.xib129
-rw-r--r--platform/osx/app/NSValue+Additions.h10
-rw-r--r--platform/osx/app/NSValue+Additions.m15
-rw-r--r--platform/osx/app/TimeIntervalTransformer.m2
-rw-r--r--platform/osx/app/mapboxgl-app.gypi77
-rw-r--r--platform/osx/bitrise.yml24
-rw-r--r--platform/osx/docs/doc-README.md9
-rw-r--r--platform/osx/docs/pod-README.md43
-rw-r--r--platform/osx/include/MGLAnnotationImage.h58
-rw-r--r--platform/osx/include/MGLMapView.h640
-rw-r--r--platform/osx/include/MGLMapViewDelegate.h199
-rw-r--r--platform/osx/jazzy.yml84
-rw-r--r--platform/osx/osx.xcodeproj/project.pbxproj1178
-rw-r--r--platform/osx/osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata7
-rw-r--r--platform/osx/osx.xcodeproj/xcshareddata/xcschemes/CI.xcscheme128
-rw-r--r--platform/osx/osx.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme113
-rw-r--r--platform/osx/osx.xcodeproj/xcshareddata/xcschemes/osxapp.xcscheme101
-rw-r--r--platform/osx/osx.xcworkspace/contents.xcworkspacedata10
-rw-r--r--platform/osx/osx.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist25
-rw-r--r--platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-offline.xcscheme92
-rw-r--r--platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-render.xcscheme92
-rw-r--r--platform/osx/osx.xcworkspace/xcshareddata/xcschemes/test.xcscheme92
-rw-r--r--platform/osx/platform.gyp104
-rw-r--r--platform/osx/screenshot.pngbin0 -> 680704 bytes
-rw-r--r--platform/osx/scripts/configure.sh15
-rw-r--r--platform/osx/scripts/defaults.mk4
-rwxr-xr-xplatform/osx/scripts/document.sh45
-rwxr-xr-xplatform/osx/scripts/install.sh7
-rw-r--r--platform/osx/scripts/osxtest.xcscheme56
-rwxr-xr-xplatform/osx/scripts/package.sh80
-rwxr-xr-xplatform/osx/scripts/run.sh21
-rwxr-xr-xplatform/osx/scripts/setup.sh8
-rwxr-xr-xplatform/osx/scripts/test.sh28
-rw-r--r--platform/osx/sdk/Base.lproj/Localizable.strings30
-rw-r--r--platform/osx/sdk/Base.lproj/MGLAnnotationCallout.xib69
-rw-r--r--platform/osx/sdk/Info.plist8
-rw-r--r--platform/osx/sdk/Mapbox.h28
-rw-r--r--platform/osx/sdk/Mapbox.m33
-rw-r--r--platform/osx/sdk/default_marker.pdf (renamed from platform/osx/src/resources/default_marker.pdf)bin2601 -> 2601 bytes
-rw-r--r--platform/osx/sdk/framework-osx.gypi68
-rw-r--r--platform/osx/sdk/mapbox.pdf (renamed from platform/osx/src/resources/mapbox.pdf)bin3762 -> 3762 bytes
-rw-r--r--platform/osx/src/MGLAnnotationImage.h64
-rw-r--r--platform/osx/src/MGLAnnotationImage.m3
-rw-r--r--platform/osx/src/MGLAnnotationImage_Private.h8
-rw-r--r--platform/osx/src/MGLAttributionButton.m5
-rw-r--r--platform/osx/src/MGLMapView+IBAdditions.h (renamed from platform/osx/include/MGLMapView+IBAdditions.h)0
-rw-r--r--platform/osx/src/MGLMapView+IBAdditions.m2
-rw-r--r--platform/osx/src/MGLMapView.h903
-rw-r--r--platform/osx/src/MGLMapView.mm517
-rw-r--r--platform/osx/src/MGLMapViewDelegate.h220
-rw-r--r--platform/osx/src/MGLMapView_Private.h2
-rw-r--r--platform/osx/src/Mapbox.h34
-rw-r--r--platform/osx/src/resources/MGLAnnotationCallout.xib53
-rw-r--r--platform/osx/test/MGLGeometryTests.mm58
-rw-r--r--platform/osx/test/MGLOfflinePackTests.m40
-rw-r--r--platform/osx/test/MGLOfflineRegionTests.m35
-rw-r--r--platform/osx/test/MGLOfflineStorageTests.m126
-rw-r--r--platform/osx/test/MGLStyleTests.mm102
-rw-r--r--platform/osx/test/osxtest.gypi84
-rw-r--r--platform/qt/README.md73
-rw-r--r--platform/qt/app/main.cpp24
-rw-r--r--platform/qt/app/mapwindow.cpp167
-rw-r--r--platform/qt/app/mapwindow.hpp49
-rw-r--r--platform/qt/app/qmapboxgl.gypi55
-rw-r--r--platform/qt/bitrise-qt4.yml56
-rw-r--r--platform/qt/bitrise-qt5.yml57
-rw-r--r--platform/qt/include/QMapbox1
-rw-r--r--platform/qt/include/QMapboxGL1
-rw-r--r--platform/qt/include/QQuickMapboxGL1
-rw-r--r--platform/qt/include/qmapbox.hpp67
-rw-r--r--platform/qt/include/qmapboxgl.hpp220
-rw-r--r--platform/qt/include/qquickmapboxgl.hpp133
-rw-r--r--platform/qt/platform.gyp217
-rw-r--r--platform/qt/qmapbox.qrc6
-rw-r--r--platform/qt/qmlapp/main.cpp22
-rw-r--r--platform/qt/qmlapp/main.qml220
-rw-r--r--platform/qt/qmlapp/qml.qrc5
-rw-r--r--platform/qt/qmlapp/qquickmapboxgl.gypi60
-rw-r--r--platform/qt/qt.gypi20
-rw-r--r--platform/qt/scripts/configure.sh82
-rwxr-xr-xplatform/qt/scripts/toolchain.sh21
-rw-r--r--platform/qt/src/async_task.cpp43
-rw-r--r--platform/qt/src/async_task_impl.hpp38
-rw-r--r--platform/qt/src/http_file_source.cpp131
-rw-r--r--platform/qt/src/http_file_source.hpp39
-rw-r--r--platform/qt/src/http_request.cpp122
-rw-r--r--platform/qt/src/http_request.hpp34
-rw-r--r--platform/qt/src/image.cpp63
-rw-r--r--platform/qt/src/qmapbox.cpp57
-rw-r--r--platform/qt/src/qmapboxgl.cpp655
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp49
-rw-r--r--platform/qt/src/qquickmapboxgl.cpp248
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.cpp78
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.hpp35
-rw-r--r--platform/qt/src/run_loop.cpp144
-rw-r--r--platform/qt/src/run_loop_impl.hpp39
-rw-r--r--platform/qt/src/timer.cpp57
-rw-r--r--platform/qt/src/timer_impl.hpp31
1089 files changed, 38291 insertions, 19864 deletions
diff --git a/platform/android/.gitignore b/platform/android/.gitignore
index 57cd31fea2..53e7540178 100644
--- a/platform/android/.gitignore
+++ b/platform/android/.gitignore
@@ -12,7 +12,6 @@ build/
# JNI
MapboxGLAndroidSDK/src/main/jniLibs/
-MapboxGLAndroidSDK/src/main/obj.target/
# Lib assets
MapboxGLAndroidSDK/src/main/assets/
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index 7ba51fd368..f73c5ba198 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,6 +2,12 @@
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.
+## 4.0.1
+
+Mapbox Android 4.0.1 is a patch release to make this bug fix available sooner.
+
+* MapboxMap.removeAnnotations() doesn't remove markers ([#4553](https://github.com/mapbox/mapbox-gl-native/issues/4553))
+
## 4.0.0
Mapbox Android 4.0.0 contains the following 3 major new features.
diff --git a/platform/android/CONTRIBUTING_LINUX.md b/platform/android/CONTRIBUTING_LINUX.md
new file mode 100644
index 0000000000..875f7a800a
--- /dev/null
+++ b/platform/android/CONTRIBUTING_LINUX.md
@@ -0,0 +1,49 @@
+# Contributing to the Android SDK on Linux
+
+_These instructions were tested on Ubuntu 16.04 LTS (aka Xenial Xerus)._
+
+Install the build dependencies:
+
+```
+$ sudo apt-get install -y android-tools-adb build-essential curl git \
+ lib32stdc++6 lib32z1 openjdk-8-jdk pkg-config python
+```
+
+Install the Android SDK. We recommend doing this by way of [Android command line tools for Linux](http://developer.android.com/sdk/index.html) but you can also achieve same results with the Android Studio package.
+
+Unpack the SDK and make sure you have the proper environment variables set.
+
+```
+$ export PATH=/path/to/android-sdk-linux/tools:$PATH
+$ export ANDROID_HOME=/path/to/android-sdk-linux
+```
+
+Update the Android SDK to the latest version:
+
+```
+$ android update sdk -u
+```
+
+## Setting Mapbox Access Token
+
+_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
+
+gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environ variable and save it to `platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. Otherwise, you can edit `developer-config.xml` and add the value manually as `mapbox_access_token`.
+
+## Building
+
+Checking out the code:
+
+```
+$ git clone https://github.com/mapbox/mapbox-gl-native.git
+$ cd mapbox-gl-native
+```
+
+Building a debug version will generated a self-signed test application that can be installed on the phone or emulator:
+
+```
+$ BUILDTYPE=Debug make android
+$ adb install -r platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk
+```
+
+The debug version will emit considerable more log information (and run slower). Use simply `make android` to build a release version.
diff --git a/platform/android/CONTRIBUTING_OSX.md b/platform/android/CONTRIBUTING_OSX.md
new file mode 100644
index 0000000000..3154ac1087
--- /dev/null
+++ b/platform/android/CONTRIBUTING_OSX.md
@@ -0,0 +1,67 @@
+# Contributing to the Android SDK on OS X
+
+Install Oracle JDK 7+ and Android Studio:
+
+ brew tap caskroom/cask
+ brew install brew-cask
+ brew cask install java
+
+ brew cask install android-studio
+
+You can [download Android Studio instead of installing it with Homebrew, but you'll still need to `brew install java`](https://developer.android.com/sdk/index.html)
+for it to work.
+
+Once Android Studio is installed, the [first time it's run it'll ask to install the Android SDK](http://developer.android.com/sdk/installing/index.html?pkg=studio) which you should do. While doing so in the Android SDK Manager make sure to also select and install the latest versions of "Android Support Repository" and "Android Support Library" from "Extras":
+
+![image](https://cloud.githubusercontent.com/assets/98601/9915837/289f398e-5c6e-11e5-9a84-ed4d08d52d1f.png)
+
+By default, the Android SDK will be installed to `/Users/<user>/Library/Android/sdk/`. For more information on how to [configure Android Studio](http://tools.android.com/tech-docs/configuration) and how to [set Project JDK vs IDE JDK](http://tools.android.com/tech-docs/configuration/osx-jdk) please see [Google's documentation](http://tools.android.com/overview).
+
+## Setting Mapbox Access Token
+
+_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
+
+If you start Android Studio from your terminal, gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. Otherwise,
+you can edit `developer-config.xml` and add the value manually as `mapbox_access_token`.
+
+## Developing In Android Studio
+
+To work with the Mapbox Android SDK, you'll first need to get it set up as a Project in Android Studio. To do so Open Android Studio and select "Import project (Eclipse ADT, Gradle, etc.)" from the Welcome to Android Studio dialog. From there select the `platform/android` directory from the local file system where `mapbox-gl-native` was cloned. For example:
+
+```sh
+/Users/<user>/development/mapbox-gl-native/platform/android
+```
+
+The Mapbox Android SDK is a multi-module Gradle based project. Specifically, the SDK itself is an [Android Library](http://developer.android.com/tools/projects/index.html#LibraryModules) module and it utilizes a companion [test module](http://developer.android.com/tools/projects/index.html#testing) (aka "the TestApp") for daily development. When Android Studio finishes importing the project both `MapboxGLAndroidSDK` and `MapboxGLAndroidSDKTestApp` modules should be visible.
+
+## Setting `ANDROID_HOME`
+
+For `build android` to work, you'll need to set the `ANDROID_HOME` environment
+to point to your Android SDK installation:
+
+```
+export ANDROID_HOME=/<installation location>/android-sdk-macosx
+```
+
+This environment variable configuration should go into a file that's read on
+your shell's startup, like `~/.profile`.
+
+## Running The TestApp
+
+In order to run the TestApp on an emulator or device the Core GL portion needs to built first. Core GL is the common C++ based OpenGL engine that powers the maps for iOS, Android, and Qt in the project. To build it, open Terminal and run the following commands from the root of the `mapbox-gl-native` source code
+
+Run:
+
+ // From /Users/<user>/development/mapbox-gl-native
+
+ // Makes arm7 ABI version of Core GL
+ // Can be run on most Android phones and arm emulator
+ make android
+
+ // Make x86 version of Core GL
+ // Useful for running faster Anroid x86 emulator on Macs
+ make android-lib-x86
+
+Once Core GL has been built, the TestApp can be run by selecting `MapboxGLAndroidSDKTestApp` from the Run menu or toolbar in Android Studio.
+
+**Next: get your app [running on a hardware Android Device](docs/ANDROID_DEVICE.md) or [simulator](docs/ANDROID_SIMULATOR.md)**
diff --git a/platform/android/DISTRIBUTE.md b/platform/android/DISTRIBUTE.md
index eb21979347..e7df5650f1 100644
--- a/platform/android/DISTRIBUTE.md
+++ b/platform/android/DISTRIBUTE.md
@@ -1,3 +1,48 @@
# Distributing Mapbox GL Native for Android
-Use the `Makefile` target `make apackage` in order to build JNI libraries for all supported ABI's for eventual distribution of the whole package.
+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 can be achieved using the different `Makefile` targets that are available.
+
+##### Build native libraries for all supported ABIs
+
+```sh
+make apackage
+```
+
+This will build native libraries to support following ABIs:
+ - armeabi
+ - armeabi-v7a
+ - arm64-v8a
+ - x86
+ - x86_64
+ - mips
+
+##### 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/INSTALL_LINUX.md b/platform/android/INSTALL_LINUX.md
deleted file mode 100644
index 353d0269d0..0000000000
--- a/platform/android/INSTALL_LINUX.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Developing for Android on Linux
-
-Install a build dependencies:
-
- apt-get install -y make git build-essential automake \
- libtool make cmake pkg-config lib32stdc++6 lib32z1
-
-Install [Oracle JDK 7 (requires license agreement)](http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html)
-
- export JAVA_HOME="/dir/to/jdk1.7.0_71"
-
-Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html).
-
- export ANDROID_HOME="/dir/to/android-sdk-linux"
-
-In the Android SDK Manager also select and install "Android Support Repository" and "Android Support Library" from "Extras":
-
-![image](https://cloud.githubusercontent.com/assets/98601/9915837/289f398e-5c6e-11e5-9a84-ed4d08d52d1f.png)
-
-## Setting Mapbox Access Token
-
-_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
-
-gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environ variable and save it to `"MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. Otherwise, you can edit `developer-config.xml` and add the value manually as `mapbox_access_token`.
-
-## Building
-
-Run:
-
- make android
-
-You can then open `android` in Android Studio via "Import project (Eclipse ADT, Gradle, etc.)".
-
-**Next: get your app [running on a hardware Android Device](docs/ANDROID_DEVICE.md) or [simulator](docs/ANDROID_SIMULATOR.md)**
diff --git a/platform/android/INSTALL_OSX.md b/platform/android/INSTALL_OSX.md
deleted file mode 100644
index 00096ebc60..0000000000
--- a/platform/android/INSTALL_OSX.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Installing the Mapbox Android SDK on OS X
-
-Install Oracle JDK 7+ and Android Studio:
-
- brew tap caskroom/cask
- brew install brew-cask
- brew cask install java
-
- brew cask install android-studio
-
-You can [download Android Studio instead of installing it with Homebrew, but you'll still need to `brew install java`](https://developer.android.com/sdk/index.html)
-for it to work.
-
-Once Android Studio is installed, the [first time it's run it'll ask to install the Android SDK](http://developer.android.com/sdk/installing/index.html?pkg=studio) which you should do. While doing so in the Android SDK Manager make sure to also select and install the latest versions of "Android Support Repository" and "Android Support Library" from "Extras":
-
-![image](https://cloud.githubusercontent.com/assets/98601/9915837/289f398e-5c6e-11e5-9a84-ed4d08d52d1f.png)
-
-By default, the Android SDK will be installed to `/Users/<user>/Library/Android/sdk/`. For more information on how to [configure Android Studio](http://tools.android.com/tech-docs/configuration) and how to [set Project JDK vs IDE JDK](http://tools.android.com/tech-docs/configuration/osx-jdk) please see [Google's documentation](http://tools.android.com/overview).
-
-## Setting Mapbox Access Token
-
-_The test application (used for development purposes) uses Mapbox vector tiles, which require a Mapbox account and API access token. Obtain a free access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
-
-If you start Android Studio from your terminal, gradle will take the value of the `MAPBOX_ACCESS_TOKEN` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml` where the app will read it from. Otherwise,
-you can edit `developer-config.xml` and add the value manually as `mapbox_access_token`.
-
-## Developing In Android Studio
-
-To work with the Mapbox Android SDK, you'll first need to get it set up as a Project in Android Studio. To do so Open Android Studio and select "Import project (Eclipse ADT, Gradle, etc.)" from the Welcome to Android Studio dialog. From there select the `platform/android` directory from the local file system where `mapbox-gl-native` was cloned. For example:
-
-```sh
-/Users/<user>/development/mapbox-gl-native/platform/android
-```
-
-The Mapbox Android SDK is a multi-module Gradle based project. Specifically, the SDK itself is an [Android Library](http://developer.android.com/tools/projects/index.html#LibraryModules) module and it utilizes a companion [test module](http://developer.android.com/tools/projects/index.html#testing) (aka "the TestApp") for daily development. When Android Studio finishes importing the project both `MapboxGLAndroidSDK` and `MapboxGLAndroidSDKTestApp` modules should be visible.
-
-## Setting `ANDROID_HOME`
-
-For `build android` to work, you'll need to set the `ANDROID_HOME` environment
-to point to your Android SDK installation:
-
-```
-export ANDROID_HOME=/<installation location>/android-sdk-macosx
-```
-
-This environment variable configuration should go into a file that's read on
-your shell's startup, like `~/.profile`.
-
-## Running The TestApp
-
-In order to run the TestApp on an emulator or device the Core GL portion needs to built first. Core GL is the common C++ based OpenGL engine that powers the maps for iOS, Android, and Qt in the project. To build it, open Terminal and run the following commands from the root of the `mapbox-gl-native` source code
-
-Run:
-
- // From /Users/<user>/development/mapbox-gl-native
-
- // Makes arm7 ABI version of Core GL
- // Can be run on most Android phones and arm emulator
- make android
-
- // Make x86 version of Core GL
- // Useful for running faster Anroid x86 emulator on Macs
- make android-lib-x86
-
-Once Core GL has been built, the TestApp can be run by selecting `MapboxGLAndroidSDKTestApp` from the Run menu or toolbar in Android Studio.
-
-**Next: get your app [running on a hardware Android Device](docs/ANDROID_DEVICE.md) or [simulator](docs/ANDROID_SIMULATOR.md)**
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index cc06995df6..38688a9aca 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -18,15 +18,15 @@ repositories {
}
ext {
- supportLibVersion = '23.1.1'
+ supportLibVersion = '23.4.0'
}
dependencies {
compile "com.android.support:support-annotations:${supportLibVersion}"
compile "com.android.support:support-v4:${supportLibVersion}"
compile "com.android.support:design:${supportLibVersion}"
- compile 'com.squareup.okhttp3:okhttp:3.2.0'
- compile 'com.mapzen.android:lost:1.0.1'
+ compile 'com.squareup.okhttp3:okhttp:3.3.0'
+ compile 'com.mapzen.android:lost:1.1.0'
}
android {
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 56cb4324fb..c2df63b0bb 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -14,7 +14,7 @@ POM_DEVELOPER_NAME=Mapbox
ANDROID_MIN_SDK=15
ANDROID_BUILD_TARGET_SDK_VERSION=23
-ANDROID_BUILD_TOOLS_VERSION=23.0.2
+ANDROID_BUILD_TOOLS_VERSION=23.0.3
ANDROID_BUILD_SDK_VERSION=23
POM_NAME=Mapbox GL Android SDK
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java
new file mode 100644
index 0000000000..211590653a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk;
+
+import android.content.Context;
+import android.text.TextUtils;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
+import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
+
+public class MapboxAccountManager {
+
+ private static MapboxAccountManager mapboxAccountManager = null;
+
+ private final String accessToken;
+ private final Context applicationContext;
+
+ /**
+ * MapboxAccountManager should NOT be instantiated directly.
+ * Use @see MapboxAccountManager#getInstance() instead.
+ * @param applicationContext Context used to get ApplicationContext
+ * @param accessToken Mapbox Access Token
+ */
+ private MapboxAccountManager(Context applicationContext, String accessToken) {
+ super();
+ this.applicationContext = applicationContext.getApplicationContext();
+ this.accessToken = accessToken;
+ }
+
+ /**
+ * Primary entry point to Mapbox for implementing developers.
+ * Must be configured in either Application.onCreate() or Launch Activity.onCreate()
+ *
+ * @param context Context used to get Application Context
+ * @param accessToken Mapbox Access Token. You can get one on the Mapbox Web site.
+ * @return MapboxAccountManager instance for app
+ */
+ public static MapboxAccountManager start(Context context, String accessToken) {
+ if (mapboxAccountManager == null) {
+ mapboxAccountManager = new MapboxAccountManager(context, accessToken);
+ }
+ MapboxEventManager eventManager = MapboxEventManager.getMapboxEventManager();
+ eventManager.initialize(mapboxAccountManager.applicationContext, mapboxAccountManager.accessToken);
+ return mapboxAccountManager;
+ }
+
+ /**
+ * Internal Use Only
+ * Get an instance of MapboxAccountManager configured with the app's Access Token
+ *
+ * @return MapboxAccountManager instance for app. May be NULL if not configured yet.
+ */
+ public static MapboxAccountManager getInstance() {
+ return mapboxAccountManager;
+ }
+
+ /**
+ * Access Token for this application
+ * @return Mapbox Access Token
+ */
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ /**
+ * Runtime validation of Access Token
+ *
+ * @param accessToken Access Token to check
+ * @throws InvalidAccessTokenException
+ */
+ public static void validateAccessToken(String accessToken) throws InvalidAccessTokenException {
+ if (TextUtils.isEmpty(accessToken) || (!accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("pk.") && !accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("sk."))) {
+ throw new InvalidAccessTokenException();
+ }
+ }
+}
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
index 114e809b9e..36d56591c8 100644
--- 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
@@ -22,7 +22,7 @@ public abstract class Annotation implements Comparable<Annotation> {
* Internal C++ id is stored as unsigned int.
*/
private long id = -1; // -1 unless added to a MapView
- private MapboxMap mapboxMap;
+ protected MapboxMap mapboxMap;
protected Annotation() {
}
@@ -75,19 +75,15 @@ public abstract class Annotation implements Comparable<Annotation> {
} else if (id > annotation.getId()) {
return -1;
}
-
- // Equal
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
+ if (o == null || !(o instanceof Annotation)) return false;
Annotation that = (Annotation) o;
-
- return getId() == that.getId();
+ return id == that.getId();
}
@Override
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
index fe8bf67920..fc2022f9e1 100644
--- 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
@@ -6,7 +6,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
/**
* Abstract builder class for composing custom Marker objects.
- * <p/>
+ *
* Extending this class requires implementing Parceable interface.
*
* @param <U> Type of the marker to be composed
@@ -43,4 +43,4 @@ public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOp
public abstract U getMarker();
-} \ No newline at end of file
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
new file mode 100644
index 0000000000..0cd54fc0f0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
@@ -0,0 +1,295 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import android.os.Parcelable;
+import android.support.annotation.AnimatorRes;
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+/**
+ * Abstract builder class for composing custom MarkerView objects.
+ * <p>
+ * Extending this class requires implementing Parceable interface.
+ * </p>
+ *
+ * @param <U> Type of the marker view to be composed
+ * @param <T> Type of the builder to be used for composing
+ */
+public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends BaseMarkerViewOptions<U, T>> implements Parcelable {
+
+ protected LatLng position;
+ protected String snippet;
+ protected String title;
+ protected Icon icon;
+ protected boolean flat;
+ protected float anchorU = 0.5f;
+ protected float anchorV = 1f;
+ protected float infoWindowAnchorU = 0.5f;
+ protected float infoWindowAnchorV = 0.0f;
+ protected int selectAnimRes;
+ protected int deselectAnimRes;
+ protected int rotation;
+ protected boolean visible = true;
+
+ /**
+ * Default constructor
+ */
+ public BaseMarkerViewOptions() {
+ }
+
+ /**
+ * Set the geographical location of the MarkerView.
+ *
+ * @param position the location to position the MarkerView
+ * @return the object for which the method was called
+ */
+ public T position(@NonNull LatLng position) {
+ this.position = position;
+ return getThis();
+ }
+
+ /**
+ * Set the snippet of the MarkerView.
+ *
+ * @param snippet the snippet of the MarkerView
+ * @return the object for which the method was called
+ */
+ public T snippet(String snippet) {
+ this.snippet = snippet;
+ return getThis();
+ }
+
+ /**
+ * Set the title of the MarkerView.
+ *
+ * @param title the title of the MarkerView
+ * @return the object for which the method was called
+ */
+ public T title(String title) {
+ this.title = title;
+ return getThis();
+ }
+
+ /**
+ * Set the icon of the MarkerView.
+ *
+ * @param icon the icon of the MarkerView
+ * @return the object for which the method was called
+ */
+ public T icon(Icon icon) {
+ this.icon = icon;
+ return getThis();
+ }
+
+ /**
+ * Set the flat state of the MarkerView.
+ *
+ * @param flat the flat state of the MarkerView
+ * @return the object for which the method was called
+ */
+ public T flat(boolean flat) {
+ this.flat = flat;
+ return getThis();
+ }
+
+ /**
+ * Set the anchor of the MarkerView.
+ *
+ * @param u the u-value
+ * @param v the v-value
+ * @return the object for which the method was called
+ */
+ public T anchor(float u, float v) {
+ this.anchorU = u;
+ this.anchorV = v;
+ return getThis();
+ }
+
+ /**
+ * Set the InfoWindow anchor of the MarkerView.
+ *
+ * @param u the u-value
+ * @param v the v-values
+ * @return the object for which the method was called
+ */
+ public T infoWindowAnchor(float u, float v) {
+ this.infoWindowAnchorU = u;
+ this.infoWindowAnchorV = v;
+ return getThis();
+ }
+
+ /**
+ * Set the animator resource to be used when an MarkerView is selected.
+ *
+ * @param selectAnimRes the used animator resource
+ * @return the object for which the method was called
+ */
+ public T selectAnimatorResource(@AnimatorRes int selectAnimRes) {
+ this.selectAnimRes = selectAnimRes;
+ return getThis();
+ }
+
+ /**
+ * Set the animator resource to be used when an MarkerView is deselected.
+ *
+ * @param deselectAnimRes the used animator resource
+ * @return the object for which the method was called
+ */
+ public T deselectAnimatorResource(@AnimatorRes int deselectAnimRes) {
+ this.deselectAnimRes = deselectAnimRes;
+ return getThis();
+ }
+
+ /**
+ * Set the rotation of the MarkerView.
+ *
+ * @param rotation the rotation value
+ * @return the object for which the method was called
+ */
+ public T rotation(int rotation) {
+ this.rotation = rotation;
+ return getThis();
+ }
+
+ /**
+ * Set the visibility state of the MarkerView.
+ *
+ * @param visible the visible state
+ * @return the object for which the method was calleds
+ */
+ public T visible(boolean visible) {
+ this.visible = visible;
+ return getThis();
+ }
+
+ /**
+ * Get the geographical location of the MarkerView.
+ *
+ * @return the geographical location
+ */
+ public LatLng getPosition() {
+ return position;
+ }
+
+ /**
+ * Get the snippet of the MarkerView.
+ *
+ * @return the snippet
+ */
+ public String getSnippet() {
+ return snippet;
+ }
+
+ /**
+ * Get the title of the MarkerView.
+ *
+ * @return the title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Get the icon of the MarkerView.
+ *
+ * @return the icon
+ */
+ public Icon getIcon() {
+ return icon;
+ }
+
+ /**
+ * Get the flat state of the MarkerView.
+ *
+ * @return the flat state
+ */
+ public boolean isFlat() {
+ return flat;
+ }
+
+ /**
+ * Get the u-value of the MarkerView anchor.
+ *
+ * @return the u-value
+ */
+ public float getAnchorU() {
+ return anchorU;
+ }
+
+ /**
+ * Get the v-value of the MarkerView anchor.
+ *
+ * @return the v-value
+ */
+ public float getAnchorV() {
+ return anchorV;
+ }
+
+ /**
+ * Get the u-value of the MarkerView InfoWindow anchor.
+ *
+ * @return the u-value
+ */
+ public float getInfoWindowAnchorU() {
+ return infoWindowAnchorU;
+ }
+
+ /**
+ * Get the v-value of the MarkerView InfoWindow anchor.
+ *
+ * @return the v-value
+ */
+ public float getInfoWindowAnchorV() {
+ return infoWindowAnchorV;
+ }
+
+ /**
+ * Get the animator resource used for selecting the MarkerView.
+ *
+ * @return the animator resource
+ */
+ public int getSelectAnimRes() {
+ return selectAnimRes;
+ }
+
+ /**
+ * Get the animator resource used for deselecting the MarkerView.
+ *
+ * @return the animator resource
+ */
+ public int getDeselectAnimRes() {
+ return deselectAnimRes;
+ }
+
+ /**
+ * Get the rotation of the MarkerView.
+ *
+ * @return the rotation value
+ */
+ public int getRotation() {
+ return rotation;
+ }
+
+ /**
+ * Get the visibility state of the MarkerView.
+ *
+ * @return the visibility state
+ */
+ public boolean isVisible() {
+ return visible;
+ }
+
+ /**
+ * 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 MarkerView.
+ *
+ * @return the MarkerView created from this builder
+ */
+ public abstract U getMarker();
+}
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
index e30b81d4c9..fceeb52713 100644
--- 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
@@ -6,9 +6,10 @@ import com.mapbox.mapboxsdk.maps.MapView;
/**
* Icon is the visual representation of a {@link Marker} on a {@link MapView}.
+ *
* @see Marker
*/
-public final class Icon {
+public class Icon {
private Bitmap mBitmap;
private String mId;
@@ -39,8 +40,13 @@ public final class Icon {
@Override
public int hashCode() {
- int result = mBitmap.hashCode();
- result = 31 * result + mId.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/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
index c1fc9bb0ab..7452ab8fac 100644
--- 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
@@ -30,6 +30,7 @@ public class InfoWindow {
protected WeakReference<View> mView;
private float mMarkerHeightOffset;
+ private float mMarkerWidthOffset;
private float mViewWidthOffset;
private PointF mCoordinates;
private boolean mIsVisible;
@@ -108,9 +109,9 @@ public class InfoWindow {
// Calculate y-offset for update method
mMarkerHeightOffset = -view.getMeasuredHeight() + offsetY;
+ mMarkerWidthOffset = -offsetX;
// Calculate default Android x,y coordinate
-
mCoordinates = mapboxMap.getProjection().toScreenLocation(position);
float x = mCoordinates.x - (view.getMeasuredWidth() / 2) + offsetX;
float y = mCoordinates.y - view.getMeasuredHeight() + offsetY;
@@ -244,7 +245,7 @@ public class InfoWindow {
View view = mView.get();
if (mapboxMap != null && marker != null && view != null) {
mCoordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
- view.setX(mCoordinates.x + mViewWidthOffset);
+ view.setX(mCoordinates.x + mViewWidthOffset - mMarkerWidthOffset);
view.setY(mCoordinates.y + mMarkerHeightOffset);
}
}
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
index c2683cbb56..16b219684f 100644
--- 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
@@ -4,16 +4,16 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
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.
- * </p>
+ * <p>
* An {@link InfoWindow} can be shown when a Marker is pressed
- * <p/>
+ * </p>
*/
public class Marker extends Annotation {
@@ -21,9 +21,12 @@ public class Marker extends Annotation {
private String snippet;
private Icon icon;
private String title;
- private InfoWindow infoWindow = null;
- private boolean infoWindowShown = false;
+
+ private InfoWindow infoWindow;
+ private boolean infoWindowShown;
+
private int topOffsetPixels;
+ private int rightOffsetPixels;
/**
* Constructor
@@ -39,6 +42,13 @@ public class Marker extends Annotation {
title = baseMarkerOptions.title;
}
+ Marker(BaseMarkerViewOptions baseMarkerViewOptions){
+ position = baseMarkerViewOptions.position;
+ snippet = baseMarkerViewOptions.snippet;
+ icon = baseMarkerViewOptions.icon;
+ title = baseMarkerViewOptions.title;
+ }
+
public LatLng getPosition() {
return position;
}
@@ -130,7 +140,7 @@ public class Marker extends Annotation {
}
private InfoWindow showInfoWindow(InfoWindow iw, MapView mapView) {
- iw.open(mapView, this, getPosition(), 0, topOffsetPixels);
+ iw.open(mapView, this, getPosition(), rightOffsetPixels, topOffsetPixels);
infoWindowShown = true;
return iw;
}
@@ -149,21 +159,11 @@ public class Marker extends Annotation {
this.topOffsetPixels = topOffsetPixels;
}
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
-
- Marker marker = (Marker) o;
- return !(getPosition() != null ? !getPosition().equals(marker.getPosition()) : marker.getPosition() != null);
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0);
- return result;
+ /**
+ * Do not use this method. Used internally by the SDK.
+ */
+ public void setRightOffsetPixels(int rightOffsetPixels) {
+ this.rightOffsetPixels = rightOffsetPixels;
}
@Override
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
index a3df23d96f..7ca3687b0d 100644
--- 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
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.annotations;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
-import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -12,9 +11,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
* <p>
* Builder for composing {@link com.mapbox.mapboxsdk.annotations.Marker} objects.
* </p>
- * <p/>
* <h3>Example</h3>
- * <p/>
* <pre>
* mMapView.addMarker(new MarkerOptions()
* .title("Intersection")
@@ -24,30 +21,24 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
*/
public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions> implements Parcelable {
- public static final Parcelable.Creator<MarkerOptions> CREATOR
- = new Parcelable.Creator<MarkerOptions>() {
- public MarkerOptions createFromParcel(Parcel in) {
- return new MarkerOptions(in);
- }
+ private Marker marker;
- public MarkerOptions[] newArray(int size) {
- return new MarkerOptions[size];
- }
- };
+ public MarkerOptions() {
+ marker = new Marker();
+ }
- private MarkerOptions(Parcel in) {
+ protected MarkerOptions(Parcel in) {
marker = new Marker();
position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
snippet(in.readString());
-
- if(in.readByte()!=0){
+ 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);
}
- title(in.readString());
}
@Override
@@ -57,26 +48,20 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions
@Override
public int describeContents() {
- return hashCode();
+ return 0;
}
@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);
}
- out.writeString(getTitle());
- }
-
- private Marker marker;
-
- public MarkerOptions() {
- marker = new Marker();
}
/**
@@ -108,6 +93,17 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions
return icon;
}
+ public static final Parcelable.Creator<MarkerOptions> CREATOR
+ = new Parcelable.Creator<MarkerOptions>() {
+ public MarkerOptions createFromParcel(Parcel in) {
+ return new MarkerOptions(in);
+ }
+
+ public MarkerOptions[] newArray(int size) {
+ return new MarkerOptions[size];
+ }
+ };
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
new file mode 100644
index 0000000000..3e51044643
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
@@ -0,0 +1,341 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+
+/**
+ * MarkerView is an annotation that shows an View at a geographical location.
+ * <p>
+ * This class uses {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter} to adapt a
+ * MarkerView model to an Android SDK {@link android.view.View} object.
+ * </p>
+ * <p>
+ * An {@link InfoWindow} can be shown when a MarkerView is pressed
+ * </p>
+ */
+public class MarkerView extends Marker {
+
+ private MarkerViewManager markerViewManager;
+
+ private float anchorU;
+ private float anchorV;
+
+ private float offsetX;
+ private float offsetY;
+
+ private float infoWindowAnchorU;
+ private float infoWindowAnchorV;
+
+ private boolean flat;
+ private boolean visible = true;
+
+ private int selectAnimRes;
+ private int deselectAnimRes;
+
+ private float tiltValue;
+ private float rotation;
+ private float alpha = 1;
+
+ /**
+ * Publicly hidden default constructor
+ */
+ MarkerView() {
+ }
+
+ /**
+ * Creates a instance of MarkerView using the builder of MarkerView
+ *
+ * @param baseMarkerViewOptions the builder used to construct the MarkerView
+ */
+ public MarkerView(BaseMarkerViewOptions baseMarkerViewOptions) {
+ super(baseMarkerViewOptions);
+ this.anchorU = baseMarkerViewOptions.getAnchorU();
+ this.anchorV = baseMarkerViewOptions.getAnchorV();
+ this.infoWindowAnchorU = baseMarkerViewOptions.getInfoWindowAnchorU();
+ this.infoWindowAnchorV = baseMarkerViewOptions.getInfoWindowAnchorV();
+ this.flat = baseMarkerViewOptions.isFlat();
+ this.selectAnimRes = baseMarkerViewOptions.getSelectAnimRes();
+ this.deselectAnimRes = baseMarkerViewOptions.getDeselectAnimRes();
+ this.infoWindowAnchorU = baseMarkerViewOptions.infoWindowAnchorU;
+ this.infoWindowAnchorV = baseMarkerViewOptions.infoWindowAnchorV;
+ this.anchorU = baseMarkerViewOptions.anchorU;
+ this.anchorV = baseMarkerViewOptions.anchorV;
+ }
+
+ /**
+ * Specifies the anchor being set on a particular point point of the MarkerView.
+ * <p>
+ * The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0)
+ * is the top-left corner of the image, and (1, 1) is the bottom-right corner.
+ * </p>
+ *
+ * @param u u-coordinate of the anchor, as a ratio of the image width (in the range [0, 1])
+ * @param v v-coordinate of the anchor, as a ratio of the image height (in the range [0, 1])
+ */
+ public void setAnchor(float u, float v) {
+ this.anchorU = u;
+ this.anchorV = v;
+ }
+
+ /**
+ * Get the horizontal distance, normalized to [0, 1], of the anchor from the left edge.
+ *
+ * @return the u-value of the anchor
+ */
+ public float getAnchorU() {
+ return anchorU;
+ }
+
+ /**
+ * Get the vertical distance, normalized to [0, 1], of the anchor from the top edge.
+ *
+ * @return the v-value of the anchor
+ */
+ public float getAnchorV() {
+ return anchorV;
+ }
+
+ /**
+ * Internal method to set the horizontal calculated offset.
+ * <p>
+ * These are calculated based on the View bounds and the provided anchor.
+ * </p>
+ *
+ * @param x the x-value of the offset
+ */
+ void setOffsetX(float x) {
+ offsetX = x;
+ }
+
+ /**
+ * Internal method to set the vertical calculated offset.
+ * <p>
+ * These are calculated based on the View bounds and the provided anchor.
+ * </p>
+ *
+ * @param y the y-value of the offset
+ */
+ void setOffsetY(float y) {
+ offsetY = y;
+ }
+
+ /**
+ * Internal method to get the horizontal calculated offset
+ *
+ * @return the calculated horizontal offset
+ */
+ float getOffsetX() {
+ return offsetX;
+ }
+
+ /**
+ * Internal method to get the vertical calculated offset
+ *
+ * @return the calculated vertical offset
+ */
+ float getOffsetY() {
+ return offsetY;
+ }
+
+ /**
+ * Specifies the anchor point of the info window on the View of the MarkerView.
+ * <p>
+ * This is specified in the same coordinate system as the anchor.
+ * </p>
+ * <p>
+ * The default is the top middle of the View.
+ * </p>
+ *
+ * @param u u-coordinate of the info window anchor, as a ratio of the image width (in the range [0, 1])
+ * @param v v-coordinate of the info window anchor, as a ratio of the image height (in the range [0, 1])
+ * @see #setAnchor(float, float) for more details.
+ */
+ public void setInfoWindowAnchor(float u, float v) {
+ this.infoWindowAnchorU = u;
+ this.infoWindowAnchorV = v;
+ }
+
+ /**
+ * Get the horizontal distance, normalized to [0, 1], of the info window anchor from the left edge.
+ *
+ * @return the u value of the InfoWindow anchor.
+ */
+ public float getInfoWindowAnchorU() {
+ return infoWindowAnchorU;
+ }
+
+ /**
+ * Get the vertical distance, normalized to [0, 1], of the info window anchor from the top edge.
+ *
+ * @return the v value of the InfoWindow anchor.
+ */
+ public float getInfoWindowAnchorV() {
+ return infoWindowAnchorV;
+ }
+
+ /**
+ * Get the flat state of a MarkerView.
+ *
+ * @return true is the MarkerView is flat; false is the MarkerView is billboard
+ */
+ public boolean isFlat() {
+ return flat;
+ }
+
+ /**
+ * Sets whether this marker should be flat against the map true or a billboard facing the camera false.
+ *
+ * @param flat the flat state of the MarkerView
+ */
+ public void setFlat(boolean flat) {
+ this.flat = flat;
+ }
+
+ /**
+ * Get the animator resource used to animate to the selected state of a MarkerView.
+ *
+ * @return the animator resource used
+ */
+ public int getSelectAnimRes() {
+ return selectAnimRes;
+ }
+
+ /**
+ * Set the animator resource used to animate to the deselected state of a MarkerView.
+ *
+ * @param selectAnimRes the animator resource used
+ */
+ public void setSelectAnimRes(int selectAnimRes) {
+ this.selectAnimRes = selectAnimRes;
+ }
+
+ /**
+ * Get the animator resource used to animate to the deslected state of a MarkerView.
+ *
+ * @return the animator resource used
+ */
+ public int getDeselectAnimRes() {
+ return deselectAnimRes;
+ }
+
+ /**
+ * Set the animator resource used to animate to the selected state of a MarkerView.
+ *
+ * @param deselectAnimRes the animator resource used
+ */
+ public void setDeselectAnimRes(int deselectAnimRes) {
+ this.deselectAnimRes = deselectAnimRes;
+ }
+
+ /**
+ * Internal method to get the current tilted value of a MarkerView.
+ *
+ * @return the tilted value
+ */
+ float getTilt() {
+ return tiltValue;
+ }
+
+ /**
+ * Internal method to set the current titled value of a MarkerView.
+ *
+ * @param tiltValue the tilted value to set
+ */
+ void setTilt(float tiltValue) {
+ this.tiltValue = tiltValue;
+ }
+
+ /**
+ * Set the visible state of a MarkerView.
+ *
+ * @param visible true will make the MarkerView visible, false will hide the MarkerViews
+ */
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ if (markerViewManager != null) {
+ markerViewManager.animateVisible(this, visible);
+ }
+ }
+
+ /**
+ * Returns the visible state of the MarkerView.
+ *
+ * @return the visible state
+ */
+ public boolean isVisible() {
+ return visible;
+ }
+
+ /**
+ * Set the rotation value of the MarkerView.
+ * <p>
+ * This will result in animating the rotation of the MarkerView using an rotation animator
+ * from current value to the provided parameter value.
+ * </p>
+ *
+ * @param rotation the rotation value to animate to
+ */
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ if (markerViewManager != null) {
+ markerViewManager.animateRotation(this, rotation);
+ }
+ }
+
+ /**
+ * Get the rotation value of the MarkerView.
+ *
+ * @return the rotation value
+ */
+ public float getRotation() {
+ return rotation;
+ }
+
+ /**
+ * Get the alpha value of the MarkerView.
+ *
+ * @return the alpha value
+ */
+ public float getAlpha() {
+ return alpha;
+ }
+
+ /**
+ * Set the alpha value of the MarkerView.
+ * <p>
+ * This will result in animating the alpha of the MarkerView using an alpha animator
+ * from current value to the provided parameter value.
+ * </p>
+ *
+ * @param alpha the alpha value to animate to
+ */
+ public void setAlpha(float alpha) {
+ this.alpha = alpha;
+ if (markerViewManager != null) {
+ markerViewManager.animateAlpha(this, alpha);
+ }
+ }
+
+ /**
+ * Set the MapboxMap associated tot the MapView containing the MarkerView.
+ * <p>
+ * This method is used to instantiate the MarkerView and provide an instance of {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter}
+ * </p>
+ *
+ * @param mapboxMap the MapboxMap instances
+ */
+ @Override
+ public void setMapboxMap(MapboxMap mapboxMap) {
+ super.setMapboxMap(mapboxMap);
+ markerViewManager = mapboxMap.getMarkerViewManager();
+ }
+
+ /**
+ * Get the String representation of a MarkerView.
+ *
+ * @return the String representation
+ */
+ @Override
+ public String toString() {
+ return "MarkerView [position[" + getPosition() + "]]";
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
new file mode 100644
index 0000000000..d9fc9e62ae
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
@@ -0,0 +1,352 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import android.graphics.PointF;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.Pools;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.utils.AnimatorUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface for interacting with ViewMarkers objects inside of a MapView.
+ * <p>
+ * This class is responsible for managing a {@link MarkerView} item.
+ * </p>
+ */
+public class MarkerViewManager {
+
+ private Map<MarkerView, View> mMarkerViewMap;
+ private MapboxMap mapboxMap;
+ private MapView mapView;
+ private List<MapboxMap.MarkerViewAdapter> markerViewAdapters;
+ private long mViewMarkerBoundsUpdateTime;
+ private MapboxMap.OnMarkerViewClickListener onMarkerViewClickListener;
+
+ /**
+ * Creates an instance of MarkerViewManager.
+ *
+ * @param mapboxMap the MapboxMap associated with the MarkerViewManager
+ * @param mapView the MapView associated with the MarkerViewManager
+ */
+ public MarkerViewManager(@NonNull MapboxMap mapboxMap, @NonNull MapView mapView) {
+ this.mapboxMap = mapboxMap;
+ this.markerViewAdapters = new ArrayList<>();
+ this.mapView = mapView;
+ mMarkerViewMap = new HashMap<>();
+ }
+
+ /**
+ * Animate a MarkerView to a given rotation.
+ * <p>
+ * The {@link MarkerView} will be rotated from its current rotation to the given rotation.
+ * </p>
+ *
+ * @param marker the MarkerView to rotate
+ * @param rotation the rotation value
+ */
+ public void animateRotation(@NonNull MarkerView marker, float rotation) {
+ View convertView = mMarkerViewMap.get(marker);
+ if (convertView != null) {
+ AnimatorUtils.rotate(convertView, rotation);
+ }
+ }
+
+ /**
+ * Animate a MarkerView to a given alpha value.
+ * <p>
+ * The {@link MarkerView} will be transformed from its current alpha value to the given value.
+ * </p>
+ *
+ * @param marker the MarkerView to change its alpha value
+ * @param alpha the alpha value
+ */
+ public void animateAlpha(@NonNull MarkerView marker, float alpha) {
+ View convertView = mMarkerViewMap.get(marker);
+ if (convertView != null) {
+ AnimatorUtils.alpha(convertView, alpha);
+ }
+ }
+
+ /**
+ * Animate a MarkerVIew to be visible or invisible
+ * <p>
+ * The {@link MarkerView} will be made {@link View#VISIBLE} or {@link View#GONE}.
+ * </p>
+ *
+ * @param marker the MarkerView to change its visibility
+ * @param visible the flag indicating if MarkerView is visible
+ */
+ public void animateVisible(@NonNull MarkerView marker, boolean visible) {
+ View convertView = mMarkerViewMap.get(marker);
+ if (convertView != null) {
+ convertView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /**
+ * Updates the position of MarkerViews currently found in the viewport.
+ * <p>
+ * The collection of {@link MarkerView} will be iterated and each item position will be updated.
+ * If an item is View state is not visible and its related flag is set to visible,
+ * The {@link MarkerView} will be animated to visible using alpha animation.
+ * </p>
+ */
+ public void update() {
+ View convertView;
+ for (MarkerView marker : mMarkerViewMap.keySet()) {
+ convertView = mMarkerViewMap.get(marker);
+ if (convertView != null) {
+ PointF point = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
+ int x = (int) (marker.getAnchorU() * convertView.getMeasuredWidth());
+ int y = (int) (marker.getAnchorV() * convertView.getMeasuredHeight());
+
+ marker.setOffsetX(x);
+ marker.setOffsetY(y);
+
+ convertView.setX(point.x - x);
+ convertView.setY(point.y - y);
+
+ if (marker.isVisible() && convertView.getVisibility() == View.GONE) {
+ convertView.animate().cancel();
+ convertView.setAlpha(0);
+ AnimatorUtils.alpha(convertView, 1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set tilt on every non flat MarkerView currently shown in the Viewport.
+ *
+ * @param tilt the tilt value
+ */
+ public void setTilt(float tilt) {
+ View convertView;
+ for (MarkerView markerView : mMarkerViewMap.keySet()) {
+ if (markerView.isFlat()) {
+ convertView = mMarkerViewMap.get(markerView);
+ if (convertView != null) {
+ markerView.setTilt(tilt);
+ convertView.setRotationX(tilt);
+ }
+ }
+ }
+ }
+
+ /**
+ * Animate a MarkerView to a deselected state.
+ * <p>
+ * The {@link MarkerView#getDeselectAnimRes()} will be called to get the related animation.
+ * If non are provided, no animation will be started.
+ * </p>
+ *
+ * @param marker the MarkerView to deselect
+ */
+ public void deselect(@NonNull MarkerView marker) {
+ final View convertView = mMarkerViewMap.get(marker);
+ if (convertView != null) {
+ int deselectAnimatorRes = marker.getDeselectAnimRes();
+ if (deselectAnimatorRes != 0) {
+ AnimatorUtils.animate(convertView, deselectAnimatorRes);
+ }
+ }
+ }
+
+ /**
+ * Remove a MarkerView from a map.
+ * <p>
+ * The {@link MarkerView} will be removed using an alpha animation and related {@link View}
+ * will be released to the {@link android.support.v4.util.Pools.SimplePool} from the related
+ * {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter}. It's possible to remove
+ * the {@link MarkerView} from the underlying collection if needed.
+ * </p>
+ *
+ * @param marker the MarkerView to remove
+ * @param removeFromMap flag indicating if a MarkerView will be removed from the collection.
+ */
+ public void removeMarkerView(MarkerView marker, boolean removeFromMap) {
+ final View viewHolder = mMarkerViewMap.get(marker);
+ if (viewHolder != null && marker != null) {
+ for (final MapboxMap.MarkerViewAdapter<?> adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass() == marker.getClass()) {
+
+ // get pool of Views associated to an adapter
+ final Pools.SimplePool<View> viewPool = adapter.getViewReusePool();
+
+ // cancel ongoing animations
+ viewHolder.animate().cancel();
+ viewHolder.setAlpha(1);
+ AnimatorUtils.alpha(viewHolder, 0, new AnimatorUtils.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd() {
+ viewHolder.setVisibility(View.GONE);
+ viewPool.release(viewHolder);
+ }
+ });
+ }
+ }
+ }
+ if (removeFromMap) {
+ mMarkerViewMap.remove(marker);
+ }
+ }
+
+ /**
+ * Add a MarkerViewAdapter.
+ *
+ * @param markerViewAdapter the MarkerViewAdapter to add
+ */
+ public void addMarkerViewAdapter(@Nullable MapboxMap.MarkerViewAdapter markerViewAdapter) {
+ if (!markerViewAdapters.contains(markerViewAdapter)) {
+ markerViewAdapters.add(markerViewAdapter);
+ invalidateViewMarkersInBounds();
+ }
+ }
+
+ /**
+ * Get all MarkerViewAdapters associated with this MarkerViewManager.
+ *
+ * @return a List of MarkerViewAdapters
+ */
+ public List<MapboxMap.MarkerViewAdapter> getMarkerViewAdapters() {
+ return markerViewAdapters;
+ }
+
+
+ /**
+ * Register a callback to be invoked when this view is clicked.
+ *
+ * @param listener the callback to be invoked
+ */
+ public void setOnMarkerViewClickListener(@Nullable MapboxMap.OnMarkerViewClickListener listener) {
+ onMarkerViewClickListener = listener;
+ }
+
+ /**
+ * Schedule that ViewMarkers found in the viewport are invalidated.
+ * <p>
+ * This method is rate limited, and {@link #invalidateViewMarkersInBounds} will only be called
+ * once each 250 ms.
+ * </p>
+ */
+ public void scheduleViewMarkerInvalidation() {
+ if (!markerViewAdapters.isEmpty()) {
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < mViewMarkerBoundsUpdateTime) {
+ return;
+ }
+ invalidateViewMarkersInBounds();
+ mViewMarkerBoundsUpdateTime = currentTime + 250;
+ }
+ }
+
+ /**
+ * Invalidate the ViewMarkers found in the viewport.
+ * <p>
+ * This method will remove any markers that aren't in the viewport any more and will add new
+ * ones for each found Marker in the changed viewport.
+ * </p>
+ */
+ public void invalidateViewMarkersInBounds() {
+ Projection projection = mapboxMap.getProjection();
+ List<MarkerView> markers = mapView.getMarkerViewsInBounds(projection.getVisibleRegion().latLngBounds);
+ View convertView;
+
+ // remove old markers
+ Iterator<MarkerView> iterator = mMarkerViewMap.keySet().iterator();
+ while (iterator.hasNext()) {
+ MarkerView m = iterator.next();
+ if (!markers.contains(m)) {
+ // remove marker
+ convertView = mMarkerViewMap.get(m);
+ int deselectAnimRes = m.getDeselectAnimRes();
+ if (deselectAnimRes != 0) {
+ AnimatorUtils.animate(convertView, deselectAnimRes, 0);
+ }
+ removeMarkerView(m, false);
+ iterator.remove();
+ }
+ }
+
+ // introduce new markers
+ for (final MarkerView marker : markers) {
+ if (!mMarkerViewMap.containsKey(marker)) {
+ for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass() == marker.getClass()) {
+ convertView = (View) adapter.getViewReusePool().acquire();
+ final View adaptedView = adapter.getView(marker, convertView, mapView);
+ if (adaptedView != null) {
+
+ // tilt
+ adaptedView.setRotationX(marker.getTilt());
+
+ // rotation
+ adaptedView.setRotation(marker.getRotation());
+
+ // alpha
+ adaptedView.setAlpha(marker.getAlpha());
+
+ // visible
+ adaptedView.setVisibility(marker.isVisible() ? View.VISIBLE : View.GONE);
+
+ if (mapboxMap.getSelectedMarkers().contains(marker)) {
+ // if a marker to be shown was selected
+ // replay that animation with duration 0
+ int selectAnimRes = marker.getSelectAnimRes();
+ if (selectAnimRes != 0) {
+ AnimatorUtils.animate(convertView, selectAnimRes, 0);
+ }
+ }
+
+ final int animSelectRes = marker.getSelectAnimRes();
+ adaptedView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ boolean clickHandled = false;
+ if (onMarkerViewClickListener != null) {
+ clickHandled = onMarkerViewClickListener.onMarkerClick(marker, v, adapter);
+ }
+
+ if (!clickHandled) {
+ // InfoWindow offset
+ int infoWindowOffsetX = (int) ((adaptedView.getWidth() * marker.getInfoWindowAnchorU()) - marker.getOffsetX());
+ int infoWindowOffsetY = (int) ((adaptedView.getHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY());
+ marker.setTopOffsetPixels(infoWindowOffsetY);
+ marker.setRightOffsetPixels(infoWindowOffsetX);
+
+ if (animSelectRes != 0) {
+ AnimatorUtils.animate(v, animSelectRes, new AnimatorUtils.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd() {
+ mapboxMap.selectMarker(marker);
+ }
+ });
+ } else {
+ mapboxMap.selectMarker(marker);
+ }
+ }
+ }
+ });
+
+ mMarkerViewMap.put(marker, adaptedView);
+ if (convertView == null) {
+ mapView.addView(adaptedView);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
new file mode 100644
index 0000000000..0c9faed355
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
@@ -0,0 +1,115 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+/**
+ * builder class for composing MarkerView objects.
+ * <p>
+ * Do not extend this class directly but extend BaseMarkerViewOptions instead.
+ * </p>
+ */
+public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerViewOptions> {
+
+ private MarkerView marker;
+
+ public MarkerViewOptions() {
+ marker = new MarkerView();
+ }
+
+ protected MarkerViewOptions(Parcel in) {
+ marker = new MarkerView();
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ title(in.readString());
+ flat(in.readByte() != 0);
+ anchor(in.readFloat(), in.readFloat());
+ infoWindowAnchor(in.readFloat(), in.readFloat());
+ selectAnimatorResource(in.readInt());
+ deselectAnimatorResource(in.readInt());
+ rotation(in.readInt());
+ visible(in.readByte() != 0);
+ 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);
+ }
+ }
+
+ @Override
+ public MarkerViewOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(getPosition(), flags);
+ out.writeString(getSnippet());
+ out.writeString(getTitle());
+ out.writeByte((byte) (isFlat() ? 1 : 0));
+ out.writeFloat(getAnchorU());
+ out.writeFloat(getAnchorV());
+ out.writeFloat(getInfoWindowAnchorU());
+ out.writeFloat(getInfoWindowAnchorV());
+ out.writeInt(getSelectAnimRes());
+ out.writeInt(getDeselectAnimRes());
+ out.writeInt(getRotation());
+ out.writeByte((byte) (isVisible() ? 1 : 0));
+ Icon icon = getIcon();
+ out.writeByte((byte) (icon != null ? 1 : 0));
+ if (icon != null) {
+ out.writeString(getIcon().getId());
+ out.writeParcelable(getIcon().getBitmap(), flags);
+ }
+ }
+
+ @Override
+ public MarkerView getMarker() {
+ marker.setPosition(position);
+ marker.setSnippet(snippet);
+ marker.setTitle(title);
+ marker.setIcon(icon);
+ marker.setFlat(flat);
+ marker.setAnchor(anchorU, anchorV);
+ marker.setInfoWindowAnchor(infoWindowAnchorU, infoWindowAnchorV);
+ marker.setSelectAnimRes(selectAnimRes);
+ marker.setDeselectAnimRes(deselectAnimRes);
+ marker.setRotation(rotation);
+ marker.setVisible(visible);
+ return marker;
+ }
+
+ public static final Parcelable.Creator<MarkerViewOptions> CREATOR
+ = new Parcelable.Creator<MarkerViewOptions>() {
+ public MarkerViewOptions createFromParcel(Parcel in) {
+ return new MarkerViewOptions(in);
+ }
+
+ public MarkerViewOptions[] newArray(int size) {
+ return new MarkerViewOptions[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MarkerViewOptions that = (MarkerViewOptions) o;
+ return marker != null ? marker.equals(that.marker) : that.marker == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return marker != null ? marker.hashCode() : 0;
+ }
+}
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
index 679a474ae7..f9346e21d6 100644
--- 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
@@ -70,7 +70,7 @@ public final class CameraPosition implements Parcelable {
@Override
public int describeContents() {
- return hashCode();
+ return 0;
}
@Override
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
index 763ab8d664..028d077a09 100644
--- 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
@@ -56,7 +56,6 @@ public final class CameraUpdateFactory {
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.
@@ -227,11 +226,18 @@ public final class CameraUpdateFactory {
// Get required objects
Projection projection = mapboxMap.getProjection();
UiSettings uiSettings = mapboxMap.getUiSettings();
- RectF padding = getPadding();
+
+ // calculate correct padding
+ int[] mapPadding = mapboxMap.getPadding();
+ RectF latLngPadding = getPadding();
+ RectF padding = new RectF(latLngPadding.left + mapPadding[0],
+ latLngPadding.top + mapPadding[1],
+ latLngPadding.right + mapPadding[2],
+ latLngPadding.bottom + mapPadding[3]);
// Calculate the bounds of the possibly rotated shape with respect to the viewport
- PointF nePixel = new PointF(-10000, -10000);
- PointF swPixel = new PointF(10000, 10000);
+ PointF nePixel = new PointF(-Float.MAX_VALUE, -Float.MAX_VALUE);
+ PointF swPixel = new PointF(Float.MAX_VALUE, Float.MAX_VALUE);
float viewportHeight = uiSettings.getHeight();
for (LatLng latLng : getBounds().toLatLngs()) {
PointF pixel = projection.toScreenLocation(latLng);
@@ -241,16 +247,20 @@ public final class CameraUpdateFactory {
nePixel.y = Math.max(nePixel.y, viewportHeight - pixel.y);
}
- // Calculate wid=th/height
+ // Calculate width/height
float width = nePixel.x - swPixel.x;
float height = nePixel.y - swPixel.y;
+ double zoom = 0;
+ float minScale = 1;
// Calculate the zoom level
- float scaleX = (uiSettings.getWidth() - padding.left - padding.right) / width;
- float scaleY = (uiSettings.getHeight() - padding.top - padding.bottom) / height;
- float minScale = scaleX < scaleY ? scaleX : scaleY;
- double zoom = projection.calculateZoom(minScale);
- zoom = MathUtils.clamp(zoom, (float) mapboxMap.getMinZoom(), (float) mapboxMap.getMaxZoom());
+ if (padding != null) {
+ float scaleX = (uiSettings.getWidth() - padding.left - padding.right) / width;
+ float scaleY = (uiSettings.getHeight() - padding.top - padding.bottom) / height;
+ minScale = scaleX < scaleY ? scaleX : scaleY;
+ zoom = projection.calculateZoom(minScale);
+ zoom = MathUtils.clamp(zoom, (float) mapboxMap.getMinZoom(), (float) mapboxMap.getMaxZoom());
+ }
// Calculate the center point
PointF paddedNEPixel = new PointF(nePixel.x + padding.right / minScale, nePixel.y + padding.top / minScale);
@@ -291,13 +301,22 @@ public final class CameraUpdateFactory {
// Convert point to LatLng
LatLng latLng = projection.fromScreenLocation(targetPoint);
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- return new CameraPosition.Builder()
- .target(latLng)
- .zoom(cameraPosition.zoom)
- .tilt(cameraPosition.tilt)
- .bearing(cameraPosition.bearing)
- .build();
+ CameraPosition previousPosition = mapboxMap.getCameraPosition();
+ if (latLng != null) {
+ return new CameraPosition.Builder()
+ .target(latLng)
+ .zoom(previousPosition.zoom)
+ .tilt(previousPosition.tilt)
+ .bearing(previousPosition.bearing)
+ .build();
+ } else {
+ return new CameraPosition.Builder(true)
+ .tilt(previousPosition.tilt)
+ .zoom(previousPosition.zoom)
+ .bearing(previousPosition.bearing)
+ .target(previousPosition.target)
+ .build();
+ }
}
}
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
index 475489f621..ece992ad54 100644
--- 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
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.constants;
+import android.content.Context;
import java.util.Locale;
/**
@@ -14,7 +15,9 @@ public class MapboxConstants {
/**
* Key used to store access token in AndroidManifest.xml
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
+ @Deprecated
public static final String KEY_META_DATA_MANIFEST = "com.mapbox.AccessToken";
/**
@@ -33,6 +36,11 @@ public class MapboxConstants {
public static final int ANIMATION_DURATION = 300;
/**
+ * Default short animation time
+ */
+ public static final int ANIMATION_DURATION_SHORT = 150;
+
+ /**
* The currently supported minimum zoom level.
*/
public static final float MINIMUM_ZOOM = 0.0f;
@@ -71,14 +79,16 @@ public class MapboxConstants {
public static final String STATE_HAS_SAVED_STATE = "savedState";
public static final String STATE_CAMERA_POSITION = "cameraPosition";
public static final String STATE_ZOOM_ENABLED = "zoomEnabled";
+ public static final String STATE_ZOOM_ENABLED_CHANGE = "zoomEnabledChange";
public static final String STATE_SCROLL_ENABLED = "scrollEnabled";
+ public static final String STATE_SCROLL_ENABLED_CHANGE = "scrollEnabledChange";
public static final String STATE_ROTATE_ENABLED = "rotateEnabled";
+ public static final String STATE_ROTATE_ENABLED_CHANGE = "rotateEnabledChange";
public static final String STATE_TILT_ENABLED = "tiltEnabled";
+ public static final String STATE_TILT_ENABLED_CHANGE = "tiltEnabledChange";
public static final String STATE_ZOOM_CONTROLS_ENABLED = "zoomControlsEnabled";
public static final String STATE_DEBUG_ACTIVE = "debugActive";
public static final String STATE_STYLE_URL = "styleUrl";
- public static final String STATE_ACCESS_TOKEN = "accessToken";
- public static final String STATE_DEFAULT_TRANSITION_DURATION = "defaultTransitionDuration";
public static final String STATE_MY_LOCATION_ENABLED = "myLocationEnabled";
public static final String STATE_MY_LOCATION_TRACKING_MODE = "myLocationTracking";
public static final String STATE_MY_BEARING_TRACKING_MODE = "myBearingTracking";
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
index 383a85417c..929df2da77 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
@@ -3,7 +3,7 @@ package com.mapbox.mapboxsdk.constants;
import android.support.annotation.IntDef;
import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -12,7 +12,7 @@ import java.lang.annotation.RetentionPolicy;
* MyBearingTracking exposes different types bearing tracking modes.
*
* @see MapView#setMyBearingTrackingMode(int)
- * @see UserLocationView#setMyBearingTrackingMode(int)
+ * @see MyLocationView#setMyBearingTrackingMode(int)
*/
public class MyBearingTracking {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
index 9b0ae7285e..b2a49c6454 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
@@ -3,7 +3,7 @@ package com.mapbox.mapboxsdk.constants;
import android.support.annotation.IntDef;
import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -12,7 +12,7 @@ import java.lang.annotation.RetentionPolicy;
* MyLocationTracking exposes different types of locational tracking modes.
*
* @see MapView#setMyLocationTrackingMode(int)
- * @see UserLocationView#setMyLocationTrackingMode(int)
+ * @see MyLocationView#setMyLocationTrackingMode(int)
*/
public class MyLocationTracking {
@@ -30,7 +30,7 @@ public class MyLocationTracking {
public static final int TRACKING_NONE = 0x00000000;
/**
- * Tracking the location of the user, {@link MapView} will reposition to center of {@link UserLocationView}
+ * Tracking the location of the user, {@link MapView} will reposition to center of {@link MyLocationView}
*/
public static final int TRACKING_FOLLOW = 0x00000004;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
index 31c146b43e..aa24d58656 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
@@ -1,11 +1,10 @@
package com.mapbox.mapboxsdk.constants;
import android.support.annotation.StringDef;
-
import com.mapbox.mapboxsdk.maps.MapView;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+
/**
* <p>
* Style provides URLs to several professional styles designed by Mapbox.
@@ -17,10 +16,154 @@ import java.lang.annotation.RetentionPolicy;
public class Style {
/**
+ * Mapbox Streets: A complete basemap, perfect for incorporating your own data.
+ */
+ private static final String MAPBOX_STREETS_BASE = "mapbox://styles/mapbox/streets-v%d";
+ /**
+ * Outdoors: A general-purpose style tailored to outdoor activities.
+ */
+ private static final String OUTDOORS_BASE = "mapbox://styles/mapbox/outdoors-v%d";
+ /**
+ * Light: Subtle light backdrop for data visualizations.
+ */
+ private static final String LIGHT_BASE = "mapbox://styles/mapbox/light-v%d";
+ /**
+ * Dark: Subtle dark backdrop for data visualizations.
+ */
+ private static final String DARK_BASE = "mapbox://styles/mapbox/dark-v%d";
+ /**
+ * Satellite: A beautiful global satellite and aerial imagery layer.
+ */
+ private static final String SATELLITE_BASE = "mapbox://styles/mapbox/satellite-v%d";
+ /**
+ * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels.
+ */
+ private static final String SATELLITE_STREETS_BASE = "mapbox://styles/mapbox/satellite-streets-v%d";
+
+ /**
+ * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels (Version 8).
+ */
+ private static final String SATELLITE_STREETS_V8 = "mapbox://styles/mapbox/satellite-hybrid-v8";
+
+ /**
+ * Get versioned url of Mapbox streets style.
+ * <p>
+ * <ul>
+ * <li>Current default version is 9.</li>
+ * </ul
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getMapboxStreetsUrl(int version) {
+ return String.format(MapboxConstants.MAPBOX_LOCALE, MAPBOX_STREETS_BASE, version);
+ }
+
+ /**
+ * Get versioned url of Outdoors streets style.
+ * <p>
+ * <ul>
+ * <li>Current version is 9.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getOutdoorsStyleUrl(int version) {
+ return String.format(MapboxConstants.MAPBOX_LOCALE, OUTDOORS_BASE, version);
+ }
+
+ /**
+ * Get versioned url of Light style.
+ * <p>
+ * <ul>
+ * <li>Current default version is 9.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getLightStyleUrl(int version) {
+ return String.format(MapboxConstants.MAPBOX_LOCALE, LIGHT_BASE, version);
+ }
+
+ /**
+ * Get versioned url of Dark style.
+ * <p>
+ * <ul>
+ * <li>Current default version is 9.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getDarkStyleUrl(int version) {
+ return String.format(MapboxConstants.MAPBOX_LOCALE, DARK_BASE, version);
+ }
+
+ /**
+ * Get versioned url of Satellite style.
+ * <p>
+ * <ul>
+ * <li>Current version is 9.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getSatelliteStyleUrl(int version) {
+ return String.format(MapboxConstants.MAPBOX_LOCALE, SATELLITE_BASE, version);
+ }
+
+ /**
+ * Get versioned url of Satellite streets style.
+ * <p>
+ * <ul>
+ * <li>Current version is 9.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
+ * </p>
+ *
+ * @param version the version of the style.
+ * @return uri to load style from
+ */
+ public static String getSatelliteStreetsStyleUrl(int version) {
+ if (version == 8) {
+ return SATELLITE_STREETS_V8;
+ }
+ return String.format(MapboxConstants.MAPBOX_LOCALE, SATELLITE_STREETS_BASE, version);
+ }
+
+ /**
* Indicates the parameter accepts one of the values from {@link Style}.
+ *
+ * @deprecated use dedicated versioned methods in {@link Style} instead.
*/
@StringDef({MAPBOX_STREETS, EMERALD, LIGHT, DARK, SATELLITE, SATELLITE_STREETS})
@Retention(RetentionPolicy.SOURCE)
+ @Deprecated
public @interface StyleUrl {
}
@@ -28,28 +171,49 @@ public class Style {
/**
* Mapbox Streets: A complete basemap, perfect for incorporating your own data.
+ *
+ * @deprecated use {@link #getMapboxStreetsUrl(int)} instead.
*/
- public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v8";
+ @Deprecated
+ public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v9";
+
/**
* Emerald: A versatile style, with emphasis on road networks and public transit.
+ *
+ * @deprecated this style has been deprecated and will be removed in future versions.
*/
+ @Deprecated
public static final String EMERALD = "mapbox://styles/mapbox/emerald-v8";
+
/**
* Light: Subtle light backdrop for data visualizations.
+ *
+ * @deprecated use {@link #getLightStyleUrl(int)} instead.
*/
- public static final String LIGHT = "mapbox://styles/mapbox/light-v8";
+ @Deprecated
+ public static final String LIGHT = "mapbox://styles/mapbox/light-v9";
+
/**
* Dark: Subtle dark backdrop for data visualizations.
+ *
+ * @deprecated use {@link #getDarkStyleUrl(int)} (int)} instead.
*/
- public static final String DARK = "mapbox://styles/mapbox/dark-v8";
+ @Deprecated
+ public static final String DARK = "mapbox://styles/mapbox/dark-v9";
+
/**
* Satellite: A beautiful global satellite and aerial imagery layer.
+ *
+ * @deprecated use {@link #getSatelliteStyleUrl(int)} instead.
*/
- public static final String SATELLITE = "mapbox://styles/mapbox/satellite-v8";
+ @Deprecated
+ public static final String SATELLITE = "mapbox://styles/mapbox/satellite-v9";
/**
* Satellite Streets: Global satellite and aerial imagery with unobtrusive labels.
+ *
+ * @deprecated use {@link #getSatelliteStreetsStyleUrl(int)} (int)} instead.
*/
- public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-hybrid-v8";
-
+ @Deprecated
+ public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v9";
}
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
index 10ab7a5bbb..5b87e70ef6 100644
--- 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
@@ -16,12 +16,13 @@ import javax.net.ssl.SSLException;
import okhttp3.Call;
import okhttp3.Callback;
+import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
class HTTPRequest implements Callback {
-
+
private static OkHttpClient mClient = new OkHttpClient();
private final String LOG_TAG = HTTPRequest.class.getName();
@@ -46,6 +47,17 @@ class HTTPRequest implements Callback {
mNativePtr = nativePtr;
try {
+ HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
+ final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
+ if (host.equals("mapbox.com") || host.endsWith(".mapbox.com")) {
+ if (httpUrl.querySize() == 0) {
+ resourceUrl = resourceUrl + "?";
+ } else {
+ resourceUrl = resourceUrl + "&";
+ }
+ resourceUrl = resourceUrl + "events=true";
+ }
+
Request.Builder builder = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).addHeader("User-Agent", userAgent);
if (etag.length() > 0) {
builder = builder.addHeader("If-None-Match", etag);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
index 95278ed508..08f18892d2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
@@ -14,11 +14,21 @@ import com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver;
import com.mapzen.android.lost.api.LocationRequest;
import com.mapzen.android.lost.api.LostApiClient;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manages locational updates. Contains methods to register and unregister location listeners.
+ * <p>
+ * <ul>
+ * <li>You can register a {@link LocationListener} with {@link #addLocationListener(LocationListener)} to receive location updates.</li>
+ * <li> You can unregister a {@link LocationListener} with {@link #removeLocationListener(LocationListener)}.</li>
+ * </ul>
+ * <p/>
+ * <p>
+ * Note: If registering a listener in your Activity.onResume() implementation, you should unregister it in Activity.onPause().
+ * (You won't receive location updates when paused, and this will cut down on unnecessary system overhead).
+ * Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack.
+ * </p>
*/
public class LocationServices implements com.mapzen.android.lost.api.LocationListener {
@@ -30,7 +40,7 @@ public class LocationServices implements com.mapzen.android.lost.api.LocationLis
private LostApiClient locationClient;
private Location lastLocation;
- private List<LocationListener> locationListeners;
+ private CopyOnWriteArrayList<LocationListener> locationListeners;
private boolean isGPSEnabled;
@@ -42,7 +52,7 @@ public class LocationServices implements com.mapzen.android.lost.api.LocationLis
this.context = context;
// Setup location services
locationClient = new LostApiClient.Builder(context).build();
- locationListeners = new ArrayList<>();
+ locationListeners = new CopyOnWriteArrayList<>();
}
/**
@@ -64,8 +74,7 @@ public class LocationServices implements com.mapzen.android.lost.api.LocationLis
* @param enableGPS true if GPS is to be enabled, false if GPS is to be disabled
*/
public void toggleGPS(boolean enableGPS) {
- if ((ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) &&
- (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ if (!areLocationPermissionsGranted()) {
Log.w(TAG, "Location Permissions Not Granted Yet. Try again after requesting.");
return;
}
@@ -123,7 +132,7 @@ public class LocationServices implements com.mapzen.android.lost.api.LocationLis
*/
@Override
public void onLocationChanged(Location location) {
- Log.d(TAG, "onLocationChanged()..." + location);
+// Log.d(TAG, "onLocationChanged()..." + location);
this.lastLocation = location;
// Update Listeners
@@ -166,4 +175,17 @@ public class LocationServices implements com.mapzen.android.lost.api.LocationLis
public boolean removeLocationListener(@NonNull LocationListener locationListener) {
return this.locationListeners.remove(locationListener);
}
+
+ /**
+ * Check status of Location Permissions
+ * @return True if granted to the app, False if not
+ */
+ public boolean areLocationPermissionsGranted() {
+ if ((ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) &&
+ (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ Log.w(TAG, "Location Permissions Not Granted Yet. Try again after requesting.");
+ return false;
+ }
+ return true;
+ }
}
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
index 2fefd805ea..27ecb7520b 100644
--- 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
@@ -1,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
import android.app.Fragment;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
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 com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
/**
* Fragment wrapper around a map view.
@@ -44,7 +49,7 @@ public final class MapFragment extends Fragment {
}
/**
- * Creates the fragment view hierachy.
+ * Creates the fragment view hierarchy.
*
* @param inflater Inflater used to inflate content.
* @param container The parent layout for the map fragment.
@@ -54,11 +59,67 @@ public final class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- MapboxMapOptions options = getArguments().getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ MapboxMapOptions options = null;
+
+ // Get bundle
+ Bundle bundle = getArguments();
+ if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) {
+ options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ }
+
+ // Assign an AccessToken if needed
+ if (options == null || options.getAccessToken() == null) {
+ String token = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ token = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ token = getToken(inflater.getContext());
+ }
+ if (TextUtils.isEmpty(token)) {
+ throw new InvalidAccessTokenException();
+ }
+ if (options == null) {
+ options = new MapboxMapOptions().accessToken(token);
+ } else {
+ options.accessToken(token);
+ }
+ }
return mMap = new MapView(inflater.getContext(), options);
}
/**
+ * <p>
+ * Returns the Mapbox access token set in the app resources.
+ * </p>
+ * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
+ * meta-data value. If not found it will then attempt to load the access token from the
+ * {@code res/raw/token.txt} development file.
+ *
+ * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
+ * @return The Mapbox access token or null if not found.
+ * @see MapboxConstants#KEY_META_DATA_MANIFEST
+ *
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
+ */
+ @Deprecated
+ private String getToken(@NonNull Context context) {
+ try {
+ // read out AndroidManifest
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
+ if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return token;
+ } catch (Exception e) {
+ // use fallback on string resource, used for development
+ int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
+ return tokenResId != 0 ? context.getString(tokenResId) : null;
+ }
+ }
+
+ /**
* Called when the fragment view hierarchy is created.
*
* @param view The content view of the fragment
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
index 38dd4bccb4..b34b947a2a 100644
--- 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
@@ -10,15 +10,15 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
+import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -58,12 +58,14 @@ import android.widget.ZoomButtonsController;
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -73,15 +75,17 @@ import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException;
-import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
-import com.mapbox.mapboxsdk.exceptions.TelemetryServiceNotConfiguredException;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.telemetry.MapboxEvent;
import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
+import com.mapbox.mapboxsdk.utils.ColorUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -106,19 +110,25 @@ import java.util.concurrent.CopyOnWriteArrayList;
* </p>
* <strong>Warning:</strong> 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.
- *
- * @see MapView#setAccessToken(String)
*/
public class MapView extends FrameLayout {
private MapboxMap mMapboxMap;
+ private boolean mInitialLoad;
+ private boolean mDestroyed;
+
private List<Icon> mIcons;
+ private int mAverageIconHeight;
+ private int mAverageIconWidth;
private NativeMapView mNativeMapView;
+ private boolean mHasSurface = false;
+
private CompassView mCompassView;
private ImageView mLogoView;
private ImageView mAttributionsView;
- private UserLocationView mUserLocationView;
+ private MyLocationView mMyLocationView;
+ private LocationListener mMyLocationListener;
private CopyOnWriteArrayList<OnMapChangedListener> mOnMapChangedListener;
private ZoomButtonsController mZoomButtonsController;
@@ -140,11 +150,9 @@ public class MapView extends FrameLayout {
private int mContentPaddingRight;
private int mContentPaddingBottom;
- private String mStyleUrl;
+ private StyleInitializer mStyleInitializer;
private List<OnMapReadyCallback> mOnMapReadyCallbackList;
- private boolean mInitialLoad;
- private boolean mDestroyed;
@UiThread
public MapView(@NonNull Context context) {
@@ -176,6 +184,7 @@ public class MapView extends FrameLayout {
mOnMapChangedListener = new CopyOnWriteArrayList<>();
mMapboxMap = new MapboxMap(this);
mIcons = new ArrayList<>();
+ mStyleInitializer = new StyleInitializer(context);
View view = LayoutInflater.from(context).inflate(R.layout.mapview_internal, this);
if (!isInEditMode()) {
@@ -215,8 +224,8 @@ public class MapView extends FrameLayout {
// Connectivity
onConnectivityChanged(isConnected());
- mUserLocationView = (UserLocationView) view.findViewById(R.id.userLocationView);
- mUserLocationView.setMapboxMap(mMapboxMap);
+ mMyLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
+ mMyLocationView.setMapboxMap(mMapboxMap);
mCompassView = (CompassView) view.findViewById(R.id.compassView);
mCompassView.setMapboxMap(mMapboxMap);
@@ -245,24 +254,45 @@ public class MapView extends FrameLayout {
mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(position));
}
- String accessToken = options.getAccessToken();
- if (!TextUtils.isEmpty(accessToken)) {
- mMapboxMap.setAccessToken(accessToken);
+ String accessToken = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ accessToken = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ accessToken = options.getAccessToken();
}
String style = options.getStyle();
- if (!TextUtils.isEmpty(style)) {
- mMapboxMap.setStyleUrl(style);
+ if (!TextUtils.isEmpty(accessToken)) {
+ mMapboxMap.setAccessToken(accessToken);
+ if (style != null) {
+ setStyleUrl(style);
+ }
+ } else {
+ mStyleInitializer.setStyle(style, true);
}
+ // MyLocationView
+ MyLocationViewSettings myLocationViewSettings = mMapboxMap.getMyLocationViewSettings();
+ myLocationViewSettings.setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable());
+ myLocationViewSettings.setForegroundTintColor(options.getMyLocationForegroundTintColor());
+ myLocationViewSettings.setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding());
+ myLocationViewSettings.setBackgroundTintColor(options.getMyLocationBackgroundTintColor());
+ myLocationViewSettings.setAccuracyAlpha(options.getMyLocationAccuracyAlpha());
+ myLocationViewSettings.setAccuracyTintColor(options.getMyLocationAccuracyTintColor());
mMapboxMap.setMyLocationEnabled(options.getLocationEnabled());
// Enable gestures
UiSettings uiSettings = mMapboxMap.getUiSettings();
uiSettings.setZoomGesturesEnabled(options.getZoomGesturesEnabled());
+ uiSettings.setZoomGestureChangeAllowed(options.getZoomGesturesEnabled());
uiSettings.setScrollGesturesEnabled(options.getScrollGesturesEnabled());
+ uiSettings.setScrollGestureChangeAllowed(options.getScrollGesturesEnabled());
uiSettings.setRotateGesturesEnabled(options.getRotateGesturesEnabled());
+ uiSettings.setRotateGestureChangeAllowed(options.getRotateGesturesEnabled());
uiSettings.setTiltGesturesEnabled(options.getTiltGesturesEnabled());
+ uiSettings.setTiltGestureChangeAllowed(options.getTiltGesturesEnabled());
+
+ // Ui Controls
uiSettings.setZoomControlsEnabled(options.getZoomControlsEnabled());
// Zoom
@@ -303,6 +333,10 @@ public class MapView extends FrameLayout {
int seventySixDp = (int) resources.getDimension(R.dimen.seventy_six_dp);
uiSettings.setAttributionMargins(seventySixDp, sevenDp, sevenDp, sevenDp);
}
+
+ int attributionTintColor = options.getAttributionTintColor();
+ uiSettings.setAttributionTintColor(attributionTintColor != -1 ?
+ attributionTintColor : ColorUtils.getPrimaryColor(getContext()));
}
//
@@ -323,7 +357,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
// Force a check for an access token
- validateAccessToken(getAccessToken());
+ MapboxAccountManager.validateAccessToken(getAccessToken());
if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
@@ -335,9 +369,13 @@ public class MapView extends FrameLayout {
UiSettings uiSettings = mMapboxMap.getUiSettings();
uiSettings.setZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED));
+ uiSettings.setZoomGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE));
uiSettings.setScrollGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED));
+ uiSettings.setScrollGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE));
uiSettings.setRotateGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED));
+ uiSettings.setRotateGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE));
uiSettings.setTiltGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED));
+ uiSettings.setTiltGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE));
uiSettings.setZoomControlsEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED));
// Compass
@@ -366,9 +404,6 @@ public class MapView extends FrameLayout {
mMapboxMap.setDebugActive(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE));
mMapboxMap.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL));
- setAccessToken(savedInstanceState.getString(MapboxConstants.STATE_ACCESS_TOKEN));
- mNativeMapView.setDefaultTransitionDuration(
- savedInstanceState.getLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION));
// User location
try {
@@ -384,10 +419,8 @@ public class MapView extends FrameLayout {
//noinspection ResourceType
trackingSettings.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE));
} else if (savedInstanceState == null) {
- // Force a check for Telemetry
- validateTelemetryServiceConfigured();
-
// Start Telemetry (authorization determined in initial MapboxEventManager constructor)
+ Log.i(MapView.class.getCanonicalName(), "MapView start Telemetry...");
MapboxEventManager eventManager = MapboxEventManager.getMapboxEventManager();
eventManager.initialize(getContext(), getAccessToken());
}
@@ -400,21 +433,21 @@ public class MapView extends FrameLayout {
addOnMapChangedListener(new OnMapChangedListener() {
@Override
public void onMapChanged(@MapChange int change) {
- if (change == DID_FINISH_RENDERING_MAP_FULLY_RENDERED) {
+ if (change == WILL_START_RENDERING_MAP && mInitialLoad) {
+ mInitialLoad = false;
reloadIcons();
reloadMarkers();
adjustTopOffsetPixels();
- if (mInitialLoad) {
- mInitialLoad = false;
- if (mOnMapReadyCallbackList.size() > 0) {
- Iterator<OnMapReadyCallback> iterator = mOnMapReadyCallbackList.iterator();
- while (iterator.hasNext()) {
- OnMapReadyCallback callback = iterator.next();
- callback.onMapReady(mMapboxMap);
- iterator.remove();
- }
+ if (mOnMapReadyCallbackList.size() > 0) {
+ Iterator<OnMapReadyCallback> iterator = mOnMapReadyCallbackList.iterator();
+ while (iterator.hasNext()) {
+ OnMapReadyCallback callback = iterator.next();
+ callback.onMapReady(mMapboxMap);
+ iterator.remove();
}
}
+ } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) {
+ mMapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation();
}
}
});
@@ -440,9 +473,7 @@ public class MapView extends FrameLayout {
outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, mMapboxMap.getCameraPosition());
outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, mMapboxMap.isDebugActive());
- outState.putString(MapboxConstants.STATE_STYLE_URL, mStyleUrl);
- outState.putString(MapboxConstants.STATE_ACCESS_TOKEN, mMapboxMap.getAccessToken());
- outState.putLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION, mNativeMapView.getDefaultTransitionDuration());
+ outState.putString(MapboxConstants.STATE_STYLE_URL, mStyleInitializer.getStyle());
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mMapboxMap.isMyLocationEnabled());
// TrackingSettings
@@ -453,9 +484,13 @@ public class MapView extends FrameLayout {
// UiSettings
UiSettings uiSettings = mMapboxMap.getUiSettings();
outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED, uiSettings.isZoomGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE, uiSettings.isZoomGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED, uiSettings.isScrollGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE, uiSettings.isScrollGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED, uiSettings.isRotateGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE, uiSettings.isRotateGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED, uiSettings.isTiltGesturesEnabled());
+ outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE, uiSettings.isTiltGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_ZOOM_CONTROLS_ENABLED, uiSettings.isZoomControlsEnabled());
// UiSettings - Compass
@@ -505,8 +540,7 @@ public class MapView extends FrameLayout {
getContext().unregisterReceiver(mConnectivityReceiver);
mConnectivityReceiver = null;
- mUserLocationView.onPause();
- mNativeMapView.pause();
+ mMyLocationView.onPause();
}
/**
@@ -518,13 +552,12 @@ public class MapView extends FrameLayout {
mConnectivityReceiver = new ConnectivityReceiver();
getContext().registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- mNativeMapView.resume();
mNativeMapView.update();
- mUserLocationView.onResume();
+ mMyLocationView.onResume();
- if (mStyleUrl == null) {
+ if (mStyleInitializer.isDefaultStyle()) {
// user has failed to supply a style url
- setStyleUrl(Style.MAPBOX_STREETS);
+ setStyleUrl(mStyleInitializer.getStyle());
}
}
@@ -567,20 +600,18 @@ public class MapView extends FrameLayout {
return mNativeMapView.getPitch();
}
- void setTilt(Double pitch, @Nullable Long duration) {
- long actualDuration = 0;
- if (duration != null) {
- actualDuration = duration;
- }
- mNativeMapView.setPitch(pitch, actualDuration);
+ void setTilt(Double pitch) {
+ mMyLocationView.setTilt(pitch);
+ mNativeMapView.setPitch(pitch, 0);
}
+
//
// Direction
//
double getDirection() {
- if(mDestroyed){
+ if (mDestroyed) {
return 0;
}
@@ -641,6 +672,14 @@ public class MapView extends FrameLayout {
return mContentPaddingBottom;
}
+ int getContentWidth(){
+ return getWidth() - mContentPaddingLeft - mContentPaddingRight;
+ }
+
+ int getContentHeight(){
+ return getHeight() - mContentPaddingBottom - mContentPaddingTop;
+ }
+
//
// Zoom
//
@@ -744,7 +783,7 @@ public class MapView extends FrameLayout {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
+ * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -760,7 +799,7 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return;
}
- mStyleUrl = url;
+ mStyleInitializer.setStyle(url);
mNativeMapView.setStyleUrl(url);
}
@@ -794,7 +833,7 @@ public class MapView extends FrameLayout {
@UiThread
@NonNull
public String getStyleUrl() {
- return mStyleUrl;
+ return mStyleInitializer.getStyle();
}
//
@@ -803,16 +842,21 @@ public class MapView extends FrameLayout {
/**
* <p>
- * Sets the current Mapbox access token used to load map styles and tiles.
+ * DEPRECATED @see MapboxAccountManager#start(String)
* </p>
* <p>
+ * <p>
+ * Sets the current Mapbox access token used to load map styles and tiles.
+ * <p>
* You must set a valid access token before you call {@link MapView#onCreate(Bundle)}
* or an exception will be thrown.
* </p>
*
* @param accessToken Your public Mapbox access token.
* @see MapView#onCreate(Bundle)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
+ @Deprecated
@UiThread
public void setAccessToken(@NonNull String accessToken) {
if (mDestroyed) {
@@ -822,15 +866,22 @@ public class MapView extends FrameLayout {
if (!TextUtils.isEmpty(accessToken)) {
accessToken = accessToken.trim();
}
- validateAccessToken(accessToken);
+ MapboxAccountManager.validateAccessToken(accessToken);
mNativeMapView.setAccessToken(accessToken);
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#getAccessToken()
+ * </p>
+ * <p/>
* Returns the current Mapbox access token used to load map styles and tiles.
+ * </p>
*
* @return The current Mapbox access token.
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()}
*/
+ @Deprecated
@UiThread
@Nullable
public String getAccessToken() {
@@ -840,31 +891,6 @@ public class MapView extends FrameLayout {
return mNativeMapView.getAccessToken();
}
- // Checks if the given token is valid
- private void validateAccessToken(String accessToken) {
- if (TextUtils.isEmpty(accessToken) || (!accessToken.startsWith("pk.") && !accessToken.startsWith("sk."))) {
- throw new InvalidAccessTokenException();
- }
- }
-
- // Checks that TelemetryService has been configured by developer
- private void validateTelemetryServiceConfigured() {
- try {
- // Check Implementing app's AndroidManifest.xml
- PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), PackageManager.GET_SERVICES);
- if (packageInfo.services != null) {
- for (ServiceInfo service : packageInfo.services) {
- if (TextUtils.equals("com.mapbox.mapboxsdk.telemetry.TelemetryService", service.name)) {
- return;
- }
- }
- }
- } catch (Exception e) {
- Log.w(MapboxConstants.TAG, "Error checking for Telemetry Service Config: " + e);
- }
- throw new TelemetryServiceNotConfiguredException();
- }
-
//
// Projection
//
@@ -873,22 +899,17 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return new LatLng();
}
- float x = point.x;
- float y = point.y;
-
- return mNativeMapView.latLngForPixel(new PointF(x / mScreenDensity, y / mScreenDensity));
+ point.set(point.x / mScreenDensity, point.y / mScreenDensity);
+ return mNativeMapView.latLngForPixel(point);
}
PointF toScreenLocation(@NonNull LatLng location) {
- if (mDestroyed) {
+ if (mDestroyed || location == null) {
return new PointF();
}
- PointF point = mNativeMapView.pixelForLatLng(location);
-
- float x = point.x * mScreenDensity;
- float y = point.y * mScreenDensity;
-
- return new PointF(x, y);
+ PointF pointF = mNativeMapView.pixelForLatLng(location);
+ pointF.set(pointF.x * mScreenDensity, pointF.y * mScreenDensity);
+ return pointF;
}
//
@@ -897,10 +918,24 @@ public class MapView extends FrameLayout {
Icon loadIconForMarker(Marker marker) {
Icon icon = marker.getIcon();
+
+ // calculating average before adding
+ int iconSize = mIcons.size() + 1;
+
+ // TODO replace former if case with anchor implementation,
+ // current workaround for having extra pixels is diving height by 2
if (icon == null) {
icon = IconFactory.getInstance(getContext()).defaultMarker();
+ Bitmap bitmap = icon.getBitmap();
+ mAverageIconHeight = mAverageIconHeight + (bitmap.getHeight() / 2 - mAverageIconHeight) / iconSize;
+ mAverageIconWidth = mAverageIconHeight + (bitmap.getWidth() - mAverageIconHeight) / iconSize;
marker.setIcon(icon);
+ } else {
+ Bitmap bitmap = icon.getBitmap();
+ mAverageIconHeight = mAverageIconHeight + (bitmap.getHeight() - mAverageIconHeight) / iconSize;
+ mAverageIconWidth = mAverageIconHeight + (bitmap.getWidth() - mAverageIconHeight) / iconSize;
}
+
if (!mIcons.contains(icon)) {
mIcons.add(icon);
loadIcon(icon);
@@ -930,7 +965,6 @@ public class MapView extends FrameLayout {
density = DisplayMetrics.DENSITY_DEFAULT;
}
float scale = density / DisplayMetrics.DENSITY_DEFAULT;
-
mNativeMapView.addAnnotationIcon(
id,
bitmap.getWidth(),
@@ -987,6 +1021,9 @@ public class MapView extends FrameLayout {
}
long addMarker(@NonNull Marker marker) {
+ if(mDestroyed){
+ return 0l;
+ }
return mNativeMapView.addMarker(marker);
}
@@ -1039,16 +1076,11 @@ public class MapView extends FrameLayout {
mNativeMapView.removeAnnotations(ids);
}
- private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
- if (mDestroyed) {
+ List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
+ if (mDestroyed || bbox == null) {
return new ArrayList<>();
}
- if (bbox == null) {
- Log.w(MapboxConstants.TAG, "bbox was null, so just returning null");
- return null;
- }
-
// TODO: filter in JNI using C++ parameter to getAnnotationsInBounds
long[] ids = mNativeMapView.getAnnotationsInBounds(bbox);
@@ -1070,10 +1102,35 @@ public class MapView extends FrameLayout {
return new ArrayList<>(annotations);
}
+ public List<MarkerView> getMarkerViewsInBounds(@NonNull LatLngBounds bbox) {
+ if (mDestroyed || bbox == null) {
+ return new ArrayList<>();
+ }
+
+ // TODO: filter in JNI using C++ parameter to getAnnotationsInBounds
+ long[] ids = mNativeMapView.getAnnotationsInBounds(bbox);
+
+ List<Long> idsList = new ArrayList<>(ids.length);
+ for (int i = 0; i < ids.length; i++) {
+ idsList.add(ids[i]);
+ }
+
+ List<MarkerView> annotations = new ArrayList<>(ids.length);
+ List<Annotation> annotationList = mMapboxMap.getAnnotations();
+ int count = annotationList.size();
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotationList.get(i);
+ if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
+ annotations.add((MarkerView) annotation);
+ }
+ }
+
+ return new ArrayList<>(annotations);
+ }
+
+
int getTopOffsetPixelsForIcon(Icon icon) {
- // This method will dead lock if map paused. Causes a freeze if you add a marker in an
- // activity's onCreate()
- if (mDestroyed || mNativeMapView.isPaused()) {
+ if (mDestroyed) {
return 0;
}
@@ -1086,18 +1143,28 @@ public class MapView extends FrameLayout {
return;
}
- if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) {
- return;
- }
+// if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) {
+// return;
+// }
mContentPaddingLeft = left;
mContentPaddingTop = top;
mContentPaddingRight = right;
mContentPaddingBottom = bottom;
+ int[] userLocationViewPadding = mMapboxMap.getMyLocationViewSettings().getPadding();
+ left += userLocationViewPadding[0];
+ top += userLocationViewPadding[1];
+ right += userLocationViewPadding[2];
+ bottom += userLocationViewPadding[3];
+
mNativeMapView.setContentPadding(top / mScreenDensity, left / mScreenDensity, bottom / mScreenDensity, right / mScreenDensity);
}
+ public void invalidateContentPadding() {
+ setContentPadding(mContentPaddingLeft, mContentPaddingTop, mContentPaddingRight, mContentPaddingBottom);
+ }
+
double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) {
if (mDestroyed) {
return 0;
@@ -1118,7 +1185,7 @@ public class MapView extends FrameLayout {
mNativeMapView.jumpTo(bearing, center, pitch, zoom);
}
- void easeTo(double bearing, LatLng center, long duration, double pitch, double zoom, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
+ void easeTo(double bearing, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
if (mDestroyed) {
return;
}
@@ -1139,7 +1206,7 @@ public class MapView extends FrameLayout {
});
}
- mNativeMapView.easeTo(bearing, center, duration, pitch, zoom);
+ mNativeMapView.easeTo(bearing, center, duration, pitch, zoom, easingInterpolator);
}
void flyTo(double bearing, LatLng center, long duration, double pitch, double zoom, @Nullable final MapboxMap.CancelableCallback cancelableCallback) {
@@ -1220,11 +1287,15 @@ public class MapView extends FrameLayout {
return;
}
- if (mDestroyed || mNativeMapView.isPaused()) {
+ if (mDestroyed) {
+ return;
+ }
+
+ if (!mHasSurface) {
return;
}
- mNativeMapView.renderSync();
+ mNativeMapView.render();
}
@Override
@@ -1250,6 +1321,10 @@ public class MapView extends FrameLayout {
private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
private Surface mSurface;
+ private View mViewHolder;
+
+ private static final int VIEW_MARKERS_POOL_SIZE = 20;
+
// Called when the native surface texture has been created
// Must do all EGL/GL ES initialization here
@@ -1257,12 +1332,15 @@ public class MapView extends FrameLayout {
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mNativeMapView.createSurface(mSurface = new Surface(surface));
mNativeMapView.resizeFramebuffer(width, height);
+ mHasSurface = true;
}
// Called when the native surface texture has been destroyed
// Must do all EGL/GL ES destruction here
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mHasSurface = false;
+
if (mNativeMapView != null) {
mNativeMapView.destroySurface();
}
@@ -1274,7 +1352,7 @@ public class MapView extends FrameLayout {
// Must handle window resizing here.
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if(mDestroyed){
+ if (mDestroyed) {
return;
}
@@ -1285,12 +1363,14 @@ public class MapView extends FrameLayout {
// Must sync with UI here
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if(mDestroyed){
+ if (mDestroyed) {
return;
}
mCompassView.update(getDirection());
- mUserLocationView.update();
+ mMyLocationView.update();
+ mMapboxMap.getMarkerViewManager().update();
+
for (InfoWindow infoWindow : mMapboxMap.getInfoWindows()) {
infoWindow.update();
}
@@ -1432,7 +1512,8 @@ public class MapView extends FrameLayout {
case MotionEvent.ACTION_POINTER_DOWN:
// Second pointer down
- mTwoTap = event.getPointerCount() == 2;
+ mTwoTap = event.getPointerCount() == 2
+ && mMapboxMap.getUiSettings().isZoomGesturesEnabled();
if (mTwoTap) {
// Confirmed 2nd Finger Down
trackGestureEvent(MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY());
@@ -1517,8 +1598,7 @@ public class MapView extends FrameLayout {
zoom(true, e.getX(), e.getY());
} else {
// Zoom in on user location view
- PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
- zoom(true, centerPoint.x, centerPoint.y);
+ zoom(true, mMyLocationView.getCenterX(), mMyLocationView.getCenterY());
}
break;
}
@@ -1540,17 +1620,15 @@ public class MapView extends FrameLayout {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
- // Open / Close InfoWindow
- PointF tapPoint = new PointF(e.getX(), e.getY());
-
List<Marker> selectedMarkers = mMapboxMap.getSelectedMarkers();
- final float toleranceSides = 15 * mScreenDensity;
- final float toleranceTop = 20 * mScreenDensity;
- final float toleranceBottom = 5 * mScreenDensity;
-
- RectF tapRect = new RectF(tapPoint.x - toleranceSides, tapPoint.y + toleranceTop,
- tapPoint.x + toleranceSides, tapPoint.y - toleranceBottom);
+ PointF tapPoint = new PointF(e.getX(), e.getY());
+ float toleranceSides = 4 * mScreenDensity;
+ float toleranceTopBottom = 10 * mScreenDensity;
+ RectF tapRect = new RectF(tapPoint.x - mAverageIconWidth / 2 - toleranceSides,
+ tapPoint.y - mAverageIconHeight / 2 - toleranceTopBottom,
+ tapPoint.x + mAverageIconWidth / 2 + toleranceSides,
+ tapPoint.y + mAverageIconHeight / 2 + toleranceTopBottom);
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(fromScreenLocation(new PointF(tapRect.left, tapRect.bottom)));
@@ -1585,7 +1663,10 @@ public class MapView extends FrameLayout {
if (annotation instanceof Marker) {
if (annotation.getId() == newSelectedMarkerId) {
if (selectedMarkers.isEmpty() || !selectedMarkers.contains(annotation)) {
- mMapboxMap.selectMarker((Marker) annotation);
+ // only handle click if no marker view is available
+ if (!(annotation instanceof MarkerView)) {
+ mMapboxMap.selectMarker((Marker) annotation);
+ }
}
break;
}
@@ -1625,9 +1706,7 @@ public class MapView extends FrameLayout {
}
// reset tracking modes if gesture occurs
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- resetTrackingModes();
- }
+ resetTrackingModesIfRequired();
// Fling the map
float ease = 0.25f;
@@ -1663,10 +1742,9 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
+
// Cancel any animation
mNativeMapView.cancelTransitions();
@@ -1695,10 +1773,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY());
@@ -1758,8 +1834,9 @@ public class MapView extends FrameLayout {
mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity);
} else {
// around user location view
- PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
- mNativeMapView.scaleBy(detector.getScaleFactor(), centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity);
+ float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
+ float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
+ mNativeMapView.scaleBy(detector.getScaleFactor(), x / mScreenDensity, y / mScreenDensity);
}
}
return true;
@@ -1780,10 +1857,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY());
@@ -1840,8 +1915,9 @@ public class MapView extends FrameLayout {
detector.getFocusY() / mScreenDensity);
} else {
// around center userlocation
- PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
- mNativeMapView.setBearing(bearing, centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity);
+ float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
+ float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
+ mNativeMapView.setBearing(bearing, x / mScreenDensity, y / mScreenDensity);
}
return true;
}
@@ -1861,10 +1937,8 @@ public class MapView extends FrameLayout {
return false;
}
- if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
- // reset tracking modes if gesture occurs
- resetTrackingModes();
- }
+ // reset tracking modes if gesture occurs
+ resetTrackingModesIfRequired();
mBeginTime = detector.getEventTime();
trackGestureEvent(MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY());
@@ -1912,14 +1986,14 @@ public class MapView extends FrameLayout {
pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch));
// Tilt the map
- setTilt(pitch, null);
+ mMapboxMap.setTilt(pitch);
return true;
}
}
// This class handles input events from the zoom control buttons
-// Zoom controls allow single touch only devices to zoom in and out
+ // Zoom controls allow single touch only devices to zoom in and out
private class OnZoomListener implements ZoomButtonsController.OnZoomListener {
// Not used
@@ -2269,7 +2343,6 @@ public class MapView extends FrameLayout {
* @param listener The callback that's invoked on every frame rendered to the map view.
* @see MapView#removeOnMapChangedListener(OnMapChangedListener)
*/
- @UiThread
public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
mOnMapChangedListener.add(listener);
@@ -2282,7 +2355,6 @@ public class MapView extends FrameLayout {
* @param listener The previously added callback to remove.
* @see MapView#addOnMapChangedListener(OnMapChangedListener)
*/
- @UiThread
public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
mOnMapChangedListener.remove(listener);
@@ -2308,22 +2380,35 @@ public class MapView extends FrameLayout {
//
void setMyLocationEnabled(boolean enabled) {
- mUserLocationView.setEnabled(enabled);
+ mMyLocationView.setEnabled(enabled);
}
Location getMyLocation() {
- return mUserLocationView.getLocation();
+ return mMyLocationView.getLocation();
}
- void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
- mUserLocationView.setOnMyLocationChangeListener(listener);
+ void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) {
+ if (listener != null) {
+ mMyLocationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (listener != null) {
+ listener.onMyLocationChange(location);
+ }
+ }
+ };
+ LocationServices.getLocationServices(getContext()).addLocationListener(mMyLocationListener);
+ } else {
+ LocationServices.getLocationServices(getContext()).removeLocationListener(mMyLocationListener);
+ mMyLocationListener = null;
+ }
}
void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && !mMapboxMap.isMyLocationEnabled()) {
mMapboxMap.setMyLocationEnabled(true);
}
- mUserLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
+ mMyLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
MapboxMap.OnMyLocationTrackingModeChangeListener listener = mMapboxMap.getOnMyLocationTrackingModeChangeListener();
if (listener != null) {
listener.onMyLocationTrackingModeChange(myLocationTrackingMode);
@@ -2334,7 +2419,7 @@ public class MapView extends FrameLayout {
if (myBearingTrackingMode != MyBearingTracking.NONE && !mMapboxMap.isMyLocationEnabled()) {
mMapboxMap.setMyLocationEnabled(true);
}
- mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
+ mMyLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
MapboxMap.OnMyBearingTrackingModeChangeListener listener = mMapboxMap.getOnMyBearingTrackingModeChangeListener();
if (listener != null) {
listener.onMyBearingTrackingModeChange(myBearingTrackingMode);
@@ -2346,11 +2431,28 @@ public class MapView extends FrameLayout {
ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
- private void resetTrackingModes() {
+ private void resetTrackingModesIfRequired() {
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ if (trackingSettings.isDismissLocationTrackingOnGesture()) {
+ resetLocationTrackingMode();
+ }
+ if (trackingSettings.isDismissBearingTrackingOnGesture()) {
+ resetBearingTrackingMode();
+ }
+ }
+
+ private void resetLocationTrackingMode() {
try {
TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
+ } catch (SecurityException ignore) {
+ // User did not accept location permissions
+ }
+ }
+
+ private void resetBearingTrackingMode() {
+ try {
+ setMyBearingTrackingMode(MyBearingTracking.NONE);
} catch (SecurityException ignore) {
// User did not accept location permissions
}
@@ -2404,6 +2506,13 @@ public class MapView extends FrameLayout {
mAttributionsView.setVisibility(visibility);
}
+ void setAtttibutionTintColor(int tintColor) {
+ ColorUtils.setTintList(mAttributionsView, tintColor);
+ }
+
+ int getAttributionTintColor() {
+ return mMapboxMap.getUiSettings().getAttributionTintColor();
+ }
//
// Custom layer
@@ -2455,6 +2564,26 @@ public class MapView extends FrameLayout {
mMapboxMap = mapboxMap;
}
+ MyLocationView getUserLocationView() {
+ return mMyLocationView;
+ }
+
+ @UiThread
+ void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
+ TextureView textureView = (TextureView) findViewById(R.id.textureView);
+ final boolean canUseBitmap = bitmap != null && textureView.getWidth() == bitmap.getWidth() && textureView.getHeight() == bitmap.getHeight();
+
+ setDrawingCacheEnabled(true);
+ Bitmap content = Bitmap.createBitmap(getDrawingCache());
+ setDrawingCacheEnabled(false);
+
+ Bitmap output = Bitmap.createBitmap(content.getWidth(), content.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ canvas.drawBitmap(canUseBitmap ? textureView.getBitmap(bitmap) : textureView.getBitmap(), 0, 0, null);
+ canvas.drawBitmap(content, new Matrix(), null);
+ callback.onSnapshotReady(output);
+ }
+
//
// View utility methods
//
@@ -2494,7 +2623,8 @@ public class MapView extends FrameLayout {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle);
builder.setTitle(R.string.attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.attribution_list_item, items), this);
- builder.show();
+ AlertDialog dialog = builder.show();
+ dialog.getWindow().setBackgroundDrawable(new ColorDrawable(mMapView.getAttributionTintColor()));
}
// Called when someone selects an attribution, 'Improve this map' adds location data to the url
@@ -2570,6 +2700,41 @@ public class MapView extends FrameLayout {
}
/**
+ * Class responsible for managing state of Style loading.
+ */
+ static class StyleInitializer {
+
+ private String mStyle;
+ private boolean mDefaultStyle;
+
+ StyleInitializer(@NonNull Context context) {
+ mStyle = Style.getMapboxStreetsUrl(context.getResources().getInteger(R.integer.style_version));
+ mDefaultStyle = true;
+ }
+
+ void setStyle(@NonNull String style) {
+ setStyle(style, false);
+ }
+
+ void setStyle(@NonNull String style, boolean defaultStyle) {
+ if (style == null) {
+ // don't override default style
+ return;
+ }
+ mStyle = style;
+ mDefaultStyle = defaultStyle;
+ }
+
+ public String getStyle() {
+ return mStyle;
+ }
+
+ boolean isDefaultStyle() {
+ return mDefaultStyle;
+ }
+ }
+
+ /**
* Definition of a map change event.
*
* @see MapView.OnMapChangedListener#onMapChanged(int)
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
index e1a9cd8cdf..fbe6a31f6a 100644
--- 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
@@ -1,5 +1,7 @@
package com.mapbox.mapboxsdk.maps;
+import android.content.Context;
+import android.graphics.Bitmap;
import android.location.Location;
import android.os.SystemClock;
import android.support.annotation.FloatRange;
@@ -7,16 +9,23 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.util.LongSparseArray;
+import android.support.v4.util.Pools;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
@@ -30,7 +39,10 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
+import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -49,13 +61,18 @@ public class MapboxMap {
private MapView mMapView;
private UiSettings mUiSettings;
private TrackingSettings mTrackingSettings;
+ private MyLocationViewSettings myLocationViewSettings;
private Projection mProjection;
private CameraPosition mCameraPosition;
private boolean mInvalidCameraPosition;
private LongSparseArray<Annotation> mAnnotations;
+
private List<Marker> mSelectedMarkers;
+ private MarkerViewManager mMarkerViewManager;
+
private List<InfoWindow> mInfoWindows;
private MapboxMap.InfoWindowAdapter mInfoWindowAdapter;
+ private Bitmap mViewMarkerBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
private boolean mMyLocationEnabled;
private boolean mAllowConcurrentMultipleInfoWindows;
@@ -85,6 +102,7 @@ public class MapboxMap {
mAnnotations = new LongSparseArray<>();
mSelectedMarkers = new ArrayList<>();
mInfoWindows = new ArrayList<>();
+ mMarkerViewManager = new MarkerViewManager(this, mapView);
}
//
@@ -186,6 +204,20 @@ public class MapboxMap {
}
//
+ // MyLocationViewSettings
+ //
+
+ /**
+ * Gets the settings of the user location for the map.
+ */
+ public MyLocationViewSettings getMyLocationViewSettings() {
+ if (myLocationViewSettings == null) {
+ myLocationViewSettings = new MyLocationViewSettings(mMapView, mMapView.getUserLocationView());
+ }
+ return myLocationViewSettings;
+ }
+
+ //
// Projection
//
@@ -254,10 +286,12 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * 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 {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update) {
@@ -265,11 +299,14 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * 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.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs) {
@@ -277,17 +314,35 @@ public class MapboxMap {
}
/**
- * Ease the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * 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 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. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null.
+ * @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 {@link CameraUpdateFactory} for a set of camera updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
+ easeCamera(update, durationMs, true, callback);
+ }
+
+ @UiThread
+ public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) {
+ easeCamera(update, durationMs, easingInterpolator, null);
+ }
+
+ @UiThread
+ public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator, final MapboxMap.CancelableCallback callback) {
mCameraPosition = update.getCameraPosition(this);
- mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() {
+ mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, easingInterpolator, new CancelableCallback() {
@Override
public void onCancel() {
if (callback != null) {
@@ -307,12 +362,13 @@ public class MapboxMap {
}
/**
- * Animates the movement of the camera from the current position to the position defined in the update.
- * During the animation, a call to getCameraPosition() returns an intermediate location of the camera.
- * <p/>
- * See CameraUpdateFactory for a set of updates.
+ * 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 {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update) {
@@ -320,12 +376,16 @@ public class MapboxMap {
}
/**
- * Animates the movement of the camera from the current position to the position defined in the update and calls an optional callback on completion.
- * See CameraUpdateFactory for a set of updates.
- * During the animation, a call to getCameraPosition() returns an intermediate location of the camera.
+ * 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().
+ * @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 {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
@@ -333,11 +393,15 @@ public class MapboxMap {
}
/**
- * Moves the map according to the update with an animation over a specified duration. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * 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.
+ * @param durationMs The duration of the animation in milliseconds. This must be strictly
+ * positive, otherwise an IllegalArgumentException will be thrown.
+ * @see {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs) {
@@ -345,12 +409,22 @@ public class MapboxMap {
}
/**
- * Moves the map according to the update with an animation over a specified duration, and calls an optional callback on completion. See CameraUpdateFactory for a set of updates.
- * If getCameraPosition() is called during the animation, it will return the current location of the camera in flight.
+ * 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. The callback should not attempt to move or animate the camera in its cancellation method. If a callback isn't required, leave it as null.
+ * @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 {@link CameraUpdateFactory} for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
@@ -476,7 +550,7 @@ public class MapboxMap {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
+ * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -506,8 +580,10 @@ public class MapboxMap {
*
* @param style The bundled style. Accepts one of the values from {@link Style}.
* @see Style
+ * @deprecated use {@link #setStyleUrl(String)} instead with versioned url methods from {@link Style}
*/
@UiThread
+ @Deprecated
public void setStyle(@Style.StyleUrl String style) {
setStyleUrl(style);
}
@@ -532,22 +608,34 @@ public class MapboxMap {
/**
* <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
+ * <p>
* Sets the current Mapbox access token used to load map styles and tiles.
* </p>
*
* @param accessToken Your public Mapbox access token.
* @see MapView#setAccessToken(String)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
+ @Deprecated
@UiThread
public void setAccessToken(@NonNull String accessToken) {
mMapView.setAccessToken(accessToken);
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#getAccessToken()
+ * </p>
+ * <p>
* Returns the current Mapbox access token used to load map styles and tiles.
+ * </p>
*
* @return The current Mapbox access token.
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()}
*/
+ @Deprecated
@UiThread
@Nullable
public String getAccessToken() {
@@ -558,6 +646,12 @@ public class MapboxMap {
// Annotations
//
+ void setTilt(double tilt) {
+ mMarkerViewManager.setTilt((float) tilt);
+ mMapView.setTilt(tilt);
+ }
+
+
/**
* <p>
* Adds a marker to this map.
@@ -597,6 +691,27 @@ public class MapboxMap {
/**
* <p>
+ * Adds a marker to this map.
+ * </p>
+ * 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.
+ */
+ @UiThread
+ @NonNull
+ public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) {
+ MarkerView marker = prepareViewMarker(markerOptions);
+ long id = mMapView.addMarker(marker);
+ marker.setMapboxMap(this);
+ marker.setId(id);
+ mAnnotations.put(id, marker);
+ return marker;
+ }
+
+ /**
+ * <p>
* Adds multiple markers to this map.
* </p>
* The marker's icon is rendered on the map at the location {@code Marker.position}.
@@ -607,11 +722,11 @@ public class MapboxMap {
*/
@UiThread
@NonNull
- public List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) {
+ public List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList) {
int count = markerOptionsList.size();
List<Marker> markers = new ArrayList<>(count);
if (count > 0) {
- MarkerOptions markerOptions;
+ BaseMarkerOptions markerOptions;
Marker marker;
for (int i = 0; i < count; i++) {
markerOptions = markerOptionsList.get(i);
@@ -693,7 +808,7 @@ public class MapboxMap {
Polyline polyline;
List<Polyline> polylines = new ArrayList<>(count);
- if(count>0) {
+ if (count > 0) {
for (PolylineOptions options : polylineOptionsList) {
polyline = options.getPolyline();
if (!polyline.getPoints().isEmpty()) {
@@ -757,7 +872,7 @@ public class MapboxMap {
Polygon polygon;
List<Polygon> polygons = new ArrayList<>(count);
- if(count>0) {
+ if (count > 0) {
for (PolygonOptions polygonOptions : polygonOptionsList) {
polygon = polygonOptions.getPolygon();
if (!polygon.getPoints().isEmpty()) {
@@ -767,8 +882,8 @@ public class MapboxMap {
long[] ids = mMapView.addPolygons(polygons);
- // if unit tests or polygons correcly added to map
- if(ids==null || ids.length==polygons.size()) {
+ // if unit tests or polygons correctly added to map
+ if (ids == null || ids.length == polygons.size()) {
long id = 0;
for (int i = 0; i < polygons.size(); i++) {
polygon = polygons.get(i);
@@ -834,7 +949,11 @@ public class MapboxMap {
@UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
long id = annotation.getId();
mMapView.removeAnnotation(id);
@@ -864,7 +983,11 @@ public class MapboxMap {
for (int i = 0; i < count; i++) {
Annotation annotation = annotationList.get(i);
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
ids[i] = annotationList.get(i).getId();
}
@@ -886,7 +1009,11 @@ public class MapboxMap {
ids[i] = mAnnotations.keyAt(i);
annotation = mAnnotations.get(ids[i]);
if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
+ Marker marker = (Marker) annotation;
+ marker.hideInfoWindow();
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ }
}
}
mMapView.removeAnnotations(ids);
@@ -894,11 +1021,18 @@ public class MapboxMap {
}
/**
+ * Removes all markers, polylines, polygons, overlays, etc from the map.
+ */
+ @UiThread
+ public void clear() {
+ removeAnnotations();
+ }
+
+ /**
* Return a annotation based on its id.
*
* @return An annotation with a matched id, null is returned if no match was found.
*/
- @UiThread
@Nullable
public Annotation getAnnotation(long id) {
return mAnnotations.get(id);
@@ -989,8 +1123,7 @@ public class MapboxMap {
@UiThread
public void selectMarker(@NonNull Marker marker) {
if (marker == null) {
- Log.w(MapboxConstants.TAG, "marker was null, so just" +
- " returning");
+ Log.w(MapboxConstants.TAG, "marker was null, so just returning");
return;
}
@@ -1031,6 +1164,10 @@ public class MapboxMap {
if (marker.isInfoWindowShown()) {
marker.hideInfoWindow();
}
+
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.deselect((MarkerView) marker);
+ }
}
// Removes all selected markers from the list
@@ -1070,6 +1207,22 @@ public class MapboxMap {
return marker;
}
+ private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
+ MarkerView marker = markerViewOptions.getMarker();
+ Icon icon = IconFactory.recreate("markerViewSettings", mViewMarkerBitmap);
+ marker.setIcon(icon);
+ return marker;
+ }
+
+ /**
+ * Get the MarkerViewManager associated to the MapView.
+ *
+ * @return the associated MarkerViewManager
+ */
+ public MarkerViewManager getMarkerViewManager() {
+ return mMarkerViewManager;
+ }
+
//
// InfoWindow
//
@@ -1142,6 +1295,7 @@ public class MapboxMap {
* 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.
+ * </p>
*
* @param left The left margin in pixels.
* @param top The top margin in pixels.
@@ -1151,8 +1305,6 @@ public class MapboxMap {
public void setPadding(int left, int top, int right, int bottom) {
mMapView.setContentPadding(left, top, right, bottom);
mUiSettings.invalidate();
-
- moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(mCameraPosition).build()));
}
/**
@@ -1381,8 +1533,10 @@ public class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationServices#addLocationListener(LocationListener)})}
*/
@UiThread
+ @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
mMapView.setOnMyLocationChangeListener(listener);
}
@@ -1451,6 +1605,14 @@ public class MapboxMap {
return mMapView;
}
+ void setUiSettings(UiSettings uiSettings) {
+ mUiSettings = uiSettings;
+ }
+
+ void setProjection(Projection projection) {
+ mProjection = projection;
+ }
+
//
// Invalidate
//
@@ -1462,6 +1624,27 @@ public class MapboxMap {
mMapView.update();
}
+ /**
+ * Takes a snapshot of the map.
+ *
+ * @param callback Callback method invoked when the snapshot is taken.
+ * @param bitmap A pre-allocated bitmap.
+ */
+ @UiThread
+ public void snapshot(@NonNull SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
+ mMapView.snapshot(callback, bitmap);
+ }
+
+ /**
+ * Takes a snapshot of the map.
+ *
+ * @param callback Callback method invoked when the snapshot is taken.
+ */
+ @UiThread
+ public void snapshot(@NonNull SnapshotReadyCallback callback) {
+ mMapView.snapshot(callback, null);
+ }
+
//
// Interfaces
//
@@ -1624,10 +1807,92 @@ public class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked when an MarkerView will be shown.
+ *
+ * @param <U> the instance type of MarkerView
+ */
+ public static abstract class MarkerViewAdapter<U extends MarkerView> {
+
+ private Context context;
+ private final Class<U> persistentClass;
+ private final Pools.SimplePool<View> mViewReusePool;
+
+ /**
+ * Create an instance of MarkerViewAdapter.
+ *
+ * @param context the context associated to a MapView
+ */
+ @SuppressWarnings("unchecked")
+ public MarkerViewAdapter(Context context) {
+ this.context = context;
+ persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ mViewReusePool = new Pools.SimplePool<>(20);
+ }
+
+ /**
+ * Called when an MarkerView will be added to the MapView.
+ *
+ * @param marker the model representing the MarkerView
+ * @param convertView the reusable view
+ * @param parent the parent ViewGroup of the convertview
+ * @return the View that is adapted to the contents of MarkerView
+ */
+ @Nullable
+ public abstract View getView(@NonNull U marker, @NonNull View convertView, @NonNull ViewGroup parent);
+
+ /**
+ * Returns the generic type of the used MarkerView.
+ *
+ * @return the generic type
+ */
+ public Class<U> getMarkerClass() {
+ return persistentClass;
+ }
+
+ /**
+ * Returns the pool used to store reusable Views.
+ *
+ * @return the pool associated to this adapter
+ */
+ public Pools.SimplePool<View> getViewReusePool() {
+ return mViewReusePool;
+ }
+
+ /**
+ * Returns the context associated to the hosting MapView.
+ *
+ * @return the context used
+ */
+ public Context getContext() {
+ return context;
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the user clicks on a MarkerView.
+ *
+ * @see MarkerViewManager#setOnMarkerViewClickListener(OnMarkerViewClickListener)
+ */
+ public interface OnMarkerViewClickListener {
+
+ /**
+ * Called when the user clicks on a MarkerView.
+ *
+ * @param marker the MarkerView associated to the clicked View
+ * @param view the clicked View
+ * @param adapter the adapter used to adapt the MarkerView to the View
+ * @return If true the listener has consumed the event and the info window will not be shown
+ */
+ boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MarkerViewAdapter adapter);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the the My Location view changes location.
*
* @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener)
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationListener}
*/
+ @Deprecated
public interface OnMyLocationChangeListener {
/**
* Called when the location of the My Location view has changed
@@ -1683,6 +1948,16 @@ public class MapboxMap {
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.
+ */
+ void onSnapshotReady(Bitmap snapshot);
+ }
+
private class MapChangeCameraPositionListener implements MapView.OnMapChangedListener {
private static final long UPDATE_RATE_MS = 400;
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
index 6b7a0db8bd..17593129e7 100644
--- 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
@@ -2,16 +2,22 @@ package com.mapbox.mapboxsdk.maps;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Color;
+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.v4.content.ContextCompat;
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.ColorUtils;
+import java.util.Arrays;
/**
* Defines configuration MapboxMapMapOptions for a MapboxMap. These options can be used when adding a
@@ -40,6 +46,8 @@ public class MapboxMapOptions implements Parcelable {
private int logoGravity = Gravity.BOTTOM | Gravity.START;
private int logoMargins[];
+ @ColorInt
+ private int attributionTintColor = -1;
private boolean attributionEnabled = true;
private int attributionGravity = Gravity.BOTTOM;
private int attributionMargins[];
@@ -53,9 +61,18 @@ public class MapboxMapOptions implements Parcelable {
private boolean zoomGesturesEnabled = true;
private boolean zoomControlsEnabled = false;
- private boolean locationEnabled;
+ private boolean myLocationEnabled;
+ private Drawable myLocationForegroundDrawable;
+ private Drawable myLocationForegroundBearingDrawable;
+ private Drawable myLocationBackgroundDrawable;
+ private int myLocationForegroundTintColor;
+ private int myLocationBackgroundTintColor;
+ private int[] myLocationBackgroundPadding;
+ private int myLocationAccuracyTintColor;
+ private int myLocationAccuracyAlpha;
private String style;
+ @Deprecated
private String accessToken;
/**
@@ -79,6 +96,7 @@ public class MapboxMapOptions implements Parcelable {
attributionEnabled = in.readByte() != 0;
attributionGravity = in.readInt();
attributionMargins = in.createIntArray();
+ attributionTintColor = in.readInt();
minZoom = in.readFloat();
maxZoom = in.readFloat();
@@ -89,7 +107,15 @@ public class MapboxMapOptions implements Parcelable {
zoomControlsEnabled = in.readByte() != 0;
zoomGesturesEnabled = in.readByte() != 0;
- locationEnabled = in.readByte() != 0;
+ myLocationEnabled = in.readByte() != 0;
+ //myLocationForegroundDrawable;
+ //myLocationForegroundBearingDrawable;
+ //myLocationBackgroundDrawable;
+ myLocationForegroundTintColor = in.readInt();
+ myLocationBackgroundTintColor = in.readInt();
+ myLocationBackgroundPadding = in.createIntArray();
+ myLocationAccuracyAlpha = in.readInt();
+ myLocationAccuracyTintColor = in.readInt();
style = in.readString();
accessToken = in.readString();
@@ -137,6 +163,7 @@ public class MapboxMapOptions implements Parcelable {
, (int) (typedArray.getDimension(R.styleable.MapView_logo_margin_right, DIMENSION_SIXTEEN_DP) * screenDensity)
, (int) (typedArray.getDimension(R.styleable.MapView_logo_margin_bottom, DIMENSION_SIXTEEN_DP) * screenDensity)});
+ mapboxMapOptions.attributionTintColor(typedArray.getColor(R.styleable.MapView_attribution_tint, -1));
mapboxMapOptions.attributionEnabled(typedArray.getBoolean(R.styleable.MapView_attribution_enabled, true));
mapboxMapOptions.attributionGravity(typedArray.getInt(R.styleable.MapView_attribution_gravity, Gravity.BOTTOM));
mapboxMapOptions.attributionMargins(new int[]{(int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_left, DIMENSION_SEVENTY_SIX_DP) * screenDensity)
@@ -145,6 +172,32 @@ public class MapboxMapOptions implements Parcelable {
, (int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_bottom, DIMENSION_SEVEN_DP) * screenDensity)});
mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false));
+ mapboxMapOptions.myLocationForegroundTintColor(typedArray.getColor(R.styleable.MapView_my_location_foreground_tint, Color.TRANSPARENT));
+ mapboxMapOptions.myLocationBackgroundTintColor(typedArray.getColor(R.styleable.MapView_my_location_background_tint, Color.TRANSPARENT));
+
+ Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground);
+ if(foregroundDrawable==null){
+ foregroundDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_normal);
+ }
+
+ Drawable foregroundBearingDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground_bearing);
+ if(foregroundBearingDrawable==null){
+ foregroundBearingDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_bearing);
+ }
+
+ Drawable backgroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_background);
+ if(backgroundDrawable==null){
+ backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_mylocationview_background);
+ }
+
+ mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
+ mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable);
+ mapboxMapOptions.myLocationBackgroundPadding(new int[]{(int) (typedArray.getDimension(R.styleable.MapView_my_location_background_left, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_top, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_right, 0) * screenDensity)
+ , (int) (typedArray.getDimension(R.styleable.MapView_my_location_background_bottom, 0) * screenDensity)});
+ mapboxMapOptions.myLocationAccuracyAlpha(typedArray.getInt(R.styleable.MapView_my_location_accuracy_alpha, 100));
+ mapboxMapOptions.myLocationAccuracyTint(typedArray.getColor(R.styleable.MapView_my_location_accuracy_tint, ColorUtils.getPrimaryColor(context)));
} finally {
typedArray.recycle();
}
@@ -163,11 +216,15 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
* Specifies the accesstoken associated with a map view.
*
* @param accessToken Token to be used to access the service
* @return This
*/
+ @Deprecated
public MapboxMapOptions accessToken(String accessToken) {
this.accessToken = accessToken;
return this;
@@ -317,6 +374,17 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Specifies the tint color of the attribution for a map view
+ *
+ * @param color integer resembling a color
+ * @return This
+ */
+ 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
@@ -378,7 +446,73 @@ public class MapboxMapOptions implements Parcelable {
* @return This
*/
public MapboxMapOptions locationEnabled(boolean locationEnabled) {
- this.locationEnabled = locationEnabled;
+ this.myLocationEnabled = locationEnabled;
+ return this;
+ }
+
+ /**
+ *
+ * @param myLocationForegroundDrawable
+ * @param myLocationBearingDrawable
+ * @return This
+ */
+ public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, Drawable myLocationBearingDrawable ) {
+ this.myLocationForegroundDrawable = myLocationForegroundDrawable;
+ this.myLocationForegroundBearingDrawable = myLocationBearingDrawable;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundDrawable
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) {
+ this.myLocationBackgroundDrawable = myLocationBackgroundDrawable;
+ return this;
+ }
+
+ /**
+ * @param myLocationForegroundTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) {
+ this.myLocationForegroundTintColor = myLocationForegroundTintColor;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) {
+ this.myLocationBackgroundTintColor = myLocationBackgroundTintColor;
+ return this;
+ }
+
+ /**
+ * @param myLocationBackgroundPadding
+ * @return This
+ */
+ public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) {
+ this.myLocationBackgroundPadding = myLocationBackgroundPadding;
+ return this;
+ }
+
+ /**
+ * @param myLocationAccuracyTintColor
+ * @return This
+ */
+ public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) {
+ this.myLocationAccuracyTintColor = myLocationAccuracyTintColor;
+ return this;
+ }
+
+ /**
+ * @param alpha
+ * @return This
+ */
+ public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
+ this.myLocationAccuracyAlpha = alpha;
return this;
}
@@ -464,10 +598,14 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * <p>
+ * DEPRECATED @see MapboxAccountManager#start(String)
+ * </p>
* Get the current configured access token for a map view.
*
* @return Access token to be used.
*/
+ @Deprecated
public String getAccessToken() {
return accessToken;
}
@@ -553,13 +691,74 @@ public class MapboxMapOptions implements Parcelable {
return attributionMargins;
}
+ @ColorInt
+ public int getAttributionTintColor() {
+ return attributionTintColor;
+ }
+
/**
* Get the current configured user location view state for a map view.
*
* @return True and user location will be shown
*/
public boolean getLocationEnabled() {
- return locationEnabled;
+ return myLocationEnabled;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationForegroundDrawable() {
+ return myLocationForegroundDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationForegroundBearingDrawable() {
+ return myLocationForegroundBearingDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public Drawable getMyLocationBackgroundDrawable() {
+ return myLocationBackgroundDrawable;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationForegroundTintColor() {
+ return myLocationForegroundTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationBackgroundTintColor() {
+ return myLocationBackgroundTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int[] getMyLocationBackgroundPadding() {
+ return myLocationBackgroundPadding;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationAccuracyTintColor() {
+ return myLocationAccuracyTintColor;
+ }
+
+ /**
+ * @return
+ */
+ public int getMyLocationAccuracyAlpha() {
+ return myLocationAccuracyAlpha;
}
/**
@@ -603,6 +802,7 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (attributionEnabled ? 1 : 0));
dest.writeInt(attributionGravity);
dest.writeIntArray(attributionMargins);
+ dest.writeInt(attributionTintColor);
dest.writeFloat(minZoom);
dest.writeFloat(maxZoom);
@@ -613,9 +813,94 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (zoomControlsEnabled ? 1 : 0));
dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0));
- dest.writeByte((byte) (locationEnabled ? 1 : 0));
+ dest.writeByte((byte) (myLocationEnabled ? 1 : 0));
+ //myLocationForegroundDrawable;
+ //myLocationForegroundBearingDrawable;
+ //myLocationBackgroundDrawable;
+ dest.writeInt(myLocationForegroundTintColor);
+ dest.writeInt(myLocationBackgroundTintColor);
+ dest.writeIntArray(myLocationBackgroundPadding);
+ dest.writeInt(myLocationAccuracyAlpha);
+ dest.writeInt(myLocationAccuracyTintColor);
dest.writeString(style);
dest.writeString(accessToken);
}
+
+ @Override
+ public boolean equals(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 (compassGravity != options.compassGravity) return false;
+ if (logoEnabled != options.logoEnabled) return false;
+ if (logoGravity != options.logoGravity) return false;
+ if (attributionEnabled != options.attributionEnabled) return false;
+ if (attributionGravity != options.attributionGravity) return false;
+ if (Float.compare(options.minZoom, minZoom) != 0) return false;
+ if (Float.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 (zoomControlsEnabled != options.zoomControlsEnabled) return false;
+ if (myLocationEnabled != options.myLocationEnabled) return false;
+ if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) return false;
+ if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) return false;
+ if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) return false;
+ if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) 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 (myLocationForegroundDrawable != null ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable) : options.myLocationForegroundDrawable != null)
+ return false;
+ if (myLocationForegroundBearingDrawable != null ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable) : options.myLocationForegroundBearingDrawable != null)
+ return false;
+ if (myLocationBackgroundDrawable != null ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable) : options.myLocationBackgroundDrawable != null)
+ return false;
+ if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding))
+ return false;
+ if (style != null ? !style.equals(options.style) : options.style != null) return false;
+ return accessToken != null ? accessToken.equals(options.accessToken) : options.accessToken == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = cameraPosition != null ? cameraPosition.hashCode() : 0;
+ result = 31 * result + (debugActive ? 1 : 0);
+ result = 31 * result + (compassEnabled ? 1 : 0);
+ result = 31 * result + compassGravity;
+ 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 + (attributionEnabled ? 1 : 0);
+ result = 31 * result + attributionGravity;
+ result = 31 * result + Arrays.hashCode(attributionMargins);
+ result = 31 * result + (minZoom != +0.0f ? Float.floatToIntBits(minZoom) : 0);
+ result = 31 * result + (maxZoom != +0.0f ? Float.floatToIntBits(maxZoom) : 0);
+ 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 + (zoomControlsEnabled ? 1 : 0);
+ result = 31 * result + (myLocationEnabled ? 1 : 0);
+ result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0);
+ result = 31 * result + (myLocationForegroundBearingDrawable != null ? myLocationForegroundBearingDrawable.hashCode() : 0);
+ result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0);
+ result = 31 * result + myLocationForegroundTintColor;
+ result = 31 * result + myLocationBackgroundTintColor;
+ result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding);
+ result = 31 * result + myLocationAccuracyTintColor;
+ result = 31 * result + myLocationAccuracyAlpha;
+ result = 31 * result + (style != null ? style.hashCode() : 0);
+ result = 31 * result + (accessToken != null ? accessToken.hashCode() : 0);
+ return result;
+ }
}
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
index c6ac6dc6e2..6c092ee0c8 100755
--- 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
@@ -119,24 +119,12 @@ final class NativeMapView {
nativeDestroySurface(mNativeMapViewPtr);
}
- public void pause() {
- nativePause(mNativeMapViewPtr);
- }
-
- public boolean isPaused() {
- return nativeIsPaused(mNativeMapViewPtr);
- }
-
- public void resume() {
- nativeResume(mNativeMapViewPtr);
- }
-
public void update() {
nativeUpdate(mNativeMapViewPtr);
}
- public void renderSync() {
- nativeRenderSync(mNativeMapViewPtr);
+ public void render() {
+ nativeRender(mNativeMapViewPtr);
}
public void resizeView(int width, int height) {
@@ -201,34 +189,12 @@ final class NativeMapView {
return nativeGetClasses(mNativeMapViewPtr);
}
- public void setDefaultTransitionDuration() {
- setDefaultTransitionDuration(0);
- }
-
- public long getDefaultTransitionDuration() {
- return nativeGetDefaultTransitionDuration(mNativeMapViewPtr);
- }
-
- public void setDefaultTransitionDuration(long milliseconds) {
- if (milliseconds < 0) {
- throw new IllegalArgumentException(
- "milliseconds cannot be negative.");
- }
-
- nativeSetDefaultTransitionDuration(mNativeMapViewPtr,
- milliseconds);
- }
-
public void setStyleUrl(String url) {
nativeSetStyleUrl(mNativeMapViewPtr, url);
}
public void setStyleJson(String newStyleJson) {
- setStyleJson(newStyleJson, "");
- }
-
- public void setStyleJson(String newStyleJson, String base) {
- nativeSetStyleJson(mNativeMapViewPtr, newStyleJson, base);
+ nativeSetStyleJson(mNativeMapViewPtr, newStyleJson);
}
public String getStyleJson() {
@@ -377,27 +343,30 @@ final class NativeMapView {
}
public long addMarker(Marker marker) {
- return nativeAddMarker(mNativeMapViewPtr, marker);
+ Marker[] markers = { marker };
+ return nativeAddMarkers(mNativeMapViewPtr, markers)[0];
}
public long[] addMarkers(List<Marker> markers) {
- return nativeAddMarkers(mNativeMapViewPtr, markers);
+ return nativeAddMarkers(mNativeMapViewPtr, markers.toArray(new Marker[markers.size()]));
}
public long addPolyline(Polyline polyline) {
- return nativeAddPolyline(mNativeMapViewPtr, polyline);
+ Polyline[] polylines = { polyline };
+ return nativeAddPolylines(mNativeMapViewPtr, polylines)[0];
}
public long[] addPolylines(List<Polyline> polylines) {
- return nativeAddPolylines(mNativeMapViewPtr, polylines);
+ return nativeAddPolylines(mNativeMapViewPtr, polylines.toArray(new Polyline[polylines.size()]));
}
public long addPolygon(Polygon polygon) {
- return nativeAddPolygon(mNativeMapViewPtr, polygon);
+ Polygon[] polygons = { polygon };
+ return nativeAddPolygons(mNativeMapViewPtr, polygons)[0];
}
- public long[] addPolygons(List<Polygon> polygon) {
- return nativeAddPolygons(mNativeMapViewPtr, polygon);
+ public long[] addPolygons(List<Polygon> polygons) {
+ return nativeAddPolygons(mNativeMapViewPtr, polygons.toArray(new Polygon[polygons.size()]));
}
public void updateMarker(Marker marker) {
@@ -405,7 +374,8 @@ final class NativeMapView {
}
public void removeAnnotation(long id) {
- nativeRemoveAnnotation(mNativeMapViewPtr, id);
+ long[] ids = { id };
+ removeAnnotations(ids);
}
public void removeAnnotations(long[] ids) {
@@ -476,8 +446,8 @@ final class NativeMapView {
nativeJumpTo(mNativeMapViewPtr, angle, center, pitch, zoom);
}
- public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom) {
- nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom);
+ public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator) {
+ nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom, easingInterpolator);
}
public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) {
@@ -533,15 +503,9 @@ final class NativeMapView {
private native void nativeDestroySurface(long nativeMapViewPtr);
- private native void nativePause(long nativeMapViewPtr);
-
- private native boolean nativeIsPaused(long nativeMapViewPtr);
-
- private native void nativeResume(long nativeMapViewPtr);
-
private native void nativeUpdate(long nativeMapViewPtr);
- private native void nativeRenderSync(long nativeMapViewPtr);
+ private native void nativeRender(long nativeMapViewPtr);
private native void nativeViewResize(long nativeMapViewPtr, int width, int height);
@@ -558,15 +522,9 @@ final class NativeMapView {
private native List<String> nativeGetClasses(long nativeMapViewPtr);
- private native void nativeSetDefaultTransitionDuration(
- long nativeMapViewPtr, long duration);
-
- private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr);
-
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
- private native void nativeSetStyleJson(long nativeMapViewPtr,
- String newStyleJson, String base);
+ private native void nativeSetStyleJson(long nativeMapViewPtr, String newStyleJson);
private native String nativeGetStyleJson(long nativeMapViewPtr);
@@ -630,21 +588,13 @@ final class NativeMapView {
private native void nativeResetNorth(long nativeMapViewPtr);
- private native long nativeAddMarker(long nativeMapViewPtr, Marker marker);
-
private native void nativeUpdateMarker(long nativeMapViewPtr, Marker marker);
- private native long[] nativeAddMarkers(long nativeMapViewPtr, List<Marker> markers);
-
- private native long nativeAddPolyline(long nativeMapViewPtr, Polyline polyline);
-
- private native long[] nativeAddPolylines(long mNativeMapViewPtr, List<Polyline> polygon);
-
- private native long nativeAddPolygon(long mNativeMapViewPtr, Polygon polygon);
+ private native long[] nativeAddMarkers(long nativeMapViewPtr, Marker[] markers);
- private native long[] nativeAddPolygons(long mNativeMapViewPtr, List<Polygon> polygon);
+ private native long[] nativeAddPolylines(long mNativeMapViewPtr, Polyline[] polylines);
- private native void nativeRemoveAnnotation(long nativeMapViewPtr, long id);
+ private native long[] nativeAddPolygons(long mNativeMapViewPtr, Polygon[] polygons);
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
@@ -682,7 +632,7 @@ final class NativeMapView {
private native void nativeJumpTo(long nativeMapViewPtr, double angle, LatLng center, double pitch, double zoom);
- private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
+ private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator);
private native void nativeFlyTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
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
index 9482b1a2f7..d37c3a02ea 100644
--- 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
@@ -57,13 +57,15 @@ public class Projection {
public VisibleRegion getVisibleRegion() {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
- int viewportWidth = mMapView.getWidth();
- int viewportHeight = mMapView.getHeight();
+ float left = mMapView.getContentPaddingLeft();
+ float right = mMapView.getWidth() - mMapView.getContentPaddingRight();
+ float top = mMapView.getContentPaddingTop();
+ float bottom = mMapView.getHeight() - mMapView.getContentPaddingBottom();
- LatLng topLeft = fromScreenLocation(new PointF(0, 0));
- LatLng topRight = fromScreenLocation(new PointF(viewportWidth, 0));
- LatLng bottomRight = fromScreenLocation(new PointF(viewportWidth, viewportHeight));
- LatLng bottomLeft = fromScreenLocation(new PointF(0, viewportHeight));
+ 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));
builder.include(topLeft)
.include(topRight)
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
index ffdb57de8c..3d96727758 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
@@ -1,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
/**
* Support Fragment wrapper around a map view.
@@ -32,6 +37,15 @@ public class SupportMapFragment extends Fragment {
/**
* Creates a MapFragment instance
*
+ * @return MapFragment created
+ */
+ public static SupportMapFragment newInstance() {
+ return new SupportMapFragment();
+ }
+
+ /**
+ * Creates a MapFragment instance
+ *
* @param mapboxMapOptions The configuration options to be used.
* @return MapFragment created.
*/
@@ -44,7 +58,7 @@ public class SupportMapFragment extends Fragment {
}
/**
- * Creates the fragment view hierachy.
+ * Creates the fragment view hierarchy.
*
* @param inflater Inflater used to inflate content.
* @param container The parent layout for the map fragment.
@@ -54,11 +68,66 @@ public class SupportMapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- MapboxMapOptions options = getArguments().getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ MapboxMapOptions options = null;
+
+ // Get bundle
+ Bundle bundle = getArguments();
+ if (bundle != null && bundle.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) {
+ options = bundle.getParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS);
+ }
+
+ // Assign an AccessToken if needed
+ if (options == null || options.getAccessToken() == null) {
+ String token = null;
+ if (MapboxAccountManager.getInstance() != null) {
+ token = MapboxAccountManager.getInstance().getAccessToken();
+ } else {
+ token = getToken(inflater.getContext());
+ }
+ if (TextUtils.isEmpty(token)) {
+ throw new InvalidAccessTokenException();
+ }
+ if (options == null) {
+ options = new MapboxMapOptions().accessToken(token);
+ } else {
+ options.accessToken(token);
+ }
+ }
return mMap = new MapView(inflater.getContext(), options);
}
/**
+ * <p>
+ * Returns the Mapbox access token set in the app resources.
+ * </p>
+ * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
+ * meta-data value. If not found it will then attempt to load the access token from the
+ * {@code res/raw/token.txt} development file.
+ *
+ * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
+ * @return The Mapbox access token or null if not found.
+ * @see MapboxConstants#KEY_META_DATA_MANIFEST
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
+ */
+ @Deprecated
+ private String getToken(@NonNull Context context) {
+ try {
+ // read out AndroidManifest
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
+ if (token == null || token.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ return token;
+ } catch (Exception e) {
+ // use fallback on string resource, used for development
+ int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
+ return tokenResId != 0 ? context.getString(tokenResId) : null;
+ }
+ }
+
+ /**
* Called when the fragment view hierarchy is created.
*
* @param view The content view of the fragment
@@ -142,4 +211,4 @@ public class SupportMapFragment extends Fragment {
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
mOnMapReadyCallback = onMapReadyCallback;
}
-} \ No newline at end of file
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index 90147929e9..30492bc421 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -5,7 +5,7 @@ import android.support.annotation.UiThread;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
/**
* Settings for the user location and bearing tracking of a MapboxMap.
@@ -14,13 +14,14 @@ public class TrackingSettings {
private MapView mapView;
private UiSettings uiSettings;
- private boolean dismissTrackingOnGesture = true;
+ private boolean dismissLocationTrackingOnGesture = true;
+ private boolean dismissBearingTrackingOnGesture = true;
@MyLocationTracking.Mode
- private int mMyLocationTrackingMode;
+ private int myLocationTrackingMode;
@MyBearingTracking.Mode
- private int mMyBearingTrackingMode;
+ private int myBearingTrackingMode;
TrackingSettings(@NonNull MapView mapView, UiSettings uiSettings) {
this.mapView = mapView;
@@ -42,9 +43,9 @@ public class TrackingSettings {
*/
@UiThread
public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- mMyLocationTrackingMode = myLocationTrackingMode;
+ this.myLocationTrackingMode = myLocationTrackingMode;
mapView.setMyLocationTrackingMode(myLocationTrackingMode);
- validateGesturesForTrackingModes();
+ validateGesturesForLocationTrackingMode();
}
/**
@@ -57,7 +58,7 @@ public class TrackingSettings {
@UiThread
@MyLocationTracking.Mode
public int getMyLocationTrackingMode() {
- return mMyLocationTrackingMode;
+ return myLocationTrackingMode;
}
/**
@@ -66,7 +67,7 @@ public class TrackingSettings {
* </p>
* Shows the direction the user is heading.
* <p>
- * When location tracking is disabled the direction of {@link UserLocationView} is rotated
+ * When location tracking is disabled the direction of {@link MyLocationView} is rotated
* When location tracking is enabled the {@link MapView} is rotated based on bearing value.
* </p>
* See {@link MyBearingTracking} for different values.
@@ -77,8 +78,9 @@ public class TrackingSettings {
*/
@UiThread
public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- mMyBearingTrackingMode = myBearingTrackingMode;
+ this.myBearingTrackingMode = myBearingTrackingMode;
mapView.setMyBearingTrackingMode(myBearingTrackingMode);
+ validateGesturesForBearingTrackingMode();
}
/**
@@ -89,52 +91,137 @@ public class TrackingSettings {
* @see MyBearingTracking
*/
@UiThread
- @MyLocationTracking.Mode
+ @MyBearingTracking.Mode
public int getMyBearingTrackingMode() {
- return mMyBearingTrackingMode;
+ return myBearingTrackingMode;
}
/**
* Returns if the tracking modes will be dismissed when a gesture occurs.
*
* @return True to indicate the tracking modes will be dismissed.
- */
+ * @deprecated use @link #isAllDismissTrackingOnGestureinstead
+ */
+ @Deprecated
public boolean isDismissTrackingOnGesture() {
- return dismissTrackingOnGesture;
+ return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
+ }
+
+ /**
+ * Returns if all tracking modes will be dismissed when a gesture occurs.
+ *
+ * @return True to indicate that location and bearing tracking will be dismissed.
+ */
+ public boolean isAllDismissTrackingOnGesture() {
+ return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
}
/**
* Set the dismissal of the tracking modes if a gesture occurs.
*
* @param dismissTrackingOnGesture True to dismiss the tracking modes.
+ * @deprecated use @link #setDismissAllTrackingOnGesture instead
*/
+ @Deprecated
public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) {
- this.dismissTrackingOnGesture = dismissTrackingOnGesture;
- validateGesturesForTrackingModes();
+ setDismissAllTrackingOnGesture(dismissTrackingOnGesture);
+ }
+
+ /**
+ * Set the dismissal of the tracking modes if a gesture occurs.
+ *
+ * @param dismissTrackingOnGesture True to dismiss all the tracking modes.
+ */
+ public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) {
+ dismissLocationTrackingOnGesture = dismissTrackingOnGesture;
+ dismissBearingTrackingOnGesture = dismissTrackingOnGesture;
+ validateAllGesturesForTrackingModes();
+ }
+
+ /**
+ * Set the dismissal of the tracking modes if a gesture occurs.
+ *
+ * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode.
+ */
+ public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) {
+ this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture;
+ validateGesturesForLocationTrackingMode();
+ }
+
+ /**
+ * Returns if the location tracking will be disabled when a gesture occurs
+ *
+ * @return True if location tracking will be disabled.
+ */
+ public boolean isDismissLocationTrackingOnGesture() {
+ return dismissLocationTrackingOnGesture;
+ }
+
+ /**
+ * Set the dismissal of the bearing tracking modes if a gesture occurs.
+ *
+ * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode
+ */
+ public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) {
+ this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture;
+ validateGesturesForBearingTrackingMode();
}
- private void validateGesturesForTrackingModes() {
- if (!dismissTrackingOnGesture) {
- int myLocationTrackingMode = getMyLocationTrackingMode();
- int myBearingTrackingMode = getMyBearingTrackingMode();
+ /**
+ * Returns if bearing will disabled when a gesture occurs
+ *
+ * @return True if bearing tracking will be disabled
+ */
+ public boolean isDismissBearingTrackingOnGesture() {
+ return dismissBearingTrackingOnGesture;
+ }
- // Enable/disable gestures based on tracking mode
+ /**
+ * Returns if location tracking is disabled
+ *
+ * @return True if location tracking is disabled.
+ */
+ public boolean isLocationTrackingDisabled() {
+ return myLocationTrackingMode == MyLocationTracking.TRACKING_NONE;
+ }
+
+ /**
+ * Retyrns uf bearing tracking is disabled
+ *
+ * @return True if bearing tracking will be disabled.
+ */
+ public boolean isBearingTrackingDisabled() {
+ return myBearingTrackingMode == MyBearingTracking.NONE;
+ }
+
+ private void validateAllGesturesForTrackingModes() {
+ validateGesturesForBearingTrackingMode();
+ validateGesturesForLocationTrackingMode();
+ }
+
+ private void validateGesturesForLocationTrackingMode() {
+ int myLocationTrackingMode = getMyLocationTrackingMode();
+ if (!dismissLocationTrackingOnGesture) {
if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
uiSettings.setScrollGesturesEnabled(true);
- uiSettings.setRotateGesturesEnabled(true);
} else {
uiSettings.setScrollGesturesEnabled(false);
- uiSettings.setRotateGesturesEnabled((myBearingTrackingMode == MyBearingTracking.NONE));
}
+ } else {
+ uiSettings.setScrollGesturesEnabled(true);
}
}
- /**
- * Return if location tracking is disabled
- *
- * @return True if location tracking is disabled.
- */
- public boolean isLocationTrackingDisabled() {
- return mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE;
+ private void validateGesturesForBearingTrackingMode() {
+ int myBearingTrackingMode = getMyBearingTrackingMode();
+ if (!dismissBearingTrackingOnGesture) {
+ if (myBearingTrackingMode == MyBearingTracking.NONE) {
+ uiSettings.setRotateGesturesEnabled(true);
+ } else {
+ uiSettings.setRotateGesturesEnabled(false);
+ }
+ } else {
+ uiSettings.setRotateGesturesEnabled(true);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
index f87ddb4ca1..3cd9efb13e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
@@ -1,13 +1,12 @@
package com.mapbox.mapboxsdk.maps;
-import android.support.annotation.FloatRange;
+import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
/**
* Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings().
@@ -20,11 +19,19 @@ public class UiSettings {
private ViewSettings logoSettings;
private ViewSettings attributionSettings;
- private boolean rotateGesturesEnabled;
- private boolean tiltGesturesEnabled;
- private boolean zoomGesturesEnabled;
+ private boolean rotateGesturesEnabled = true;
+ private boolean rotateGestureChangeAllowed = true;
+
+ private boolean tiltGesturesEnabled = true;
+ private boolean tiltGestureChangeAllowed = true;
+
+ private boolean zoomGesturesEnabled = true;
+ private boolean zoomGestureChangeAllowed = true;
+
+ private boolean scrollGesturesEnabled = true;
+ private boolean scrollGestureChangeAllowed = true;
+
private boolean zoomControlsEnabled;
- private boolean scrollGesturesEnabled;
UiSettings(@NonNull MapView mapView) {
this.mapView = mapView;
@@ -232,11 +239,11 @@ public class UiSettings {
/**
* <p>
- * Enables or disables the Mapbox logo.
+ * Enables or disables the attribution.
* </p>
- * By default, the compass is enabled.
+ * By default, the attribution is enabled.
*
- * @param enabled True to enable the logo; false to disable the logo.
+ * @param enabled True to enable the attribution; false to disable the attribution.
*/
public void setAttributionEnabled(boolean enabled) {
attributionSettings.setEnabled(enabled);
@@ -244,9 +251,9 @@ public class UiSettings {
}
/**
- * Returns whether the logo is enabled.
+ * Returns whether the attribution is enabled.
*
- * @return True if the logo is enabled; false if the logo is disabled.
+ * @return True if the attribution is enabled; false if the attribution is disabled.
*/
public boolean isAttributionEnabled() {
return attributionSettings.isEnabled();
@@ -254,10 +261,9 @@ public class UiSettings {
/**
* <p>
- * Sets the gravity of the logo view. Use this to change the corner of the map view that the
- * Mapbox logo is displayed in.
+ * Sets the gravity of the attribution.
* </p>
- * By default, the logo is in the bottom left corner.
+ * By default, the attribution is in the bottom left corner next to the Mapbox logo.
*
* @param gravity One of the values from {@link Gravity}.
* @see Gravity
@@ -277,8 +283,7 @@ public class UiSettings {
}
/**
- * Sets the margins of the logo view. Use this to change the distance of the Mapbox logo from the
- * map view edge.
+ * Sets the margins of the attribution view.
*
* @param left The left margin in pixels.
* @param top The top margin in pixels.
@@ -291,7 +296,29 @@ public class UiSettings {
}
/**
- * Returns the left side margin of the logo
+ * <p>
+ * Sets the tint of the attribution view. Use this to change the color of the attribution.
+ * </p>
+ * By default, the logo is tinted with the primary color of your theme.
+ *
+ * @param tintColor Color to tint the attribution.
+ */
+ public void setAttributionTintColor(@ColorInt int tintColor) {
+ attributionSettings.setTintColor(tintColor);
+ mapView.setAtttibutionTintColor(tintColor);
+ }
+
+ /**
+ * Returns the tint color value of the attribution view.
+ *
+ * @return The tint color
+ */
+ public int getAttributionTintColor() {
+ return attributionSettings.getTintColor();
+ }
+
+ /**
+ * Returns the left side margin of the attribution view.
*
* @return The left margin in pixels
*/
@@ -300,7 +327,7 @@ public class UiSettings {
}
/**
- * Returns the top side margin of the logo
+ * Returns the top side margin of the attribution view.
*
* @return The top margin in pixels
*/
@@ -309,7 +336,7 @@ public class UiSettings {
}
/**
- * Returns the right side margin of the logo
+ * Returns the right side margin of the attribution view.
*
* @return The right margin in pixels
*/
@@ -339,7 +366,9 @@ public class UiSettings {
* @param rotateGesturesEnabled If true, rotating is enabled.
*/
public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) {
- this.rotateGesturesEnabled = rotateGesturesEnabled;
+ if (rotateGestureChangeAllowed) {
+ this.rotateGesturesEnabled = rotateGesturesEnabled;
+ }
}
/**
@@ -351,6 +380,14 @@ public class UiSettings {
return rotateGesturesEnabled;
}
+ void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) {
+ this.rotateGestureChangeAllowed = rotateGestureChangeAllowed;
+ }
+
+ boolean isRotateGestureChangeAllowed() {
+ return rotateGestureChangeAllowed;
+ }
+
/**
* <p>
* Changes whether the user may tilt the map.
@@ -364,7 +401,9 @@ public class UiSettings {
* @param tiltGesturesEnabled If true, tilting is enabled.
*/
public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) {
- this.tiltGesturesEnabled = tiltGesturesEnabled;
+ if (tiltGestureChangeAllowed) {
+ this.tiltGesturesEnabled = tiltGesturesEnabled;
+ }
}
/**
@@ -376,6 +415,14 @@ public class UiSettings {
return tiltGesturesEnabled;
}
+ void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) {
+ this.tiltGestureChangeAllowed = tiltGestureChangeAllowed;
+ }
+
+ boolean isTiltGestureChangeAllowed() {
+ return tiltGestureChangeAllowed;
+ }
+
/**
* <p>
* Changes whether the user may zoom the map.
@@ -389,7 +436,9 @@ public class UiSettings {
* @param zoomGesturesEnabled If true, zooming is enabled.
*/
public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) {
- this.zoomGesturesEnabled = zoomGesturesEnabled;
+ if (zoomGestureChangeAllowed) {
+ this.zoomGesturesEnabled = zoomGesturesEnabled;
+ }
}
/**
@@ -401,6 +450,14 @@ public class UiSettings {
return zoomGesturesEnabled;
}
+ void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) {
+ this.zoomGestureChangeAllowed = zoomGestureChangeAllowed;
+ }
+
+ boolean isZoomGestureChangeAllowed() {
+ return zoomGestureChangeAllowed;
+ }
+
/**
* <p>
* Sets whether the zoom controls are enabled.
@@ -439,7 +496,9 @@ public class UiSettings {
* @param scrollGesturesEnabled If true, scrolling is enabled.
*/
public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) {
- this.scrollGesturesEnabled = scrollGesturesEnabled;
+ if (scrollGestureChangeAllowed) {
+ this.scrollGesturesEnabled = scrollGesturesEnabled;
+ }
}
/**
@@ -451,6 +510,14 @@ public class UiSettings {
return scrollGesturesEnabled;
}
+ void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) {
+ this.scrollGestureChangeAllowed = scrollGestureChangeAllowed;
+ }
+
+ boolean isScrollGestureChangeAllowed() {
+ return scrollGestureChangeAllowed;
+ }
+
/**
* <p>
* Sets the preference for whether all gestures should be enabled or disabled.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java
index 0726b7bbbf..f08a1bdeb4 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java
@@ -8,6 +8,7 @@ class ViewSettings {
private boolean enabled;
private int gravity;
private int[]margins;
+ private int tintColor;
public ViewSettings() {
margins = new int[4];
@@ -36,4 +37,12 @@ class ViewSettings {
public void setMargins(int[] margins) {
this.margins = margins;
}
+
+ public int getTintColor() {
+ return tintColor;
+ }
+
+ public void setTintColor(int tintColor) {
+ this.tintColor = tintColor;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
new file mode 100644
index 0000000000..aed4e87c07
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -0,0 +1,705 @@
+package com.mapbox.mapboxsdk.maps.widgets;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.location.Location;
+import android.os.SystemClock;
+import android.support.annotation.ColorInt;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MyBearingTracking;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * UI element overlaid on a map to show the user's location.
+ */
+public class MyLocationView extends View {
+
+ private MyLocationBehavior myLocationBehavior;
+ private MapboxMap mapboxMap;
+ private Projection projection;
+ private int maxSize;
+ private int[] contentPadding = new int[4];
+
+ private Location location;
+ private LatLng latLng;
+ private LatLng interpolatedLocation;
+ private LatLng previousLocation;
+ private long locationUpdateTimestamp;
+
+ private float gpsDirection;
+ private float previousDirection;
+
+ private float accuracy = 0;
+ private Paint accuracyPaint = new Paint();
+
+ private ValueAnimator locationChangeAnimator;
+ private ValueAnimator accuracyAnimator;
+ private ObjectAnimator directionAnimator;
+
+ private Drawable foregroundDrawable;
+ private Drawable foregroundBearingDrawable;
+ private Drawable backgroundDrawable;
+
+ private int foregroundTintColor;
+ private int backgroundTintColor;
+
+ private Rect foregroundBounds;
+ private Rect backgroundBounds;
+
+ private int backgroundOffsetLeft;
+ private int backgroundOffsetTop;
+ private int backgroundOffsetRight;
+ private int backgroundOffsetBottom;
+
+ @MyLocationTracking.Mode
+ private int myLocationTrackingMode;
+
+ @MyBearingTracking.Mode
+ private int myBearingTrackingMode;
+
+ private GpsLocationListener userLocationListener;
+ private CompassListener compassListener;
+
+ public MyLocationView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public MyLocationView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ setEnabled(false);
+ myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE);
+ compassListener = new CompassListener(context);
+ maxSize = (int) context.getResources().getDimension(R.dimen.my_locationview_size);
+ }
+
+ public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) {
+ if (defaultDrawable == null || bearingDrawable == null) {
+ return;
+ }
+ if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) {
+ throw new RuntimeException("The dimensions from location and bearing drawables should be match");
+ }
+
+ foregroundDrawable = defaultDrawable;
+ foregroundBearingDrawable = bearingDrawable;
+ setForegroundDrawableTint(foregroundTintColor);
+
+ invalidateBounds();
+ }
+
+ public final void setForegroundDrawableTint(@ColorInt int color) {
+ if (color != Color.TRANSPARENT) {
+ foregroundTintColor = color;
+ if (foregroundDrawable != null) {
+ foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ if (foregroundBearingDrawable != null) {
+ foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ }
+ invalidate();
+ }
+
+ public final void setShadowDrawable(Drawable drawable) {
+ setShadowDrawable(drawable, 0, 0, 0, 0);
+ }
+
+ public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) {
+ if (drawable != null) {
+ backgroundDrawable = drawable;
+ }
+
+ backgroundOffsetLeft = left;
+ backgroundOffsetTop = top;
+ backgroundOffsetRight = right;
+ backgroundOffsetBottom = bottom;
+
+ setShadowDrawableTint(backgroundTintColor);
+
+ invalidateBounds();
+ }
+
+ public final void setShadowDrawableTint(@ColorInt int color) {
+ if (color != Color.TRANSPARENT) {
+ backgroundTintColor = color;
+ if (backgroundDrawable == null) {
+ return;
+ }
+ backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ invalidate();
+ }
+
+ public final void setAccuracyTint(@ColorInt int color) {
+ int alpha = accuracyPaint.getAlpha();
+ accuracyPaint.setColor(color);
+ accuracyPaint.setAlpha(alpha);
+ invalidate();
+ }
+
+ public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
+ accuracyPaint.setAlpha(alpha);
+ invalidate();
+ }
+
+ private void invalidateBounds() {
+ if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) {
+ return;
+ }
+
+ int backgroundWidth = backgroundDrawable.getIntrinsicWidth();
+ int backgroundHeight = backgroundDrawable.getIntrinsicHeight();
+
+ int foregroundWidth = foregroundDrawable.getIntrinsicWidth();
+ int foregroundHeight = foregroundDrawable.getIntrinsicHeight();
+
+ int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight;
+ int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom;
+
+ int accuracyWidth = 2 * maxSize;
+
+ backgroundBounds = new Rect(accuracyWidth - (backgroundWidth / 2) + horizontalOffset, accuracyWidth + verticalOffset - (backgroundWidth / 2), accuracyWidth + (backgroundWidth / 2) + horizontalOffset, accuracyWidth + (backgroundHeight / 2) + verticalOffset);
+ foregroundBounds = new Rect(accuracyWidth - (foregroundWidth / 2), accuracyWidth - (foregroundHeight / 2), accuracyWidth + (foregroundWidth / 2), accuracyWidth + (foregroundHeight / 2));
+
+ // invoke a new measure
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null) {
+ // Not ready yet
+ return;
+ }
+
+ // Draw circle
+ float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude());
+ float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel;
+ float maxRadius = getWidth() / 2;
+ canvas.drawCircle(foregroundBounds.centerX(), foregroundBounds.centerY(), accuracyPixels <= maxRadius ? accuracyPixels : maxRadius, accuracyPaint);
+
+ // Draw shadow
+ if (backgroundDrawable != null) {
+ backgroundDrawable.draw(canvas);
+ }
+
+ // Draw foreground
+ if (myBearingTrackingMode == MyBearingTracking.NONE) {
+ if (foregroundDrawable != null) {
+ foregroundDrawable.draw(canvas);
+ }
+ } else if (foregroundBearingDrawable != null && foregroundBounds != null) {
+ foregroundBearingDrawable.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (foregroundDrawable != null && foregroundBounds != null) {
+ foregroundDrawable.setBounds(foregroundBounds);
+ }
+
+ if (foregroundBearingDrawable != null && foregroundBounds != null) {
+ foregroundBearingDrawable.setBounds(foregroundBounds);
+ }
+
+ if (backgroundDrawable != null && backgroundBounds != null) {
+ backgroundDrawable.setBounds(backgroundBounds);
+ }
+
+ setMeasuredDimension(4 * maxSize, 4 * maxSize);
+ }
+
+ public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) {
+ setRotationX((float) tilt);
+ }
+
+ void updateOnNextFrame() {
+ mapboxMap.invalidate();
+ }
+
+ public void onPause() {
+ compassListener.onPause();
+ toggleGps(false);
+ }
+
+ public void onResume() {
+ if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ compassListener.onResume();
+ }
+ if (isEnabled()) {
+ toggleGps(true);
+ }
+ }
+
+ public void update() {
+ if (isEnabled()) {
+ myLocationBehavior.invalidate();
+ } else {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void setMapboxMap(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ this.projection = mapboxMap.getProjection();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+ toggleGps(enabled);
+ }
+
+ /**
+ * Enabled / Disable GPS location updates along with updating the UI
+ *
+ * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
+ */
+ private void toggleGps(boolean enableGps) {
+ LocationServices locationServices = LocationServices.getLocationServices(getContext());
+ if (enableGps) {
+ // Set an initial location if one available
+ Location lastLocation = locationServices.getLastLocation();
+
+ if (lastLocation != null) {
+ setLocation(lastLocation);
+ }
+
+ if (userLocationListener == null) {
+ userLocationListener = new GpsLocationListener(this);
+ }
+
+ locationServices.addLocationListener(userLocationListener);
+ } else {
+ // Disable location and user dot
+ location = null;
+ locationServices.removeLocationListener(userLocationListener);
+ }
+
+ locationServices.toggleGPS(enableGps);
+ }
+
+ public Location getLocation() {
+ return location;
+ }
+
+ public void setLocation(Location location) {
+ if (location == null) {
+ this.location = null;
+ return;
+ }
+
+ this.location = location;
+ myLocationBehavior.updateLatLng(location);
+ }
+
+ public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
+ this.myBearingTrackingMode = myBearingTrackingMode;
+ if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ compassListener.onResume();
+ } else {
+ compassListener.onPause();
+ if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
+ // always face north
+ gpsDirection = 0;
+ setCompass(gpsDirection);
+ }
+ }
+ invalidate();
+ update();
+ }
+
+ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
+ this.myLocationTrackingMode = myLocationTrackingMode;
+
+ MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory();
+ myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode);
+
+ if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && location != null) {
+ // center map directly if we have a location fix
+ myLocationBehavior.updateLatLng(location);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location)));
+ }
+ invalidate();
+ update();
+ }
+
+ private void setCompass(float bearing) {
+ float oldDir = previousDirection;
+ if (directionAnimator != null) {
+ oldDir = (Float) directionAnimator.getAnimatedValue();
+ directionAnimator.end();
+ directionAnimator = null;
+ }
+
+ float newDir = bearing;
+ float diff = oldDir - newDir;
+ if (diff > 180.0f) {
+ newDir += 360.0f;
+ } else if (diff < -180.0f) {
+ newDir -= 360.f;
+ }
+ previousDirection = newDir;
+
+ directionAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, oldDir, newDir);
+ directionAnimator.setDuration(1000);
+ directionAnimator.start();
+ }
+
+ public float getCenterX() {
+ return getX() + getMeasuredWidth() / 2;
+ }
+
+ public float getCenterY() {
+ return getY() + getMeasuredHeight() / 2;
+ }
+
+ public void setContentPadding(int[] padding) {
+ contentPadding = padding;
+ }
+
+ private static class GpsLocationListener implements LocationListener {
+
+ private WeakReference<MyLocationView> mUserLocationView;
+
+ public GpsLocationListener(MyLocationView myLocationView) {
+ mUserLocationView = new WeakReference<>(myLocationView);
+ }
+
+ /**
+ * Callback method for receiving location updates from LocationServices.
+ *
+ * @param location The new Location data
+ */
+ @Override
+ public void onLocationChanged(Location location) {
+ MyLocationView locationView = mUserLocationView.get();
+ if (locationView != null) {
+ locationView.setLocation(location);
+ }
+ }
+ }
+
+ private class CompassListener implements SensorEventListener {
+
+ private SensorManager mSensorManager;
+ private Sensor mAccelerometer;
+ private Sensor mMagnetometer;
+ private boolean paused;
+
+ private float mCurrentDegree = 0f;
+
+ private float[] mOrientation = new float[3];
+ private float[] mGData = new float[3];
+ private float[] mMData = new float[3];
+ private float[] mR = new float[16];
+ private float[] mI = new float[16];
+
+ // Controls the sensor updateLatLng rate in milliseconds
+ private static final int UPDATE_RATE_MS = 500;
+
+ // Compass data
+ private long mCompassUpdateNextTimestamp = 0;
+
+ public CompassListener(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ }
+
+ public void onResume() {
+ paused = false;
+ mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
+ mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
+ }
+
+ public void onPause() {
+ paused = true;
+ mSensorManager.unregisterListener(this, mAccelerometer);
+ mSensorManager.unregisterListener(this, mMagnetometer);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (paused) {
+ return;
+ }
+
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < mCompassUpdateNextTimestamp) {
+ return;
+ }
+
+ int type = event.sensor.getType();
+ float[] data;
+ if (type == Sensor.TYPE_ACCELEROMETER) {
+ data = mGData;
+ } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
+ data = mMData;
+ } else {
+ // we should not be here.
+ return;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ data[i] = event.values[i];
+ }
+
+ SensorManager.getRotationMatrix(mR, mI, mGData, mMData);
+ SensorManager.getOrientation(mR, mOrientation);
+ setCompass((int) (mOrientation[0] * 180.0f / Math.PI));
+ mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
+ }
+
+ public float getCurrentDegree() {
+ return mCurrentDegree;
+ }
+
+ public boolean isPaused() {
+ return paused;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
+ private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
+
+ private MyLocationBehavior behavior;
+ private double fromLat;
+ private double fromLng;
+ private double toLat;
+ private double toLng;
+
+ private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) {
+ behavior = myLocationBehavior;
+ fromLat = from.getLatitude();
+ fromLng = from.getLongitude();
+ toLat = to.getLatitude();
+ toLng = to.getLongitude();
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float frac = animation.getAnimatedFraction();
+ double latitude = fromLat + (toLat - fromLat) * frac;
+ double longitude = fromLng + (toLng - fromLng) * frac;
+ behavior.updateLatLng(latitude, longitude);
+ updateOnNextFrame();
+ }
+ }
+
+ private class MyLocationBehaviorFactory {
+
+ public MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) {
+ if (mode == MyLocationTracking.TRACKING_NONE) {
+ return new MyLocationShowBehavior();
+ } else {
+ return new MyLocationTrackingBehavior();
+ }
+ }
+ }
+
+ private abstract class MyLocationBehavior {
+
+ abstract void updateLatLng(@NonNull Location location);
+
+ public void updateLatLng(double lat, double lon) {
+ if (latLng != null) {
+ latLng.setLatitude(lat);
+ latLng.setLongitude(lon);
+ }
+ }
+
+ protected void updateAccuracy(@NonNull Location location) {
+ if (accuracyAnimator != null && accuracyAnimator.isRunning()) {
+ // use current accuracy as a starting point
+ accuracy = (Float) accuracyAnimator.getAnimatedValue();
+ accuracyAnimator.end();
+ }
+
+ accuracyAnimator = ValueAnimator.ofFloat(accuracy * 10, location.getAccuracy() * 10);
+ accuracyAnimator.setDuration(750);
+ accuracyAnimator.start();
+ accuracy = location.getAccuracy();
+ }
+
+ abstract void invalidate();
+ }
+
+ private class MyLocationTrackingBehavior extends MyLocationBehavior {
+
+ @Override
+ void updateLatLng(@NonNull Location location) {
+ if (latLng == null) {
+ // first location fix
+ latLng = new LatLng(location);
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ }
+
+ // updateLatLng timestamp
+ long previousUpdateTimeStamp = locationUpdateTimestamp;
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+
+ // calculate animation duration
+ long locationUpdateDuration;
+ if (previousUpdateTimeStamp == 0) {
+ locationUpdateDuration = 0;
+ } else {
+ locationUpdateDuration = locationUpdateTimestamp - previousUpdateTimeStamp;
+ }
+
+ // calculate interpolated location
+ previousLocation = latLng;
+ latLng = new LatLng(location);
+ interpolatedLocation = new LatLng((latLng.getLatitude() + previousLocation.getLatitude()) / 2, (latLng.getLongitude() + previousLocation.getLongitude()) / 2);
+
+ // build new camera
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(interpolatedLocation);
+
+ // add direction
+ if (myBearingTrackingMode == MyBearingTracking.GPS) {
+ if (location.hasBearing()) {
+ builder.bearing(location.getBearing());
+ }
+ gpsDirection = 0;
+ setCompass(gpsDirection);
+ } else if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
+ if (!compassListener.isPaused()) {
+ builder.bearing(compassListener.getCurrentDegree());
+ setCompass(0);
+ }
+ }
+
+ // accuracy
+ updateAccuracy(location);
+
+ // ease to new camera position with a linear interpolator
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), (int) locationUpdateDuration, false /*linear interpolator*/);
+ }
+
+ @Override
+ void invalidate() {
+ int[] mapPadding = mapboxMap.getPadding();
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ setX((uiSettings.getWidth() - getWidth() + mapPadding[0] - mapPadding[2]) / 2 + (contentPadding[0] - contentPadding[2]) / 2);
+ setY((uiSettings.getHeight() - getHeight() - mapPadding[3] + mapPadding[1]) / 2 + (contentPadding[1] - contentPadding[3]) / 2);
+ MyLocationView.this.invalidate();
+ }
+ }
+
+ private class MyLocationShowBehavior extends MyLocationBehavior {
+
+ @Override
+ void updateLatLng(@NonNull final Location location) {
+ if (latLng == null) {
+ // first location update
+ latLng = new LatLng(location);
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ }
+
+ // update LatLng location
+ previousLocation = latLng;
+ latLng = new LatLng(location);
+
+ // update LatLng direction
+ if (location.hasBearing()) {
+ gpsDirection = clamp(location.getBearing() - (float) mapboxMap.getCameraPosition().bearing);
+ setCompass(gpsDirection);
+ }
+
+ // update LatLng accuracy
+ updateAccuracy(location);
+
+ // calculate updateLatLng time + add some extra offset to improve animation
+ long previousUpdateTimeStamp = locationUpdateTimestamp;
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1);
+
+ // calculate interpolated entity
+ interpolatedLocation = new LatLng((latLng.getLatitude() + previousLocation.getLatitude()) / 2, (latLng.getLongitude() + previousLocation.getLongitude()) / 2);
+
+ // animate changes
+ if (locationChangeAnimator != null) {
+ locationChangeAnimator.end();
+ locationChangeAnimator = null;
+ }
+
+ locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ locationChangeAnimator.setDuration((long) (locationUpdateDuration * 1.2));
+ locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this,
+ previousLocation, interpolatedLocation
+ ));
+ locationChangeAnimator.start();
+
+ // use interpolated location as current location
+ latLng = interpolatedLocation;
+ }
+
+ private float clamp(float direction) {
+ float diff = previousDirection - direction;
+ if (diff > 180.0f) {
+ direction += 360.0f;
+ } else if (diff < -180.0f) {
+ direction -= 360.f;
+ }
+ previousDirection = direction;
+ return direction;
+ }
+
+ @Override
+ void invalidate() {
+ PointF screenLocation = projection.toScreenLocation(latLng);
+ if (screenLocation != null) {
+ setX((screenLocation.x - getWidth() / 2));
+ setY((screenLocation.y - getHeight() / 2));
+ }
+ MyLocationView.this.invalidate();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
new file mode 100644
index 0000000000..9ae96ebf7b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
@@ -0,0 +1,146 @@
+package com.mapbox.mapboxsdk.maps.widgets;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntRange;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+
+public class MyLocationViewSettings {
+
+ private MapView mapView;
+ private MyLocationView myLocationView;
+
+ //
+ // State
+ //
+
+ private boolean enabled;
+
+ //
+ // Foreground
+ //
+
+ private Drawable foregroundDrawable;
+ private Drawable foregroundBearingDrawable;
+
+ @ColorInt
+ private int foregroundTintColor;
+
+ //
+ // Background
+ //
+
+ private Drawable backgroundDrawable;
+ private int[] backgroundOffset = new int[4];
+
+ @ColorInt
+ private int backgroundTintColor;
+
+ //
+ // Accuracy
+ //
+
+ private int accuracyAlpha;
+
+ @ColorInt
+ private int accuracyTintColor;
+
+ //
+ // Padding
+ //
+
+ private int[] padding = new int[4];
+
+ public MyLocationViewSettings(MapView mapView, MyLocationView myLocationView) {
+ this.mapView = mapView;
+ this.myLocationView = myLocationView;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ myLocationView.setEnabled(enabled);
+ }
+
+ public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) {
+ this.foregroundDrawable = foregroundDrawable;
+ this.foregroundBearingDrawable = foregroundBearingDrawable;
+ myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
+ }
+
+ public Drawable getForegroundDrawable() {
+ return foregroundDrawable;
+ }
+
+ public Drawable getForegroundBearingDrawable() {
+ return foregroundBearingDrawable;
+ }
+
+ public void setForegroundTintColor(@ColorInt int foregroundTintColor) {
+ this.foregroundTintColor = foregroundTintColor;
+ myLocationView.setForegroundDrawableTint(foregroundTintColor);
+ }
+
+ public int getForegroundTintColor() {
+ return foregroundTintColor;
+ }
+
+ public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) {
+ this.backgroundDrawable = backgroundDrawable;
+ this.backgroundOffset = padding;
+ if (padding != null && padding.length == 4) {
+ myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]);
+ } else {
+ myLocationView.setShadowDrawable(backgroundDrawable);
+ }
+ }
+
+ public Drawable getBackgroundDrawable() {
+ return backgroundDrawable;
+ }
+
+ public void setBackgroundTintColor(@ColorInt int backgroundTintColor) {
+ this.backgroundTintColor = backgroundTintColor;
+ myLocationView.setShadowDrawableTint(backgroundTintColor);
+ }
+
+ public int getBackgroundTintColor() {
+ return backgroundTintColor;
+ }
+
+ public int[] getBackgroundOffset() {
+ return backgroundOffset;
+ }
+
+ public void setPadding(int left, int top, int right, int bottom) {
+ padding = new int[]{left, top, right, bottom};
+ myLocationView.setContentPadding(padding);
+ mapView.invalidateContentPadding();
+ }
+
+ public int[] getPadding() {
+ return padding;
+ }
+
+ public int getAccuracyAlpha() {
+ return accuracyAlpha;
+ }
+
+ public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int arruracyAlpha) {
+ this.accuracyAlpha = arruracyAlpha;
+ myLocationView.setAccuracyAlpha(arruracyAlpha);
+ }
+
+ public int getAccuracyTintColor() {
+ return accuracyTintColor;
+ }
+
+ public void setAccuracyTintColor(@ColorInt int accuracyTintColor) {
+ this.accuracyTintColor = accuracyTintColor;
+ myLocationView.setAccuracyTint(accuracyTintColor);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
deleted file mode 100644
index 36e48488fa..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
+++ /dev/null
@@ -1,757 +0,0 @@
-package com.mapbox.mapboxsdk.maps.widgets;
-
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.location.Location;
-import android.os.Build;
-import android.os.SystemClock;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.mapbox.mapboxsdk.R;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.location.LocationListener;
-import com.mapbox.mapboxsdk.location.LocationServices;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.Projection;
-
-import java.lang.ref.WeakReference;
-
-/**
- * UI element overlaid on a map to show the user's location.
- */
-public final class UserLocationView extends View {
-
- private MapboxMap mMapboxMap;
- private Projection mProjection;
-
- private boolean mShowMarker;
- private boolean mShowDirection;
- private boolean mShowAccuracy;
- private boolean mStaleMarker;
-
- private PointF mMarkerScreenPoint;
- private Matrix mMarkerScreenMatrix;
-
- private Paint mAccuracyPaintFill;
- private Paint mAccuracyPaintStroke;
- private Path mAccuracyPath;
- private RectF mAccuracyBounds;
-
- private Drawable mUserLocationDrawable;
- private RectF mUserLocationDrawableBoundsF;
- private Rect mUserLocationDrawableBounds;
-
- private Drawable mUserLocationBearingDrawable;
- private RectF mUserLocationBearingDrawableBoundsF;
- private Rect mUserLocationBearingDrawableBounds;
-
- private Drawable mUserLocationStaleDrawable;
- private RectF mUserLocationStaleDrawableBoundsF;
- private Rect mUserLocationStaleDrawableBounds;
-
- private Rect mDirtyRect;
- private RectF mDirtyRectF;
-
- private LatLng mMarkerCoordinate;
- private ValueAnimator mMarkerCoordinateAnimator;
- private float mGpsMarkerDirection;
- private float mCompassMarkerDirection;
- private ObjectAnimator mMarkerDirectionAnimator;
- private float mMarkerAccuracy;
- private ObjectAnimator mMarkerAccuracyAnimator;
-
- private LatLng mCurrentMapViewCoordinate;
- private double mCurrentBearing;
- private boolean mPaused = false;
- private Location mUserLocation;
- private UserLocationListener mUserLocationListener;
-
- private MapboxMap.OnMyLocationChangeListener mOnMyLocationChangeListener;
-
- @MyLocationTracking.Mode
- private int mMyLocationTrackingMode;
-
- @MyBearingTracking.Mode
- private int mMyBearingTrackingMode;
-
- // Compass data
- private MyBearingListener mBearingChangeListener;
-
- public UserLocationView(Context context) {
- super(context);
- initialize(context);
- }
-
- public UserLocationView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize(context);
- }
-
- public UserLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initialize(context);
- }
-
- private void initialize(Context context) {
- // View configuration
- setEnabled(false);
- setWillNotDraw(false);
-
- // Layout params
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- setLayoutParams(lp);
-
- // Setup sensors
- mBearingChangeListener = new MyBearingListener(context);
-
- // Setup the custom paint
- Resources resources = context.getResources();
- int accuracyColor = ContextCompat.getColor(context,R.color.my_location_ring);
-
- float density = resources.getDisplayMetrics().density;
- mMarkerCoordinate = new LatLng(0.0, 0.0);
- mMarkerScreenPoint = new PointF();
- mMarkerScreenMatrix = new Matrix();
-
- mAccuracyPaintFill = new Paint();
- mAccuracyPaintFill.setAntiAlias(true);
- mAccuracyPaintFill.setStyle(Paint.Style.FILL);
- mAccuracyPaintFill.setColor(accuracyColor);
- mAccuracyPaintFill.setAlpha((int) (255 * 0.25f));
-
- mAccuracyPaintStroke = new Paint();
- mAccuracyPaintStroke.setAntiAlias(true);
- mAccuracyPaintStroke.setStyle(Paint.Style.STROKE);
- mAccuracyPaintStroke.setStrokeWidth(0.5f * density);
- mAccuracyPaintStroke.setColor(accuracyColor);
- mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f));
-
- mAccuracyPath = new Path();
- mAccuracyBounds = new RectF();
-
- mUserLocationDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location);
- mUserLocationDrawableBounds = new Rect(
- -mUserLocationDrawable.getIntrinsicWidth() / 2,
- -mUserLocationDrawable.getIntrinsicHeight() / 2,
- mUserLocationDrawable.getIntrinsicWidth() / 2,
- mUserLocationDrawable.getIntrinsicHeight() / 2);
- mUserLocationDrawableBoundsF = new RectF(
- -mUserLocationDrawable.getIntrinsicWidth() / 2,
- -mUserLocationDrawable.getIntrinsicHeight() / 2,
- mUserLocationDrawable.getIntrinsicWidth() / 2,
- mUserLocationDrawable.getIntrinsicHeight() / 2);
- mUserLocationDrawable.setBounds(mUserLocationDrawableBounds);
-
- mUserLocationBearingDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_bearing);
- mUserLocationBearingDrawableBounds = new Rect(
- -mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- -mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
- mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
- mUserLocationBearingDrawableBoundsF = new RectF(
- -mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- -mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
- mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
- mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
- mUserLocationBearingDrawable.setBounds(mUserLocationBearingDrawableBounds);
-
- mUserLocationStaleDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_stale);
- mUserLocationStaleDrawableBounds = new Rect(
- -mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- -mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
- mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
- mUserLocationStaleDrawableBoundsF = new RectF(
- -mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- -mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
- mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
- mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
- mUserLocationStaleDrawable.setBounds(mUserLocationStaleDrawableBounds);
- }
-
- public void setMapboxMap(MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mProjection = mapboxMap.getProjection();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- if (!mShowMarker) {
- return;
- }
-
- canvas.concat(mMarkerScreenMatrix);
-
- Drawable dotDrawable = mShowDirection ? mUserLocationBearingDrawable : mUserLocationDrawable;
- dotDrawable = mStaleMarker ? mUserLocationStaleDrawable : dotDrawable;
- // IMPORTANT also update in update()
- RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
- dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
-
- boolean willDraw = true;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN || !canvas.isHardwareAccelerated()) {
- willDraw = mShowAccuracy && !mStaleMarker && !canvas.quickReject(mAccuracyPath, Canvas.EdgeType.AA);
- }
- willDraw |= !canvas.quickReject(dotBounds, Canvas.EdgeType.AA);
-
- if (willDraw) {
- if (mShowAccuracy && !mStaleMarker) {
- canvas.drawPath(mAccuracyPath, mAccuracyPaintFill);
- canvas.drawPath(mAccuracyPath, mAccuracyPaintStroke);
- }
- dotDrawable.draw(canvas);
- }
- }
-
- public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- mMyLocationTrackingMode = myLocationTrackingMode;
-
- if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && mUserLocation != null) {
- // center map directly if we have a location fix
- mMarkerCoordinate = new LatLng(mUserLocation.getLatitude(), mUserLocation.getLongitude());
- mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation)));
-
- // center view directly
- mMarkerScreenMatrix.reset();
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
- }
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
- toggleGps(enabled);
- }
-
- public void update() {
- if (isEnabled() && mShowMarker) {
- setVisibility(View.VISIBLE);
-
- mStaleMarker = isStale(mUserLocation);
-
- // compute new marker position
- // TODO add JNI method that takes existing pointf
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.reset();
- mMarkerScreenMatrix.setTranslate(
- mMarkerScreenPoint.x,
- mMarkerScreenPoint.y);
-
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- double bearing;
- if (mShowDirection) {
- bearing = mMyBearingTrackingMode == MyBearingTracking.COMPASS ? mBearingChangeListener.getCompassBearing() : mUserLocation.getBearing();
- } else {
- bearing = mMapboxMap.getCameraPosition().bearing;
- }
-
- if (mCurrentMapViewCoordinate == null) {
- mCurrentMapViewCoordinate = mMapboxMap.getCameraPosition().target;
- }
-
- // only update if there is an actual change
- if ((!mCurrentMapViewCoordinate.equals(mMarkerCoordinate)) || (!(mCurrentBearing == bearing))) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(mMarkerCoordinate)
- .bearing(bearing)
- .build();
- mMapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null);
- mMarkerScreenMatrix.reset();
- mMarkerScreenPoint = getMarkerScreenPoint();
- mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
-
- // set values for next check for actual change
- mCurrentMapViewCoordinate = mMarkerCoordinate;
- mCurrentBearing = bearing;
- }
- }
-
- // rotate so arrow in points to bearing
- if (mShowDirection) {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS && mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate((float)(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing));
- } else if (mMyBearingTrackingMode == MyBearingTracking.GPS) {
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate((float)(mGpsMarkerDirection + mMapboxMap.getCameraPosition().bearing));
- } else {
- mMarkerScreenMatrix.preRotate(mGpsMarkerDirection);
- }
- }
- }
-
- // adjust accuracy circle
- if (mShowAccuracy && !mStaleMarker) {
- mAccuracyPath.reset();
- mAccuracyPath.addCircle(0.0f, 0.0f,
- (float) (mMarkerAccuracy / mMapboxMap.getProjection().getMetersPerPixelAtLatitude(
- mMarkerCoordinate.getLatitude())),
- Path.Direction.CW);
-
- mAccuracyPath.computeBounds(mAccuracyBounds, false);
- mAccuracyBounds.inset(-1.0f, -1.0f);
- }
-
- // invalidate changed pixels
- if (mDirtyRect == null) {
- mDirtyRect = new Rect();
- mDirtyRectF = new RectF();
- } else {
- // the old marker location
- invalidate(mDirtyRect);
- }
-
- RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
- dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
- RectF largerBounds = mShowAccuracy && !mStaleMarker && mAccuracyBounds.contains(dotBounds)
- ? mAccuracyBounds : dotBounds;
- mMarkerScreenMatrix.mapRect(mDirtyRectF, largerBounds);
- mDirtyRectF.roundOut(mDirtyRect);
- invalidate(mDirtyRect); // the new marker location
- } else {
- setVisibility(View.INVISIBLE);
- }
- }
-
- public Location getLocation() {
- return mUserLocation;
- }
-
- /**
- * Enabled / Disable GPS location updates along with updating the UI
- *
- * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
- */
- private void toggleGps(boolean enableGps) {
-
- LocationServices locationServices = LocationServices.getLocationServices(getContext());
-
- if (enableGps) {
- // Set an initial location if one available
- Location lastLocation = locationServices.getLastLocation();
- if (lastLocation != null) {
- setLocation(lastLocation);
- }
-
- if (mUserLocationListener == null) {
- mUserLocationListener = new UserLocationListener(this);
- }
-
- // Register for Location Updates
- locationServices.addLocationListener(mUserLocationListener);
- } else {
- // Disable location and user dot
- setLocation(null);
-
- // Deregister for Location Updates
- locationServices.removeLocationListener(mUserLocationListener);
- }
-
- locationServices.toggleGPS(enableGps);
- }
-
- public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- mMyBearingTrackingMode = myBearingTrackingMode;
-
- if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
- mShowAccuracy = false;
- mShowDirection = true;
- mBearingChangeListener.onResume();
- } else {
- mBearingChangeListener.onPause();
- if (myBearingTrackingMode == MyBearingTracking.GPS) {
- mShowDirection = (mUserLocation != null) && mUserLocation.hasBearing();
- } else {
- mShowDirection = false;
- }
- }
- update();
- }
-
- private class MyBearingListener implements SensorEventListener {
-
- private SensorManager mSensorManager;
- private Sensor mAccelerometer;
- private Sensor mMagnetometer;
- private float[] mLastAccelerometer = new float[3];
- private float[] mLastMagnetometer = new float[3];
- private boolean mLastAccelerometerSet = false;
- private boolean mLastMagnetometerSet = false;
- private float[] mR = new float[9];
- private float[] mOrientation = new float[3];
- private float mCurrentDegree = 0f;
-
- // Controls the sensor update rate in milliseconds
- private static final int UPDATE_RATE_MS = 300;
-
- // Compass data
- private float mCompassBearing;
- private long mCompassUpdateNextTimestamp = 0;
-
- public MyBearingListener(Context context) {
- mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- }
-
- public void onResume() {
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
- }
-
- public void onPause() {
- mSensorManager.unregisterListener(this, mAccelerometer);
- mSensorManager.unregisterListener(this, mMagnetometer);
- }
-
- public float getCompassBearing() {
- return mCurrentDegree;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mPaused) {
- return;
- }
-
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime < mCompassUpdateNextTimestamp) {
- return;
- }
-
- if (event.sensor == mAccelerometer) {
- System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
- mLastAccelerometerSet = true;
- } else if (event.sensor == mMagnetometer) {
- System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
- mLastMagnetometerSet = true;
- }
-
- if (mLastAccelerometerSet && mLastMagnetometerSet) {
- SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
- SensorManager.getOrientation(mR, mOrientation);
- float azimuthInRadians = mOrientation[0];
-
- mCompassBearing = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360;
- if (mCompassBearing < 0) {
- // only allow positive degrees
- mCompassBearing += 360;
- }
-
- if (mCompassBearing > mCurrentDegree + 15 || mCompassBearing < mCurrentDegree - 15) {
- mCurrentDegree = mCompassBearing;
- setCompass(mCurrentDegree);
- }
- }
- mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // TODO add accuracy to the equiation
- }
- }
-
- private static class UserLocationListener implements LocationListener {
-
- private WeakReference<UserLocationView> mUserLocationView;
-
- public UserLocationListener(UserLocationView userLocationView) {
- mUserLocationView = new WeakReference<>(userLocationView);
- }
-
-
- /**
- * Callback method for receiving location updates from LocationServices.
- *
- * @param location The new Location data
- */
- @Override
- public void onLocationChanged(Location location) {
- UserLocationView locationView = mUserLocationView.get();
- if (locationView != null && !locationView.isPaused()) {
- locationView.setLocation(location);
- }
- }
- }
-
- private boolean isStale(Location location) {
- if (location != null && mMyBearingTrackingMode != MyBearingTracking.COMPASS) {
- long ageInNanos;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- ageInNanos = SystemClock.elapsedRealtimeNanos() -
- location.getElapsedRealtimeNanos();
- } else {
- ageInNanos = (System.currentTimeMillis() - location.getTime()) * 1000 * 1000;
- }
- final long oneMinuteInNanos = 60L * 1000 * 1000 * 1000;
- return ageInNanos > oneMinuteInNanos;
- } else {
- return false;
- }
- }
-
- // Handles location updates from GPS
- private void setLocation(Location location) {
- // if null we should hide the marker
- if (location == null) {
- mShowMarker = false;
- mShowDirection = false;
- mShowAccuracy = false;
-
- cancelAnimations();
-
- mUserLocation = null;
- return;
- }
-
- if (mMarkerCoordinateAnimator != null) {
- mMarkerCoordinateAnimator.end();
- mMarkerCoordinateAnimator = null;
- }
-
- if (mMarkerDirectionAnimator != null) {
- mMarkerDirectionAnimator.end();
- mMarkerDirectionAnimator = null;
- }
-
- if (mMarkerAccuracyAnimator != null) {
- mMarkerAccuracyAnimator.end();
- mMarkerAccuracyAnimator = null;
- }
-
- mShowMarker = true;
-
- LatLng previousCoordinate;
- if (mUserLocation == null) {
- previousCoordinate = new LatLng(location);
- } else {
- previousCoordinate = new LatLng(mUserLocation);
- }
-
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- // moving marker above map
- mMarkerCoordinateAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mMarkerCoordinateAnimator.setDuration(1000);
- mMarkerCoordinateAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(
- previousCoordinate, new LatLng(location)
- ));
- mMarkerCoordinateAnimator.start();
- mMarkerCoordinate = new LatLng(location);
- } else {
- // moving map under the tracker
- mMarkerCoordinate = new LatLng(location);
- }
-
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE && mMyBearingTrackingMode == MyBearingTracking.GPS) {
- // show GPS direction
- mShowDirection = location.hasBearing();
- if (mShowDirection) {
- if (mUserLocation != null && mUserLocation.hasBearing()) {
- mGpsMarkerDirection = mUserLocation.getBearing();
- }
- float oldDir = mGpsMarkerDirection;
- float newDir = location.getBearing();
- float diff = oldDir - newDir;
- if (diff > 180.0f) {
- newDir += 360.0f;
- } else if (diff < -180.0f) {
- newDir -= 360.f;
- }
- mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
- mMarkerDirectionAnimator.setDuration(1000);
- mMarkerDirectionAnimator.start();
- }
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW && mMyBearingTrackingMode == MyBearingTracking.GPS) {
- // always show north & rotate map below
- mShowDirection = true;
- mGpsMarkerDirection = 0;
- }
-
- mShowAccuracy = location.hasAccuracy();
- if (mShowAccuracy) {
- if (mUserLocation != null && mUserLocation.hasAccuracy()) {
- mMarkerAccuracy = mUserLocation.getAccuracy();
- }
- mMarkerAccuracyAnimator = ObjectAnimator.ofFloat(this, "accuracy", location.getAccuracy());
- mMarkerAccuracyAnimator.setDuration(1000);
- mMarkerAccuracyAnimator.start();
- }
-
- mUserLocation = location;
- updateOnNextFrame();
-
- if (mOnMyLocationChangeListener != null) {
- mOnMyLocationChangeListener.onMyLocationChange(location);
- }
- }
-
- // handles compass sensor updates
- private void setCompass(float bearing) {
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- // animate marker
- mShowDirection = true;
- float oldDir = mCompassMarkerDirection;
- float newDir = bearing;
- float diff = oldDir - newDir;
- if (diff > 180.0f) {
- newDir += 360.0f;
- } else if (diff < -180.0f) {
- newDir -= 360.f;
- }
- mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
- mMarkerDirectionAnimator.setDuration(1000);
- mMarkerDirectionAnimator.start();
- mCompassMarkerDirection = bearing;
-
- } else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- cancelAnimations();
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- // always show north & change map direction
- mShowDirection = true;
- mGpsMarkerDirection = 0;
- mCompassMarkerDirection = 0;
- update();
- }
- }
- }
-
- void updateOnNextFrame() {
- mMapboxMap.invalidate();
- }
-
- public void onPause() {
- mPaused = true;
- mBearingChangeListener.onPause();
- cancelAnimations();
- toggleGps(false);
- }
-
- public void onResume() {
- mPaused = false;
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- mBearingChangeListener.onResume();
- }
- if (isEnabled()) {
- toggleGps(true);
- }
- }
-
- public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
- mOnMyLocationChangeListener = listener;
- }
-
- // public for animator only
- public float getDirection() {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- return mCompassMarkerDirection;
- }
- return mGpsMarkerDirection;
- }
-
- // public for animator only
- public void setDirection(float direction) {
- if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
- mCompassMarkerDirection = direction % 360.0f;
- } else {
- mGpsMarkerDirection = direction % 360.0f;
- }
- updateOnNextFrame();
- }
-
- // public for animator only
- public float getAccuracy() {
- return mMarkerAccuracy;
- }
-
- // public for animator only
- public void setAccuracy(float accuracy) {
- mMarkerAccuracy = accuracy;
- updateOnNextFrame();
- }
-
- private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
-
- private double mFromLat;
- private double mFromLng;
- private double mToLat;
- private double mToLng;
-
- private MarkerCoordinateAnimatorListener(LatLng from, LatLng to) {
- mFromLat = from.getLatitude();
- mFromLng = from.getLongitude();
- mToLat = to.getLatitude();
- mToLng = to.getLongitude();
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float frac = animation.getAnimatedFraction();
- double latitude = mFromLat + (mToLat - mFromLat) * frac;
- double longitude = mFromLng + (mToLng - mFromLng) * frac;
- mMarkerCoordinate.setLatitude(latitude);
- mMarkerCoordinate.setLongitude(longitude);
- updateOnNextFrame();
- }
- }
-
- public void cancelAnimations() {
- if (mMarkerCoordinateAnimator != null) {
- mMarkerCoordinateAnimator.cancel();
- mMarkerCoordinateAnimator = null;
- }
-
- if (mMarkerDirectionAnimator != null) {
- mMarkerDirectionAnimator.cancel();
- mMarkerDirectionAnimator = null;
- }
-
- if (mMarkerAccuracyAnimator != null) {
- mMarkerAccuracyAnimator.cancel();
- mMarkerAccuracyAnimator = null;
- }
- }
-
- public boolean isPaused() {
- return mPaused;
- }
-
- public PointF getMarkerScreenPoint() {
- if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenPoint = mProjection.toScreenLocation(mMarkerCoordinate);
- } else {
- int[] contentPadding = mMapboxMap.getPadding();
- mMarkerScreenPoint = new PointF(((getMeasuredWidth() + contentPadding[0] - contentPadding[2]) / 2)
- , ((getMeasuredHeight() - contentPadding[3] + contentPadding[1]) / 2));
- }
- return mMarkerScreenPoint;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index 85b3d619b8..e81c366dba 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -5,7 +5,7 @@ import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.util.Log;
-
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import java.io.File;
/**
@@ -92,6 +92,10 @@ public class OfflineManager {
String cachePath = assetRoot + File.separator + DATABASE_NAME;
mDefaultFileSourcePtr = createDefaultFileSource(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE);
+ if (MapboxAccountManager.getInstance() != null) {
+ setAccessToken(mDefaultFileSourcePtr, MapboxAccountManager.getInstance().getAccessToken());
+ }
+
// Delete any existing previous ambient cache database
deleteAmbientDatabase(context);
}
@@ -123,13 +127,24 @@ public class OfflineManager {
return instance;
}
- /*
+ /**
* Access token getter/setter
+ * @param accessToken
+ *
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#start(Context, String)} ()}
*/
+ @Deprecated
public void setAccessToken(String accessToken) {
setAccessToken(mDefaultFileSourcePtr, accessToken);
}
+ /**
+ * Get Access Token
+ * @return Access Token
+ *
+ * @deprecated As of release 4.1.0, replaced by {@link MapboxAccountManager#getAccessToken()}
+ */
+ @Deprecated
public String getAccessToken() {
return getAccessToken(mDefaultFileSourcePtr);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
index e65a20f18e..48609a13bb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
@@ -13,17 +13,29 @@ public class OfflineRegionStatus {
@OfflineRegion.DownloadState private int downloadState = OfflineRegion.STATE_INACTIVE;
/**
- * The number of resources that have been fully downloaded and are ready for
- * offline access.
+ * The number of resources (inclusive of tiles) that have been fully downloaded
+ * and are ready for offline access.
*/
private long completedResourceCount = 0;
/**
- * The cumulative size, in bytes, of all resources that have been fully downloaded.
+ * The cumulative size, in bytes, of all resources (inclusive of tiles) that have
+ * been fully downloaded.
*/
private long completedResourceSize = 0;
/**
+ * The number of tiles that have been fully downloaded and are ready for
+ * offline access.
+ */
+ private long completedTileCount = 0;
+
+ /**
+ * The cumulative size, in bytes, of all tiles that have been fully downloaded.
+ */
+ private long completedTileSize = 0;
+
+ /**
* The number of resources that are known to be required for this region. See the
* documentation for `requiredResourceCountIsPrecise` for an important caveat
* about this number.
@@ -73,6 +85,14 @@ public class OfflineRegionStatus {
return completedResourceSize;
}
+ public long getCompletedTileCount() {
+ return completedTileCount;
+ }
+
+ public long getCompletedTileSize() {
+ return completedTileSize;
+ }
+
public long getRequiredResourceCount() {
return requiredResourceCount;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
index 736247bbac..b386ed6549 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
@@ -8,6 +8,7 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.location.Location;
import android.net.ConnectivityManager;
@@ -17,6 +18,7 @@ import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Build;
+import android.os.Handler;
import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -26,6 +28,7 @@ import android.view.WindowManager;
import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.constants.GeoConstants;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.exceptions.TelemetryServiceNotConfiguredException;
import com.mapbox.mapboxsdk.location.LocationServices;
import com.mapbox.mapboxsdk.utils.MathUtils;
import org.json.JSONArray;
@@ -36,7 +39,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
-import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
@@ -47,6 +49,7 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
+import okhttp3.internal.Util;
/**
* Singleton control center for managing Telemetry Data.
@@ -58,11 +61,12 @@ public class MapboxEventManager {
private static MapboxEventManager mapboxEventManager = null;
+ private boolean initialized = false;
private boolean telemetryEnabled;
private final Vector<Hashtable<String, Object>> events = new Vector<>();
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
- private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
+ private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", MapboxConstants.MAPBOX_LOCALE);
private Context context = null;
private String accessToken = null;
@@ -81,7 +85,7 @@ public class MapboxEventManager {
private long mapboxSessionIdLastSet = 0;
private static long hourInMillis = 1000 * 60 * 60;
private static long flushDelayInitialInMillis = 1000 * 10; // 10 Seconds
- private static long flushDelayInMillis = 1000 * 60 * 2; // 2 Minutes
+ private static long flushDelayInMillis = 1000 * 60 * 3; // 3 Minutes
private static final int SESSION_ID_ROTATION_HOURS = 24;
private static MessageDigest messageDigest = null;
@@ -97,16 +101,26 @@ public class MapboxEventManager {
/**
* Internal setup of MapboxEventsManager. It needs to be called once before @link MapboxEventManager#getMapboxEventManager
- * <p/>
+ *
* This allows for a cleaner getMapboxEventManager() that doesn't require context and accessToken
*
* @param context The context associated with MapView
* @param accessToken The accessToken to load MapView
*/
public void initialize(@NonNull Context context, @NonNull String accessToken) {
+
+ Log.i(TAG, "Telemetry initialize() called...");
+
+ if (initialized) {
+ Log.i(TAG, "Mapbox Telemetry has already been initialized.");
+ return;
+ }
+
this.context = context.getApplicationContext();
this.accessToken = accessToken;
+ validateTelemetryServiceConfigured();
+
// Setup Message Digest
try {
messageDigest = MessageDigest.getInstance("SHA-1");
@@ -114,9 +128,13 @@ public class MapboxEventManager {
Log.w(TAG, "Error getting Encryption Algorithm: " + e);
}
+ // Create Initial Session Id
+ rotateSessionId();
+
SharedPreferences prefs = context.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
// Determine if Telemetry Should Be Enabled
+ Log.i(TAG, "Right before Telemetry set enabled in initialized()");
setTelemetryEnabled(prefs.getBoolean(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED, true));
// Load / Create Vendor Id
@@ -132,9 +150,6 @@ public class MapboxEventManager {
editor.commit();
}
- // Create Initial Session Id
- rotateSessionId();
-
// Get DisplayMetrics Setup
displayMetrics = new DisplayMetrics();
((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics);
@@ -160,6 +175,8 @@ public class MapboxEventManager {
// Build User Agent
if (TextUtils.equals(userAgent, BuildConfig.MAPBOX_EVENTS_USER_AGENT_BASE) && !TextUtils.isEmpty(appName) && !TextUtils.isEmpty(versionName)) {
userAgent = appName + "/" + versionName + "/" + versionCode + " " + userAgent;
+ // Ensure that only ASCII characters are sent
+ userAgent = Util.toHumanReadableAscii(userAgent);
}
} catch (Exception e) {
@@ -169,6 +186,8 @@ public class MapboxEventManager {
// Register for battery updates
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
batteryStatus = context.registerReceiver(null, iFilter);
+
+ initialized = true;
}
/**
@@ -183,6 +202,24 @@ public class MapboxEventManager {
return mapboxEventManager;
}
+ // Checks that TelemetryService has been configured by developer
+ private void validateTelemetryServiceConfigured() {
+ try {
+ // Check Implementing app's AndroidManifest.xml
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SERVICES);
+ if (packageInfo.services != null) {
+ for (ServiceInfo service : packageInfo.services) {
+ if (TextUtils.equals("com.mapbox.mapboxsdk.telemetry.TelemetryService", service.name)) {
+ return;
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.w(MapboxConstants.TAG, "Error checking for Telemetry Service Config: " + e);
+ }
+ throw new TelemetryServiceNotConfiguredException();
+ }
+
public static String generateCreateDate() {
return dateFormat.format(new Date());
}
@@ -196,6 +233,7 @@ public class MapboxEventManager {
* @param telemetryEnabled True to start telemetry, false to stop it
*/
public void setTelemetryEnabled(boolean telemetryEnabled) {
+ Log.i(TAG, "setTelemetryEnabled(); this.telemetryEnabled = " + this.telemetryEnabled + "; telemetryEnabled = " + telemetryEnabled);
if (this.telemetryEnabled == telemetryEnabled) {
Log.d(TAG, "No need to start / stop telemetry as it's already in that state.");
return;
@@ -207,8 +245,33 @@ public class MapboxEventManager {
context.startService(new Intent(context, TelemetryService.class));
// Make sure Ambient Mode is started at a minimum
- if (LocationServices.getLocationServices(context).isGPSEnabled()) {
- LocationServices.getLocationServices(context).toggleGPS(false);
+ if (LocationServices.getLocationServices(context).areLocationPermissionsGranted()) {
+ Log.i(TAG, "Permissions are good, see if GPS is enabled and if not then setup Ambient.");
+ if (LocationServices.getLocationServices(context).isGPSEnabled()) {
+ LocationServices.getLocationServices(context).toggleGPS(false);
+ }
+ } else {
+ // Start timer that checks for Permissions
+ Log.i(TAG, "Permissions are not good. Need to do some looping to check on stuff.");
+
+ final Handler permsHandler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (LocationServices.getLocationServices(context).areLocationPermissionsGranted()) {
+ Log.i(TAG, "Permissions finally granted, so starting Ambient if GPS isn't already enabled");
+ // Start Ambient
+ if (LocationServices.getLocationServices(context).isGPSEnabled()) {
+ LocationServices.getLocationServices(context).toggleGPS(false);
+ }
+ } else {
+ // Restart Handler
+ Log.i(TAG, "Permissions not granted yet... let's try again in 30 seconds");
+ permsHandler.postDelayed(this, 1000 * 30);
+ }
+ }
+ };
+ permsHandler.postDelayed(runnable, 1000 * 10);
}
// Manage Timer Flush
@@ -381,7 +444,7 @@ public class MapboxEventManager {
*/
private void rotateSessionId() {
long now = System.currentTimeMillis();
- if (now - mapboxSessionIdLastSet > (SESSION_ID_ROTATION_HOURS * hourInMillis)) {
+ if ((TextUtils.isEmpty(mapboxSessionId)) || (now - mapboxSessionIdLastSet > (SESSION_ID_ROTATION_HOURS * hourInMillis))) {
mapboxSessionId = UUID.randomUUID().toString();
mapboxSessionIdLastSet = System.currentTimeMillis();
}
@@ -524,7 +587,7 @@ public class MapboxEventManager {
@Override
protected Void doInBackground(Void... voids) {
- if (events.size() < 1) {
+ if (events.isEmpty()) {
Log.d(TAG, "No events in the queue to send so returning.");
return null;
}
@@ -533,7 +596,10 @@ public class MapboxEventManager {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnected()) {
- Log.w(TAG, "Not connected to network, so returning without attempting to send events");
+ Log.w(TAG, "Not connected to network, so empty events cache and return without attempting to send events");
+ // Make sure that events don't pile up when Offline
+ // and thus impact available memory over time.
+ events.removeAllElements();
return null;
}
@@ -642,12 +708,13 @@ public class MapboxEventManager {
Response response = client.newCall(request).execute();
Log.d(TAG, "response code = " + response.code() + " for events " + events.size());
- // Reset Events
- // ============
- events.removeAllElements();
} catch (Exception e) {
Log.e(TAG, "FlushTheEventsTask borked: " + e);
e.printStackTrace();
+ } finally {
+ // Reset Events
+ // ============
+ events.removeAllElements();
}
return null;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java
index a35a87eb09..c18da41e4b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java
@@ -37,7 +37,7 @@ public class TelemetryLocationReceiver extends BroadcastReceiver {
* 10 seconds that the system allows before considering the receiver to
* be blocked and a candidate to be killed). You cannot launch a popup dialog
* in your implementation of onReceive().
- * <p/>
+ *
* <p><b>If this BroadcastReceiver was launched through a &lt;receiver&gt; tag,
* then the object is no longer alive after returning from this
* function.</b> This means you should not perform any operations that
@@ -47,7 +47,7 @@ public class TelemetryLocationReceiver extends BroadcastReceiver {
* {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish
* to interact with a service that is already running, you can use
* {@link #peekService}.
- * <p/>
+ *
* <p>The Intent filters used in {@link Context#registerReceiver}
* and in application manifests are <em>not</em> guaranteed to be exclusive. They
* are hints to the operating system about how to find suitable recipients. It is
@@ -63,7 +63,7 @@ public class TelemetryLocationReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Location location = (Location)intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
if (location != null) {
- Log.d(TAG, "location received = " + location);
+// Log.d(TAG, "location received = " + location);
MapboxEventManager.getMapboxEventManager().addLocationEvent(location);
} else {
Log.d(TAG, "location NOT received");
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java
index 0db75c92f4..008f355f29 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java
@@ -25,7 +25,7 @@ public class TelemetryService extends Service {
* {@link IBinder} is usually for a complex interface
* that has been <a href="{@docRoot}guide/components/aidl.html">described using
* aidl</a>.
- * <p/>
+ *
* <p><em>Note that unlike other application components, calls on to the
* IBinder interface returned here may not happen on the main thread
* of the process</em>. More information about the main thread can be found in
@@ -93,19 +93,17 @@ public class TelemetryService extends Service {
* Called by the system every time a client explicitly starts the service by calling
* {@link Context#startService}, providing the arguments it supplied and a
* unique integer token representing the start request. Do not call this method directly.
- * <p/>
+ *
* <p>For backwards compatibility, the default implementation calls
* {@link #onStart} and returns either {@link #START_STICKY}
* or {@link #START_STICKY_COMPATIBILITY}.
- * <p/>
+ * </p>
* <p>If you need your application to run on platform versions prior to API
* level 5, you can use the following model to handle the older {@link #onStart}
* callback in that case. The <code>handleCommand</code> method is implemented by
* you as appropriate:
- * <p/>
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
- * start_compatibility}
- * <p/>
+ * </p>
+ *
* <p class="caution">Note that the system calls this on your
* service's main thread. A service's main thread is the same
* thread where UI operations take place for Activities running in the
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java
new file mode 100644
index 0000000000..495393c258
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java
@@ -0,0 +1,93 @@
+package com.mapbox.mapboxsdk.utils;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.support.annotation.AnimatorRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+public class AnimatorUtils {
+
+ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes, @Nullable OnAnimationEndListener listener) {
+ animate(view, animatorRes, -1, listener);
+ }
+
+ public static void animate(final View view, @AnimatorRes int animatorRes, int duration, @Nullable final OnAnimationEndListener listener) {
+ if (view == null) {
+ return;
+ }
+
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ Animator animator = AnimatorInflater.loadAnimator(view.getContext(), animatorRes);
+ if (duration != -1) {
+ animator.setDuration(duration);
+ }
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (listener != null) {
+ listener.onAnimationEnd();
+ }
+ }
+ });
+ animator.setTarget(view);
+ animator.start();
+ }
+
+ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes) {
+ animate(view, animatorRes, -1);
+ }
+
+ public static void animate(@NonNull final View view, @AnimatorRes int animatorRes, int duration) {
+ animate(view, animatorRes, duration, null);
+ }
+
+ public static void rotate(@NonNull final View view, float rotation) {
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(view, View.ROTATION, view.getRotation(), rotation);
+ rotateAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ rotateAnimator.start();
+ }
+
+ public static void alpha(@NonNull final View convertView, float alpha, @Nullable final OnAnimationEndListener listener) {
+ convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ALPHA, convertView.getAlpha(), alpha);
+ rotateAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ convertView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ convertView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (listener != null) {
+ listener.onAnimationEnd();
+ }
+ }
+ });
+ rotateAnimator.start();
+ }
+
+ public static void alpha(@NonNull final View convertView, float alpha) {
+ alpha(convertView, alpha, null);
+ }
+
+ public interface OnAnimationEndListener {
+ void onAnimationEnd();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
new file mode 100644
index 0000000000..a0de07c5f1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
@@ -0,0 +1,90 @@
+package com.mapbox.mapboxsdk.utils;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.util.TypedValue;
+import android.widget.ImageView;
+
+import com.mapbox.mapboxsdk.R;
+
+public class ColorUtils {
+
+ /**
+ * Returns a color integer associated as primary color from a theme based on a {@link Context}.
+ *
+ * @param context The context used to style the color attributes.
+ * @return The primary color value of current theme in the form 0xAARRGGBB.
+ */
+ @ColorInt
+ public static int getPrimaryColor(@NonNull Context context) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
+ return typedValue.data;
+ }
+
+ /**
+ * Returns a color integer associated as primary dark color from a theme based on a {@link Context}.
+ *
+ * @param context The context used to style the color attributes.
+ * @return The primary dark color value of current theme in the form 0xAARRGGBB.
+ */
+ @ColorInt
+ public static int getPrimaryDarkColor(@NonNull Context context) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
+ return typedValue.data;
+ }
+
+ /**
+ * Returns a color integer associated as accent color from a theme based on a {@link Context}.
+ *
+ * @param context The context used to style the color attributes.
+ * @return The accent color value of current theme in the form 0xAARRGGBB.
+ */
+ @ColorInt
+ public static int getAccentColor(@NonNull Context context) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(R.attr.colorAccent, typedValue, true);
+ return typedValue.data;
+ }
+
+ /**
+ * Returns a color state list associated with a theme based on a {@link Context}
+ *
+ * @param color The color used for tinting.
+ * @return A ColorStateList object containing the primary color of a theme
+ */
+ @NonNull
+ public static ColorStateList getSelector(@ColorInt int color) {
+ return new ColorStateList(
+ new int[][]{
+ new int[]{android.R.attr.state_pressed},
+ new int[]{}
+ },
+ new int[]{
+ color,
+ color
+ }
+ );
+ }
+
+ /**
+ * Set a color tint list to the {@link Drawable} of an {@link ImageView}.
+ *
+ * @param imageView The view to set the default tint list.
+ * @param tintColor The color to tint.
+ */
+ public static void setTintList(@NonNull ImageView imageView, @ColorInt int tintColor) {
+ Drawable originalDrawable = imageView.getDrawable();
+ Drawable wrappedDrawable = DrawableCompat.wrap(originalDrawable);
+ DrawableCompat.setTintList(wrappedDrawable, getSelector(tintColor));
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
index 922fb11868..a92999c0d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
@@ -27,7 +27,7 @@ public class MathUtils {
/**
* Constrains value to the given range (including min, excluding max) via modular arithmetic.
*
- * Same formula as used in Core GL (math.hpp)
+ * Same formula as used in Core GL (wrap.hpp)
* std::fmod((std::fmod((value - min), d) + d), d) + min;
*
* @param value Value to wrap
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/overview.html b/platform/android/MapboxGLAndroidSDK/src/main/java/overview.html
index 951b8d8e75..f218b3ccf9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/overview.html
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/overview.html
@@ -1,9 +1,11 @@
<html>
<body>
+<p>
An open source OpenGL-based vector map solution for Android with full styling capabilities.
-<p/>
+</p>
+<p>
For more information, check out <a href="https://www.mapbox.com/android-sdk/">our online overview</a>.
-<p/>
+</p>
<!--TODO: Need an Android image-->
<!--<img src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/ios/screenshot.png"/>-->
<!--<h2>Changelog</h2>
@@ -14,4 +16,4 @@ Initial preview release.
<h3>0.1.0</h3>
<p>Initial beta release.</p>-->
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_bearing.png
new file mode 100755
index 0000000000..c93fa4781a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_bearing.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_normal.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_normal.png
new file mode 100755
index 0000000000..120b7dd612
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/ic_mylocationview_normal.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location.png
deleted file mode 100755
index 1ae8d541af..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_bearing.png
deleted file mode 100755
index 8ecaffa2e8..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_bearing.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_stale.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_stale.png
deleted file mode 100755
index 0d599c01fa..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/my_location_stale.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_bearing.png
new file mode 100755
index 0000000000..0d7d89a8b4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_bearing.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_normal.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_normal.png
new file mode 100755
index 0000000000..831ef1acf4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/ic_mylocationview_normal.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location.png
deleted file mode 100755
index 542cd25e22..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_bearing.png
deleted file mode 100755
index 429f03f648..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_bearing.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_stale.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_stale.png
deleted file mode 100755
index 6613c41153..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/my_location_stale.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_bearing.png
new file mode 100755
index 0000000000..1056d617e9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_bearing.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_normal.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_normal.png
new file mode 100755
index 0000000000..37b1f7adbf
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/ic_mylocationview_normal.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location.png
deleted file mode 100755
index ca1f1fe630..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_bearing.png
deleted file mode 100755
index 1b88f9f489..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_bearing.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_stale.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_stale.png
deleted file mode 100755
index 7af3789ff0..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/my_location_stale.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_bearing.png
new file mode 100755
index 0000000000..1ff2590606
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_bearing.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_normal.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_normal.png
new file mode 100755
index 0000000000..520b85eff7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/ic_mylocationview_normal.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location.png
deleted file mode 100755
index 6f175df168..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_bearing.png
deleted file mode 100755
index f4bb454a06..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_bearing.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_stale.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_stale.png
deleted file mode 100755
index f1d2f2eca0..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/my_location_stale.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_bearing.png
new file mode 100755
index 0000000000..671c33a08e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_bearing.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_normal.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_normal.png
new file mode 100755
index 0000000000..f9f4265d2a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/ic_mylocationview_normal.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location.png
deleted file mode 100755
index d43541ac3c..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_bearing.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_bearing.png
deleted file mode 100755
index a8cccbb3e2..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_bearing.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_stale.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_stale.png
deleted file mode 100755
index 33e952391f..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/my_location_stale.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/ic_mylocationview_background.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/ic_mylocationview_background.xml
new file mode 100644
index 0000000000..e1df60788e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/ic_mylocationview_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="@color/white" />
+
+ <size
+ android:width="@dimen/my_locationview_outer_circle"
+ android:height="@dimen/my_locationview_outer_circle" />
+</shape> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
index 04b7033282..9799487f12 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
@@ -29,7 +29,7 @@
android:src="@drawable/ic_info_outline_24dp_selector"
android:background="@drawable/bg_default_selector"/>
- <com.mapbox.mapboxsdk.maps.widgets.UserLocationView
+ <com.mapbox.mapboxsdk.maps.widgets.MyLocationView
android:id="@+id/userLocationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index a6e1f044c8..66ddd251a5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -1,23 +1,47 @@
<resources>
<!-- these are public -->
<declare-styleable name="MapView">
+
+ <!--Configuration-->
+ <attr name="access_token" format="string" />
+ <attr name="style_url" format="string" />
+
+ <!--Camera-->
<attr name="center_longitude" format="float" />
<attr name="center_latitude" format="float" />
<attr name="zoom" format="float" />
- <attr name="zoom_max" format="float" />
- <attr name="zoom_min" format="float" />
<attr name="direction" format="float" />
<attr name="tilt" format="float" />
+
+ <!--Zoom-->
+ <attr name="zoom_max" format="float" />
+ <attr name="zoom_min" format="float" />
+
+ <!--Gestures-->
<attr name="zoom_enabled" format="boolean" />
<attr name="scroll_enabled" format="boolean" />
<attr name="rotate_enabled" format="boolean" />
<attr name="tilt_enabled" format="boolean" />
- <attr name="zoom_controls_enabled" format="boolean" />
<attr name="debug_active" format="boolean" />
- <attr name="style_url" format="string" />
- <attr name="access_token" format="string" />
- <attr name="style_classes" format="string" />
+
+ <!--UI-Controls-->
+ <attr name="zoom_controls_enabled" format="boolean" />
+
+ <!--MyLocation-->
<attr name="my_location_enabled" format="boolean" />
+ <attr name="my_location_foreground" format="reference" />
+ <attr name="my_location_foreground_bearing" format="reference" />
+ <attr name="my_location_foreground_tint" format="color" />
+ <attr name="my_location_background" format="reference" />
+ <attr name="my_location_background_tint" format="color" />
+ <attr name="my_location_background_left" format="dimension" />
+ <attr name="my_location_background_top" format="dimension" />
+ <attr name="my_location_background_right" format="dimension" />
+ <attr name="my_location_background_bottom" format="dimension" />
+ <attr name="my_location_accuracy_tint" format="color" />
+ <attr name="my_location_accuracy_alpha" format="integer" />
+
+ <!--Compass-->
<attr name="compass_enabled" format="boolean" />
<attr name="compass_gravity">
<flag name="top" value="0x30" />
@@ -39,6 +63,8 @@
<attr name="compass_margin_top" format="dimension" />
<attr name="compass_margin_right" format="dimension" />
<attr name="compass_margin_bottom" format="dimension" />
+
+ <!--Logo-->
<attr name="logo_gravity">
<flag name="top" value="0x30" />
<flag name="bottom" value="0x50" />
@@ -61,6 +87,7 @@
<attr name="logo_margin_bottom" format="dimension" />
<attr name="logo_enabled" format="boolean" />
+ <!--Attribution-->
<attr name="attribution_gravity">
<flag name="top" value="0x30" />
<flag name="bottom" value="0x50" />
@@ -82,5 +109,6 @@
<attr name="attribution_margin_right" format="dimension" />
<attr name="attribution_margin_bottom" format="dimension" />
<attr name="attribution_enabled" format="boolean" />
+ <attr name="attribution_tint" format="color" />
</declare-styleable>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index 97d29c7d45..63f739c30c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <color name="white">#FFFFFF</color>
+ <color name="white">#F9F9F9</color>
<color name="black">#000000</color>
<color name="gray">#7D7F80</color>
<color name="gray_light">#EEEEEE</color>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index 5167ecf936..d6cca7090f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -9,4 +9,6 @@
<dimen name="ten_dp">10dp</dimen>
<dimen name="sixteen_dp">16dp</dimen>
<dimen name="seventy_six_dp">76dp</dimen>
+ <dimen name="my_locationview_size">64dp</dimen>
+ <dimen name="my_locationview_outer_circle">18dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/integers.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/integers.xml
new file mode 100644
index 0000000000..3727365659
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/integers.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="style_version">9</integer>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
index 1f3cd8c4e3..becbcce0b0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
@@ -11,10 +11,11 @@
<string name="infoWindowAddress">Address</string>
<!-- these are public -->
- <string name="style_mapbox_streets">mapbox://styles/mapbox/streets-v8</string>
+ <!-- {@deprecated Use Style.getXStyleUrl(int version) instead.} -->
+ <string name="style_mapbox_streets">mapbox://styles/mapbox/streets-v9</string>
<string name="style_emerald">mapbox://styles/mapbox/emerald-v8</string>
- <string name="style_light">mapbox://styles/mapbox/light-v8</string>
- <string name="style_dark">mapbox://styles/mapbox/dark-v8</string>
- <string name="style_satellite">mapbox://styles/mapbox/satellite-v8</string>
- <string name="style_satellite_streets">mapbox://styles/mapbox/satellite-hybrid-v8</string>
+ <string name="style_light">mapbox://styles/mapbox/light-v9</string>
+ <string name="style_dark">mapbox://styles/mapbox/dark-v9</string>
+ <string name="style_satellite">mapbox://styles/mapbox/satellite-v9</string>
+ <string name="style_satellite_streets">mapbox://styles/mapbox/satellite-hybrid-v9</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
index 9ad12f76ad..a46f58ae80 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
@@ -4,7 +4,6 @@
<!-- these are public -->
<style name="AttributionAlertDialogStyle" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:textColorPrimary">@android:color/white</item>
- <item name="android:background">@color/mapbox_blue</item>
</style>
</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/README.md b/platform/android/MapboxGLAndroidSDKTestApp/README.md
index 646f29c4b4..0acb509b3e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/README.md
+++ b/platform/android/MapboxGLAndroidSDKTestApp/README.md
@@ -42,7 +42,7 @@ Then:
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 behaviour,
+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
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index 76dd07b7d6..5c6265c725 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -22,19 +22,19 @@ gradle.projectsEvaluated {
}
ext {
- supportLibVersion = '23.1.1'
+ supportLibVersion = '23.4.0'
}
android {
compileSdkVersion 23
- buildToolsVersion "23.0.2"
+ buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.mapbox.mapboxsdk.testapp"
minSdkVersion 15
targetSdkVersion 23
- versionCode 8
- versionName "4.0.0"
+ versionCode 9
+ versionName "4.1.0"
// Specify AndroidJUnitRunner as the default test instrumentation runner
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -63,7 +63,7 @@ android {
}
buildTypes {
- debug{
+ debug {
// run code coverage reports
testCoverageEnabled = true
}
@@ -84,19 +84,20 @@ dependencies {
compile "com.android.support:support-v4:${supportLibVersion}"
compile "com.android.support:appcompat-v7:${supportLibVersion}"
compile "com.android.support:design:${supportLibVersion}"
+ compile "com.android.support:recyclerview-v7:${supportLibVersion}"
// Leak Canary
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
// Directions SDK
- compile ('com.mapbox.mapboxsdk:mapbox-android-directions:1.0.0@aar'){
- transitive=true
+ compile('com.mapbox.mapboxsdk:mapbox-android-directions:1.0.0@aar') {
+ transitive = true
}
// Geocoder SDK
- compile ('com.mapbox.mapboxsdk:mapbox-android-geocoder:1.0.0@aar'){
- transitive=true
+ compile('com.mapbox.mapboxsdk:mapbox-android-geocoder:1.0.0@aar') {
+ transitive = true
}
// Testing dependencies
@@ -106,6 +107,7 @@ dependencies {
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+ androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.5.4'
}
checkstyle {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro b/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro
index 255a724744..fbeca9f23b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro
+++ b/platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro
@@ -3,4 +3,6 @@
-dontwarn com.squareup.**
-dontwarn com.retrofit.**
-dontwarn java.lang.**
--dontwarn org.codehaus.** \ No newline at end of file
+-dontwarn org.codehaus.**
+-keep class com.google.**
+-dontwarn com.google.**
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java
new file mode 100644
index 0000000000..d41c692509
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java
@@ -0,0 +1,28 @@
+package com.mapbox.mapboxsdk.maps;
+
+/**
+ * Utility class to fetch a MapboxMap instance by bypassing package visibility
+ */
+public class MapboxMapUtils {
+
+ /**
+ * Get the MapboxMap instance linked to passed MapView
+ *
+ * @param mapView
+ * @return
+ */
+ public static MapboxMap getMapboxMap(MapView mapView) {
+ return mapView.getMapboxMap();
+ }
+
+ /**
+ * Set the direction of the user
+ *
+ * @param mapView
+ * @param direction
+ */
+ public static void setDirection(MapView mapView, double direction) {
+ mapView.setDirection(direction);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BaseTest.java
deleted file mode 100644
index e432d5f576..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BaseTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-
-import com.mapbox.mapboxsdk.testapp.utils.ScreenshotUtil;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-/**
- * Base Espresso class for all tests, helps working with ActivityInstrumentationTestCase2
- */
-public class BaseTest {
-
- protected final static String HOME_BUTTON_STRING = "Navigate up";
-
- /*
- * Shortcuts for common UI tests
- */
-
- protected void checkViewIsDisplayed(int id) {
- onView(withId(id))
- .check(matches(isDisplayed()));
- }
-
- /*
- * Screenshots logic
- */
-
- protected void takeNamedScreenshot(final Activity activity, final String name) {
-
- // Screenshots need to be taken on the UI thread
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- ScreenshotUtil.take(activity, name);
- }
- });
-
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivityTest.java
deleted file mode 100644
index d66ae8f514..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on BulkMarkerActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class BulkMarkerActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<BulkMarkerActivity> mActivityRule = new ActivityTestRule<>(
- BulkMarkerActivity.class);
-
- private BulkMarkerActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CameraActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CameraActivityTest.java
deleted file mode 100644
index 1f176af879..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CameraActivityTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on CameraActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class CameraActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<CameraActivity> mActivityRule = new ActivityTestRule<>(CameraActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.cameraMapView);
- }
-}
-
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivityTest.java
deleted file mode 100644
index 68633547ed..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on CameraActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class CoordinateChangeActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<CoordinateChangeActivity> mActivityRule = new ActivityTestRule<>(CoordinateChangeActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DirectionsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DirectionsActivityTest.java
deleted file mode 100644
index 3ce8d00aa0..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DirectionsActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on DirectionsActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class DirectionsActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<DirectionsActivity> mActivityRule = new ActivityTestRule<>(DirectionsActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivityTest.java
deleted file mode 100644
index db014b69ac..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivityTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on DoubleMapActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class DoubleMapActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<DoubleMapActivity> mActivityRule = new ActivityTestRule<>(DoubleMapActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- checkViewIsDisplayed(R.id.mini_map);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivityTest.java
deleted file mode 100644
index b6feb8f1be..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivityTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import com.mapbox.mapboxsdk.testapp.utils.ViewUtils;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on DynamicMarkerChange
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class DynamicMarkerChangeActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<DynamicMarkerChangeActivity> mActivityRule = new ActivityTestRule<>(
- DynamicMarkerChangeActivity.class);
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-
- @Test
- public void testFabClick() {
- ViewUtils.clickView(R.id.fab);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/GeocoderActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/GeocoderActivityTest.java
deleted file mode 100644
index c1ca7413c2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/GeocoderActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on GeocoderActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class GeocoderActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<GeocoderActivity> mActivityRule = new ActivityTestRule<>(GeocoderActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivityTest.java
deleted file mode 100644
index 9fb99fe262..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on InfoWindowActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class InfoWindowActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<InfoWindowActivity> mActivityRule = new ActivityTestRule<>(
- InfoWindowActivity.class);
-
- private InfoWindowActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivityTest.java
deleted file mode 100644
index 51bd163fc1..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on InfoWindowAdapterActivity.
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class InfoWindowAdapterActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<InfoWindowAdapterActivity> mActivityRule = new ActivityTestRule<>(
- InfoWindowAdapterActivity.class);
-
- private InfoWindowAdapterActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivityTest.java
deleted file mode 100644
index e507c6cd61..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on InfoWindowConcurrentActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class InfoWindowConcurrentActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<InfoWindowConcurrentActivity> mActivityRule = new ActivityTestRule<>(InfoWindowConcurrentActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.infoWindowConcurrentMapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivityTest.java
deleted file mode 100644
index 057979aeb8..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on LatLngBoundsActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class LatLngBoundsActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<LatLngBoundsActivity> mActivityRule = new ActivityTestRule<>(
- LatLngBoundsActivity.class);
-
- private LatLngBoundsActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityScreenTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityScreenTest.java
deleted file mode 100644
index 7c56f4c874..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityScreenTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import com.mapbox.mapboxsdk.testapp.utils.DrawerUtils;
-import com.mapbox.mapboxsdk.testapp.utils.GestureUtils;
-import com.mapbox.mapboxsdk.testapp.utils.ScreenshotUtil;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.doubleClick;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-/**
- * Tests on MainActivity with screenshots
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MainActivityScreenTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
- MainActivity.class);
-
- private MainActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mainMapView);
- }
-
- @Test
- public void testStyleEmerald() {
- DrawerUtils.openDrawer();
- DrawerUtils.clickItem(R.string.styleEmerald);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- takeNamedScreenshot(mActivity, "testEmeraldStyle");
- }
-
- @Test
- public void testStyleStreets() {
- DrawerUtils.openDrawer();
- DrawerUtils.clickItem(R.string.styleMapboxStreets);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- takeNamedScreenshot(mActivity, "testStreetsStyle");
- }
-
- @Test
- public void testStyleDark() {
- DrawerUtils.openDrawer();
- DrawerUtils.clickItem(R.string.styleDark);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- takeNamedScreenshot(mActivity, "testDarkStyle");
- }
-
- @Test
- public void testStyleLight() {
- DrawerUtils.openDrawer();
- DrawerUtils.clickItem(R.string.styleLight);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- takeNamedScreenshot(mActivity, "testLightStyle");
- }
-
- @Test
- public void testStyleSatellite() {
- DrawerUtils.openDrawer();
- DrawerUtils.clickItem(R.string.styleSatellite);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- GestureUtils.doubleClickGesture(R.id.mainMapView);
- takeNamedScreenshot(mActivity, "testSatelliteStyle");
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityTest.java
deleted file mode 100644
index b9d00de089..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MainActivityTest.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.doubleClick;
-import static android.support.test.espresso.action.ViewActions.longClick;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static org.hamcrest.Matchers.not;
-
-/**
- * Tests on MainActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MainActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
- MainActivity.class);
-
- private MainActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- /*
- * Note that we need to keep the `test` prefix if we want to be able to run these
- * tests on AWS Device Farm, annotations are not enough:
- * https://github.com/awslabs/aws-device-farm-sample-app-for-android/issues/5#issuecomment-138258444
- */
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mainMapView);
- }
-
- /*
- * Check UI ids are visible
- */
-
- @Test
- public void testDrawerLayoutIsDisplayed() {
- onView(withId(R.id.drawer_layout))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testToolbarIsDisplayed() {
- onView(withId(R.id.toolbar))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testContentFrameIsDisplayed() {
- onView(withId(R.id.content_frame))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testViewFpsIsDisplayed() {
- // By default, R.id.view_fps is not displayed
- onView(withId(R.id.view_fps))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testLocationFABIsDisplayed() {
- onView(withId(R.id.locationFAB))
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testNavViewIsNotDisplayed() {
- // By default, nav_view not displayed
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewIsDisplayed() {
- // However, it's displayed when we click the home icon
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withId(R.id.nav_view))
- .check(matches(isDisplayed()));
- }
-
- /*
- * Some more tests on the map view (clicks, gestures)
- */
-
- @Test
- public void testClickMap() {
- onView(withId(R.id.mainMapView))
- .perform(click())
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testDoubleClickMap() {
- onView(withId(R.id.mainMapView))
- .perform(doubleClick())
- .check(matches(isDisplayed()));
- }
-
- @Test
- public void testLongClickMap() {
- onView(withId(R.id.mainMapView))
- .perform(longClick())
- .check(matches(isDisplayed()));
- }
-
- /*
- * Test the main drawer options
- */
-
- @Test
- public void testNavViewActionDebug() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.action_debug))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewActionPointAnnotations() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.action_point_annotations))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewActionCompass() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.action_compass))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewStyleMapboxStreets() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.styleMapboxStreets))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewStyleEmerald() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.styleEmerald))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewStyleLight() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.styleLight))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewStyleDark() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.styleDark))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testNavViewStyleSatellite() {
- onView(withContentDescription(HOME_BUTTON_STRING))
- .perform(click());
- onView(withText(R.string.styleSatellite))
- .perform(click());
-
- // Clicking the item closes the drawer
- onView(withId(R.id.nav_view))
- .check(matches(not(isDisplayed())));
- }
-
- /*
- * We can also check the map inner elements are visible
- */
-
- @Test
- public void testMapIconContentDescription() {
- // Mapbox logo
- onView(withContentDescription(mActivity.getResources()
- .getString(R.string.mapboxIconContentDescription)))
- .check(matches(isDisplayed()));
- }
-
-// @Test
-// public void testMapCompassContentDescription() {
-// //FIXME this is currently broken hence compass view is only being showed when rotating the map
-// // Map compass
-//// onView(withContentDescription(mActivity.getResources()
-//// .getString(R.string.compassContentDescription)))
-//// .check(matches(isDisplayed()));
-// }
-
- @Test
- public void testMapAttributionsIconContentDescription() {
- // Attribution icon
- onView(withContentDescription(mActivity.getResources()
- .getString(R.string.attributionsIconContentDescription)))
- .check(matches(isDisplayed()));
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivityTest.java
deleted file mode 100644
index 8ce8498d64..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on ManualZoomActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class ManualZoomActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<ManualZoomActivity> mActivityRule = new ActivityTestRule<>(ManualZoomActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.manualZoomMapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivityTest.java
deleted file mode 100644
index e65860f4b2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivityTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on MapFragmentActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MapFragmentActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MapFragmentActivity> mActivityRule = new ActivityTestRule<>(
- MapFragmentActivity.class);
-
- private MapFragmentActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.fragment_container);
- }
-
- // FIXME need a way to check if fragment was correctly added to Activity
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivityTest.java
deleted file mode 100644
index c252077856..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on MapPaddingActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MapPaddingActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MapPaddingActivity> mActivityRule = new ActivityTestRule<>(MapPaddingActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivityTest.java
deleted file mode 100644
index eaa1584a3f..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on MapboxMapActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MapboxMapActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MapboxMapActivity> mActivityRule = new ActivityTestRule<>(MapboxMapActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivityTest.java
deleted file mode 100644
index e93a7675f0..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on MaxMinZoomActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MaxMinZoomActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MaxMinZoomActivity> mActivityRule = new ActivityTestRule<>(MaxMinZoomActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.manualZoomMapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivityTest.java
deleted file mode 100644
index b21cd070a6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivityTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on User LocationTrackingModeActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class MyLocationTrackingModeActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<MyLocationTrackingModeActivity> mActivityRule = new ActivityTestRule<>(MyLocationTrackingModeActivity.class);
-
- private MyLocationTrackingModeActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PolylineActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PolylineActivityTest.java
deleted file mode 100644
index 653921497e..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PolylineActivityTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on PolyLineActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PolylineActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<PolylineActivity> mActivityRule = new ActivityTestRule<>(
- PolylineActivity.class);
-
- private PolylineActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivityTest.java
deleted file mode 100644
index ccb8354fc5..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivityTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on PressForMarkerActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PressForMarkerActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<PressForMarkerActivity> mActivityRule = new ActivityTestRule<>(
- PressForMarkerActivity.class);
-
- private PressForMarkerActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- // we are adding mapview on the fly.. just validating if we have a toolbar
- checkViewIsDisplayed(R.id.secondToolBar);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ScrollByActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ScrollByActivityTest.java
deleted file mode 100644
index 05386b089e..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/ScrollByActivityTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on ScrollByActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class ScrollByActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<ScrollByActivity> mActivityRule = new ActivityTestRule<>(ScrollByActivity.class);
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.mapView);
- }
-}
-
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivityTest.java
deleted file mode 100644
index 7ae03e0d07..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivityTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.Activity;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests on SupportMapFragmentActivity
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class SupportMapFragmentActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<SupportMapFragmentActivity> mActivityRule = new ActivityTestRule<>(SupportMapFragmentActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.fragment_container);
- }
-}
-
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/TiltActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/TiltActivityTest.java
deleted file mode 100644
index a38af03a61..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/TiltActivityTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.support.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class TiltActivityTest extends BaseTest {
-
- @Rule
- public ActivityTestRule<TiltActivity> mActivityRule = new ActivityTestRule<>(
- TiltActivity.class);
-
- private TiltActivity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
- }
-
- @Test
- public void testSanity() {
- checkViewIsDisplayed(R.id.tiltMapView);
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java
new file mode 100644
index 0000000000..e9292332d5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java
@@ -0,0 +1,44 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+
+import com.mapbox.mapboxsdk.testapp.espresso.utils.ScreenshotUtil;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+/**
+ * Base Espresso class for all tests, helps working with ActivityInstrumentationTestCase2
+ */
+public class BaseTest {
+
+ protected final static String HOME_BUTTON_STRING = "Navigate up";
+
+ /*
+ * Shortcuts for common UI tests
+ */
+
+ protected void checkViewIsDisplayed(int id) {
+ onView(withId(id))
+ .check(matches(isDisplayed()));
+ }
+
+ /*
+ * Screenshots logic
+ */
+
+ protected void takeNamedScreenshot(final Activity activity, final String name) {
+
+ // Screenshots need to be taken on the UI thread
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ScreenshotUtil.take(activity, name);
+ }
+ });
+
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java
new file mode 100644
index 0000000000..b85fe41243
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.annotation.BulkMarkerActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on BulkMarkerActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BulkMarkerActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<BulkMarkerActivity> mActivityRule = new ActivityTestRule<>(
+ BulkMarkerActivity.class);
+
+ private BulkMarkerActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java
new file mode 100644
index 0000000000..a84345b14d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.directions.DirectionsActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on DirectionsActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class DirectionsActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<DirectionsActivity> mActivityRule = new ActivityTestRule<>(DirectionsActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java
new file mode 100644
index 0000000000..700da5e3bc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java
@@ -0,0 +1,35 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.annotation.DynamicMarkerChangeActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.espresso.utils.ViewUtils;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on DynamicMarkerChange
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class DynamicMarkerChangeActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<DynamicMarkerChangeActivity> mActivityRule = new ActivityTestRule<>(
+ DynamicMarkerChangeActivity.class);
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+
+ @Test
+ public void testFabClick() {
+ ViewUtils.clickView(R.id.fab);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java
new file mode 100644
index 0000000000..9bd7a49b9c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.geocoding.GeocoderActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on GeocoderActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class GeocoderActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<GeocoderActivity> mActivityRule = new ActivityTestRule<>(GeocoderActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java
new file mode 100644
index 0000000000..feb0bb1a02
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on InfoWindowActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class InfoWindowActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<InfoWindowActivity> mActivityRule = new ActivityTestRule<>(
+ InfoWindowActivity.class);
+
+ private InfoWindowActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java
new file mode 100644
index 0000000000..0c62387e36
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowAdapterActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on InfoWindowAdapterActivity.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class InfoWindowAdapterActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<InfoWindowAdapterActivity> mActivityRule = new ActivityTestRule<>(
+ InfoWindowAdapterActivity.class);
+
+ private InfoWindowAdapterActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java
new file mode 100644
index 0000000000..39e7e4766e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.camera.LatLngBoundsActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on LatLngBoundsActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LatLngBoundsActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<LatLngBoundsActivity> mActivityRule = new ActivityTestRule<>(
+ LatLngBoundsActivity.class);
+
+ private LatLngBoundsActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java
new file mode 100644
index 0000000000..5c88bd371a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.camera.ManualZoomActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on ManualZoomActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ManualZoomActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<ManualZoomActivity> mActivityRule = new ActivityTestRule<>(ManualZoomActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.manualZoomMapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java
new file mode 100644
index 0000000000..d7522b2d7f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java
@@ -0,0 +1,39 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.fragment.MapFragmentActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on MapFragmentActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MapFragmentActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<MapFragmentActivity> mActivityRule = new ActivityTestRule<>(
+ MapFragmentActivity.class);
+
+ private MapFragmentActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.fragment_container);
+ }
+
+ // FIXME need a way to check if fragment was correctly added to Activity
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java
new file mode 100644
index 0000000000..fe6c4eb487
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.maplayout.MapPaddingActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on MapPaddingActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MapPaddingActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<MapPaddingActivity> mActivityRule = new ActivityTestRule<>(MapPaddingActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java
new file mode 100644
index 0000000000..48152286e9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.camera.MaxMinZoomActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on MaxMinZoomActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MaxMinZoomActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<MaxMinZoomActivity> mActivityRule = new ActivityTestRule<>(MaxMinZoomActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java
new file mode 100644
index 0000000000..ceda7cff2c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.userlocation.MyLocationTrackingModeActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on User LocationTrackingModeActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MyLocationTrackingModeActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<MyLocationTrackingModeActivity> mActivityRule = new ActivityTestRule<>(MyLocationTrackingModeActivity.class);
+
+ private MyLocationTrackingModeActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java
new file mode 100644
index 0000000000..425d803952
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java
@@ -0,0 +1,37 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.activity.annotation.PolylineActivity;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on PolyLineActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PolylineActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<PolylineActivity> mActivityRule = new ActivityTestRule<>(
+ PolylineActivity.class);
+
+ private PolylineActivity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java
new file mode 100644
index 0000000000..60649ad4ae
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java
@@ -0,0 +1,124 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.IdlingPolicies;
+import android.support.test.espresso.IdlingResource;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.format.DateUtils;
+
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.camera.LatLngBoundsActivity;
+import com.mapbox.mapboxsdk.testapp.espresso.utils.ScreenshotUtil;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class
+ScreenshotActivityTest {
+
+ @Rule
+ public ActivityTestRule<LatLngBoundsActivity> mActivityRule = new ActivityTestRule<>(LatLngBoundsActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+
+ // Make sure Espresso does not time out
+ IdlingPolicies.setMasterPolicyTimeout(60, TimeUnit.SECONDS);
+ IdlingPolicies.setIdlingResourceTimeout(26, TimeUnit.SECONDS);
+ }
+
+ @Test
+ public void testScreenshot() {
+ waitFor(DateUtils.SECOND_IN_MILLIS * 5, mActivity);
+ }
+
+ private static void waitFor(long waitingTime, Activity a) {
+ // Start
+ onView(withId(R.id.mapView)).perform(click());
+
+ // Make sure Espresso does not time out
+ IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
+ IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
+
+ // Now we wait
+ IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
+ Espresso.registerIdlingResources(idlingResource);
+
+ // Stop and verify
+ onView(withId(R.id.mapView)).perform(click());
+
+ ScreenshotUtil.take(a, "testing");
+
+ // Clean up
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+
+// @Test
+// public void takeScreenshot() {
+// // Start
+// checkViewIsDisplayed(R.id.mapView);
+//
+//
+// // Now we wait
+// IdlingResource idlingResource = new ElapsedTimeIdlingResource(4);
+// Espresso.registerIdlingResources(idlingResource);
+//
+// // We take a screenshot
+// ScreenshotUtil.take(mActivity, "test");
+//
+// // Clean up
+// Espresso.unregisterIdlingResources(idlingResource);
+// }
+
+// @Test
+// public void testSanity() {
+// checkViewIsDisplayed(R.id.mapView);
+// }
+
+ public static class ElapsedTimeIdlingResource implements IdlingResource {
+ private final long startTime;
+ private final long waitingTime;
+ private ResourceCallback resourceCallback;
+
+ public ElapsedTimeIdlingResource(long waitingTime) {
+ this.startTime = System.currentTimeMillis();
+ this.waitingTime = waitingTime;
+ }
+
+ @Override
+ public String getName() {
+ return ElapsedTimeIdlingResource.class.getName() + ":" + waitingTime;
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ long elapsed = System.currentTimeMillis() - startTime;
+ boolean idle = (elapsed >= waitingTime);
+ if (idle) {
+ resourceCallback.onTransitionToIdle();
+ }
+ return idle;
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
+ this.resourceCallback = resourceCallback;
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java
new file mode 100644
index 0000000000..c545bc6e04
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.camera.ScrollByActivity;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on ScrollByActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ScrollByActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<ScrollByActivity> mActivityRule = new ActivityTestRule<>(ScrollByActivity.class);
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.mapView);
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java
new file mode 100644
index 0000000000..e6c1cadf1e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java
@@ -0,0 +1,38 @@
+package com.mapbox.mapboxsdk.testapp.espresso;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.fragment.SupportMapFragmentActivity;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests on SupportMapFragmentActivity
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SupportMapFragmentActivityTest extends BaseTest {
+
+ @Rule
+ public ActivityTestRule<SupportMapFragmentActivity> mActivityRule = new ActivityTestRule<>(SupportMapFragmentActivity.class);
+
+ private Activity mActivity = null;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testSanity() {
+ checkViewIsDisplayed(R.id.fragment_container);
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java
new file mode 100644
index 0000000000..7143fa948f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.testapp.espresso.utils;
+
+import android.support.annotation.StringRes;
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.espresso.matcher.ViewMatchers;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+
+public class DrawerUtils {
+
+ private final static String HOME_BUTTON_STRING = "Navigate up";
+
+ public static void openDrawer(){
+ onView(withContentDescription(HOME_BUTTON_STRING)).perform(click());
+ }
+
+ public static void clickItem(@StringRes int txtId){
+ Espresso.onView(Matchers.allOf(ViewMatchers.withId(R.id.design_menu_item_text),
+ ViewMatchers.hasSibling(ViewMatchers.withText(txtId)))).perform(ViewActions.click());
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java
new file mode 100644
index 0000000000..e2807af5b5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java
@@ -0,0 +1,15 @@
+package com.mapbox.mapboxsdk.testapp.espresso.utils;
+
+import android.support.annotation.IdRes;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.doubleClick;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+public class GestureUtils {
+
+ public static void doubleClickGesture(@IdRes int id){
+ onView(withId(id)).perform(doubleClick());
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java
new file mode 100644
index 0000000000..fdb06e52d8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java
@@ -0,0 +1,136 @@
+package com.mapbox.mapboxsdk.testapp.espresso.utils;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.os.Environment;
+import android.util.Log;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * The built-in Fuzz Suite on AWS Device Farm takes screenshots after every test. However,
+ * this doesn't happen with Espresso unless we manually do it. This class fixes it.
+ */
+public class ScreenshotUtil {
+
+ private static final String LOG_TAG = ScreenshotUtil.class.getName();
+
+ // Where to store the files. This path is required by AWS Device Farm:
+ // http://docs.aws.amazon.com/devicefarm/latest/developerguide/test-types-android-instrumentation.html#test-types-android-instrumentation-screenshots
+ private static final String SCREENSHOT_FOLDER = "test-screenshots";
+
+ // Image type and quality
+ private static final String DEFAULT_IMAGE_EXTENSION = ".png";
+ private static final Bitmap.CompressFormat DEFAULT_IMAGE_FORMAT = Bitmap.CompressFormat.PNG;
+ private static final int DEFAULT_IMAGE_QUALITY = 100;
+
+ public static void take(Activity activity, String testName) {
+
+ // Check if storage is available
+ if (!isExternalStorageWritable()) {
+ Log.d(LOG_TAG, "External storage is not available.");
+ return;
+ }
+
+ // Get a bitmap from the activity root view. When the drawing cache is enabled,
+// // the next call to getDrawingCache() will draw the view in a bitmap.
+ View rootView = activity.getWindow().getDecorView().getRootView();
+// rootView.setDrawingCacheEnabled(true);r
+// rootView.setDrawingCacheEnabled(false);
+
+ Bitmap bitmap = null;
+
+ // Add the SurfaceView bit (see getAllTextureViews() below)
+ List<TextureView> tilingViews = getAllTextureViews(rootView);
+ if (tilingViews.size() > 0) {
+ bitmap = Bitmap.createBitmap(tilingViews.get(0).getHeight(),tilingViews.get(0).getWidth(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ for (TextureView TextureView : tilingViews) {
+ Bitmap b = TextureView.getBitmap(TextureView.getWidth(), TextureView.getHeight());
+ int[] location = new int[2];
+ TextureView.getLocationInWindow(location);
+ int[] location2 = new int[2];
+ TextureView.getLocationOnScreen(location2);
+ canvas.drawBitmap(b, 0,0, null);
+ }
+ }
+
+ // Save the bitmap in external storage
+ String uniqueAbsolutePath = getUniqueAbsolutePath(testName);
+ File outputFile = new File(uniqueAbsolutePath);
+ OutputStream outputStream = null;
+ try {
+ outputStream = new FileOutputStream(outputFile);
+ bitmap.compress(DEFAULT_IMAGE_FORMAT, DEFAULT_IMAGE_QUALITY, outputStream);
+ outputStream.flush();
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ } finally {
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /*
+ * The classic way of taking a screenshot (above) doesn't work with TextureView, this fixes it:
+ * http://stackoverflow.com/questions/19704060/screen-capture-textureview-is-black-using-drawingcache
+ */
+
+ public static List<TextureView> getAllTextureViews(View view)
+ {
+ List<TextureView> tilingViews = new ArrayList<TextureView>();
+ if (view instanceof TextureView) {
+ tilingViews.add((TextureView)view);
+ } else if(view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup)view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ tilingViews.addAll(getAllTextureViews(viewGroup.getChildAt(i)));
+ }
+ }
+
+ return tilingViews;
+ }
+
+ /*
+ * Utils
+ */
+
+ public static boolean isExternalStorageWritable() {
+ // Checks if external storage is available for read and write
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state);
+ }
+
+ private static String getUniqueAbsolutePath(String testName) {
+ // A screenshot after every test vs. manual tests
+ String filename = UUID.randomUUID().toString() + DEFAULT_IMAGE_EXTENSION;
+ if (testName != null && !testName.isEmpty()) {
+ filename = testName + DEFAULT_IMAGE_EXTENSION;
+ }
+
+ String externalPath = Environment.getExternalStorageDirectory().toString();
+ String path = externalPath + File.separator + SCREENSHOT_FOLDER;
+ File dir = new File(path);
+ dir.mkdirs();
+ path += File.separator + filename;
+ Log.d(LOG_TAG, "Screenshot path: " + path);
+ return path;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java
new file mode 100644
index 0000000000..6611c83545
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java
@@ -0,0 +1,15 @@
+package com.mapbox.mapboxsdk.testapp.espresso.utils;
+
+import android.support.annotation.IdRes;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+public class ViewUtils {
+
+ public static void clickView(@IdRes int viewRes) {
+ onView(withId(viewRes))
+ .perform(click());
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java
new file mode 100644
index 0000000000..0eedca5458
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java
@@ -0,0 +1,46 @@
+package com.mapbox.mapboxsdk.testapp.robotium;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.PolygonActivity;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.PolylineActivity;
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
+import com.robotium.solo.Solo;
+
+import org.junit.Test;
+
+public abstract class BaseMainActivityTest{
+//
+//} extends ActivityInstrumentationTestCase2<InfoWindowActivity> {
+//
+// protected Solo solo;
+// protected MapView mapView;
+// protected MapboxMap mapboxMap;
+//
+// public BaseMainActivityTest() {
+// super(InfoWindowActivity.class);
+// }
+//
+// @Override
+// public void setUp() throws Exception {
+// solo = new Solo(getInstrumentation(), getActivity());
+// mapView = (MapView) solo.getView(R.id.mapView);
+// mapboxMap = MapboxMapUtils.getMapboxMap(mapView);
+// }
+//
+// @Override
+// public void tearDown() throws Exception {
+// solo.finishOpenedActivities();
+// }
+//
+// @Test
+// public void testSanity() throws Exception {
+// assertNotNull("Should be non null", solo);
+// assertNotNull("Should be non null", mapView);
+// assertNotNull("Should be non null", mapboxMap);
+// }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java
new file mode 100644
index 0000000000..9990d71d67
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java
@@ -0,0 +1,48 @@
+package com.mapbox.mapboxsdk.testapp.robotium;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.widgets.CompassView;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.robotium.utils.ViewAssertion;
+
+import org.junit.Test;
+
+public class CompassViewTest {
+// extends
+//} BaseMainActivityTest {
+//
+// private CompassView compassView;
+//
+// @Override
+// public void setUp() throws Exception {
+// super.setUp();
+// compassView = (CompassView) solo.getView(R.id.compassView);
+// }
+//
+//// @Test
+//// public void testCompass() {
+//// setCameraDirection(0);
+//// solo.sleep(200);
+//// ViewAssertion.isInvisible(compassView);
+//// }
+//
+// @Test
+// public void testCompassClick() throws Exception {
+// setCameraDirection(20);
+// solo.sleep(200);
+// ViewAssertion.isVisible(compassView);
+// solo.clickOnView(compassView);
+// solo.sleep(200);
+// ViewAssertion.isVisible(compassView);
+// }
+//
+// private void setCameraDirection(float bearing) {
+// mapboxMap.setCameraPosition(new CameraPosition.Builder()
+// .target(new LatLng(0, 0))
+// .tilt(0)
+// .zoom(15)
+// .bearing(bearing)
+// .build());
+// }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java
new file mode 100644
index 0000000000..8d1feed6ff
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.testapp.robotium;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
+
+import org.junit.Test;
+
+public class SimpleRotateTest {
+// extends
+//} BaseMainActivityTest {
+//
+// @Override
+// public void setUp() throws Exception {
+// super.setUp();
+// }
+//
+// @Test
+// public void testRotate() {
+// mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(47.376271, 8.539116), 15));
+// double rotation = 0;
+// for (int i = 0; i < 180; i++) {
+// rotation += 1;
+// MapboxMapUtils.setDirection(mapView, rotation);
+// solo.sleep(100);
+// }
+// }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java
new file mode 100644
index 0000000000..bc8e9caac0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java
@@ -0,0 +1,28 @@
+package com.mapbox.mapboxsdk.testapp.robotium;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+import org.junit.Test;
+
+public class SimpleZoomTest {
+// extends
+//} BaseMainActivityTest {
+//
+// @Override
+// public void setUp() throws Exception {
+// super.setUp();
+// }
+//
+// @Test
+// public void testZoomIn() {
+// float currentZoomlevel = MapboxConstants.MINIMUM_ZOOM;
+// while (currentZoomlevel < MapboxConstants.MAXIMUM_ZOOM - 5) {
+// mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(47.376271, 8.539116),currentZoomlevel));
+// currentZoomlevel++;
+// solo.sleep(1400);
+// }
+// }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java
new file mode 100644
index 0000000000..74410cce50
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java
@@ -0,0 +1,17 @@
+package com.mapbox.mapboxsdk.testapp.robotium.utils;
+
+import android.view.View;
+
+import static junit.framework.Assert.assertEquals;
+
+public class ViewAssertion {
+
+ public static void isVisible(View view) {
+ assertEquals(view.getClass().getSimpleName() + " should be visible", view.getVisibility(), View.VISIBLE);
+ }
+
+ public static void isInvisible(View view) {
+ assertEquals(view.getClass().getSimpleName() + " should be visible", view.getVisibility(), View.INVISIBLE);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/DrawerUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/DrawerUtils.java
deleted file mode 100644
index 8d8905fdf4..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/DrawerUtils.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.utils;
-
-import android.support.annotation.StringRes;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.action.ViewActions;
-import android.support.test.espresso.matcher.ViewMatchers;
-
-import com.mapbox.mapboxsdk.testapp.R;
-
-import org.hamcrest.Matchers;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
-
-public class DrawerUtils {
-
- private final static String HOME_BUTTON_STRING = "Navigate up";
-
- public static void openDrawer(){
- onView(withContentDescription(HOME_BUTTON_STRING)).perform(click());
- }
-
- public static void clickItem(@StringRes int txtId){
- Espresso.onView(Matchers.allOf(ViewMatchers.withId(R.id.design_menu_item_text),
- ViewMatchers.hasSibling(ViewMatchers.withText(txtId)))).perform(ViewActions.click());
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/GestureUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/GestureUtils.java
deleted file mode 100644
index 37c18399e4..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/GestureUtils.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.utils;
-
-import android.support.annotation.IdRes;
-
-import com.mapbox.mapboxsdk.testapp.R;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.doubleClick;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-public class GestureUtils {
-
- public static void doubleClickGesture(@IdRes int id){
- onView(withId(id)).perform(doubleClick());
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ScreenshotUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ScreenshotUtil.java
deleted file mode 100644
index d25afec815..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ScreenshotUtil.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.utils;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.os.Environment;
-import android.util.Log;
-import android.view.TextureView;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * The built-in Fuzz Suite on AWS Device Farm takes screenshots after every test. However,
- * this doesn't happen with Espresso unless we manually do it. This class fixes it.
- */
-public class ScreenshotUtil {
-
- private static final String LOG_TAG = ScreenshotUtil.class.getName();
-
- // Where to store the files. This path is required by AWS Device Farm:
- // http://docs.aws.amazon.com/devicefarm/latest/developerguide/test-types-android-instrumentation.html#test-types-android-instrumentation-screenshots
- private static final String SCREENSHOT_FOLDER = "test-screenshots";
-
- // Image type and quality
- private static final String DEFAULT_IMAGE_EXTENSION = ".png";
- private static final Bitmap.CompressFormat DEFAULT_IMAGE_FORMAT = Bitmap.CompressFormat.PNG;
- private static final int DEFAULT_IMAGE_QUALITY = 100;
-
- public static void take(Activity activity, String testName) {
-
- // Check if storage is available
- if (!isExternalStorageWritable()) {
- Log.d(LOG_TAG, "External storage is not available.");
- return;
- }
-
- // Get a bitmap from the activity root view. When the drawing cache is enabled,
- // the next call to getDrawingCache() will draw the view in a bitmap.
- View rootView = activity.getWindow().getDecorView().getRootView();
- rootView.setDrawingCacheEnabled(true);
- Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache());
- rootView.setDrawingCacheEnabled(false);
-
- // Add the SurfaceView bit (see getAllTextureViews() below)
- List<TextureView> tilingViews = getAllTextureViews(rootView);
- if (tilingViews.size() > 0) {
- Canvas canvas = new Canvas(bitmap);
- for (TextureView TextureView : tilingViews) {
- Bitmap b = TextureView.getBitmap(TextureView.getWidth(), TextureView.getHeight());
- int[] location = new int[2];
- TextureView.getLocationInWindow(location);
- int[] location2 = new int[2];
- TextureView.getLocationOnScreen(location2);
- canvas.drawBitmap(b, location[0], location[1], null);
- }
- }
-
- // Save the bitmap in external storage
- String uniqueAbsolutePath = getUniqueAbsolutePath(testName);
- File outputFile = new File(uniqueAbsolutePath);
- OutputStream outputStream = null;
- try {
- outputStream = new FileOutputStream(outputFile);
- bitmap.compress(DEFAULT_IMAGE_FORMAT, DEFAULT_IMAGE_QUALITY, outputStream);
- outputStream.flush();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (outputStream != null) {
- try {
- outputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /*
- * The classic way of taking a screenshot (above) doesn't work with TextureView, this fixes it:
- * http://stackoverflow.com/questions/19704060/screen-capture-textureview-is-black-using-drawingcache
- */
-
- public static List<TextureView> getAllTextureViews(View view)
- {
- List<TextureView> tilingViews = new ArrayList<TextureView>();
- if (view instanceof TextureView) {
- tilingViews.add((TextureView)view);
- } else if(view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup)view;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- tilingViews.addAll(getAllTextureViews(viewGroup.getChildAt(i)));
- }
- }
-
- return tilingViews;
- }
-
- /*
- * Utils
- */
-
- public static boolean isExternalStorageWritable() {
- // Checks if external storage is available for read and write
- String state = Environment.getExternalStorageState();
- return Environment.MEDIA_MOUNTED.equals(state);
- }
-
- private static String getUniqueAbsolutePath(String testName) {
- // A screenshot after every test vs. manual tests
- String filename = UUID.randomUUID().toString() + DEFAULT_IMAGE_EXTENSION;
- if (testName != null && !testName.isEmpty()) {
- filename = testName + DEFAULT_IMAGE_EXTENSION;
- }
-
- String externalPath = Environment.getExternalStorageDirectory().toString();
- String path = externalPath + File.separator + SCREENSHOT_FOLDER + File.separator + filename;
- Log.d(LOG_TAG, "Screenshot path: " + path);
- return path;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ViewUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ViewUtils.java
deleted file mode 100644
index 01ee3e9b9f..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/ViewUtils.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.utils;
-
-import android.support.annotation.IdRes;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-public class ViewUtils {
-
- public static void clickView(@IdRes int viewRes) {
- onView(withId(viewRes))
- .perform(click());
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 0d2b78acf8..7268b7d551 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".MapboxApplication"
@@ -16,93 +17,252 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
- android:name=".MainActivity"
+ android:name=".activity.FeatureOverviewActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
<activity
- android:name=".MapboxMapActivity"
- android:label="@string/action_mapboxmap" />
+ android:name=".activity.infowindow.InfoWindowActivity"
+ android:description="@string/description_info_window"
+ android:label="@string/activity_info_window">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_infowindow" />
+ </activity>
<activity
- android:name=".LatLngBoundsActivity"
- android:label="@string/activity_visible_coordinate_bounds" />
+ android:name=".activity.infowindow.InfoWindowAdapterActivity"
+ android:description="@string/description_info_window_adapter"
+ android:label="@string/activity_infowindow_adapter">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_infowindow" />
+ </activity>
<activity
- android:name=".InfoWindowAdapterActivity"
- android:label="@string/activity_infowindow_adapter" />
+ android:name=".activity.annotation.BulkMarkerActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize"
+ android:description="@string/description_add_bulk_markers"
+ android:label="@string/activity_add_bulk_markers">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
<activity
- android:name=".InfoWindowActivity"
- android:label="@string/activity_info_window" />
+ android:name=".activity.annotation.AnimatedMarkerActivity"
+ android:description="@string/description_animated_marker"
+ android:label="@string/activity_animated_marker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
<activity
- android:name=".InfoWindowConcurrentActivity"
- android:label="@string/activity_info_window_concurrent" />
+ android:name=".activity.annotation.DynamicMarkerChangeActivity"
+ android:description="@string/description_dynamic_marker"
+ android:label="@string/activity_dynamic_marker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
+ <activity android:name=".activity.annotation.PressForMarkerActivity"
+ android:description="@string/description_press_for_marker"
+ android:label="@string/activity_press_for_marker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
<activity
- android:name=".BulkMarkerActivity"
- android:configChanges="orientation|keyboardHidden|screenSize"
- android:label="@string/activity_add_bulk_markers" />
+ android:name=".activity.camera.CameraAnimationTypeActivity"
+ android:description="@string/description_camera_animation_types"
+ android:label="@string/activity_camera_animation_types">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".CameraActivity"
- android:label="@string/activity_camera" />
+ android:name=".activity.camera.CameraPositionActivity"
+ android:description="@string/description_cameraposition"
+ android:label="@string/activity_camera_position">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".TiltActivity"
- android:label="@string/activity_tilt" />
+ android:name=".activity.camera.LatLngBoundsActivity"
+ android:description="@string/description_visible_bounds"
+ android:screenOrientation="portrait"
+ android:label="@string/activity_visible_coordinate_bounds">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".MapFragmentActivity"
- android:label="@string/activity_map_fragment" />
+ android:name=".activity.fragment.MapFragmentActivity"
+ android:description="@string/description_map_fragment"
+ android:label="@string/activity_map_fragment">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_fragment" />
+ </activity>
<activity
- android:name=".SupportMapFragmentActivity"
- android:label="@string/activity_map_fragment_suport" />
+ android:name=".activity.fragment.SupportMapFragmentActivity"
+ android:description="@string/description_map_fragment_support"
+ android:label="@string/activity_map_fragment_suport">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_fragment" />
+ </activity>
<activity
- android:name=".PressForMarkerActivity"
- android:label="@string/activity_press_for_marker" />
+ android:name=".activity.camera.ManualZoomActivity"
+ android:description="@string/description_camera_zoom"
+ android:label="@string/activity_camera_zoom">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".ManualZoomActivity"
- android:label="@string/action_manual_zoom" />
+ android:name=".activity.camera.MaxMinZoomActivity"
+ android:description="@string/description_minmax_zoom"
+ android:label="@string/activity_minmax_zoom">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".MaxMinZoomActivity"
- android:label="@string/action_minmax_zoom" />
+ android:name=".activity.customlayer.CustomLayerActivity"
+ android:description="@string/description_custom_layer"
+ android:label="@string/activity_custom_layer">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_custom_layer" />
+ </activity>
<activity
- android:name=".MyLocationTrackingModeActivity"
- android:label="@string/activity_user_tracking_mode" />
+ android:name=".activity.userlocation.MyLocationTrackingModeActivity"
+ android:description="@string/description_user_location_tracking"
+ android:label="@string/activity_user_tracking_mode">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_userlocation" />
+ </activity>
<activity
- android:name=".PolylineActivity"
- android:label="@string/activity_polyline" />
+ android:name=".activity.userlocation.MyLocationDrawableActivity"
+ android:description="@string/description_user_location_customization"
+ android:label="@string/activity_user_tracking_customization">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_userlocation" />
+ </activity>
<activity
- android:name=".CoordinateChangeActivity"
- android:label="@string/activity_animate_coordinate_change" />
+ android:name=".activity.userlocation.MyLocationTintActivity"
+ android:description="@string/description_user_location_dot_color"
+ android:label="@string/activity_user_dot_color">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_userlocation" />
+ </activity>
<activity
- android:name=".DirectionsActivity"
- android:label="@string/activity_directions" />
+ android:name=".activity.userlocation.MyLocationToggleActivity"
+ android:description="@string/description_user_location_toggle"
+ android:label="@string/activity_user_location_toggle">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_userlocation" />
+ </activity>
<activity
- android:name=".GeocoderActivity"
- android:label="@string/activity_geocoder" />
+ android:name=".activity.annotation.PolygonActivity"
+ android:description="@string/description_polygon"
+ android:label="@string/activity_polygon">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
+
<activity
- android:name=".DoubleMapActivity"
- android:label="@string/activity_double_map" />
+ android:name=".activity.annotation.PolylineActivity"
+ android:description="@string/description_polyline"
+ android:label="@string/activity_polyline">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
+ <activity
+ android:name=".activity.directions.DirectionsActivity"
+ android:description="@string/description_directions"
+ android:label="@string/activity_directions">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_directions" />
+ </activity>
<activity
- android:name=".ScrollByActivity"
- android:label="@string/activity_scroll_by" />
+ android:name=".activity.geocoding.GeocoderActivity"
+ android:description="@string/description_geocoder"
+ android:label="@string/activity_geocoder">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_geocoding" />
+ </activity>
<activity
- android:name=".DynamicMarkerChangeActivity"
- android:label="@string/activity_dynamic_marker" />
+ android:name=".activity.camera.ScrollByActivity"
+ android:description="@string/description_scroll_by"
+ android:label="@string/activity_scroll_by">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
+ </activity>
<activity
- android:name=".MapPaddingActivity"
+ android:name=".activity.maplayout.MapPaddingActivity"
+ android:description="@string/description_map_padding"
android:label="@string/activity_map_padding"
- android:screenOrientation="portrait" />
+ android:screenOrientation="portrait">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout" />
+ </activity>
+ <activity
+ android:name=".activity.maplayout.DebugModeActivity"
+ android:description="@string/description_debug_mode"
+ android:label="@string/activity_debug_mode">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout" />
+ </activity>
<activity
- android:name=".OfflineActivity"
- android:label="@string/activity_offline" />
+ android:name=".activity.offline.OfflineActivity"
+ android:description="@string/description_offline"
+ android:label="@string/activity_offline">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_offline" />
+ </activity>
+ <activity
+ android:name=".activity.imagegenerator.SnapshotActivity"
+ android:description="@string/description_snapshot"
+ android:label="@string/activity_snapshot">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator" />
+ </activity>
<activity
- android:name=".AnimatedMarkerActivity"
- android:label="@string/title_activity_animated_marker" />
+ android:name=".activity.maplayout.DoubleMapActivity"
+ android:description="@string/description_doublemap"
+ android:label="@string/activity_double_map">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout" />
+ </activity>
- <!-- Configuration Settings -->
- <meta-data
- android:name="com.mapbox.AccessToken"
- android:value="" />
+ <activity
+ android:name=".activity.annotation.MarkerViewActivity"
+ android:description="@string/description_view_marker"
+ android:label="@string/activity_view_marker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
+ <!-- Configuration Settings -->
<meta-data
android:name="com.mapbox.TestEventsServer"
android:value="https://cloudfront-staging.tilestream.net" />
@@ -112,7 +272,6 @@
<service android:name="com.mapbox.mapboxsdk.telemetry.TelemetryService" />
-
</application>
</manifest>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Medium.ttf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Medium.ttf
new file mode 100644
index 0000000000..a3c1a1f170
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Medium.ttf
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Regular.ttf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Regular.ttf
new file mode 100644
index 0000000000..0e58508a64
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Regular.ttf
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/AnimatedMarkerActivity.java
deleted file mode 100644
index 27cd7f175c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/AnimatedMarkerActivity.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.animation.AccelerateDecelerateInterpolator;
-
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class AnimatedMarkerActivity extends AppCompatActivity {
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_animated_marker);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyleUrl(Style.LIGHT);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- LatLng brussels = new LatLng(50.900446, 4.485251);
- LatLng washington = new LatLng(38.897108, -77.036716);
-
- final Marker marker = mapboxMap.addMarker(new MarkerOptions().position(brussels));
- ValueAnimator markerAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), (Object[]) new LatLng[]{brussels, washington});
- markerAnimator.setDuration(5000);
- markerAnimator.setRepeatCount(ValueAnimator.INFINITE);
- markerAnimator.setRepeatMode(ValueAnimator.REVERSE);
- markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
- markerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (marker != null) {
- marker.setPosition((LatLng) animation.getAnimatedValue());
- }
- }
- });
- markerAnimator.start();
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_zoom, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- onBackPressed();
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- private class LatLngEvaluator implements TypeEvaluator<LatLng> {
- @Override
- public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
- return new LatLng(startValue.getLatitude() + (endValue.getLatitude() - startValue.getLatitude()) * fraction,
- startValue.getLongitude() + (endValue.getLongitude() - startValue.getLongitude()) * fraction);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivity.java
deleted file mode 100644
index 9381992bb4..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/BulkMarkerActivity.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-import org.json.JSONException;
-
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-public class BulkMarkerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
-
- private MapboxMap mMapboxMap;
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_marker_bulk);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- final ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(38.87031, -77.00897))
- .zoom(10)
- .build()
- ));
-
- ArrayAdapter<CharSequence> spinnerAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.bulk_marker_list, android.R.layout.simple_spinner_item);
- spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- Spinner spinner = (Spinner) findViewById(R.id.spinner);
- spinner.setAdapter(spinnerAdapter);
- spinner.setOnItemSelectedListener(BulkMarkerActivity.this);
- }
- });
-
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- int markersAmount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]);
- new LoadBulkMarkerTask(this, mMapboxMap, markersAmount).execute();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // nothing selected, nothing to do!
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- private static class LoadBulkMarkerTask extends AsyncTask<Void, Integer, List<MarkerOptions>> {
-
- private static final String TAG = "LoadBulkMarkerTask";
- private MapboxMap mMapboxMap;
- private Context mAppContext;
- private ProgressDialog mProgressDialog;
- private int mAmount;
-
- public LoadBulkMarkerTask(Context context, MapboxMap mapboxMap, int amount) {
- mMapboxMap = mapboxMap;
- mapboxMap.removeAnnotations();
- mProgressDialog = ProgressDialog.show(context, "Loading", "Fetching markers", false);
- mAppContext = context.getApplicationContext();
- mAmount = amount;
- }
-
- @Override
- protected List<MarkerOptions> doInBackground(Void... params) {
- List<MarkerOptions> markerOptions = new ArrayList<>(mAmount);
- try {
- DecimalFormat formatter = new DecimalFormat("#.#####");
- String json = GeoParseUtil.loadStringFromAssets(mAppContext, "points.geojson");
- List<LatLng> locations = GeoParseUtil.parseGeoJSONCoordinates(json);
-
- if (locations.size() < mAmount) {
- mAmount = locations.size();
- }
-
- LatLng location;
- for (int i = 0; i < mAmount; i++) {
- location = locations.get(i);
- markerOptions.add(new MarkerOptions()
- .position(location)
- .title("Marker")
- .snippet(formatter.format(location.getLatitude()) + ", " + formatter.format(location.getLongitude())));
- }
-
- } catch (IOException | JSONException e) {
- Log.e(TAG, "Could not add markers,", e);
- }
- return markerOptions;
- }
-
- @Override
- protected void onProgressUpdate(Integer... values) {
- super.onProgressUpdate(values);
- }
-
- @Override
- protected void onPostExecute(List<MarkerOptions> markerOptions) {
- super.onPostExecute(markerOptions);
- mMapboxMap.addMarkers(markerOptions);
- mProgressDialog.hide();
- }
- }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CameraActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CameraActivity.java
deleted file mode 100644
index 3f7703954b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CameraActivity.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Toast;
-
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class CameraActivity extends AppCompatActivity {
-
- private static final String TAG = "CameraActivity";
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_camera);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.cameraMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- // set a style
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- Log.v(MapboxConstants.TAG, position.toString());
- }
- });
-
- // handle move button clicks
- findViewById(R.id.cameraMoveButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(new LatLng(44.50128, -88.06216)) // Sets the center of the map to Lambeau Field
- .zoom(14) // Sets the zoom
- .tilt(30) // Sets the tilt of the camera to 30 degrees
- .build();
-
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
- }
- });
-
- // handle ease button clicks
- findViewById(R.id.cameraEaseButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(new LatLng(48.21874, 11.62465)) // Sets the center of the map to Allianz Arena
- .zoom(16) // Sets the zoom
- .bearing(180) // Sets the orientation of the camera to south
- .build(); // Creates a CameraPosition from the builder
-
- MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- Log.i(TAG, "Duration onCancel Callback called.");
- Toast.makeText(CameraActivity.this, "Ease onCancel Callback called.", Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void onFinish() {
- Log.i(TAG, "Duration onFinish Callback called.");
- Toast.makeText(CameraActivity.this, "Ease onFinish Callback called.", Toast.LENGTH_LONG).show();
- }
- };
-
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 25000, callback);
- }
- });
-
- // handle animate button clicks
- findViewById(R.id.cameraAnimateButton).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(new LatLng(-22.91214, -43.23012)) // Sets the center of the map to Maracanã
- .bearing(270) // Sets the orientation of the camera to west
- .tilt(20) // Sets the tilt of the camera to 30 degrees
- .build(); // Creates a CameraPosition from the builder
-
- MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- Log.i(TAG, "Duration onCancel Callback called.");
- Toast.makeText(CameraActivity.this, "Duration onCancel Callback called.", Toast.LENGTH_LONG).show();
- }
-
- @Override
- public void onFinish() {
- Log.i(TAG, "Duration onFinish Callback called.");
- Toast.makeText(CameraActivity.this, "Duration onFinish Callback called.", Toast.LENGTH_LONG).show();
- }
- };
-
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 25000, callback);
- }
- });
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivity.java
deleted file mode 100644
index f6b3095a00..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/CoordinateChangeActivity.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class CoordinateChangeActivity extends AppCompatActivity {
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_animate_coordinate);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setTag(true);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(getNextLatLng(), 16));
-
- UiSettings uiSettings = mapboxMap.getUiSettings();
- uiSettings.setCompassEnabled(false);
- }
- });
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMapboxMap != null) {
- mMapboxMap.animateCamera(CameraUpdateFactory.newLatLng(getNextLatLng()));
- }
- }
- });
- }
-
- private LatLng getNextLatLng() {
- boolean first = (boolean) mMapView.getTag();
- mMapView.setTag(!first);
- return first ? new LatLng(38.87000, -77.00800) : new LatLng(38.87031, -77.00897);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DirectionsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DirectionsActivity.java
deleted file mode 100644
index 0292ce2061..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DirectionsActivity.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-
-import com.mapbox.directions.DirectionsCriteria;
-import com.mapbox.directions.MapboxDirections;
-import com.mapbox.directions.service.models.DirectionsResponse;
-import com.mapbox.directions.service.models.DirectionsRoute;
-import com.mapbox.directions.service.models.Waypoint;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.annotations.PolylineOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-import java.util.List;
-
-import retrofit.Callback;
-import retrofit.Response;
-import retrofit.Retrofit;
-
-public class DirectionsActivity extends AppCompatActivity {
-
- private final static String LOG_TAG = "DirectionsActivity";
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_directions);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- loadRoute();
- }
- });
- }
-
- private void loadRoute() {
- // Dupont Circle (Washington, DC)
- Waypoint origin = new Waypoint(-77.04341, 38.90962);
-
- // The White House (Washington, DC)
- Waypoint destination = new Waypoint(-77.0365, 38.8977);
-
- // Set map at centroid
- LatLng centroid = new LatLng(
- (origin.getLatitude() + destination.getLatitude()) / 2,
- (origin.getLongitude() + destination.getLongitude()) / 2);
-
- mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder()
- .target(centroid)
- .zoom(14)
- .build()));
-
- // Add origin and destination to the map
- mMapboxMap.addMarker(new MarkerOptions()
- .position(new LatLng(origin.getLatitude(), origin.getLongitude()))
- .title("Origin")
- .snippet("Dupont Circle"));
- mMapboxMap.addMarker(new MarkerOptions()
- .position(new LatLng(destination.getLatitude(), destination.getLongitude()))
- .title("Destination")
- .snippet("The White House"));
-
- // Get route from API
- getRoute(origin, destination);
- }
-
- private void getRoute(Waypoint origin, Waypoint destination) {
- MapboxDirections md = new MapboxDirections.Builder()
- .setAccessToken(ApiAccess.getToken(this))
- .setOrigin(origin)
- .setDestination(destination)
- .setProfile(DirectionsCriteria.PROFILE_WALKING)
- .build();
-
- md.enqueue(new Callback<DirectionsResponse>() {
- @Override
- public void onResponse(Response<DirectionsResponse> response, Retrofit retrofit) {
- // You can get generic HTTP info about the response
- Log.d(LOG_TAG, "Response code: " + response.code());
-
- // Print some info about the route
- DirectionsRoute currentRoute = response.body().getRoutes().get(0);
- Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
-
- // Draw the route on the map
- drawRoute(currentRoute);
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.e(LOG_TAG, "Error: " + t.getMessage());
- }
- });
- }
-
- private void drawRoute(DirectionsRoute route) {
- // Convert List<Waypoint> into LatLng[]
- List<Waypoint> waypoints = route.getGeometry().getWaypoints();
- LatLng[] point = new LatLng[waypoints.size()];
- for (int i = 0; i < waypoints.size(); i++) {
- point[i] = new LatLng(
- waypoints.get(i).getLatitude(),
- waypoints.get(i).getLongitude());
- }
-
- // Draw Points on MapView
- mMapboxMap.addPolyline(new PolylineOptions()
- .add(point)
- .color(Color.parseColor("#3887be"))
- .width(5));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivity.java
deleted file mode 100644
index b4dd6bd2c4..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DoubleMapActivity.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-
-public class DoubleMapActivity extends AppCompatActivity {
-
- private static final String TAG_FRAGMENT = "map";
- private DoubleMapFragment mMapFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_map_fragment);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- if (savedInstanceState == null) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.add(R.id.fragment_container, mMapFragment = new DoubleMapFragment(), TAG_FRAGMENT);
- transaction.commit();
- } else {
- mMapFragment = (DoubleMapFragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
- }
- }
-
- public static class DoubleMapFragment extends Fragment {
-
- private MapView mMapView;
- private MapView mMapViewMini;
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_double_map, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // MapView large
- mMapView = (MapView) view.findViewById(R.id.mapView);
- mMapView.setStyleUrl(Style.DARK);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18));
- try {
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- } catch (SecurityException e) {
- // permission is handled in MainActivity
- getActivity().finish();
- }
- }
- });
-
- // MapView mini
- mMapViewMini = (MapView) view.findViewById(R.id.mini_map);
- mMapViewMini.setStyle(Style.LIGHT);
- mMapViewMini.onCreate(savedInstanceState);
- mMapViewMini.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
-
- UiSettings uiSettings = mapboxMap.getUiSettings();
- uiSettings.setAllGesturesEnabled(false);
- uiSettings.setCompassEnabled(false);
- uiSettings.setAttributionEnabled(false);
- uiSettings.setLogoEnabled(false);
-
- try {
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- }catch (SecurityException e){
- // permission is handled in MainActivity
- getActivity().finish();
- }
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- mMapViewMini.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- mMapViewMini.onPause();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- mMapViewMini.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- mMapViewMini.onLowMemory();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- mMapViewMini.onSaveInstanceState(outState);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivity.java
deleted file mode 100644
index a665a085ef..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/DynamicMarkerChangeActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class DynamicMarkerChangeActivity extends AppCompatActivity {
-
- private static final LatLng LAT_LNG_CHELSEA = new LatLng(51.481670, -0.190849);
- private static final LatLng LAT_LNG_ARSENAL = new LatLng(51.555062, -0.108417);
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private IconFactory mIconFactory;
- private Marker mMarker;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_dynamic_marker);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mIconFactory = IconFactory.getInstance(this);
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setTag(false);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.506675, -0.128699), 10));
-
- // Create marker
- MarkerOptions markerOptions = new MarkerOptions()
- .position(LAT_LNG_CHELSEA)
- .icon(mIconFactory.fromResource(R.drawable.ic_chelsea));
- mMarker = mapboxMap.addMarker(markerOptions);
- }
- });
-
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMapboxMap != null) {
- updateMarker();
- }
- }
- });
- }
-
- private void updateMarker() {
- // update model
- boolean first = (boolean) mMapView.getTag();
- mMapView.setTag(!first);
-
- // update marker
- mMarker.setPosition(first ? LAT_LNG_CHELSEA : LAT_LNG_ARSENAL);
- mMarker.setIcon(mIconFactory.fromResource(first ? R.drawable.ic_chelsea : R.drawable.ic_arsenal));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/GeocoderActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/GeocoderActivity.java
deleted file mode 100644
index 6185151471..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/GeocoderActivity.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.PointF;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.MenuItem;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.mapbox.geocoder.GeocoderCriteria;
-import com.mapbox.geocoder.MapboxGeocoder;
-import com.mapbox.geocoder.service.models.GeocoderFeature;
-import com.mapbox.geocoder.service.models.GeocoderResponse;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-import java.util.List;
-
-import retrofit.Callback;
-import retrofit.Response;
-import retrofit.Retrofit;
-
-public class GeocoderActivity extends AppCompatActivity {
-
- private static final String LOG_TAG = "GeocoderActivity";
- private static final LatLng DC_DUPONT_CIRCLE = new LatLng(38.90962, -77.04341);
-
- private MapView mapView;
- private TextView textView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_geocoder);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- textView = (TextView) findViewById(R.id.message);
- setMessage("Tap the map to trigger the geocoder.");
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.setAccessToken(ApiAccess.getToken(this));
- mapView.setStyle(Style.MAPBOX_STREETS);
- mapView.onCreate(savedInstanceState);
-
- final ImageView dropPinView = new ImageView(this);
- dropPinView.setImageResource(R.drawable.ic_droppin_24dp);
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
- dropPinView.setLayoutParams(params);
- mapView.addView(dropPinView);
-
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- final Projection projection = mapboxMap.getProjection();
- final int width = mapView.getMeasuredWidth();
- final int height = mapView.getMeasuredHeight();
-
- // Camera position
- mapboxMap.setCameraPosition(
- new CameraPosition.Builder()
- .target(DC_DUPONT_CIRCLE)
- .zoom(15)
- .bearing(0)
- .tilt(0)
- .build()
- );
-
- // Click listener
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- PointF centerPoint = new PointF(width / 2, (height + dropPinView.getHeight()) / 2);
- LatLng centerLatLng = new LatLng(projection.fromScreenLocation(centerPoint));
-
- setMessage("Geocoding...");
-
- mapboxMap.removeAnnotations();
- mapboxMap.addMarker(new MarkerOptions().position(centerLatLng));
-
- geocode(centerLatLng);
- }
- });
- }
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mapView.onPause();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mapView.onResume();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- /*
- * Forward geocoding
- */
-
- private void geocode(final LatLng point) {
- new AsyncTask<Void, Void, Void>() {
-
- @Override
- protected Void doInBackground(Void... params) {
- MapboxGeocoder client = new MapboxGeocoder.Builder()
- .setAccessToken(ApiAccess.getToken(GeocoderActivity.this))
- .setCoordinates(point.getLongitude(), point.getLatitude())
- .setType(GeocoderCriteria.TYPE_POI)
- .build();
-
- client.enqueue(new Callback<GeocoderResponse>()
-
- {
- @Override
- public void onResponse(Response<GeocoderResponse> response, Retrofit retrofit) {
- List<GeocoderFeature> results = response.body().getFeatures();
- if (results.size() > 0) {
- String placeName = results.get(0).getPlaceName();
- setSuccess(placeName);
- } else {
- setMessage("No results.");
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- setError(t.getMessage());
- }
- }
-
- );
- return null;
- }
- }.execute();
- }
-
- /*
- * Update text view
- */
-
- private void setMessage(String message) {
- Log.d(LOG_TAG, "Message: " + message);
- textView.setText(message);
- }
-
- private void setSuccess(String placeName) {
- Log.d(LOG_TAG, "Place name: " + placeName);
- textView.setText(placeName);
- }
-
- private void setError(String message) {
- Log.e(LOG_TAG, "Error: " + message);
- textView.setText("Error: " + message);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java
deleted file mode 100644
index d857d665bd..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowActivity.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-import java.text.DecimalFormat;
-
-public class InfoWindowActivity extends AppCompatActivity {
-
- private MapView mMapView;
- private Marker mCustomMarker;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_infowindow);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- mapboxMap.addMarker(new MarkerOptions()
- .title("Intersection")
- .snippet("H St NW with 15th St NW")
- .position(new LatLng(38.9002073, -77.03364419)));
-
- Marker marker = mapboxMap.addMarker(new MarkerOptions()
- .title("White House")
- .snippet("The official residence and principal workplace of the President of the United States, located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every U.S. president since John Adams in 1800.")
- .position(new LatLng(38.897705003219784, -77.03655168667463)));
-
- // open InfoWindow at startup!
- mapboxMap.selectMarker(marker);
-
- mapboxMap.addMarker(new MarkerOptions().title("Intersection")
- .snippet("E St NW with 17th St NW")
- .position(new LatLng(38.8954236, -77.0394623)));
-
- mapboxMap.setOnInfoWindowCloseListener(new MapboxMap.OnInfoWindowCloseListener() {
- @Override
- public void onInfoWindowClose(Marker marker) {
- Toast.makeText(getApplicationContext(), "OnClose: " + marker.getTitle(), Toast.LENGTH_LONG).show();
- }
- });
-
- final DecimalFormat formatter = new DecimalFormat("#.#####");
- mapboxMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() {
- @Override
- public void onMapLongClick(@NonNull LatLng point) {
- // Remove previous added marker
- if (mCustomMarker != null) {
- mapboxMap.removeAnnotation(mCustomMarker);
- mCustomMarker = null;
- }
-
- // Add marker on long click location with default marker image
- mCustomMarker = mapboxMap.addMarker(new MarkerOptions()
- .title("Custom Marker")
- .snippet(formatter.format(point.getLatitude()) + ", " + formatter.format(point.getLongitude()))
- .position(point));
- }
- });
-
-
- mapboxMap.setOnInfoWindowClickListener(new MapboxMap.OnInfoWindowClickListener() {
- @Override
- public boolean onInfoWindowClick(@NonNull Marker marker) {
- Toast.makeText(getApplicationContext(), "OnClick: " + marker.getTitle(), Toast.LENGTH_LONG).show();
- // return false to close the info window
- // return true to leave the info window open
- return false;
- }
- });
-
- mapboxMap.setOnInfoWindowLongClickListener(new MapboxMap.OnInfoWindowLongClickListener() {
- @Override
- public void onInfoWindowLongClick(Marker marker) {
- Toast.makeText(getApplicationContext(), "OnLongClick: " + marker.getTitle(), Toast.LENGTH_LONG).show();
- }
- });
-
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java
deleted file mode 100644
index 56aa965025..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowAdapterActivity.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.annotations.CityStateMarker;
-import com.mapbox.mapboxsdk.testapp.annotations.CityStateMarkerOptions;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class InfoWindowAdapterActivity extends AppCompatActivity {
-
- private MapView mMapView;
- private IconFactory mIconFactory;
- private Drawable mIconDrawable;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_infowindow_adapter);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
-
- mIconFactory = IconFactory.getInstance(this);
- mIconDrawable = ContextCompat.getDrawable(this, R.drawable.ic_location_city_24dp);
-
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- // add markers
- mapboxMap.addMarker(generateCityStateMarker("Andorra", 42.505777, 1.52529, "#F44336"));
- mapboxMap.addMarker(generateCityStateMarker("Luxembourg", 49.815273, 6.129583, "#3F51B5"));
- mapboxMap.addMarker(generateCityStateMarker("Monaco", 43.738418, 7.424616, "#673AB7"));
- mapboxMap.addMarker(generateCityStateMarker("Vatican City", 41.902916, 12.453389, "#009688"));
- mapboxMap.addMarker(generateCityStateMarker("San Marino", 43.942360, 12.457777, "#795548"));
- mapboxMap.addMarker(generateCityStateMarker("Liechtenstein", 47.166000, 9.555373, "#FF5722"));
-
- // add custom window adapter
- mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
-
- private int tenDp = (int) getResources().getDimension(R.dimen.attr_margin);
-
- @Override
- public View getInfoWindow(@NonNull Marker marker) {
- TextView textView = new TextView(InfoWindowAdapterActivity.this);
- textView.setText(marker.getTitle());
- textView.setTextColor(Color.WHITE);
-
- if(marker instanceof CityStateMarker){
- CityStateMarker cityStateMarker = (CityStateMarker)marker;
- textView.setBackgroundColor(Color.parseColor(cityStateMarker.getInfoWindowBackgroundColor()));
- }
-
- textView.setPadding(tenDp, tenDp, tenDp, tenDp);
- return textView;
- }
- });
- }
- });
- }
-
- private CityStateMarkerOptions generateCityStateMarker(String title, double lat, double lng, String color) {
- CityStateMarkerOptions marker = new CityStateMarkerOptions();
- marker.title(title);
- marker.position(new LatLng(lat, lng));
- marker.infoWindowBackground(color);
-
- mIconDrawable.setColorFilter(Color.parseColor(color), PorterDuff.Mode.SRC_IN);
- Icon icon = mIconFactory.fromDrawable(mIconDrawable);
- marker.icon(icon);
- return marker;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java
deleted file mode 100644
index 6fa5be3ce6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/InfoWindowConcurrentActivity.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class InfoWindowConcurrentActivity extends AppCompatActivity {
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_infowindow_concurrent);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.infoWindowConcurrentMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
-
- mapboxMap.addMarker(new MarkerOptions()
- .title("Intersection")
- .snippet("H St NW with 15th St NW")
- .position(new LatLng(38.9002073, -77.03364419)));
-
- mapboxMap.addMarker(new MarkerOptions()
- .title("White House")
- .snippet("The official residence and principal workplace of the President of the United States, located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every U.S. president since John Adams in 1800.")
- .position(new LatLng(38.897705003219784, -77.03655168667463)));
-
- mapboxMap.addMarker(new MarkerOptions().title("Intersection")
- .snippet("E St NW with 17th St NW")
- .position(new LatLng(38.8954236, -77.0394623)));
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivity.java
deleted file mode 100644
index 24c6e7eb28..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/LatLngBoundsActivity.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class LatLngBoundsActivity extends AppCompatActivity {
-
- private static final LatLng LOS_ANGELES = new LatLng(34.053940, -118.242622);
- private static final LatLng NEW_YORK = new LatLng(40.712730, -74.005953);
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_visible_bounds);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyle(Style.DARK);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- mapboxMap.setStyle(Style.DARK);
-
- UiSettings uiSettings = mapboxMap.getUiSettings();
- uiSettings.setAllGesturesEnabled(false);
-
- mapboxMap.addMarker(new MarkerOptions()
- .title("Los Angeles")
- .snippet("City Hall")
- .position(LOS_ANGELES));
-
- mapboxMap.addMarker(new MarkerOptions()
- .title("New York")
- .snippet("City Hall")
- .position(NEW_YORK));
-
- // Create Bounds
- final LatLngBounds bounds = new LatLngBounds.Builder()
- .include(NEW_YORK)
- .include(LOS_ANGELES)
- .build();
-
- // And padding
- int padding = (int) getResources().getDimension(R.dimen.coordinatebounds_margin);
-
- // Move camera to the bounds with padding
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));
-
- // Call mapboxMap.getProjection().getVisibleRegion().latLngBounds to retrieve the bounds
- Log.v(MapboxConstants.TAG, mapboxMap.getProjection().getVisibleRegion().latLngBounds.toString());
- }
- });
-
-
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
deleted file mode 100644
index 17bf767264..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
+++ /dev/null
@@ -1,685 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.Manifest;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Color;
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.NavigationView;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-
-import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.annotations.PolygonOptions;
-import com.mapbox.mapboxsdk.annotations.PolylineOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.layers.CustomLayer;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.testapp.layers.ExampleCustomLayer;
-import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-public class MainActivity extends AppCompatActivity {
-
- private static final String TAG = "MainActivity";
-
- //
- // Static members
- //
-
- // Used for saving instance state
- private static final String STATE_IS_ANNOTATIONS_ON = "isAnnotationsOn";
- private static final String STATE_SELECTED_STYLE = "selectedStyle";
- private static final String STATE_MARKER_LIST = "markerList";
-
- // Used for permissions requests
- private static final int PERMISSIONS_LOCATION = 0;
- private static final int PERMISSIONS_TRACKING_MODE_ACTIVITY = 1;
-
- // Used for info window
- private static final DecimalFormat LAT_LON_FORMATTER = new DecimalFormat("#.#####");
-
- //
- // Instance members
- //
-
- // Used for the UI
- private DrawerLayout mDrawerLayout;
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private TextView mFpsTextView;
- private int mSelectedStyle = R.id.actionStyleMapboxStreets;
- private NavigationView mNavigationView;
- private CoordinatorLayout mCoordinatorLayout;
- private boolean mIsShowingCustomLayer;
-
- // Used for GPS
- private FloatingActionButton mLocationFAB;
-
- // Used for Annotations
- private boolean mIsAnnotationsOn = false;
- private ArrayList<MarkerOptions> mMarkerList = new ArrayList<>();
-
- //
- // Lifecycle events
- //
-
- // Called when activity is created
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Load the layout
- setContentView(R.layout.activity_main);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- final ActionBar ab = getSupportActionBar();
- if (ab != null) {
- ab.setHomeAsUpIndicator(R.drawable.ic_menu_24dp);
- ab.setDisplayHomeAsUpEnabled(true);
- }
-
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-
- mNavigationView = (NavigationView) findViewById(R.id.nav_view);
- if (mNavigationView != null) {
- setupDrawerContent(mNavigationView);
- }
-
- mCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator_layout);
-
- mFpsTextView = (TextView) findViewById(R.id.view_fps);
- mFpsTextView.setText("");
-
- mLocationFAB = (FloatingActionButton) findViewById(R.id.locationFAB);
- mLocationFAB.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Toggle GPS position updates
- if (mMapboxMap != null) {
- toggleGps(!mMapboxMap.isMyLocationEnabled());
- }
- }
- });
-
- mMapView = (MapView) findViewById(R.id.mainMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- changeMapStyle(mSelectedStyle);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
-
- mMapboxMap.setOnFpsChangedListener(new MyOnFpsChangedListener());
-
- // add location listener to MapboxMap
- mapboxMap.setOnMyLocationChangeListener(new MapboxMap.OnMyLocationChangeListener() {
- @Override
- public void onMyLocationChange(@Nullable Location location) {
- String desc = "Loc Chg: ";
- boolean noInfo = true;
- if (location.hasSpeed()) {
- desc += String.format("Spd = %.1f km/h ", location.getSpeed() * 3.6f);
- noInfo = false;
- }
- if (location.hasAltitude()) {
- desc += String.format("Alt = %.0f m ", location.getAltitude());
- noInfo = false;
- }
- if (noInfo) {
- desc += "No extra info";
- }
- Snackbar.make(mCoordinatorLayout, desc, Snackbar.LENGTH_SHORT).show();
- }
- });
-
- // Set default UI state
- mNavigationView.getMenu().findItem(R.id.action_compass).setChecked(mapboxMap.getUiSettings().isCompassEnabled());
- mNavigationView.getMenu().findItem(R.id.action_debug).setChecked(mapboxMap.isDebugActive());
- mNavigationView.getMenu().findItem(R.id.action_markers).setChecked(mIsAnnotationsOn);
- toggleGps(mapboxMap.isMyLocationEnabled());
-
- // Listeners
- mapboxMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() {
- @Override
- public void onMapLongClick(@NonNull LatLng point) {
- MarkerOptions marker = new MarkerOptions()
- .position(point)
- .title("Dropped Pin")
- .snippet(LAT_LON_FORMATTER.format(point.getLatitude()) + ", " +
- LAT_LON_FORMATTER.format(point.getLongitude()))
- .icon(null);
-
- mMarkerList.add(marker);
- mapboxMap.addMarker(marker);
- }
- });
-
- // Restore saved state
- if (savedInstanceState != null) {
- mIsAnnotationsOn = savedInstanceState.getBoolean(STATE_IS_ANNOTATIONS_ON);
- mSelectedStyle = savedInstanceState.getInt(STATE_SELECTED_STYLE);
- mMarkerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST);
- mapboxMap.addMarkers(mMarkerList);
- }
-
- mMapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
- @Override
- public boolean onMarkerClick(@NonNull Marker marker) {
- Snackbar.make(mCoordinatorLayout, "Marker Click Listener for " + marker.getTitle(), Snackbar.LENGTH_SHORT).show();
- return false;
- }
- });
-
- mMapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- String location = LAT_LON_FORMATTER.format(point.getLatitude()) + ", " +
- LAT_LON_FORMATTER.format(point.getLongitude());
- Snackbar.make(mCoordinatorLayout, "Map Click Listener " + location, Snackbar.LENGTH_SHORT).show();
- }
- });
-
- mMapboxMap.setOnInfoWindowClickListener(new MapboxMap.OnInfoWindowClickListener() {
- @Override
- public boolean onInfoWindowClick(@NonNull Marker marker) {
- Snackbar.make(mCoordinatorLayout, "InfoWindow Click Listener for " + marker.getTitle(), Snackbar.LENGTH_SHORT).show();
- marker.hideInfoWindow();
- return true;
- }
- });
-
-
- mMapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- Log.v(TAG, "OnCameraChange : " + position);
- }
- });
-
- }
- });
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- // We need to recheck permissions in case user revoked them via settings app
- if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) ||
- (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- toggleGps(false);
- }
- }
-
- // Called when our app comes into the foreground
- @Override
- public void onResume() {
- super.onResume();
-
- mMapView.onResume();
- }
-
- // Called when our app goes into the background
- @Override
- public void onPause() {
- super.onPause();
-
- mMapView.onPause();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- mMapView.onDestroy();
- }
-
- // Called when we need to save instance state
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- mMapView.onSaveInstanceState(outState);
- outState.putBoolean(STATE_IS_ANNOTATIONS_ON, mIsAnnotationsOn);
- outState.putInt(STATE_SELECTED_STYLE, mSelectedStyle);
- outState.putParcelableArrayList(STATE_MARKER_LIST, mMarkerList);
- }
-
- // Called when the system is running low on memory
- @Override
- public void onLowMemory() {
- super.onLowMemory();
-
- mMapView.onLowMemory();
- }
-
- //
- // Other events
- //
-
- // Called when pressing action bar items
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- mDrawerLayout.openDrawer(GravityCompat.START);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
- switch (requestCode) {
- case PERMISSIONS_LOCATION:
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- toggleGps(true);
- }
- break;
-
- case PERMISSIONS_TRACKING_MODE_ACTIVITY:
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- startActivity(new Intent(getApplicationContext(), MyLocationTrackingModeActivity.class));
- }
- break;
- }
- }
-
- private void setupDrawerContent(NavigationView navigationView) {
- navigationView.setNavigationItemSelectedListener(
- new NavigationView.OnNavigationItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(MenuItem menuItem) {
- mDrawerLayout.closeDrawers();
-
- // Respond To Selection
- switch (menuItem.getItemId()) {
-
- case R.id.action_debug:
- // Cycle map debug options
- mMapboxMap.cycleDebugOptions();
- toggleFpsCounter(mMapboxMap.isDebugActive());
- return true;
-
- case R.id.action_markers:
- // Toggle markers
- toggleAnnotations(!mIsAnnotationsOn);
- return true;
-
- case R.id.action_compass:
- // Toggle compass
- UiSettings uiSettings = mMapboxMap.getUiSettings();
- uiSettings.setCompassEnabled(!uiSettings.isCompassEnabled());
- return true;
-
- case R.id.action_mapboxmap:
- startActivity(new Intent(getApplicationContext(), MapboxMapActivity.class));
- return true;
-
- case R.id.action_info_window_adapter:
- startActivity(new Intent(getApplicationContext(), InfoWindowAdapterActivity.class));
- return true;
-
- case R.id.action_camera:
- startActivity(new Intent(getApplicationContext(), CameraActivity.class));
- return true;
-
- case R.id.action_tilt:
- startActivity(new Intent(getApplicationContext(), TiltActivity.class));
- return true;
-
- case R.id.action_map_fragment:
- startActivity(new Intent(getApplicationContext(), MapFragmentActivity.class));
- return true;
-
- case R.id.action_support_map_fragment:
- startActivity(new Intent(getApplicationContext(), SupportMapFragmentActivity.class));
- return true;
-
- case R.id.action_press_for_marker:
- startActivity(new Intent(getApplicationContext(), PressForMarkerActivity.class));
- return true;
-
- case R.id.action_manual_zoom:
- startActivity(new Intent(getApplicationContext(), ManualZoomActivity.class));
- return true;
-
- case R.id.action_minmax_zoom:
- startActivity(new Intent(getApplicationContext(), MaxMinZoomActivity.class));
- return true;
-
- case R.id.action_coordinate_change:
- startActivity(new Intent(getApplicationContext(), CoordinateChangeActivity.class));
- return true;
-
- case R.id.action_bulk_markers:
- startActivity(new Intent(getApplicationContext(), BulkMarkerActivity.class));
- return true;
-
- case R.id.action_info_window:
- startActivity(new Intent(getApplicationContext(), InfoWindowActivity.class));
- return true;
-
- case R.id.action_info_window_concurrent:
- startActivity(new Intent(getApplicationContext(), InfoWindowConcurrentActivity.class));
- return true;
-
- case R.id.action_visible_bounds:
- startActivity(new Intent(getApplicationContext(), LatLngBoundsActivity.class));
- return true;
-
- case R.id.action_user_tracking_mode:
- if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) ||
- (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- ActivityCompat.requestPermissions(MainActivity.this,
- new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
- PERMISSIONS_TRACKING_MODE_ACTIVITY);
- } else {
- startActivity(new Intent(getApplicationContext(), MyLocationTrackingModeActivity.class));
- }
- return true;
-
- case R.id.action_polyline:
- startActivity(new Intent(getApplicationContext(), PolylineActivity.class));
- return true;
-
- case R.id.action_custom_layer:
- if (mIsShowingCustomLayer) {
- removeCustomLayer();
- } else {
- addCustomLayer();
- }
- return true;
-
- case R.id.action_directions:
- startActivity(new Intent(getApplicationContext(), DirectionsActivity.class));
- return true;
-
- case R.id.action_double_mapview:
- if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) ||
- (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- ActivityCompat.requestPermissions(MainActivity.this,
- new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
- PERMISSIONS_TRACKING_MODE_ACTIVITY);
- } else {
- startActivity(new Intent(getApplicationContext(), DoubleMapActivity.class));
- }
- return true;
-
- case R.id.action_geocoder:
- startActivity(new Intent(getApplicationContext(), GeocoderActivity.class));
- return true;
-
- case R.id.action_scroll_by:
- startActivity(new Intent(getApplicationContext(), ScrollByActivity.class));
- return true;
-
- case R.id.action_dynamic_marker:
- startActivity(new Intent(getApplicationContext(), DynamicMarkerChangeActivity.class));
- return true;
-
- case R.id.action_map_padding:
- startActivity(new Intent(getApplicationContext(), MapPaddingActivity.class));
- return true;
-
- case R.id.action_offline:
- startActivity(new Intent(getApplicationContext(), OfflineActivity.class));
- return true;
-
- case R.id.action_animated_marker:
- startActivity(new Intent(getApplicationContext(), AnimatedMarkerActivity.class));
- return true;
-
- default:
- return changeMapStyle(menuItem.getItemId());
- }
- }
- });
- }
-
- private void toggleFpsCounter(boolean enableFps) {
- // Show the FPS counter
- if (enableFps) {
- mFpsTextView.setVisibility(View.VISIBLE);
- mFpsTextView.setText(getResources().getString(R.string.label_fps));
- } else {
- mFpsTextView.setVisibility(View.INVISIBLE);
- }
- }
-
- private boolean changeMapStyle(int id) {
- switch (id) {
- case R.id.actionStyleMapboxStreets:
- mMapView.setStyleUrl(Style.MAPBOX_STREETS);
- mSelectedStyle = id;
- return true;
-
- case R.id.actionStyleEmerald:
- mMapView.setStyleUrl(Style.EMERALD);
- mSelectedStyle = id;
- return true;
-
- case R.id.actionStyleLight:
- mMapView.setStyleUrl(Style.LIGHT);
- mSelectedStyle = id;
- return true;
-
- case R.id.actionStyleDark:
- mMapView.setStyleUrl(Style.DARK);
- mSelectedStyle = id;
- return true;
-
- case R.id.actionStyleSatellite:
- mMapView.setStyleUrl(Style.SATELLITE);
- mSelectedStyle = id;
- return true;
-
- case R.id.actionStyleSatelliteStreets:
- mMapView.setStyleUrl(Style.SATELLITE_STREETS);
- mSelectedStyle = id;
- return true;
-
- default:
- return false;
- }
- }
-
- /**
- * Enabled / Disable GPS location updates along with updating the UI
- *
- * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
- */
- private void toggleGps(boolean enableGps) {
- if (enableGps) {
- if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) ||
- (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
- PERMISSIONS_LOCATION);
- } else {
- mMapboxMap.setOnMyLocationChangeListener(new MapboxMap.OnMyLocationChangeListener() {
- @Override
- public void onMyLocationChange(@Nullable Location location) {
- if (location != null) {
- mMapboxMap.setCameraPosition(new CameraPosition.Builder()
- .target(new LatLng(location))
- .zoom(16)
- .bearing(0)
- .tilt(0)
- .build());
- mMapboxMap.setOnMyLocationChangeListener(null);
- }
- }
- });
- mMapboxMap.setMyLocationEnabled(true);
-
- TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.GPS);
-
- mLocationFAB.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- }
- } else {
- if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) ||
- (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED)) {
- mMapboxMap.setMyLocationEnabled(false);
- }
- mLocationFAB.setColorFilter(Color.TRANSPARENT);
- }
- }
-
- /**
- * Enable / Disable Annotations.
- *
- * @param enableAnnotations True to display, False to hide
- */
- private void toggleAnnotations(boolean enableAnnotations) {
- if (enableAnnotations) {
- if (!mIsAnnotationsOn) {
- mIsAnnotationsOn = true;
- addMarkers();
- addPolyline();
- addPolygon();
- mMapboxMap.setCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(38.11727, -122.22839))
- .zoom(7)
- .bearing(0)
- .tilt(0)
- .build());
- }
- } else {
- if (mIsAnnotationsOn) {
- mIsAnnotationsOn = false;
- removeAnnotations();
- }
- }
- }
-
- private void addMarkers() {
- List<MarkerOptions> markerOptionsList = new ArrayList<>();
-
- final MarkerOptions backLot = generateMarker("Back Lot", "The back lot behind my house", null, 38.649441, -121.369064);
- markerOptionsList.add(backLot);
-
- final Icon dogIcon = IconFactory.getInstance(this).fromAsset("dog-park-24.png");
- final MarkerOptions cheeseRoom = generateMarker("Cheese Room", "The only air conditioned room on the property", dogIcon, 38.531577, -122.010646);
- markerOptionsList.add(cheeseRoom);
-
- mMapboxMap.addMarkers(markerOptionsList);
- }
-
- private MarkerOptions generateMarker(String title, String snippet, Icon icon, double lat, double lng) {
- return new MarkerOptions()
- .position(new LatLng(lat, lng))
- .title(title)
- .icon(icon)
- .snippet(snippet);
- }
-
- private void addPolyline() {
- try {
- String geojsonStr = GeoParseUtil.loadStringFromAssets(this, "small_line.geojson");
- List<LatLng> latLngs = GeoParseUtil.parseGeoJSONCoordinates(geojsonStr);
- mMapboxMap.addPolyline(new PolylineOptions()
- .add(latLngs.toArray(new LatLng[latLngs.size()]))
- .width(2)
- .color(Color.RED));
- } catch (Exception e) {
- Log.e(TAG, "Error adding Polyline: " + e);
- e.printStackTrace();
- }
- }
-
- private void addPolygon() {
- try {
- String geojsonStr = GeoParseUtil.loadStringFromAssets(this, "small_poly.geojson");
- List<LatLng> latLngs = GeoParseUtil.parseGeoJSONCoordinates(geojsonStr);
- List<PolygonOptions> polygonOptions = new ArrayList<>();
- polygonOptions.add(new PolygonOptions()
- .add(latLngs.toArray(new LatLng[latLngs.size()]))
- .strokeColor(Color.MAGENTA)
- .fillColor(Color.BLUE).alpha(0.5f));
- mMapboxMap.addPolygons(polygonOptions).get(0);
- } catch (Exception e) {
- Log.e(TAG, "Error adding Polygon: " + e);
- e.printStackTrace();
- }
- }
-
- private void removeAnnotations() {
- mMarkerList.clear();
- mMapboxMap.removeAnnotations();
- }
-
- private void addCustomLayer() {
- mIsShowingCustomLayer = true;
- mMapboxMap.addCustomLayer(
- new CustomLayer("custom",
- ExampleCustomLayer.createContext(),
- ExampleCustomLayer.InitializeFunction,
- ExampleCustomLayer.RenderFunction,
- ExampleCustomLayer.DeinitializeFunction),
- null);
- }
-
- private void removeCustomLayer() {
- mIsShowingCustomLayer = false;
- mMapboxMap.removeCustomLayer("custom");
- }
-
- // Called when FPS changes
- private class MyOnFpsChangedListener implements MapboxMap.OnFpsChangedListener {
-
- @Override
- public void onFpsChanged(double fps) {
- mFpsTextView.setText(getResources().getString(R.string.label_fps) + String.format(" %4.2f", fps));
- }
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivity.java
deleted file mode 100644
index 5d0d4b1eef..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ManualZoomActivity.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.Point;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class ManualZoomActivity extends AppCompatActivity {
-
- private MapboxMap mMapboxMap;
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_manual_zoom);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.manualZoomMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyleUrl(Style.SATELLITE_STREETS);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
-
- UiSettings uiSettings = mMapboxMap.getUiSettings();
- uiSettings.setAllGesturesEnabled(false);
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_zoom, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- onBackPressed();
- return true;
-
- case R.id.action_zoom_in:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomIn());
- return true;
-
- case R.id.action_zoom_out:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomOut());
- return true;
-
- case R.id.action_zoom_by:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2));
- return true;
- case R.id.action_zoom_to:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomTo(2));
- return true;
-
- case R.id.action_zoom_to_point:
- View view = getWindow().getDecorView();
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(1, new Point(view.getMeasuredWidth() / 4, view.getMeasuredHeight() / 4)));
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivity.java
deleted file mode 100644
index 74516a2e48..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapFragmentActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.app.FragmentTransaction;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapFragment;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class MapFragmentActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_map_fragment);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- MapFragment mapFragment;
- if (savedInstanceState == null) {
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
-
- MapboxMapOptions options = new MapboxMapOptions();
- options.accessToken(ApiAccess.getToken(this));
- options.styleUrl(Style.SATELLITE_STREETS);
-
- options.scrollGesturesEnabled(false);
- options.zoomGesturesEnabled(false);
- options.tiltGesturesEnabled(false);
- options.rotateGesturesEnabled(false);
-
- options.debugActive(false);
-// options.compassEnabled(false);
-// options.attributionEnabled(false);
-// options.logoEnabled(false);
-
- options.minZoom(9);
- options.maxZoom(11);
- options.camera(new CameraPosition.Builder()
- .target(new LatLng(48.861431, 2.334166))
- .zoom(9)
- .build());
-
- mapFragment = MapFragment.newInstance(options);
-
- transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map");
- transaction.commit();
- } else {
- mapFragment = (MapFragment) getFragmentManager().findFragmentByTag("com.mapbox.map");
- }
-
- mapFragment.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2), 3500);
- }
- });
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivity.java
deleted file mode 100644
index c5e25f48d0..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapPaddingActivity.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class MapPaddingActivity extends AppCompatActivity {
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_map_padding);
-
- final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setTag(true);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
-
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
-
- int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
- int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
- int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
- mapboxMap.setPadding(paddingLeft, toolbar.getHeight(), paddingRight, paddingBottom);
-
- moveToBangalore();
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_padding, menu);
- return true;
- }
-
- private void toggleGps(boolean enable) {
- try {
- // Enable user location
- mMapboxMap.setMyLocationEnabled(enable);
-
- TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
- trackingSettings.setDismissTrackingOnGesture(false);
- trackingSettings.setMyLocationTrackingMode(enable ? MyLocationTracking.TRACKING_FOLLOW : MyLocationTracking.TRACKING_NONE);
- } catch (SecurityException e) {
- // permission not granted is handled in MainActivity
- finish();
- }
- }
-
- private void moveToBangalore() {
- toggleGps(false);
-
- LatLng bangalore = new LatLng(12.9810816, 77.6368034);
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .zoom(16)
- .target(bangalore)
- .bearing(40)
- .tilt(45)
- .build();
-
- mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
- mMapboxMap.addMarker(new MarkerOptions().title("Center map").position(bangalore));
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
-
- case R.id.action_user_tracking:
- if (mMapboxMap != null) {
- toggleGps(true);
- }
- return true;
-
- case R.id.action_bangalore:
- if (mMapboxMap != null) {
- moveToBangalore();
- }
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
-
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
index 4b1305daed..33c75db4d0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.testapp;
import android.app.Application;
import android.os.StrictMode;
+import com.mapbox.mapboxsdk.MapboxAccountManager;
import com.squareup.leakcanary.LeakCanary;
public class MapboxApplication extends Application {
@@ -10,6 +11,7 @@ public class MapboxApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
+ MapboxAccountManager.start(getApplicationContext(), getString(R.string.mapbox_access_token));
LeakCanary.install(this);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivity.java
deleted file mode 100644
index b397d5d688..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxMapActivity.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class MapboxMapActivity extends AppCompatActivity {
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_mapboxmap);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyle(Style.SATELLITE_STREETS);
- mMapView.onCreate(savedInstanceState);
-
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(43.876550, -103.454791))
- .zoom(14)
- .build()));
- }
- });
-
- Snackbar.make(findViewById(android.R.id.content),
- "Demo MapboxMap API",
- Snackbar.LENGTH_INDEFINITE).show();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivity.java
deleted file mode 100644
index f167e1e5e6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MaxMinZoomActivity.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.Point;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class MaxMinZoomActivity extends AppCompatActivity {
-
- private MapboxMap mMapboxMap;
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_maxmin_zoom);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.manualZoomMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyle(Style.SATELLITE_STREETS);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mMapboxMap.setMinZoom(3);
- mMapboxMap.setMaxZoom(5);
-
- mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(-1.063510, 32.895425)));
- }
- });
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_zoom, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case android.R.id.home:
- onBackPressed();
- return true;
-
- case R.id.action_zoom_in:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomIn());
- return true;
-
- case R.id.action_zoom_out:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomOut());
- return true;
-
- case R.id.action_zoom_by:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2));
- return true;
- case R.id.action_zoom_to:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomTo(2));
- return true;
-
- case R.id.action_zoom_to_point:
- mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(12, new Point(100, 100)));
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivity.java
deleted file mode 100644
index 5a349c396f..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MyLocationTrackingModeActivity.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.Toast;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-public class MyLocationTrackingModeActivity extends AppCompatActivity implements MapboxMap.OnMyLocationChangeListener, AdapterView.OnItemSelectedListener {
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private Spinner mLocationSpinner, mBearingSpinner;
- private Location mLocation;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_location_tracking);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- final ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
-
- mapboxMap.setOnMyLocationChangeListener(MyLocationTrackingModeActivity.this);
-
- ArrayAdapter<CharSequence> locationTrackingAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.user_tracking_mode, android.R.layout.simple_spinner_item);
- locationTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mLocationSpinner = (Spinner) findViewById(R.id.spinner_location);
- mLocationSpinner.setAdapter(locationTrackingAdapter);
- mLocationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
-
- ArrayAdapter<CharSequence> bearingTrackingAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.user_bearing_mode, android.R.layout.simple_spinner_item);
- bearingTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mBearingSpinner = (Spinner) findViewById(R.id.spinner_bearing);
- mBearingSpinner.setAdapter(bearingTrackingAdapter);
- mBearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
-
- try {
- mapboxMap.setMyLocationEnabled(true);
- } catch (SecurityException e) {
- //should not occur, permission was checked in MainActivity
- Toast.makeText(MyLocationTrackingModeActivity.this,
- "Location permission is not available", Toast.LENGTH_SHORT).show();
- finish();
- }
-
- mapboxMap.setOnMyLocationTrackingModeChangeListener(new MapboxMap.OnMyLocationTrackingModeChangeListener() {
- @Override
- public void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode) {
- if (MyLocationTracking.TRACKING_NONE == myLocationTrackingMode) {
- mLocationSpinner.setOnItemSelectedListener(null);
- mLocationSpinner.setSelection(0);
- mLocationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
- }
- }
- });
-
- mapboxMap.setOnMyBearingTrackingModeChangeListener(new MapboxMap.OnMyBearingTrackingModeChangeListener() {
- @Override
- public void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode) {
- if (MyBearingTracking.NONE == myBearingTrackingMode) {
- mBearingSpinner.setOnItemSelectedListener(null);
- mBearingSpinner.setSelection(0);
- mBearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
- }
- }
- });
- }
- });
- }
-
- @Override
- public void onMyLocationChange(@Nullable Location location) {
- if (location != null) {
- if (mLocation == null) {
- // initial location to reposition map
- mMapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 14));
- mLocationSpinner.setEnabled(true);
- mBearingSpinner.setEnabled(true);
- }
- mLocation = location;
- showSnackBar();
- }
- }
-
- private void showSnackBar() {
- String desc = "Loc Chg: ";
- boolean noInfo = true;
- if (mLocation.hasSpeed()) {
- desc += String.format("Spd = %.1f km/h ", mLocation.getSpeed() * 3.6f);
- noInfo = false;
- }
- if (mLocation.hasAltitude()) {
- desc += String.format("Alt = %.0f m ", mLocation.getAltitude());
- noInfo = false;
- }
- if (noInfo) {
- desc += "No extra info";
- }
- Snackbar.make(findViewById(android.R.id.content), desc, Snackbar.LENGTH_SHORT).show();
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) throws SecurityException {
- TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
- if (parent.getId() == R.id.spinner_location) {
- switch (position) {
- case 0:
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- break;
-
- case 1:
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- break;
- }
- } else if (parent.getId() == R.id.spinner_bearing) {
- switch (position) {
- case 0:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
- break;
-
- case 1:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.GPS);
- break;
-
- case 2:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.COMPASS);
- break;
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java
deleted file mode 100644
index c6fae95d7a..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java
+++ /dev/null
@@ -1,360 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.Toast;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.offline.OfflineManager;
-import com.mapbox.mapboxsdk.offline.OfflineRegion;
-import com.mapbox.mapboxsdk.offline.OfflineRegionError;
-import com.mapbox.mapboxsdk.offline.OfflineRegionStatus;
-import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition;
-import com.mapbox.mapboxsdk.testapp.offline.DownloadRegionDialog;
-import com.mapbox.mapboxsdk.testapp.offline.ListRegionsDialog;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-
-public class OfflineActivity extends AppCompatActivity
- implements DownloadRegionDialog.DownloadRegionDialogListener {
-
- private final static String LOG_TAG = "OfflineActivity";
-
- // JSON encoding/decoding
- public final static String JSON_CHARSET = "UTF-8";
- public final static String JSON_FIELD_REGION_NAME = "FIELD_REGION_NAME";
-
- /*
- * UI elements
- */
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private ProgressBar mProgressBar;
- private Button downloadRegion;
- private Button listRegions;
-
- private boolean isEndNotified;
-
- /*
- * Offline objects
- */
- private OfflineManager mOfflineManager;
- private OfflineRegion mOfflineRegion;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_offline);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- // Set up map
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyle(Style.MAPBOX_STREETS);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- Log.d(LOG_TAG, "Map is ready");
- mMapboxMap = mapboxMap;
-
- // Set initial position to UNHQ in NYC
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(40.749851, -73.967966))
- .zoom(14)
- .bearing(0)
- .tilt(0)
- .build()));
- }
- });
-
- // The progress bar
- mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
-
- // Set up button listeners
- downloadRegion = (Button) findViewById(R.id.button_download_region);
- downloadRegion.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleDownloadRegion();
- }
- });
-
- listRegions = (Button) findViewById(R.id.button_list_regions);
- listRegions.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleListRegions();
- }
- });
-
- // Set up the OfflineManager
- mOfflineManager = OfflineManager.getInstance(this);
- mOfflineManager.setAccessToken(ApiAccess.getToken(this));
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- /*
- * Buttons logic
- */
-
- private void handleDownloadRegion() {
- Log.d(LOG_TAG, "handleDownloadRegion");
-
- // Show dialog
- DownloadRegionDialog downloadRegionDialog = new DownloadRegionDialog();
- downloadRegionDialog.show(getSupportFragmentManager(), "download");
- }
-
- private void handleListRegions() {
- Log.d(LOG_TAG, "handleListRegions");
-
- // Query the DB asynchronously
- mOfflineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
- @Override
- public void onList(OfflineRegion[] offlineRegions) {
- // Check result
- if (offlineRegions == null || offlineRegions.length == 0) {
- Toast.makeText(OfflineActivity.this, "You have no regions yet.", Toast.LENGTH_SHORT).show();
- return;
- }
-
- // Get regions info
- ArrayList<String> offlineRegionsNames = new ArrayList<>();
- for (OfflineRegion offlineRegion : offlineRegions) {
- offlineRegionsNames.add(getRegionName(offlineRegion));
- }
-
- // Create args
- Bundle args = new Bundle();
- args.putStringArrayList(ListRegionsDialog.ITEMS, offlineRegionsNames);
-
- // Show dialog
- ListRegionsDialog listRegionsDialog = new ListRegionsDialog();
- listRegionsDialog.setArguments(args);
- listRegionsDialog.show(getSupportFragmentManager(), "list");
- }
-
- @Override
- public void onError(String error) {
- Log.e(LOG_TAG, "Error: " + error);
- }
-
- private String getRegionName(OfflineRegion offlineRegion) {
- String regionName;
-
- try {
- byte[] metadata = offlineRegion.getMetadata();
- String json = new String(metadata, JSON_CHARSET);
- JSONObject jsonObject = new JSONObject(json);
- regionName = jsonObject.getString(JSON_FIELD_REGION_NAME);
- } catch (Exception e) {
- Log.e(LOG_TAG, "Failed to decode metadata: " + e.getMessage());
- regionName = "Region " + offlineRegion.getID();
- }
-
- return regionName;
- }
- });
- }
-
- /*
- * Dialogs
- */
-
- @Override
- public void onDownloadRegionDialogPositiveClick(final String regionName) {
- if (TextUtils.isEmpty(regionName)) {
- Toast.makeText(OfflineActivity.this, "Region name cannot be empty.", Toast.LENGTH_SHORT).show();
- return;
- }
-
- // Start progress bar
- Log.d(LOG_TAG, "Download started: " + regionName);
- startProgress();
-
- // Definition
- String styleURL = mMapboxMap.getStyleUrl();
- LatLngBounds bounds = mMapboxMap.getProjection().getVisibleRegion().latLngBounds;
- double minZoom = mMapboxMap.getCameraPosition().zoom;
- double maxZoom = mMapboxMap.getMaxZoom();
- float pixelRatio = this.getResources().getDisplayMetrics().density;
- OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
- styleURL, bounds, minZoom, maxZoom, pixelRatio);
-
- // Sample way of encoding metadata from a JSONObject
- byte[] metadata;
- try {
- JSONObject jsonObject = new JSONObject();
- jsonObject.put(JSON_FIELD_REGION_NAME, regionName);
- String json = jsonObject.toString();
- metadata = json.getBytes(JSON_CHARSET);
- } catch (Exception e) {
- Log.e(LOG_TAG, "Failed to encode metadata: " + e.getMessage());
- metadata = null;
- }
-
- // Create region
- mOfflineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
- @Override
- public void onCreate(OfflineRegion offlineRegion) {
- Log.d(LOG_TAG, "Offline region created: " + regionName);
- mOfflineRegion = offlineRegion;
- launchDownload();
- }
-
- @Override
- public void onError(String error) {
- Log.e(LOG_TAG, "Error: " + error);
- }
- });
- }
-
- private void launchDownload() {
- // Set an observer
- mOfflineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
- @Override
- public void onStatusChanged(OfflineRegionStatus status) {
- // Compute a percentage
- double percentage = status.getRequiredResourceCount() >= 0 ?
- (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :
- 0.0;
-
- if (status.isComplete()) {
- // Download complete
- endProgress("Region downloaded successfully.");
- return;
- } else if (status.isRequiredResourceCountPrecise()) {
- // Switch to determinate state
- setPercentage((int) Math.round(percentage));
- }
-
- // Debug
- Log.d(LOG_TAG, String.format("%s/%s resources; %s bytes downloaded.",
- String.valueOf(status.getCompletedResourceCount()),
- String.valueOf(status.getRequiredResourceCount()),
- String.valueOf(status.getCompletedResourceSize())));
- }
-
- @Override
- public void onError(OfflineRegionError error) {
- Log.e(LOG_TAG, "onError reason: " + error.getReason());
- Log.e(LOG_TAG, "onError message: " + error.getMessage());
- }
-
- @Override
- public void mapboxTileCountLimitExceeded(long limit) {
- Log.e(LOG_TAG, "Mapbox tile count limit exceeded: " + limit);
- }
- });
-
- // Change the region state
- mOfflineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);
- }
-
- /*
- * Progress bar
- */
-
- private void startProgress() {
- // Disable buttons
- downloadRegion.setEnabled(false);
- listRegions.setEnabled(false);
-
- // Start and show the progress bar
- isEndNotified = false;
- mProgressBar.setIndeterminate(true);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- private void setPercentage(final int percentage) {
- mProgressBar.setIndeterminate(false);
- mProgressBar.setProgress(percentage);
- }
-
- private void endProgress(final String message) {
- // Don't notify more than once
- if (isEndNotified) return;
-
- // Enable buttons
- downloadRegion.setEnabled(true);
- listRegions.setEnabled(true);
-
- // Stop and hide the progress bar
- isEndNotified = true;
- mProgressBar.setIndeterminate(false);
- mProgressBar.setVisibility(View.GONE);
-
- // Show a toast
- Toast.makeText(OfflineActivity.this, message, Toast.LENGTH_LONG).show();
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PolylineActivity.java
deleted file mode 100644
index 997009d434..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PolylineActivity.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.annotations.Polyline;
-import com.mapbox.mapboxsdk.annotations.PolylineOptions;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class PolylineActivity extends AppCompatActivity {
-
- private static final String STATE_POLYLINE_OPTIONS = "polylineOptions";
- private static final LatLng ANDORRA = new LatLng(42.505777, 1.52529);
- private static final LatLng LUXEMBOURG = new LatLng(49.815273, 6.129583);
- private static final LatLng MONACO = new LatLng(43.738418, 7.424616);
- private static final LatLng VATICAN_CITY = new LatLng(41.902916, 12.453389);
- private static final LatLng SAN_MARINO = new LatLng(43.942360, 12.457777);
- private static final LatLng LIECHTENSTEIN = new LatLng(47.166000, 9.555373);
-
- private List<Polyline> mPolylines;
- private ArrayList<PolylineOptions> mPolylineOptions = new ArrayList<>();
- private MapView mMapView;
- private MapboxMap mMapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_polyline);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- if (savedInstanceState != null) {
- mPolylineOptions = savedInstanceState.getParcelableArrayList(STATE_POLYLINE_OPTIONS);
- } else {
- mPolylineOptions.addAll(getAllPolylines());
- }
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mPolylines = mapboxMap.addPolylines(mPolylineOptions);
- }
- });
-
- findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMapboxMap != null) {
- if (mPolylines != null && mPolylines.size() > 0) {
- if (mPolylines.size() == 1) {
- // test for removing annotation
- mMapboxMap.removeAnnotation(mPolylines.get(0));
- } else {
- // test for removing annotations
- mMapboxMap.removeAnnotations(mPolylines);
- }
- }
- mPolylineOptions.clear();
- mPolylineOptions.addAll(getRandomLine());
- mPolylines = mMapboxMap.addPolylines(mPolylineOptions);
- }
- }
- });
- }
-
- private List<PolylineOptions> getAllPolylines() {
- List<PolylineOptions> options = new ArrayList<>();
- options.add(generatePolyline(ANDORRA, LUXEMBOURG, "#F44336"));
- options.add(generatePolyline(ANDORRA, MONACO, "#FF5722"));
- options.add(generatePolyline(MONACO, VATICAN_CITY, "#673AB7"));
- options.add(generatePolyline(VATICAN_CITY, SAN_MARINO, "#009688"));
- options.add(generatePolyline(SAN_MARINO, LIECHTENSTEIN, "#795548"));
- options.add(generatePolyline(LIECHTENSTEIN, LUXEMBOURG, "#3F51B5"));
- return options;
- }
-
- private PolylineOptions generatePolyline(LatLng start, LatLng end, String color) {
- PolylineOptions line = new PolylineOptions();
- line.add(start);
- line.add(end);
- line.color(Color.parseColor(color));
- return line;
- }
-
- public List<PolylineOptions> getRandomLine() {
- final List<PolylineOptions> randomLines = getAllPolylines();
- Collections.shuffle(randomLines);
- return new ArrayList<PolylineOptions>() {{
- add(randomLines.get(0));
- }};
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- outState.putParcelableArrayList(STATE_POLYLINE_OPTIONS, mPolylineOptions);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_polyline, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_id_remove:
- // test to remove all annotations
- mPolylineOptions.clear();
- mMapboxMap.removeAnnotations();
- return true;
-
- case android.R.id.home:
- onBackPressed();
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivity.java
deleted file mode 100644
index de78de62fb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/PressForMarkerActivity.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.graphics.PointF;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.ViewGroup;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-import com.mapbox.mapboxsdk.maps.MapView;
-
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-
-public class PressForMarkerActivity extends AppCompatActivity implements MapboxMap.OnMapLongClickListener {
-
- private static final DecimalFormat LAT_LON_FORMATTER = new DecimalFormat("#.#####");
- private static final String STATE_MARKER_LIST = "markerList";
-
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private ArrayList<MarkerOptions> mMarkerList;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_press_for_marker);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.secondToolBar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- // Adding MapView programmatically
- mMapView = new MapView(this);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyle(Style.EMERALD);
- mMapView.onCreate(savedInstanceState);
- ((ViewGroup) findViewById(R.id.activity_container)).addView(mMapView);
-
- if (savedInstanceState != null) {
- mMarkerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST);
- }else{
- mMarkerList = new ArrayList<>();
- }
-
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
-
- // set camera
- mapboxMap.setCameraPosition(new CameraPosition.Builder()
- .target(new LatLng(45.1855569, 5.7215506))
- .zoom(11)
- .build());
-
- // add markers
- mapboxMap.addMarkers(mMarkerList);
-
- // set map long click listeners
- mapboxMap.setOnMapLongClickListener(PressForMarkerActivity.this);
- }
- });
- }
-
- @Override
- public void onMapLongClick(@NonNull LatLng point) {
- PointF pixel = mMapboxMap.getProjection().toScreenLocation(point);
- String title = LAT_LON_FORMATTER.format(point.getLatitude()) + ", " + LAT_LON_FORMATTER.format(point.getLongitude());
- String snippet = "X = " + (int) pixel.x + ", Y = " + (int) pixel.y;
-
- MarkerOptions marker = new MarkerOptions()
- .position(point)
- .title(title)
- .snippet(snippet);
-
- mMarkerList.add(marker);
- mMapboxMap.addMarker(marker);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- outState.putParcelableArrayList(STATE_MARKER_LIST, mMarkerList);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ScrollByActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ScrollByActivity.java
deleted file mode 100644
index 57099e3ad8..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ScrollByActivity.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class ScrollByActivity extends AppCompatActivity {
-
- public static final int MULTIPLIER_PER_PIXEL = 50;
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_scroll_by);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- final SeekBar xBar = (SeekBar) findViewById(R.id.seekbar_move_x);
- TextView xText = (TextView) findViewById(R.id.textview_x);
- xBar.setOnSeekBarChangeListener(new PixelBarChangeListener(xText, R.string.scrollby_x_value));
-
- final SeekBar yBar = (SeekBar) findViewById(R.id.seekbar_move_y);
- TextView yText = (TextView) findViewById(R.id.textview_y);
- yBar.setOnSeekBarChangeListener(new PixelBarChangeListener(yText, R.string.scrollby_y_value));
-
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setTag(true);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.setStyleUrl(Style.MAPBOX_STREETS);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- UiSettings uiSettings = mapboxMap.getUiSettings();
- uiSettings.setLogoEnabled(false);
- uiSettings.setAttributionEnabled(false);
-
- LatLng grenada = new LatLng(37.176546, -3.599007);
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(grenada, 16));
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setColorFilter(ContextCompat.getColor(ScrollByActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
- xBar.getProgress() * MULTIPLIER_PER_PIXEL,
- yBar.getProgress() * MULTIPLIER_PER_PIXEL)
- );
- }
- });
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mMapView.onLowMemory();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- private static class PixelBarChangeListener implements SeekBar.OnSeekBarChangeListener {
-
- @StringRes
- private int mPrefixTextResource;
- private TextView mValueView;
-
- public PixelBarChangeListener(@NonNull TextView textView, @StringRes int textRes) {
- mValueView = textView;
- mPrefixTextResource = textRes;
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- int value = progress * ScrollByActivity.MULTIPLIER_PER_PIXEL;
- mValueView.setText(String.format(seekBar.getResources().getString(mPrefixTextResource), value));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- }
-
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivity.java
deleted file mode 100644
index efd4f52890..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/SupportMapFragmentActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.maps.SupportMapFragment;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class SupportMapFragmentActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_map_fragment);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- SupportMapFragment mapFragment;
- if (savedInstanceState == null) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-
- MapboxMapOptions options = new MapboxMapOptions();
- options.accessToken(ApiAccess.getToken(this));
- options.styleUrl(Style.SATELLITE_STREETS);
-
- options.scrollGesturesEnabled(false);
- options.zoomGesturesEnabled(false);
- options.tiltGesturesEnabled(false);
- options.rotateGesturesEnabled(false);
-
- options.debugActive(false);
- options.compassEnabled(false);
- options.attributionEnabled(false);
- options.logoEnabled(false);
-
- options.minZoom(9);
- options.maxZoom(11);
- options.camera(new CameraPosition.Builder()
- .target(new LatLng(48.861431, 2.334166))
- .zoom(9)
- .build());
-
- mapFragment = SupportMapFragment.newInstance(options);
-
- transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map");
- transaction.commit();
- } else {
- mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentByTag("com.mapbox.map");
- }
-
- mapFragment.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2), 3500);
- }
- });
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java
deleted file mode 100644
index 9ed66e4dc3..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/TiltActivity.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.mapbox.mapboxsdk.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.utils.ApiAccess;
-
-public class TiltActivity extends AppCompatActivity {
-
- private MapView mMapView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_tilt);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mMapView = (MapView) findViewById(R.id.tiltMapView);
- mMapView.setAccessToken(ApiAccess.getToken(this));
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- // initial position has been set in R.layout.activity_tilt
- CameraPosition tiltedCameraPosition = new CameraPosition.Builder().tilt(45.0f).build();
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(tiltedCameraPosition), 10000);
- }
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mMapView.onPause();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mMapView.onResume();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mMapView.onDestroy();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
new file mode 100644
index 0000000000..9d514b2870
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
@@ -0,0 +1,166 @@
+package com.mapbox.mapboxsdk.testapp.activity;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.StringRes;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter;
+import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter;
+import com.mapbox.mapboxsdk.testapp.model.activity.Feature;
+import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class FeatureOverviewActivity extends AppCompatActivity {
+
+ private static final String KEY_STATE_FEATURES = "featureList";
+
+ private RecyclerView recyclerView;
+ private FeatureSectionAdapter sectionAdapter;
+ private List<Feature> features;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitle(getString(R.string.app_name));
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setElevation(getResources().getDimension(R.dimen.toolbar_shadow));
+ }
+
+ recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener());
+ recyclerView.setHasFixedSize(true);
+
+ ItemClickSupport.addTo(recyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
+ @Override
+ public void onItemClicked(RecyclerView recyclerView, int position, View v) {
+ if (!sectionAdapter.isSectionHeaderPosition(position)) {
+ Intent intent = new Intent();
+ int realPosition = sectionAdapter.getConvertedPosition(position);
+ intent.setComponent(new ComponentName(getPackageName(), features.get(realPosition).getName()));
+ startActivity(intent);
+ }
+ }
+ });
+
+ if (savedInstanceState == null) {
+ loadFeatures();
+ } else {
+ features = savedInstanceState.getParcelableArrayList(KEY_STATE_FEATURES);
+ onFeaturesLoaded(features);
+ }
+ }
+
+ private void loadFeatures() {
+ try {
+ new LoadFeatureTask().execute(getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(MapboxConstants.TAG, "Could not resolve package info", e);
+ }
+ }
+
+ private void onFeaturesLoaded(List<Feature> featuresList) {
+ features = featuresList;
+
+ List<FeatureSectionAdapter.Section> sections = new ArrayList<>();
+ String currentCat = "";
+ for (int i = 0; i < features.size(); i++) {
+ String category = features.get(i).getCategory();
+ if (!currentCat.equals(category)) {
+ sections.add(new FeatureSectionAdapter.Section(i, category));
+ currentCat = category;
+ }
+ }
+
+ FeatureSectionAdapter.Section[] dummy = new FeatureSectionAdapter.Section[sections.size()];
+ sectionAdapter = new FeatureSectionAdapter(this, R.layout.section_main_layout, R.id.section_text, new FeatureAdapter(features));
+ sectionAdapter.setSections(sections.toArray(dummy));
+ recyclerView.setAdapter(sectionAdapter);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelableArrayList(KEY_STATE_FEATURES, (ArrayList<Feature>) features);
+ }
+
+ private class LoadFeatureTask extends AsyncTask<PackageInfo, Void, List<Feature>> {
+
+ @Override
+ protected List<Feature> doInBackground(PackageInfo... params) {
+ List<Feature> features = new ArrayList<>();
+ PackageInfo app = params[0];
+
+ String packageName = getApplicationContext().getPackageName();
+ String metaDataKey = getString(R.string.category);
+ for (ActivityInfo info : app.activities) {
+ if (info.name.startsWith(packageName) && !info.name.equals(FeatureOverviewActivity.class.getName())) {
+ String label = getString(info.labelRes);
+ String description = resolveString(info.descriptionRes);
+ String category = resolveMetaData(info.metaData, metaDataKey);
+ features.add(new Feature(info.name, label, description, category));
+ }
+ }
+
+ if (!features.isEmpty()) {
+ Comparator<Feature> comparator = new Comparator<Feature>() {
+ @Override
+ public int compare(Feature lhs, Feature rhs) {
+ return lhs.getCategory().compareToIgnoreCase(rhs.getCategory());
+ }
+ };
+ Collections.sort(features, comparator);
+ }
+
+ return features;
+ }
+
+ private String resolveMetaData(Bundle bundle, String key) {
+ String category = null;
+ if (bundle != null) {
+ category = bundle.getString(key);
+ }
+ return category;
+ }
+
+ private String resolveString(@StringRes int stringRes){
+ try{
+ return getString(stringRes);
+ }catch (Resources.NotFoundException e){
+ return "-";
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List<Feature> features) {
+ super.onPostExecute(features);
+ onFeaturesLoaded(features);
+ }
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
new file mode 100644
index 0000000000..1e15c9ea36
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
@@ -0,0 +1,125 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.animation.AccelerateDecelerateInterpolator;
+
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class AnimatedMarkerActivity extends AppCompatActivity {
+
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_animated_marker);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ LatLng brussels = new LatLng(50.900446, 4.485251);
+ LatLng washington = new LatLng(38.897108, -77.036716);
+
+ final Marker marker = mapboxMap.addMarker(new MarkerOptions().position(brussels));
+ ValueAnimator markerAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), (Object[]) new LatLng[]{brussels, washington});
+ markerAnimator.setDuration(5000);
+ markerAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ markerAnimator.setRepeatMode(ValueAnimator.REVERSE);
+ markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
+ markerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (marker != null) {
+ marker.setPosition((LatLng) animation.getAnimatedValue());
+ }
+ }
+ });
+ markerAnimator.start();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_zoom, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ private class LatLngEvaluator implements TypeEvaluator<LatLng> {
+
+ private LatLng mLatLng = new LatLng();
+
+ @Override
+ public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
+ mLatLng.setLatitude(startValue.getLatitude() + (endValue.getLatitude() - startValue.getLatitude()) * fraction);
+ mLatLng.setLongitude(startValue.getLongitude() + (endValue.getLongitude() - startValue.getLongitude()) * fraction);
+ return mLatLng;
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
new file mode 100644
index 0000000000..7a2241a84c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java
@@ -0,0 +1,307 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
+
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class BulkMarkerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
+
+ private MapboxMap mMapboxMap;
+ private MapView mMapView;
+ private boolean mCustomMarkerView;
+ private List<LatLng> mLocations;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_marker_bulk);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+
+ if (actionBar != null) {
+ ArrayAdapter<CharSequence> spinnerAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.bulk_marker_list, android.R.layout.simple_spinner_item);
+ spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Spinner spinner = (Spinner) findViewById(R.id.spinner);
+ spinner.setAdapter(spinnerAdapter);
+ spinner.setOnItemSelectedListener(BulkMarkerActivity.this);
+ }
+ }
+ });
+
+ final View fab = findViewById(R.id.fab);
+ if (fab != null) {
+ fab.setOnClickListener(new FabClickListener());
+ }
+ }
+
+ public static class TextAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
+
+ private LayoutInflater inflater;
+
+ public TextAdapter(@NonNull Context context) {
+ super(context);
+ this.inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_text_marker, parent, false);
+ viewHolder.title = (TextView) convertView.findViewById(R.id.textView);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+ viewHolder.title.setText(marker.getTitle());
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ TextView title;
+ }
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]);
+ if (mLocations == null) {
+ new LoadLocationTask(this, amount).execute();
+ } else {
+ showMarkers(amount);
+ }
+ }
+
+
+ private void onLatLngListLoaded(List<LatLng> latLngs, int amount) {
+ mLocations = latLngs;
+ showMarkers(amount);
+ }
+
+ private void showMarkers(int amount) {
+ mMapboxMap.clear();
+
+ if (mLocations.size() < amount) {
+ amount = mLocations.size();
+ }
+
+ if (mCustomMarkerView) {
+ showNativeMarkers(amount);
+ } else {
+ showGlMarkers(amount);
+ }
+ }
+
+ private void showNativeMarkers(int amount) {
+ DecimalFormat formatter = new DecimalFormat("#.#####");
+ Random random = new Random();
+ int randomIndex;
+
+ for (int i = 0; i < amount; i++) {
+ randomIndex = random.nextInt(mLocations.size());
+ LatLng latLng = mLocations.get(randomIndex);
+ mMapboxMap.addMarker(new MarkerViewOptions()
+ .position(latLng)
+ .title(String.valueOf(i))
+ .snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude())));
+ }
+ }
+
+ private void showGlMarkers(int amount) {
+ List<MarkerOptions> markerOptionsList = new ArrayList<>();
+ DecimalFormat formatter = new DecimalFormat("#.#####");
+ Random random = new Random();
+ int randomIndex;
+
+ for (int i = 0; i < amount; i++) {
+ randomIndex = random.nextInt(mLocations.size());
+ LatLng latLng = mLocations.get(randomIndex);
+ markerOptionsList.add(new MarkerOptions()
+ .position(latLng)
+ .title(String.valueOf(i))
+ .snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude())));
+ }
+
+ mMapboxMap.addMarkers(markerOptionsList);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // nothing selected, nothing to do!
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private class FabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(final View v) {
+ if (mMapboxMap != null) {
+ mCustomMarkerView = true;
+
+ // remove fab
+ v.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ v.setVisibility(View.GONE);
+ }
+ }).start();
+
+ // reload markers
+ Spinner spinner = (Spinner) findViewById(R.id.spinner);
+ if (spinner != null) {
+ int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[spinner.getSelectedItemPosition()]);
+ showMarkers(amount);
+ }
+
+ // add adapter
+ mMapboxMap.getMarkerViewManager().addMarkerViewAdapter(new TextAdapter(BulkMarkerActivity.this));
+
+ mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(@MapView.MapChange int change) {
+ if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
+ if (!mMapboxMap.getMarkerViewManager().getMarkerViewAdapters().isEmpty()) {
+ TextView viewCountView = (TextView) findViewById(R.id.countView);
+ viewCountView.setText("ViewCache size " + (mMapView.getChildCount() - 5));
+ }
+ }
+ }
+ });
+
+ mMapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() {
+ @Override
+ public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) {
+ Toast.makeText(BulkMarkerActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ });
+ }
+ }
+ }
+
+ private static class LoadLocationTask extends AsyncTask<Void, Integer, List<LatLng>> {
+
+ private static final String TAG = "LoadLocationTask";
+ private BulkMarkerActivity mActivity;
+ private ProgressDialog mProgressDialog;
+ private int mAmount;
+
+ public LoadLocationTask(BulkMarkerActivity activity, int amount) {
+ mAmount = amount;
+ mActivity = activity;
+ mProgressDialog = ProgressDialog.show(activity, "Loading", "Fetching markers", false);
+ }
+
+ @Override
+ protected List<LatLng> doInBackground(Void... params) {
+ try {
+ String json = GeoParseUtil.loadStringFromAssets(mActivity.getApplicationContext(), "points.geojson");
+ return GeoParseUtil.parseGeoJSONCoordinates(json);
+ } catch (IOException | JSONException e) {
+ Log.e(TAG, "Could not add markers,", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List<LatLng> locations) {
+ super.onPostExecute(locations);
+ mActivity.onLatLngListLoaded(locations, mAmount);
+ mProgressDialog.hide();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
new file mode 100644
index 0000000000..fcddf8ca54
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
@@ -0,0 +1,129 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class DynamicMarkerChangeActivity extends AppCompatActivity {
+
+ private static final LatLng LAT_LNG_CHELSEA = new LatLng(51.481670, -0.190849);
+ private static final LatLng LAT_LNG_ARSENAL = new LatLng(51.555062, -0.108417);
+
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+ private IconFactory mIconFactory;
+ private Marker mMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_dynamic_marker);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mIconFactory = IconFactory.getInstance(this);
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setTag(false);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.506675, -0.128699), 10));
+
+ // Create marker
+ MarkerOptions markerOptions = new MarkerOptions()
+ .position(LAT_LNG_CHELSEA)
+ .icon(mIconFactory.fromResource(R.drawable.ic_chelsea));
+ mMarker = mapboxMap.addMarker(markerOptions);
+ }
+ });
+
+
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMapboxMap != null) {
+ updateMarker();
+ }
+ }
+ });
+ }
+
+ private void updateMarker() {
+ // update model
+ boolean first = (boolean) mMapView.getTag();
+ mMapView.setTag(!first);
+
+ // update marker
+ mMarker.setPosition(first ? LAT_LNG_CHELSEA : LAT_LNG_ARSENAL);
+ mMarker.setIcon(mIconFactory.fromResource(first ? R.drawable.ic_chelsea : R.drawable.ic_arsenal));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
new file mode 100644
index 0000000000..65a5afa602
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
@@ -0,0 +1,228 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerOptions;
+import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerView;
+import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions;
+
+public class MarkerViewActivity extends AppCompatActivity {
+
+ private MapboxMap mMapboxMap;
+ private MapView mMapView;
+
+ private final static LatLng[] LAT_LNGS = new LatLng[]{
+ new LatLng(38.907327, -77.041293),
+ new LatLng(38.909698, -77.029642),
+ new LatLng(38.907227, -77.036530),
+ new LatLng(38.905607, -77.031916),
+ new LatLng(38.897424, -77.036508),
+ new LatLng(38.897642, -77.041980),
+ new LatLng(38.889876, -77.008849),
+ new LatLng(38.889441, -77.050134)
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_marker_view);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ final TextView viewCountView = (TextView) findViewById(R.id.countView);
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+
+ // add text markers
+ for (int i = 0; i < LAT_LNGS.length; i++) {
+ mMapboxMap.addMarker(new MarkerViewOptions()
+ .position(LAT_LNGS[i])
+ .title(String.valueOf(i))
+ .selectAnimatorResource(R.animator.scale_up)
+ .deselectAnimatorResource(R.animator.scale_down)
+ );
+ }
+
+ // add flag marker
+ CountryMarkerViewOptions options = new CountryMarkerViewOptions();
+ options.title("United States");
+ options.abbrevName("us");
+ options.flagRes(R.drawable.ic_us);
+ options.position(new LatLng(38.899774, -77.023237));
+ options.selectAnimatorResource(R.animator.rotate_360);
+ options.deselectAnimatorResource(R.animator.rotate_360);
+ options.flat(true);
+ mapboxMap.addMarker(options);
+
+ // default GL marker
+ mMapboxMap.addMarker(new CountryMarkerOptions()
+ .title("United States")
+ .position(new LatLng(38.902580, -77.050102))
+ );
+
+ // set adapters
+ final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
+ markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this));
+ markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this));
+
+ mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(@MapView.MapChange int change) {
+ if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) {
+ if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) {
+ viewCountView.setText("ViewCache size " + (mMapView.getChildCount() - 5));
+ }
+ }
+ }
+ });
+
+ mMapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() {
+ @Override
+ public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) {
+ Toast.makeText(MarkerViewActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ });
+ }
+ });
+ }
+
+ private static class TextAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
+
+ private LayoutInflater inflater;
+
+ public TextAdapter(@NonNull Context context) {
+ super(context);
+ this.inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_text_marker, parent, false);
+ viewHolder.title = (TextView) convertView.findViewById(R.id.textView);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+ viewHolder.title.setText(marker.getTitle());
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ TextView title;
+ }
+ }
+
+ private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> {
+
+ private LayoutInflater inflater;
+
+ public CountryAdapter(@NonNull Context context) {
+ super(context);
+ this.inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull CountryMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_custom_marker, parent, false);
+ viewHolder.flag = (ImageView) convertView.findViewById(R.id.imageView);
+ viewHolder.abbrev = (TextView) convertView.findViewById(R.id.textView);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+ viewHolder.flag.setImageResource(marker.getFlagRes());
+ viewHolder.abbrev.setText(marker.getAbbrevName());
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ ImageView flag;
+ TextView abbrev;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
new file mode 100644
index 0000000000..f08d65163b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java
@@ -0,0 +1,124 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PolygonActivity extends AppCompatActivity {
+
+ private MapView mapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_polygon);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ // configure inital map state
+ MapboxMapOptions options = new MapboxMapOptions()
+ .attributionTintColor(Color.RED)
+ .accessToken(getString(R.string.mapbox_access_token))
+ .styleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION))
+ .camera(new CameraPosition.Builder()
+ .target(new LatLng(45.520486, -122.673541))
+ .zoom(12)
+ .tilt(40)
+ .build());
+
+ // create map
+ mapView = new MapView(this, options);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ List<LatLng> polygon = new ArrayList<>();
+ polygon.add(new LatLng(45.522585, -122.685699));
+ polygon.add(new LatLng(45.534611, -122.708873));
+ polygon.add(new LatLng(45.530883, -122.678833));
+ polygon.add(new LatLng(45.547115, -122.667503));
+ polygon.add(new LatLng(45.530643, -122.660121));
+ polygon.add(new LatLng(45.533529, -122.636260));
+ polygon.add(new LatLng(45.521743, -122.659091));
+ polygon.add(new LatLng(45.510677, -122.648792));
+ polygon.add(new LatLng(45.515008, -122.664070));
+ polygon.add(new LatLng(45.502496, -122.669048));
+ polygon.add(new LatLng(45.515369, -122.678489));
+ polygon.add(new LatLng(45.506346, -122.702007));
+ polygon.add(new LatLng(45.522585, -122.685699));
+ mapboxMap.addPolygon(new PolygonOptions()
+ .addAll(polygon)
+ .fillColor(Color.parseColor("#3bb2d0")));
+ }
+ });
+
+ // add to layout
+ ((ViewGroup) findViewById(R.id.container)).addView(mapView);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
new file mode 100644
index 0000000000..9eb4bc1741
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
@@ -0,0 +1,172 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.annotations.Polyline;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PolylineActivity extends AppCompatActivity {
+
+ private static final String STATE_POLYLINE_OPTIONS = "polylineOptions";
+ private static final LatLng ANDORRA = new LatLng(42.505777, 1.52529);
+ private static final LatLng LUXEMBOURG = new LatLng(49.815273, 6.129583);
+ private static final LatLng MONACO = new LatLng(43.738418, 7.424616);
+ private static final LatLng VATICAN_CITY = new LatLng(41.902916, 12.453389);
+ private static final LatLng SAN_MARINO = new LatLng(43.942360, 12.457777);
+ private static final LatLng LIECHTENSTEIN = new LatLng(47.166000, 9.555373);
+
+ private List<Polyline> mPolylines;
+ private ArrayList<PolylineOptions> mPolylineOptions = new ArrayList<>();
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_polyline);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ if (savedInstanceState != null) {
+ mPolylineOptions = savedInstanceState.getParcelableArrayList(STATE_POLYLINE_OPTIONS);
+ } else {
+ mPolylineOptions.addAll(getAllPolylines());
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+ mPolylines = mapboxMap.addPolylines(mPolylineOptions);
+ }
+ });
+
+ findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMapboxMap != null) {
+ if (mPolylines != null && mPolylines.size() > 0) {
+ if (mPolylines.size() == 1) {
+ // test for removing annotation
+ mMapboxMap.removeAnnotation(mPolylines.get(0));
+ } else {
+ // test for removing annotations
+ mMapboxMap.removeAnnotations(mPolylines);
+ }
+ }
+ mPolylineOptions.clear();
+ mPolylineOptions.addAll(getRandomLine());
+ mPolylines = mMapboxMap.addPolylines(mPolylineOptions);
+ }
+ }
+ });
+ }
+
+ private List<PolylineOptions> getAllPolylines() {
+ List<PolylineOptions> options = new ArrayList<>();
+ options.add(generatePolyline(ANDORRA, LUXEMBOURG, "#F44336"));
+ options.add(generatePolyline(ANDORRA, MONACO, "#FF5722"));
+ options.add(generatePolyline(MONACO, VATICAN_CITY, "#673AB7"));
+ options.add(generatePolyline(VATICAN_CITY, SAN_MARINO, "#009688"));
+ options.add(generatePolyline(SAN_MARINO, LIECHTENSTEIN, "#795548"));
+ options.add(generatePolyline(LIECHTENSTEIN, LUXEMBOURG, "#3F51B5"));
+ return options;
+ }
+
+ private PolylineOptions generatePolyline(LatLng start, LatLng end, String color) {
+ PolylineOptions line = new PolylineOptions();
+ line.add(start);
+ line.add(end);
+ line.color(Color.parseColor(color));
+ return line;
+ }
+
+ public List<PolylineOptions> getRandomLine() {
+ final List<PolylineOptions> randomLines = getAllPolylines();
+ Collections.shuffle(randomLines);
+ return new ArrayList<PolylineOptions>() {{
+ add(randomLines.get(0));
+ }};
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ outState.putParcelableArrayList(STATE_POLYLINE_OPTIONS, mPolylineOptions);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_polyline, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_id_remove:
+ // test to remove all annotations
+ mPolylineOptions.clear();
+ mMapboxMap.clear();
+ return true;
+
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
new file mode 100644
index 0000000000..88008d64fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
@@ -0,0 +1,141 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+
+public class PressForMarkerActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private ArrayList<MarkerOptions> mMarkerList = new ArrayList<>();
+
+ private static final DecimalFormat LAT_LON_FORMATTER = new DecimalFormat("#.#####");
+
+ private static String STATE_MARKER_LIST = "markerList";
+
+ @Override
+ protected void onCreate(@Nullable final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_press_for_marker);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.pressForMarkerMapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(final MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.setStyleUrl(Style.getOutdoorsStyleUrl(9));
+ resetMap();
+
+ mapboxMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() {
+ @Override
+ public void onMapLongClick(@NonNull LatLng point) {
+ final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
+
+ String title = LAT_LON_FORMATTER.format(point.getLatitude()) + ", " + LAT_LON_FORMATTER.format(point.getLongitude());
+ String snippet = "X = " + (int) pixel.x + ", Y = " + (int) pixel.y;
+
+ MarkerOptions marker = new MarkerOptions()
+ .position(point)
+ .title(title)
+ .snippet(snippet);
+
+ mMarkerList.add(marker);
+ mapboxMap.addMarker(marker);
+ }
+ });
+
+ if (savedInstanceState != null) {
+ mMarkerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST);
+ mapboxMap.addMarkers(mMarkerList);
+ }
+ }
+ });
+ }
+
+ private void resetMap() {
+ if (mapboxMap == null) {
+ return;
+ }
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(45.1855569, 5.7215506), 11));
+ mapboxMap.removeAnnotations();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_press_for_marker, menu);
+ return true;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ mapView.onSaveInstanceState(outState);
+ outState.putParcelableArrayList(STATE_MARKER_LIST, mMarkerList);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ case R.id.menuItemReset:
+ resetMap();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java
new file mode 100644
index 0000000000..44758cd146
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java
@@ -0,0 +1,180 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+public class CameraAnimationTypeActivity extends AppCompatActivity {
+
+ private static final String TAG = "CameraActivity";
+
+ private MapView mapView;
+
+ private boolean cameraState;
+ private static final LatLng LAT_LNG_LONDON_EYE = new LatLng(51.50325, -0.11968);
+ private static final LatLng LAT_LNG_TOWER_BRIDGE = new LatLng(51.50550, -0.07520);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_camera_animation_types);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ mapboxMap.getUiSettings().setAttributionEnabled(false);
+ mapboxMap.getUiSettings().setLogoEnabled(false);
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ Log.v(MapboxConstants.TAG, position.toString());
+ }
+ });
+
+ // handle move button clicks
+ findViewById(R.id.cameraMoveButton).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .zoom(14)
+ .tilt(30)
+ .tilt(0)
+ .build();
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
+ }
+ });
+
+ // handle ease button clicks
+ findViewById(R.id.cameraEaseButton).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .zoom(15)
+ .bearing(180)
+ .tilt(30)
+ .build();
+
+ MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ Log.i(TAG, "Duration onCancel Callback called.");
+ Toast.makeText(CameraAnimationTypeActivity.this, "Ease onCancel Callback called.", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onFinish() {
+ Log.i(TAG, "Duration onFinish Callback called.");
+ Toast.makeText(CameraAnimationTypeActivity.this, "Ease onFinish Callback called.", Toast.LENGTH_LONG).show();
+ }
+ };
+
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
+ }
+ });
+
+ // handle animate button clicks
+ findViewById(R.id.cameraAnimateButton).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(getNextLatLng())
+ .bearing(270)
+ .tilt(20)
+ .build();
+
+ MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ Log.i(TAG, "Duration onCancel Callback called.");
+ Toast.makeText(CameraAnimationTypeActivity.this, "Duration onCancel Callback called.", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onFinish() {
+ Log.i(TAG, "Duration onFinish Callback called.");
+ Toast.makeText(CameraAnimationTypeActivity.this, "Duration onFinish Callback called.", Toast.LENGTH_LONG).show();
+ }
+ };
+
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback);
+ }
+ });
+ }
+ });
+ }
+
+ private LatLng getNextLatLng() {
+ cameraState = !cameraState;
+ return cameraState ? LAT_LNG_TOWER_BRIDGE : LAT_LNG_LONDON_EYE;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
new file mode 100644
index 0000000000..220bef197b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java
@@ -0,0 +1,176 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+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.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private MapView mapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_camera_position);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ // add a listener to FAB
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Context context = v.getContext();
+ final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context, com.mapbox.mapboxsdk.R.style.AttributionAlertDialogStyle);
+ builder.setTitle(R.string.dialog_camera_position);
+ builder.setView(onInflateDialogContent(dialogContent));
+ builder.setPositiveButton("Animate", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ double latitude = Double.parseDouble(((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
+ double longitude = Double.parseDouble(((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
+ double zoom = Double.parseDouble(((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
+ double bearing = Double.parseDouble(((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
+ double tilt = Double.parseDouble(((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());
+
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(new LatLng(latitude, longitude))
+ .zoom(zoom)
+ .bearing(bearing)
+ .tilt(tilt)
+ .build();
+
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000);
+ Log.v(MapboxConstants.TAG, cameraPosition.toString());
+ }
+ });
+ builder.setNegativeButton("Cancel", null);
+ builder.setCancelable(false);
+ builder.show();
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private View onInflateDialogContent(View view) {
+ linkTextView(view, R.id.value_lat, R.id.seekbar_lat, new LatLngChangeListener(), 180 + 38);
+ linkTextView(view, R.id.value_lon, R.id.seekbar_lon, new LatLngChangeListener(), 180 - 77);
+ linkTextView(view, R.id.value_zoom, R.id.seekbar_zoom, new ValueChangeListener(), 6);
+ linkTextView(view, R.id.value_bearing, R.id.seekbar_bearing, new ValueChangeListener(), 90);
+ linkTextView(view, R.id.value_tilt, R.id.seekbar_tilt, new ValueChangeListener(), 40);
+ return view;
+ }
+
+ private void linkTextView(View view, @IdRes int textViewRes, @IdRes int seekBarRes, ValueChangeListener listener, int defaultValue) {
+ final TextView value = (TextView) view.findViewById(textViewRes);
+ SeekBar seekBar = (SeekBar) view.findViewById(seekBarRes);
+ listener.setLinkedValueView(value);
+ seekBar.setOnSeekBarChangeListener(listener);
+ seekBar.setProgress(defaultValue);
+ }
+
+ private class ValueChangeListener implements SeekBar.OnSeekBarChangeListener {
+
+ protected TextView textView;
+
+ public void setLinkedValueView(TextView textView) {
+ this.textView = textView;
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ textView.setText(String.valueOf(progress));
+ }
+ }
+
+ private class LatLngChangeListener extends ValueChangeListener {
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ super.onProgressChanged(seekBar, progress - 180, fromUser);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
new file mode 100644
index 0000000000..b9a438b7d5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java
@@ -0,0 +1,125 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class LatLngBoundsActivity extends AppCompatActivity {
+
+ private static final LatLng LOS_ANGELES = new LatLng(34.053940, -118.242622);
+ private static final LatLng NEW_YORK = new LatLng(40.712730, -74.005953);
+
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_visible_bounds);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setStyleUrl(Style.getDarkStyleUrl(AppConstant.STYLE_VERSION));
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
+
+ mapboxMap.addMarker(new MarkerOptions()
+ .title("Los Angeles")
+ .snippet("City Hall")
+ .position(LOS_ANGELES));
+
+ mapboxMap.addMarker(new MarkerOptions()
+ .title("New York")
+ .snippet("City Hall")
+ .position(NEW_YORK));
+
+ // Create Bounds
+ final LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(NEW_YORK)
+ .include(LOS_ANGELES)
+ .build();
+
+ // Add map padding
+ int mapPadding = (int) getResources().getDimension(R.dimen.fab_margin);
+ mapboxMap.setPadding(mapPadding, mapPadding, mapPadding, mapPadding);
+
+ // Move camera to the bounds with added padding
+ int padding = (int) getResources().getDimension(R.dimen.coordinatebounds_margin);
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));
+
+ // Call mapboxMap.getProjection().getVisibleRegion().latLngBounds to retrieve the bounds
+ Log.v(MapboxConstants.TAG, mapboxMap.getProjection().getVisibleRegion().latLngBounds.toString());
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
new file mode 100644
index 0000000000..bc3691a2f7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
@@ -0,0 +1,124 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.graphics.Point;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class ManualZoomActivity extends AppCompatActivity {
+
+ private MapboxMap mMapboxMap;
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_manual_zoom);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.manualZoomMapView);
+ mMapView.setStyleUrl(Style.getSatelliteStyleUrl(AppConstant.STYLE_VERSION));
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+
+ UiSettings uiSettings = mMapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+// findViewById(R.id.)
+ getMenuInflater().inflate(R.menu.menu_zoom, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+
+ case R.id.action_zoom_in:
+ mMapboxMap.animateCamera(CameraUpdateFactory.zoomIn());
+ return true;
+
+ case R.id.action_zoom_out:
+ mMapboxMap.animateCamera(CameraUpdateFactory.zoomOut());
+ return true;
+
+ case R.id.action_zoom_by:
+ mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2));
+ return true;
+ case R.id.action_zoom_to:
+ mMapboxMap.animateCamera(CameraUpdateFactory.zoomTo(2));
+ return true;
+
+ case R.id.action_zoom_to_point:
+ View view = getWindow().getDecorView();
+ mMapboxMap.animateCamera(CameraUpdateFactory.zoomBy(1, new Point(view.getMeasuredWidth() / 4, view.getMeasuredHeight() / 4)));
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
new file mode 100644
index 0000000000..e9a9647d45
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java
@@ -0,0 +1,85 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class MaxMinZoomActivity extends AppCompatActivity {
+
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_maxmin_zoom);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ mapboxMap.setMinZoom(3);
+ mapboxMap.setMaxZoom(5);
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java
new file mode 100644
index 0000000000..729a08758e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java
@@ -0,0 +1,146 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class ScrollByActivity extends AppCompatActivity {
+
+ public static final int MULTIPLIER_PER_PIXEL = 50;
+
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_scroll_by);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ final SeekBar xBar = (SeekBar) findViewById(R.id.seekbar_move_x);
+ TextView xText = (TextView) findViewById(R.id.textview_x);
+ xBar.setOnSeekBarChangeListener(new PixelBarChangeListener(xText, R.string.scrollby_x_value));
+
+ final SeekBar yBar = (SeekBar) findViewById(R.id.seekbar_move_y);
+ TextView yText = (TextView) findViewById(R.id.textview_y);
+ yBar.setOnSeekBarChangeListener(new PixelBarChangeListener(yText, R.string.scrollby_y_value));
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setTag(true);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ uiSettings.setLogoEnabled(false);
+ uiSettings.setAttributionEnabled(false);
+
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(ScrollByActivity.this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mapboxMap.easeCamera(CameraUpdateFactory.scrollBy(
+ xBar.getProgress() * MULTIPLIER_PER_PIXEL,
+ yBar.getProgress() * MULTIPLIER_PER_PIXEL)
+ );
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private static class PixelBarChangeListener implements SeekBar.OnSeekBarChangeListener {
+
+ @StringRes
+ private int mPrefixTextResource;
+ private TextView mValueView;
+
+ public PixelBarChangeListener(@NonNull TextView textView, @StringRes int textRes) {
+ mValueView = textView;
+ mPrefixTextResource = textRes;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ int value = progress * ScrollByActivity.MULTIPLIER_PER_PIXEL;
+ mValueView.setText(String.format(seekBar.getResources().getString(mPrefixTextResource), value));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
new file mode 100644
index 0000000000..98c86d4313
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
@@ -0,0 +1,123 @@
+package com.mapbox.mapboxsdk.testapp.activity.customlayer;
+
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer;
+
+public class CustomLayerActivity extends AppCompatActivity {
+
+ private MapboxMap mapboxMap;
+ private MapView mapView;
+
+ private boolean isShowingCustomLayer = false;
+ private FloatingActionButton fab;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_custom_layer);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10));
+
+ }
+ });
+
+ fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null) {
+ swapCustomLayer();
+ }
+ }
+ });
+ }
+
+ private void swapCustomLayer() {
+
+ if (isShowingCustomLayer) {
+ mapboxMap.removeCustomLayer("custom");
+ fab.setImageResource(R.drawable.ic_layers_24dp);
+ } else {
+ mapboxMap.addCustomLayer(new CustomLayer("custom",
+ ExampleCustomLayer.createContext(),
+ ExampleCustomLayer.InitializeFunction,
+ ExampleCustomLayer.RenderFunction,
+ ExampleCustomLayer.DeinitializeFunction), null);
+ fab.setImageResource(R.drawable.ic_layers_clear_24dp);
+ }
+
+ isShowingCustomLayer = !isShowingCustomLayer;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java
new file mode 100644
index 0000000000..d5f248f89d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java
@@ -0,0 +1,183 @@
+package com.mapbox.mapboxsdk.testapp.activity.directions;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.mapbox.directions.DirectionsCriteria;
+import com.mapbox.directions.MapboxDirections;
+import com.mapbox.directions.service.models.DirectionsResponse;
+import com.mapbox.directions.service.models.DirectionsRoute;
+import com.mapbox.directions.service.models.Waypoint;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+import java.util.List;
+
+import retrofit.Callback;
+import retrofit.Response;
+import retrofit.Retrofit;
+
+public class DirectionsActivity extends AppCompatActivity {
+
+ private final static String LOG_TAG = "DirectionsActivity";
+
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_directions);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+ loadRoute();
+ }
+ });
+ }
+
+ private void loadRoute() {
+ // Dupont Circle (Washington, DC)
+ Waypoint origin = new Waypoint(-77.04341, 38.90962);
+
+ // The White House (Washington, DC)
+ Waypoint destination = new Waypoint(-77.0365, 38.8977);
+
+ // Set map at centroid
+ LatLng centroid = new LatLng(
+ (origin.getLatitude() + destination.getLatitude()) / 2,
+ (origin.getLongitude() + destination.getLongitude()) / 2);
+
+ mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder()
+ .target(centroid)
+ .zoom(14)
+ .build()));
+
+ // Add origin and destination to the map
+ mMapboxMap.addMarker(new MarkerOptions()
+ .position(new LatLng(origin.getLatitude(), origin.getLongitude()))
+ .title("Origin")
+ .snippet("Dupont Circle"));
+ mMapboxMap.addMarker(new MarkerOptions()
+ .position(new LatLng(destination.getLatitude(), destination.getLongitude()))
+ .title("Destination")
+ .snippet("The White House"));
+
+ // Get route from API
+ getRoute(origin, destination);
+ }
+
+ private void getRoute(Waypoint origin, Waypoint destination) {
+ MapboxDirections md = new MapboxDirections.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setOrigin(origin)
+ .setDestination(destination)
+ .setProfile(DirectionsCriteria.PROFILE_WALKING)
+ .build();
+
+ md.enqueue(new Callback<DirectionsResponse>() {
+ @Override
+ public void onResponse(Response<DirectionsResponse> response, Retrofit retrofit) {
+ // You can get generic HTTP info about the response
+ Log.d(LOG_TAG, "Response code: " + response.code());
+
+ // Print some info about the route
+ DirectionsRoute currentRoute = response.body().getRoutes().get(0);
+ Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
+
+ // Draw the route on the map
+ drawRoute(currentRoute);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ Log.e(LOG_TAG, "Error: " + t.getMessage());
+ }
+ });
+ }
+
+ private void drawRoute(DirectionsRoute route) {
+ // Convert List<Waypoint> into LatLng[]
+ List<Waypoint> waypoints = route.getGeometry().getWaypoints();
+ LatLng[] point = new LatLng[waypoints.size()];
+ for (int i = 0; i < waypoints.size(); i++) {
+ point[i] = new LatLng(
+ waypoints.get(i).getLatitude(),
+ waypoints.get(i).getLongitude());
+ }
+
+ // Draw Points on MapView
+ mMapboxMap.addPolyline(new PolylineOptions()
+ .add(point)
+ .color(Color.parseColor("#3887be"))
+ .width(5));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
new file mode 100644
index 0000000000..5987855aac
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
@@ -0,0 +1,90 @@
+package com.mapbox.mapboxsdk.testapp.activity.fragment;
+
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapFragment;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class MapFragmentActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_fragment);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ MapFragment mapFragment;
+ if (savedInstanceState == null) {
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+
+ MapboxMapOptions options = new MapboxMapOptions();
+ options.styleUrl(Style.getOutdoorsStyleUrl(AppConstant.STYLE_VERSION));
+
+ options.scrollGesturesEnabled(false);
+ options.zoomGesturesEnabled(false);
+ options.tiltGesturesEnabled(false);
+ options.rotateGesturesEnabled(false);
+
+ options.debugActive(false);
+// options.compassEnabled(false);
+// options.attributionEnabled(false);
+// options.logoEnabled(false);
+
+ LatLng dc = new LatLng(38.90252, -77.02291);
+
+ options.minZoom(9);
+ options.maxZoom(11);
+ options.camera(new CameraPosition.Builder()
+ .target(dc)
+ .zoom(11)
+ .build());
+
+ mapFragment = MapFragment.newInstance(options);
+
+ transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map");
+ transaction.commit();
+ } else {
+ mapFragment = (MapFragment) getFragmentManager().findFragmentByTag("com.mapbox.map");
+ }
+
+ mapFragment.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000);
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
new file mode 100644
index 0000000000..bb391fc93d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
@@ -0,0 +1,90 @@
+package com.mapbox.mapboxsdk.testapp.activity.fragment;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.SupportMapFragment;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class SupportMapFragmentActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_fragment);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ SupportMapFragment mapFragment;
+ if (savedInstanceState == null) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ MapboxMapOptions options = new MapboxMapOptions();
+ options.styleUrl(Style.getSatelliteStreetsStyleUrl(AppConstant.STYLE_VERSION));
+
+ options.scrollGesturesEnabled(false);
+ options.zoomGesturesEnabled(false);
+ options.tiltGesturesEnabled(false);
+ options.rotateGesturesEnabled(false);
+
+ options.debugActive(false);
+ options.compassEnabled(false);
+ options.attributionEnabled(false);
+ options.logoEnabled(false);
+
+ LatLng dc = new LatLng(38.90252, -77.02291);
+
+ options.minZoom(9);
+ options.maxZoom(11);
+ options.camera(new CameraPosition.Builder()
+ .target(dc)
+ .zoom(11)
+ .build());
+
+ mapFragment = SupportMapFragment.newInstance(options);
+
+ transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map");
+ transaction.commit();
+ } else {
+ mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentByTag("com.mapbox.map");
+ }
+
+ mapFragment.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000);
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
new file mode 100644
index 0000000000..fc03066a4e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
@@ -0,0 +1,192 @@
+package com.mapbox.mapboxsdk.testapp.activity.geocoding;
+
+import android.graphics.PointF;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.mapbox.geocoder.GeocoderCriteria;
+import com.mapbox.geocoder.MapboxGeocoder;
+import com.mapbox.geocoder.service.models.GeocoderFeature;
+import com.mapbox.geocoder.service.models.GeocoderResponse;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+import java.util.List;
+
+import retrofit.Callback;
+import retrofit.Response;
+import retrofit.Retrofit;
+
+public class GeocoderActivity extends AppCompatActivity {
+
+ private static final String LOG_TAG = "GeocoderActivity";
+
+ private MapView mapView;
+ private TextView textView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_geocoder);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ textView = (TextView) findViewById(R.id.message);
+ setMessage(getString(R.string.geocoder_instructions));
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.setStyleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
+ mapView.onCreate(savedInstanceState);
+
+ final ImageView dropPinView = new ImageView(this);
+ dropPinView.setImageResource(R.drawable.ic_droppin_24dp);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
+ dropPinView.setLayoutParams(params);
+ mapView.addView(dropPinView);
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ final Projection projection = mapboxMap.getProjection();
+ final int width = mapView.getMeasuredWidth();
+ final int height = mapView.getMeasuredHeight();
+
+ // Click listener
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ PointF centerPoint = new PointF(width / 2, (height + dropPinView.getHeight()) / 2);
+ LatLng centerLatLng = new LatLng(projection.fromScreenLocation(centerPoint));
+
+ setMessage("Geocoding...");
+
+ mapboxMap.removeAnnotations();
+ mapboxMap.addMarker(new MarkerOptions().position(centerLatLng));
+
+ geocode(centerLatLng);
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ /*
+ * Forward geocoding
+ */
+
+ private void geocode(final LatLng point) {
+ new AsyncTask<Void, Void, Void>() {
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ MapboxGeocoder client = new MapboxGeocoder.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setCoordinates(point.getLongitude(), point.getLatitude())
+ .setType(GeocoderCriteria.TYPE_POI)
+ .build();
+
+ client.enqueue(new Callback<GeocoderResponse>()
+
+ {
+ @Override
+ public void onResponse(Response<GeocoderResponse> response, Retrofit retrofit) {
+ List<GeocoderFeature> results = response.body().getFeatures();
+ if (results.size() > 0) {
+ String placeName = results.get(0).getPlaceName();
+ setSuccess(placeName);
+ } else {
+ setMessage("No results.");
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ setError(t.getMessage());
+ }
+ }
+
+ );
+ return null;
+ }
+ }.execute();
+ }
+
+ /*
+ * Update text view
+ */
+
+ private void setMessage(String message) {
+ Log.d(LOG_TAG, "Message: " + message);
+ textView.setText(message);
+ }
+
+ private void setSuccess(String placeName) {
+ Log.d(LOG_TAG, "Place name: " + placeName);
+ textView.setText(placeName);
+ }
+
+ private void setError(String message) {
+ Log.e(LOG_TAG, "Error: " + message);
+ textView.setText("Error: " + message);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
new file mode 100644
index 0000000000..93ebdcd157
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
@@ -0,0 +1,106 @@
+package com.mapbox.mapboxsdk.testapp.activity.imagegenerator;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class SnapshotActivity extends AppCompatActivity {
+
+ private MapView mapView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_snapshot);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab.setColorFilter(ContextCompat.getColor(SnapshotActivity.this, R.color.primary));
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final long startTime = System.nanoTime();
+ mapboxMap.snapshot(new MapboxMap.SnapshotReadyCallback() {
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ long endTime = System.nanoTime();
+ long duration = (long) ((endTime - startTime) / 1e6);
+ ImageView snapshotView = (ImageView) findViewById(R.id.imageView);
+ snapshotView.setImageBitmap(snapshot);
+ Toast.makeText(SnapshotActivity.this, String.format("Snapshot taken in %d ms", duration), Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java
new file mode 100644
index 0000000000..7ac3c59667
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java
@@ -0,0 +1,167 @@
+package com.mapbox.mapboxsdk.testapp.activity.infowindow;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+import java.text.DecimalFormat;
+
+public class InfoWindowActivity extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnInfoWindowCloseListener, MapboxMap.OnMapLongClickListener, MapboxMap.OnInfoWindowClickListener, MapboxMap.OnInfoWindowLongClickListener {
+
+ private MapboxMap mapboxMap;
+ private MapView mapView;
+ private Marker customMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_infowindow);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ addMarkers();
+ addInfoWindowListeners();
+ }
+
+ private void addMarkers() {
+ mapboxMap.addMarker(new MarkerOptions()
+ .title("Intersection")
+ .snippet("H St NW with 15th St NW")
+ .position(new LatLng(38.9002073, -77.03364419)));
+
+ mapboxMap.addMarker(new MarkerOptions().title("Intersection")
+ .snippet("E St NW with 17th St NW")
+ .position(new LatLng(38.8954236, -77.0394623)));
+
+ Marker marker = mapboxMap.addMarker(new MarkerOptions()
+ .title("White House")
+ .snippet("The official residence and principal workplace of the President of the United States, located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every U.S. president since John Adams in 1800.")
+ .position(new LatLng(38.897705003219784, -77.03655168667463)));
+
+ // open InfoWindow at startup
+ mapboxMap.selectMarker(marker);
+ }
+
+ private void addInfoWindowListeners() {
+ mapboxMap.setOnInfoWindowCloseListener(this);
+ mapboxMap.setOnMapLongClickListener(this);
+ mapboxMap.setOnInfoWindowClickListener(this);
+ mapboxMap.setOnInfoWindowLongClickListener(this);
+ }
+
+ private void toggleConcurrentInfoWindow(boolean allowConcurrentInfoWindow) {
+ mapboxMap.deselectMarkers();
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(allowConcurrentInfoWindow);
+ }
+
+ @Override
+ public boolean onInfoWindowClick(@NonNull Marker marker) {
+ Toast.makeText(getApplicationContext(), "OnClick: " + marker.getTitle(), Toast.LENGTH_LONG).show();
+ // returning true will leave the info window open
+ return false;
+ }
+
+ @Override
+ public void onInfoWindowClose(Marker marker) {
+ Toast.makeText(getApplicationContext(), "OnClose: " + marker.getTitle(), Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onInfoWindowLongClick(Marker marker) {
+ Toast.makeText(getApplicationContext(), "OnLongClick: " + marker.getTitle(), Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onMapLongClick(@NonNull LatLng point) {
+ if (customMarker != null) {
+ // Remove previous added marker
+ mapboxMap.removeAnnotation(customMarker);
+ customMarker = null;
+ }
+
+ // Add marker on long click location with default marker image
+ customMarker = mapboxMap.addMarker(new MarkerOptions()
+ .title("Custom Marker")
+ .snippet(new DecimalFormat("#.#####").format(point.getLatitude()) + ", " + new DecimalFormat("#.#####").format(point.getLongitude()))
+ .position(point));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_infowindow, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_toggle_concurrent_infowindow:
+ toggleConcurrentInfoWindow(!item.isChecked());
+ item.setChecked(!item.isChecked());
+ return true;
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
new file mode 100644
index 0000000000..2c74702253
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java
@@ -0,0 +1,144 @@
+package com.mapbox.mapboxsdk.testapp.activity.infowindow;
+
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarker;
+import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarkerOptions;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+public class InfoWindowAdapterActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private IconFactory iconFactory;
+ private Drawable iconDrawable;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_infowindow_adapter);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ iconFactory = IconFactory.getInstance(this);
+ iconDrawable = ContextCompat.getDrawable(this, R.drawable.ic_location_city_24dp);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ addMarkers(mapboxMap);
+ addCustomInfoWindowAdapter(mapboxMap);
+ }
+ });
+ }
+
+ private void addMarkers(MapboxMap mapboxMap){
+ mapboxMap.addMarker(generateCityStateMarker("Andorra", 42.505777, 1.52529, "#F44336"));
+ mapboxMap.addMarker(generateCityStateMarker("Luxembourg", 49.815273, 6.129583, "#3F51B5"));
+ mapboxMap.addMarker(generateCityStateMarker("Monaco", 43.738418, 7.424616, "#673AB7"));
+ mapboxMap.addMarker(generateCityStateMarker("Vatican City", 41.902916, 12.453389, "#009688"));
+ mapboxMap.addMarker(generateCityStateMarker("San Marino", 43.942360, 12.457777, "#795548"));
+ mapboxMap.addMarker(generateCityStateMarker("Liechtenstein", 47.166000, 9.555373, "#FF5722"));
+ }
+
+ private CityStateMarkerOptions generateCityStateMarker(String title, double lat, double lng, String color) {
+ CityStateMarkerOptions marker = new CityStateMarkerOptions();
+ marker.title(title);
+ marker.position(new LatLng(lat, lng));
+ marker.infoWindowBackground(color);
+
+ iconDrawable.setColorFilter(Color.parseColor(color), PorterDuff.Mode.SRC_IN);
+ Icon icon = iconFactory.fromDrawable(iconDrawable);
+ marker.icon(icon);
+ return marker;
+ }
+
+ private void addCustomInfoWindowAdapter(MapboxMap mapboxMap){
+ mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
+
+ private int tenDp = (int) getResources().getDimension(R.dimen.attr_margin);
+
+ @Override
+ public View getInfoWindow(@NonNull Marker marker) {
+ TextView textView = new TextView(InfoWindowAdapterActivity.this);
+ textView.setText(marker.getTitle());
+ textView.setTextColor(Color.WHITE);
+
+ if (marker instanceof CityStateMarker) {
+ CityStateMarker cityStateMarker = (CityStateMarker) marker;
+ textView.setBackgroundColor(Color.parseColor(cityStateMarker.getInfoWindowBackgroundColor()));
+ }
+
+ textView.setPadding(tenDp, tenDp, tenDp, tenDp);
+ return textView;
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
new file mode 100644
index 0000000000..f5835ab7bd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
@@ -0,0 +1,131 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class DebugModeActivity extends AppCompatActivity {
+
+ private static final String TAG = "DebugModeActivity";
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ private int currentStyleIndex = 0;
+
+ private static final String[] STYLES = new String[]{
+ Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION),
+ Style.getOutdoorsStyleUrl(AppConstant.STYLE_VERSION),
+ Style.getLightStyleUrl(AppConstant.STYLE_VERSION),
+ Style.getDarkStyleUrl(AppConstant.STYLE_VERSION),
+ Style.getSatelliteStyleUrl(AppConstant.STYLE_VERSION),
+ Style.getSatelliteStreetsStyleUrl(AppConstant.STYLE_VERSION)
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_debug_mode);
+
+ final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.setTag(true);
+ mapView.setStyleUrl(STYLES[currentStyleIndex]);
+ mapView.onCreate(savedInstanceState);
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap map) {
+ mapboxMap = map;
+ }
+ });
+
+ FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug);
+ fabDebug.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null) {
+ Log.d(TAG, "Debug FAB: isDebug Active? " + mapboxMap.isDebugActive());
+ mapboxMap.cycleDebugOptions();
+ }
+ }
+ });
+
+ FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles);
+ fabStyles.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null) {
+ currentStyleIndex++;
+ if (currentStyleIndex == STYLES.length) {
+ currentStyleIndex = 0;
+ }
+ mapboxMap.setStyleUrl(STYLES[currentStyleIndex]);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
new file mode 100644
index 0000000000..288817d670
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
@@ -0,0 +1,169 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.TrackingSettings;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class DoubleMapActivity extends AppCompatActivity {
+
+ private static final String TAG_FRAGMENT = "map";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_fragment);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ Fragment mapFragment;
+ if (savedInstanceState == null) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.add(R.id.fragment_container, mapFragment = new DoubleMapFragment(), TAG_FRAGMENT);
+ transaction.commit();
+ } else {
+ mapFragment = (DoubleMapFragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
+ }
+ }
+
+ public static class DoubleMapFragment extends Fragment {
+
+ private MapView mMapView;
+ private MapView mMapViewMini;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_double_map, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // MapView large
+ mMapView = (MapView) view.findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mapboxMap.setStyleUrl(Style.getDarkStyleUrl(AppConstant.STYLE_VERSION));
+
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18));
+ try {
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+ } catch (SecurityException e) {
+ // permission is handled in MainActivity
+ getActivity().finish();
+ }
+ }
+ });
+
+ // MapView mini
+ mMapViewMini = (MapView) view.findViewById(R.id.mini_map);
+ mMapViewMini.onCreate(savedInstanceState);
+ mMapViewMini.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mapboxMap.setStyleUrl(Style.getLightStyleUrl(AppConstant.STYLE_VERSION));
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
+
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
+ uiSettings.setCompassEnabled(false);
+ uiSettings.setAttributionEnabled(false);
+ uiSettings.setLogoEnabled(false);
+
+ try {
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+ } catch (SecurityException e) {
+ // permission is handled in MainActivity
+ getActivity().finish();
+ }
+
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ // test if we can open 2 activities after each other
+ startActivity(new Intent(mMapViewMini.getContext(), DoubleMapActivity.class));
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ mMapViewMini.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ mMapViewMini.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ mMapViewMini.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ mMapViewMini.onLowMemory();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ mMapViewMini.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
new file mode 100644
index 0000000000..62762f7116
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
@@ -0,0 +1,150 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.TrackingSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class MapPaddingActivity extends AppCompatActivity {
+
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_padding);
+
+ final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setTag(true);
+ mMapView.onCreate(savedInstanceState);
+
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+
+ int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
+ int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
+ int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
+ mapboxMap.setPadding(paddingLeft, toolbar.getHeight(), paddingRight, paddingBottom);
+
+ moveToBangalore();
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_padding, menu);
+ return true;
+ }
+
+ private void toggleGps(boolean enable) {
+ try {
+ // Enable user location
+ mMapboxMap.setMyLocationEnabled(enable);
+
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ trackingSettings.setDismissTrackingOnGesture(false);
+ trackingSettings.setMyLocationTrackingMode(enable ? MyLocationTracking.TRACKING_FOLLOW : MyLocationTracking.TRACKING_NONE);
+ } catch (SecurityException e) {
+ // permission not granted is handled in FeatureOverviewActivity
+ finish();
+ }
+ }
+
+ private void moveToBangalore() {
+ toggleGps(false);
+
+ LatLng bangalore = new LatLng(12.9810816, 77.6368034);
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .zoom(16)
+ .target(bangalore)
+ .bearing(40)
+ .tilt(45)
+ .build();
+
+ mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
+ mMapboxMap.addMarker(new MarkerOptions().title("Center map").position(bangalore));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+
+ case R.id.action_user_tracking:
+ if (mMapboxMap != null) {
+ toggleGps(true);
+ }
+ return true;
+
+ case R.id.action_bangalore:
+ if (mMapboxMap != null) {
+ moveToBangalore();
+ }
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
new file mode 100644
index 0000000000..fde46b1cab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java
@@ -0,0 +1,356 @@
+package com.mapbox.mapboxsdk.testapp.activity.offline;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.offline.OfflineManager;
+import com.mapbox.mapboxsdk.offline.OfflineRegion;
+import com.mapbox.mapboxsdk.offline.OfflineRegionError;
+import com.mapbox.mapboxsdk.offline.OfflineRegionStatus;
+import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+import com.mapbox.mapboxsdk.testapp.model.other.OfflineDownloadRegionDialog;
+import com.mapbox.mapboxsdk.testapp.model.other.OfflineListRegionsDialog;
+import org.json.JSONObject;
+import java.util.ArrayList;
+
+public class OfflineActivity extends AppCompatActivity
+ implements OfflineDownloadRegionDialog.DownloadRegionDialogListener {
+
+ private final static String LOG_TAG = "OfflineActivity";
+
+ // JSON encoding/decoding
+ public final static String JSON_CHARSET = "UTF-8";
+ public final static String JSON_FIELD_REGION_NAME = "FIELD_REGION_NAME";
+
+ /*
+ * UI elements
+ */
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+ private ProgressBar mProgressBar;
+ private Button downloadRegion;
+ private Button listRegions;
+
+ private boolean isEndNotified;
+
+ /*
+ * Offline objects
+ */
+ private OfflineManager mOfflineManager;
+ private OfflineRegion mOfflineRegion;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_offline);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ // Set up map
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setStyleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ Log.d(LOG_TAG, "Map is ready");
+ mMapboxMap = mapboxMap;
+
+ // Set initial position to UNHQ in NYC
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(40.749851, -73.967966))
+ .zoom(14)
+ .bearing(0)
+ .tilt(0)
+ .build()));
+ }
+ });
+
+ // The progress bar
+ mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
+
+ // Set up button listeners
+ downloadRegion = (Button) findViewById(R.id.button_download_region);
+ downloadRegion.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleDownloadRegion();
+ }
+ });
+
+ listRegions = (Button) findViewById(R.id.button_list_regions);
+ listRegions.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleListRegions();
+ }
+ });
+
+ // Set up the OfflineManager
+ mOfflineManager = OfflineManager.getInstance(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /*
+ * Buttons logic
+ */
+
+ private void handleDownloadRegion() {
+ Log.d(LOG_TAG, "handleDownloadRegion");
+
+ // Show dialog
+ OfflineDownloadRegionDialog offlineDownloadRegionDialog = new OfflineDownloadRegionDialog();
+ offlineDownloadRegionDialog.show(getSupportFragmentManager(), "download");
+ }
+
+ private void handleListRegions() {
+ Log.d(LOG_TAG, "handleListRegions");
+
+ // Query the DB asynchronously
+ mOfflineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
+ @Override
+ public void onList(OfflineRegion[] offlineRegions) {
+ // Check result
+ if (offlineRegions == null || offlineRegions.length == 0) {
+ Toast.makeText(OfflineActivity.this, "You have no regions yet.", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Get regions info
+ ArrayList<String> offlineRegionsNames = new ArrayList<>();
+ for (OfflineRegion offlineRegion : offlineRegions) {
+ offlineRegionsNames.add(getRegionName(offlineRegion));
+ }
+
+ // Create args
+ Bundle args = new Bundle();
+ args.putStringArrayList(OfflineListRegionsDialog.ITEMS, offlineRegionsNames);
+
+ // Show dialog
+ OfflineListRegionsDialog offlineListRegionsDialog = new OfflineListRegionsDialog();
+ offlineListRegionsDialog.setArguments(args);
+ offlineListRegionsDialog.show(getSupportFragmentManager(), "list");
+ }
+
+ @Override
+ public void onError(String error) {
+ Log.e(LOG_TAG, "Error: " + error);
+ }
+
+ private String getRegionName(OfflineRegion offlineRegion) {
+ String regionName;
+
+ try {
+ byte[] metadata = offlineRegion.getMetadata();
+ String json = new String(metadata, JSON_CHARSET);
+ JSONObject jsonObject = new JSONObject(json);
+ regionName = jsonObject.getString(JSON_FIELD_REGION_NAME);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to decode metadata: " + e.getMessage());
+ regionName = "Region " + offlineRegion.getID();
+ }
+
+ return regionName;
+ }
+ });
+ }
+
+ /*
+ * Dialogs
+ */
+
+ @Override
+ public void onDownloadRegionDialogPositiveClick(final String regionName) {
+ if (TextUtils.isEmpty(regionName)) {
+ Toast.makeText(OfflineActivity.this, "Region name cannot be empty.", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Start progress bar
+ Log.d(LOG_TAG, "Download started: " + regionName);
+ startProgress();
+
+ // Definition
+ String styleURL = mMapboxMap.getStyleUrl();
+ LatLngBounds bounds = mMapboxMap.getProjection().getVisibleRegion().latLngBounds;
+ double minZoom = mMapboxMap.getCameraPosition().zoom;
+ double maxZoom = mMapboxMap.getMaxZoom();
+ float pixelRatio = this.getResources().getDisplayMetrics().density;
+ OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
+ styleURL, bounds, minZoom, maxZoom, pixelRatio);
+
+ // Sample way of encoding metadata from a JSONObject
+ byte[] metadata;
+ try {
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put(JSON_FIELD_REGION_NAME, regionName);
+ String json = jsonObject.toString();
+ metadata = json.getBytes(JSON_CHARSET);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to encode metadata: " + e.getMessage());
+ metadata = null;
+ }
+
+ // Create region
+ mOfflineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
+ @Override
+ public void onCreate(OfflineRegion offlineRegion) {
+ Log.d(LOG_TAG, "Offline region created: " + regionName);
+ mOfflineRegion = offlineRegion;
+ launchDownload();
+ }
+
+ @Override
+ public void onError(String error) {
+ Log.e(LOG_TAG, "Error: " + error);
+ }
+ });
+ }
+
+ private void launchDownload() {
+ // Set an observer
+ mOfflineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
+ @Override
+ public void onStatusChanged(OfflineRegionStatus status) {
+ // Compute a percentage
+ double percentage = status.getRequiredResourceCount() >= 0 ?
+ (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :
+ 0.0;
+
+ if (status.isComplete()) {
+ // Download complete
+ endProgress("Region downloaded successfully.");
+ return;
+ } else if (status.isRequiredResourceCountPrecise()) {
+ // Switch to determinate state
+ setPercentage((int) Math.round(percentage));
+ }
+
+ // Debug
+ Log.d(LOG_TAG, String.format("%s/%s resources; %s bytes downloaded.",
+ String.valueOf(status.getCompletedResourceCount()),
+ String.valueOf(status.getRequiredResourceCount()),
+ String.valueOf(status.getCompletedResourceSize())));
+ }
+
+ @Override
+ public void onError(OfflineRegionError error) {
+ Log.e(LOG_TAG, "onError reason: " + error.getReason());
+ Log.e(LOG_TAG, "onError message: " + error.getMessage());
+ }
+
+ @Override
+ public void mapboxTileCountLimitExceeded(long limit) {
+ Log.e(LOG_TAG, "Mapbox tile count limit exceeded: " + limit);
+ }
+ });
+
+ // Change the region state
+ mOfflineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);
+ }
+
+ /*
+ * Progress bar
+ */
+
+ private void startProgress() {
+ // Disable buttons
+ downloadRegion.setEnabled(false);
+ listRegions.setEnabled(false);
+
+ // Start and show the progress bar
+ isEndNotified = false;
+ mProgressBar.setIndeterminate(true);
+ mProgressBar.setVisibility(View.VISIBLE);
+ }
+
+ private void setPercentage(final int percentage) {
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setProgress(percentage);
+ }
+
+ private void endProgress(final String message) {
+ // Don't notify more than once
+ if (isEndNotified) return;
+
+ // Enable buttons
+ downloadRegion.setEnabled(true);
+ listRegions.setEnabled(true);
+
+ // Stop and hide the progress bar
+ isEndNotified = true;
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setVisibility(View.GONE);
+
+ // Show a toast
+ Toast.makeText(OfflineActivity.this, message, Toast.LENGTH_LONG).show();
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
new file mode 100644
index 0000000000..a4a283907e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
@@ -0,0 +1,136 @@
+package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+
+import android.graphics.Color;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+public class MyLocationDrawableActivity extends AppCompatActivity implements LocationListener {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private Location location;
+ private boolean firstRun;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_my_location_customization);
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ findViewById(R.id.progress).setVisibility(View.GONE);
+ location = LocationServices.getLocationServices(this).getLastLocation();
+
+ MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
+ mapboxMapOptions.accessToken(getString(R.string.mapbox_access_token));
+ mapboxMapOptions.styleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
+ mapboxMapOptions.locationEnabled(true);
+ mapboxMapOptions.camera(new CameraPosition.Builder()
+ .target(location != null ? new LatLng(location) : new LatLng(0, 0))
+ .zoom(11)
+ .tilt(25)
+ .build());
+
+ mapboxMapOptions.myLocationForegroundDrawables(ContextCompat.getDrawable(this, R.drawable.ic_chelsea),
+ ContextCompat.getDrawable(this, R.drawable.ic_chelsea));
+ mapboxMapOptions.myLocationBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_arsenal));
+ mapboxMapOptions.myLocationForegroundTintColor(Color.GREEN);
+ mapboxMapOptions.myLocationBackgroundTintColor(Color.YELLOW);
+ mapboxMapOptions.myLocationBackgroundPadding(new int[]{0, 0,
+ (int) getResources().getDimension(R.dimen.locationview_background_drawable_padding),
+ (int) getResources().getDimension(R.dimen.locationview_background_drawable_padding)});
+
+ mapboxMapOptions.myLocationAccuracyTint(Color.RED);
+ mapboxMapOptions.myLocationAccuracyAlpha(155);
+
+ mapView = new MapView(this, mapboxMapOptions);
+ ViewGroup parent = (ViewGroup) findViewById(R.id.container);
+ parent.addView(mapView);
+
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ }
+ });
+
+ LocationServices.getLocationServices(this).addLocationListener(this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ if (mapboxMap != null && firstRun) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 10));
+ firstRun = false;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
new file mode 100644
index 0000000000..f665b88f60
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
@@ -0,0 +1,188 @@
+package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class MyLocationTintActivity extends AppCompatActivity implements LocationListener {
+
+ private MapView mapView;
+ private MapboxMap map;
+ private boolean firstRun;
+
+ private static final int PERMISSIONS_LOCATION = 0;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_my_location_dot_color);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ map = mapboxMap;
+ toggleGps(!mapboxMap.isMyLocationEnabled());
+
+ final MyLocationViewSettings myLocationViewSettings = mapboxMap.getMyLocationViewSettings();
+
+ // handle default button clicks
+ ViewUtils.attachClickListener(MyLocationTintActivity.this, R.id.default_user_dot_coloring_button,new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.my_location_ring));
+ myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
+ }
+ });
+
+ // handle tint user dot button clicks
+ ViewUtils.attachClickListener(MyLocationTintActivity.this, R.id.tint_user_dot_button,new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_green));
+ myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_green));
+ }
+ });
+
+ // handle tint accuracy ring button clicks
+ ViewUtils.attachClickListener(MyLocationTintActivity.this, R.id.user_accuracy_ring_tint_button,new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.accent));
+ myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
+ }
+ });
+ }
+ });
+
+ LocationServices.getLocationServices(this).addLocationListener(this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ if (map != null && firstRun) {
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
+ firstRun = false;
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @UiThread
+ public void toggleGps(boolean enableGps) {
+ if (enableGps) {
+ if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
+ (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
+ } else {
+ enableLocation(true);
+ }
+ } else {
+ enableLocation(false);
+ }
+ }
+
+ private void enableLocation(boolean enabled) {
+ if (enabled) {
+ map.setMyLocationEnabled(true);
+ if (map.getMyLocation() != null) {
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(map.getMyLocation().getLatitude(), map.getMyLocation().getLongitude()), 15));
+ }
+ } else {
+ map.setMyLocationEnabled(false);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSIONS_LOCATION: {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ enableLocation(true);
+ }
+ }
+ }
+ }
+
+ private static class ViewUtils {
+
+ public static void attachClickListener(@NonNull Activity activity, @IdRes int buttonId, @Nullable View.OnClickListener clickListener) {
+ View view = activity.findViewById(buttonId);
+ if(view!=null){
+ view.setOnClickListener(clickListener);
+ }
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
new file mode 100644
index 0000000000..b92bb5937c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
@@ -0,0 +1,160 @@
+package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class MyLocationToggleActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private FloatingActionButton locationToggleFAB;
+
+ private static final int PERMISSIONS_LOCATION = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_my_location_toggle);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ }
+ });
+
+ locationToggleFAB = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
+ locationToggleFAB.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null) {
+ toggleGps(!mapboxMap.isMyLocationEnabled());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @UiThread
+ public void toggleGps(boolean enableGps) {
+ if (enableGps) {
+ if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
+ (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
+ } else {
+ enableLocation(true);
+ }
+ } else {
+ enableLocation(false);
+ }
+ }
+
+ private void enableLocation(boolean enabled) {
+ if (enabled) {
+ mapboxMap.setOnMyLocationChangeListener(new MapboxMap.OnMyLocationChangeListener() {
+ @Override
+ public void onMyLocationChange(@Nullable Location location) {
+ if (location != null) {
+ mapboxMap.setCameraPosition(new CameraPosition.Builder()
+ .target(new LatLng(location))
+ .zoom(16)
+ .bearing(0)
+ .tilt(0)
+ .build());
+ mapboxMap.setOnMyLocationChangeListener(null);
+ }
+ }
+ });
+ locationToggleFAB.setImageResource(R.drawable.ic_location_disabled_24dp);
+ } else {
+ locationToggleFAB.setImageResource(R.drawable.ic_my_location_24dp);
+ }
+
+ mapboxMap.setMyLocationEnabled(enabled);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSIONS_LOCATION: {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ enableLocation(true);
+ }
+ }
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
new file mode 100644
index 0000000000..fcd61e70b4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
@@ -0,0 +1,242 @@
+package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.Toast;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.constants.MyBearingTracking;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationServices;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.TrackingSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class MyLocationTrackingModeActivity extends AppCompatActivity implements MapboxMap.OnMyLocationChangeListener, AdapterView.OnItemSelectedListener {
+
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+ private Spinner mLocationSpinner, mBearingSpinner;
+ private Location mLocation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_my_location_tracking);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+
+ // disable dismissal when a gesture occurs
+ mMapboxMap.getTrackingSettings().setDismissLocationTrackingOnGesture(false);
+ mMapboxMap.getTrackingSettings().setDismissBearingTrackingOnGesture(false);
+
+ mapboxMap.setOnMyLocationChangeListener(MyLocationTrackingModeActivity.this);
+
+ ArrayAdapter<CharSequence> locationTrackingAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.user_tracking_mode, android.R.layout.simple_spinner_item);
+ locationTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mLocationSpinner = (Spinner) findViewById(R.id.spinner_location);
+ mLocationSpinner.setAdapter(locationTrackingAdapter);
+ mLocationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+
+ ArrayAdapter<CharSequence> bearingTrackingAdapter = ArrayAdapter.createFromResource(actionBar.getThemedContext(), R.array.user_bearing_mode, android.R.layout.simple_spinner_item);
+ bearingTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mBearingSpinner = (Spinner) findViewById(R.id.spinner_bearing);
+ mBearingSpinner.setAdapter(bearingTrackingAdapter);
+ mBearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+
+ mapboxMap.setOnMyLocationTrackingModeChangeListener(new MapboxMap.OnMyLocationTrackingModeChangeListener() {
+ @Override
+ public void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode) {
+ if (MyLocationTracking.TRACKING_NONE == myLocationTrackingMode) {
+ mLocationSpinner.setOnItemSelectedListener(null);
+ mLocationSpinner.setSelection(0);
+ mLocationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ }
+ }
+ });
+
+ mapboxMap.setOnMyBearingTrackingModeChangeListener(new MapboxMap.OnMyBearingTrackingModeChangeListener() {
+ @Override
+ public void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode) {
+ if (MyBearingTracking.NONE == myBearingTrackingMode) {
+ mBearingSpinner.setOnItemSelectedListener(null);
+ mBearingSpinner.setSelection(0);
+ mBearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ }
+ }
+ });
+
+
+ mLocation = LocationServices.getLocationServices(MyLocationTrackingModeActivity.this).getLastLocation();
+ if(mLocation!=null){
+ setInitialPosition(new LatLng(mLocation));
+ }
+ }
+ });
+ }
+
+ private void setInitialPosition(LatLng latLng){
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14));
+ mMapboxMap.setMyLocationEnabled(true);
+ mLocationSpinner.setEnabled(true);
+ mBearingSpinner.setEnabled(true);
+ }
+
+ @Override
+ public void onMyLocationChange(@Nullable Location location) {
+ if (location != null) {
+ if (mLocation == null) {
+ // initial location to reposition map
+ setInitialPosition(new LatLng(location));
+ }
+ mLocation = location;
+ showSnackBar();
+ }
+ }
+
+ private void showSnackBar() {
+ String desc = "Loc Chg: ";
+ boolean noInfo = true;
+ if (mLocation.hasSpeed()) {
+ desc += String.format(MapboxConstants.MAPBOX_LOCALE, "Spd = %.1f km/h ", mLocation.getSpeed() * 3.6f);
+ noInfo = false;
+ }
+ if (mLocation.hasAltitude()) {
+ desc += String.format(MapboxConstants.MAPBOX_LOCALE, "Alt = %.0f m ", mLocation.getAltitude());
+ noInfo = false;
+ }
+ if (mLocation.hasAccuracy()) {
+ desc += String.format(MapboxConstants.MAPBOX_LOCALE, "Acc = %.0f m", mLocation.getAccuracy());
+ }
+
+ if (noInfo) {
+ desc += "No extra info";
+ }
+ Snackbar.make(findViewById(android.R.id.content), desc, Snackbar.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) throws SecurityException {
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ if (parent.getId() == R.id.spinner_location) {
+ switch (position) {
+ case 0:
+ trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
+ break;
+
+ case 1:
+ trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+ break;
+ }
+ } else if (parent.getId() == R.id.spinner_bearing) {
+ switch (position) {
+ case 0:
+ trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
+ break;
+
+ case 1:
+ trackingSettings.setMyBearingTrackingMode(MyBearingTracking.GPS);
+ break;
+
+ case 2:
+ trackingSettings.setMyBearingTrackingMode(MyBearingTracking.COMPASS);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_tracking, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean state;
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ case R.id.action_toggle_dismissible_tracking:
+ state = !item.isChecked();
+ mMapboxMap.getTrackingSettings().setDismissLocationTrackingOnGesture(state);
+ Toast.makeText(this, "Dismiss tracking mode on gesture = " + state, Toast.LENGTH_SHORT).show();
+ item.setChecked(state);
+ return true;
+ case R.id.action_toggle_dismissible_bearing:
+ state = !item.isChecked();
+ mMapboxMap.getTrackingSettings().setDismissBearingTrackingOnGesture(state);
+ Toast.makeText(this, "Dismiss bearing mode on gesture = " + state, Toast.LENGTH_SHORT).show();
+ item.setChecked(state);
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureAdapter.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureAdapter.java
new file mode 100644
index 0000000000..fac7e34ce1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureAdapter.java
@@ -0,0 +1,55 @@
+package com.mapbox.mapboxsdk.testapp.adapter;
+
+import android.graphics.Typeface;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.activity.Feature;
+import com.mapbox.mapboxsdk.testapp.utils.FontCache;
+
+import java.util.List;
+
+public class FeatureAdapter extends RecyclerView.Adapter<FeatureAdapter.ViewHolder> {
+
+ private List<Feature> features;
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+
+ public TextView labelView;
+ public TextView descriptionView;
+
+ public ViewHolder(View v) {
+ super(v);
+ Typeface typeface = FontCache.get("Roboto-Regular.ttf",v.getContext());
+ labelView = (TextView) v.findViewById(R.id.nameView);
+ labelView.setTypeface(typeface);
+ descriptionView = (TextView) v.findViewById(R.id.descriptionView);
+ descriptionView.setTypeface(typeface);
+ }
+ }
+
+ public FeatureAdapter(List<Feature> features) {
+ this.features = features;
+ }
+
+ @Override
+ public FeatureAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main_feature, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ holder.labelView.setText(features.get(position).getLabel());
+ holder.descriptionView.setText(features.get(position).getDescription());
+ }
+
+ @Override
+ public int getItemCount() {
+ return features.size();
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java
new file mode 100644
index 0000000000..d86e06ea6d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java
@@ -0,0 +1,178 @@
+package com.mapbox.mapboxsdk.testapp.adapter;
+
+import android.content.Context;
+import android.support.annotation.IdRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.testapp.utils.FontCache;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class FeatureSectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ private static final int SECTION_TYPE = 0;
+
+ private final Context context;
+ private final SparseArray<Section> sections;
+ private final RecyclerView.Adapter adapter;
+
+ @LayoutRes
+ private final int sectionRes;
+
+ @IdRes
+ private final int textRes;
+
+ private boolean valid = true;
+
+ public FeatureSectionAdapter(Context ctx, int sectionResourceId, int textResourceId, RecyclerView.Adapter baseAdapter) {
+ context = ctx;
+ sectionRes = sectionResourceId;
+ textRes = textResourceId;
+ adapter = baseAdapter;
+ sections = new SparseArray<>();
+ adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ valid = adapter.getItemCount() > 0;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onItemRangeChanged(int positionStart, int itemCount) {
+ valid = adapter.getItemCount() > 0;
+ notifyItemRangeChanged(positionStart, itemCount);
+ }
+
+ @Override
+ public void onItemRangeInserted(int positionStart, int itemCount) {
+ valid = adapter.getItemCount() > 0;
+ notifyItemRangeInserted(positionStart, itemCount);
+ }
+
+ @Override
+ public void onItemRangeRemoved(int positionStart, int itemCount) {
+ valid = adapter.getItemCount() > 0;
+ notifyItemRangeRemoved(positionStart, itemCount);
+ }
+ });
+ }
+
+
+ public static class SectionViewHolder extends RecyclerView.ViewHolder {
+
+ public TextView title;
+
+ public SectionViewHolder(@NonNull View view, @IdRes int textRes) {
+ super(view);
+ title = (TextView) view.findViewById(textRes);
+ title.setTypeface(FontCache.get("Roboto-Medium.ttf", view.getContext()));
+ }
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
+ if (typeView == SECTION_TYPE) {
+ final View view = LayoutInflater.from(context).inflate(sectionRes, parent, false);
+ return new SectionViewHolder(view, textRes);
+ } else {
+ return adapter.onCreateViewHolder(parent, typeView - 1);
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
+ if (isSectionHeaderPosition(position)) {
+ ((SectionViewHolder) sectionViewHolder).title.setText(sections.get(position).title);
+ } else {
+ adapter.onBindViewHolder(sectionViewHolder, getConvertedPosition(position));
+ }
+
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return isSectionHeaderPosition(position)
+ ? SECTION_TYPE
+ : adapter.getItemViewType(getConvertedPosition(position)) + 1;
+ }
+
+
+ public static class Section {
+ int firstPosition;
+ int sectionedPosition;
+ CharSequence title;
+
+ public Section(int firstPosition, CharSequence title) {
+ this.firstPosition = firstPosition;
+ this.title = title;
+ }
+
+ public CharSequence getTitle() {
+ return title;
+ }
+ }
+
+
+ public void setSections(Section[] sections) {
+ this.sections.clear();
+
+ Arrays.sort(sections, new Comparator<Section>() {
+ @Override
+ public int compare(Section o, Section o1) {
+ return (o.firstPosition == o1.firstPosition)
+ ? 0
+ : ((o.firstPosition < o1.firstPosition) ? -1 : 1);
+ }
+ });
+
+ int offset = 0;
+ for (Section section : sections) {
+ section.sectionedPosition = section.firstPosition + offset;
+ this.sections.append(section.sectionedPosition, section);
+ ++offset;
+ }
+
+ notifyDataSetChanged();
+ }
+
+ public int getConvertedPosition(int sectionedPosition) {
+ if (isSectionHeaderPosition(sectionedPosition)) {
+ return RecyclerView.NO_POSITION;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < sections.size(); i++) {
+ if (sections.valueAt(i).sectionedPosition > sectionedPosition) {
+ break;
+ }
+ --offset;
+ }
+ return sectionedPosition + offset;
+ }
+
+ public boolean isSectionHeaderPosition(int position) {
+ return sections.get(position) != null;
+ }
+
+
+ @Override
+ public long getItemId(int position) {
+ return isSectionHeaderPosition(position)
+ ? Integer.MAX_VALUE - sections.indexOfKey(position)
+ : adapter.getItemId(getConvertedPosition(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return (valid ? adapter.getItemCount() + sections.size() : 0);
+ }
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarker.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarker.java
deleted file mode 100644
index 1ec5aa9c76..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarker.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.annotations;
-
-import com.mapbox.mapboxsdk.annotations.Marker;
-
-public class CityStateMarker extends Marker {
-
- private String mInfoWindowBackgroundColor;
-
- public CityStateMarker(CityStateMarkerOptions cityStateOptions, String color) {
- super(cityStateOptions);
- mInfoWindowBackgroundColor = color;
- }
-
- public String getInfoWindowBackgroundColor() {
- return mInfoWindowBackgroundColor;
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarkerOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarkerOptions.java
deleted file mode 100644
index 379a9210e5..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/annotations/CityStateMarkerOptions.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.annotations;
-
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
-import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-
-public class CityStateMarkerOptions extends BaseMarkerOptions<CityStateMarker, CityStateMarkerOptions> {
-
- private String mInfoWindowBackgroundColor;
-
- public CityStateMarkerOptions infoWindowBackground(String color) {
- mInfoWindowBackgroundColor = color;
- return getThis();
- }
-
- public CityStateMarkerOptions() {
- }
-
- private CityStateMarkerOptions(Parcel in) {
- position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
- snippet(in.readString());
- String iconId = in.readString();
- Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
- Icon icon = IconFactory.recreate(iconId, iconBitmap);
- icon(icon);
- title(in.readString());
- }
-
- @Override
- public CityStateMarkerOptions getThis() {
- return this;
- }
-
- @Override
- public CityStateMarker getMarker() {
- return new CityStateMarker(this, mInfoWindowBackgroundColor);
- }
-
- public static final Parcelable.Creator<CityStateMarkerOptions> CREATOR
- = new Parcelable.Creator<CityStateMarkerOptions>() {
- public CityStateMarkerOptions createFromParcel(Parcel in) {
- return new CityStateMarkerOptions(in);
- }
-
- public CityStateMarkerOptions[] newArray(int size) {
- return new CityStateMarkerOptions[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(position, flags);
- out.writeString(snippet);
- out.writeString(icon.getId());
- out.writeParcelable(icon.getBitmap(), flags);
- out.writeString(title);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/layers/ExampleCustomLayer.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/layers/ExampleCustomLayer.java
deleted file mode 100644
index 3a97c26595..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/layers/ExampleCustomLayer.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.layers;
-
-public class ExampleCustomLayer {
- static {
- System.loadLibrary("example-custom-layer");
- }
-
- public native static long createContext();
- public static long InitializeFunction;
- public static long RenderFunction;
- public static long DeinitializeFunction;
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java
new file mode 100644
index 0000000000..66c51a1a13
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java
@@ -0,0 +1,69 @@
+package com.mapbox.mapboxsdk.testapp.model.activity;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class Feature implements Parcelable{
+
+ private String name;
+ private String label;
+ private String description;
+ private String category;
+
+ public Feature(String name, String label, String description, String category) {
+ this.name = name;
+ this.label = label;
+ this.description = description;
+ this.category = category;
+ }
+
+ private Feature(Parcel in) {
+ name = in.readString();
+ label = in.readString();
+ description = in.readString();
+ category = in.readString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSimpleName() {
+ String[] split = name.split("\\.");
+ return split[split.length - 1];
+ }
+
+ public String getLabel() {
+ return label != null ? label : getSimpleName();
+ }
+
+ public String getDescription() {
+ return description != null ? description : "-";
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(name);
+ out.writeString(label);
+ out.writeString(description);
+ out.writeString(category);
+ }
+
+ public static final Parcelable.Creator<Feature> CREATOR
+ = new Parcelable.Creator<Feature>() {
+ public Feature createFromParcel(Parcel in) {
+ return new Feature(in);
+ }
+
+ public Feature[] newArray(int size) {
+ return new Feature[size];
+ }
+ };
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarker.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarker.java
new file mode 100644
index 0000000000..e0141586e4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarker.java
@@ -0,0 +1,18 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.Marker;
+
+public class CityStateMarker extends Marker {
+
+ private String mInfoWindowBackgroundColor;
+
+ public CityStateMarker(CityStateMarkerOptions cityStateOptions, String color) {
+ super(cityStateOptions);
+ mInfoWindowBackgroundColor = color;
+ }
+
+ public String getInfoWindowBackgroundColor() {
+ return mInfoWindowBackgroundColor;
+ }
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarkerOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarkerOptions.java
new file mode 100644
index 0000000000..a716518c22
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarkerOptions.java
@@ -0,0 +1,68 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class CityStateMarkerOptions extends BaseMarkerOptions<CityStateMarker, CityStateMarkerOptions> {
+
+ private String mInfoWindowBackgroundColor;
+
+ public CityStateMarkerOptions infoWindowBackground(String color) {
+ mInfoWindowBackgroundColor = color;
+ return getThis();
+ }
+
+ public CityStateMarkerOptions() {
+ }
+
+ private CityStateMarkerOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ title(in.readString());
+ }
+
+ @Override
+ public CityStateMarkerOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public CityStateMarker getMarker() {
+ return new CityStateMarker(this, mInfoWindowBackgroundColor);
+ }
+
+ public static final Parcelable.Creator<CityStateMarkerOptions> CREATOR
+ = new Parcelable.Creator<CityStateMarkerOptions>() {
+ public CityStateMarkerOptions createFromParcel(Parcel in) {
+ return new CityStateMarkerOptions(in);
+ }
+
+ public CityStateMarkerOptions[] newArray(int size) {
+ return new CityStateMarkerOptions[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(position, flags);
+ out.writeString(snippet);
+ out.writeString(icon.getId());
+ out.writeParcelable(icon.getBitmap(), flags);
+ out.writeString(title);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarker.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarker.java
new file mode 100644
index 0000000000..7503b48df3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarker.java
@@ -0,0 +1,24 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+
+public class CountryMarker extends Marker {
+
+ private String abbrevName;
+ private int flagRes;
+
+ public CountryMarker(BaseMarkerOptions baseMarkerOptions, String abbrevName, int iconRes) {
+ super(baseMarkerOptions);
+ this.abbrevName = abbrevName;
+ this.flagRes = iconRes;
+ }
+
+ public String getAbbrevName() {
+ return abbrevName;
+ }
+
+ public int getFlagRes() {
+ return flagRes;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerOptions.java
new file mode 100644
index 0000000000..ac1ff25bf2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerOptions.java
@@ -0,0 +1,75 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class CountryMarkerOptions extends BaseMarkerOptions<CountryMarker, CountryMarkerOptions> {
+
+ private String abbrevName;
+ private int flagRes;
+
+ public CountryMarkerOptions abbrevName(String name) {
+ abbrevName = name;
+ return getThis();
+ }
+
+ public CountryMarkerOptions flagRes(int imageRes) {
+ flagRes = imageRes;
+ return getThis();
+ }
+
+ public CountryMarkerOptions() {
+ }
+
+ private CountryMarkerOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ title(in.readString());
+ }
+
+ @Override
+ public CountryMarkerOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public CountryMarker getMarker() {
+ return new CountryMarker(this, abbrevName, flagRes);
+ }
+
+ public static final Parcelable.Creator<CountryMarkerOptions> CREATOR
+ = new Parcelable.Creator<CountryMarkerOptions>() {
+ public CountryMarkerOptions createFromParcel(Parcel in) {
+ return new CountryMarkerOptions(in);
+ }
+
+ public CountryMarkerOptions[] newArray(int size) {
+ return new CountryMarkerOptions[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(position, flags);
+ out.writeString(snippet);
+ out.writeString(icon.getId());
+ out.writeParcelable(icon.getBitmap(), flags);
+ out.writeString(title);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerView.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerView.java
new file mode 100644
index 0000000000..64f0565026
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerView.java
@@ -0,0 +1,24 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+
+public class CountryMarkerView extends MarkerView {
+
+ private String abbrevName;
+ private int flagRes;
+
+ public CountryMarkerView(BaseMarkerViewOptions baseMarkerViewOptions, String abbrevName, int flagRes) {
+ super(baseMarkerViewOptions);
+ this.abbrevName = abbrevName;
+ this.flagRes = flagRes;
+ }
+
+ public String getAbbrevName() {
+ return abbrevName;
+ }
+
+ public int getFlagRes() {
+ return flagRes;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java
new file mode 100644
index 0000000000..c4ef4a8d13
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java
@@ -0,0 +1,104 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class CountryMarkerViewOptions extends BaseMarkerViewOptions<CountryMarkerView, CountryMarkerViewOptions> {
+
+ private String abbrevName;
+ private int flagRes;
+
+ public CountryMarkerViewOptions() {
+ }
+
+ protected CountryMarkerViewOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ title(in.readString());
+ flat(in.readByte() != 0);
+ anchor(in.readFloat(), in.readFloat());
+ infoWindowAnchor(in.readFloat(), in.readFloat());
+ selectAnimatorResource(in.readInt());
+ deselectAnimatorResource(in.readInt());
+ rotation(in.readInt());
+ if (in.readByte() != 0) {
+ // this means we have an icon
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ }
+ abbrevName(in.readString());
+ flagRes(in.readInt());
+ }
+
+ @Override
+ public CountryMarkerViewOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(getPosition(), flags);
+ out.writeString(getSnippet());
+ out.writeString(getTitle());
+ out.writeByte((byte) (isFlat() ? 1 : 0));
+ out.writeFloat(getAnchorU());
+ out.writeFloat(getAnchorV());
+ out.writeFloat(getInfoWindowAnchorU());
+ out.writeFloat(getInfoWindowAnchorV());
+ out.writeInt(getSelectAnimRes());
+ out.writeInt(getDeselectAnimRes());
+ out.writeInt(getRotation());
+ Icon icon = getIcon();
+ out.writeByte((byte) (icon != null ? 1 : 0));
+ if (icon != null) {
+ out.writeString(getIcon().getId());
+ out.writeParcelable(getIcon().getBitmap(), flags);
+ }
+ out.writeString(abbrevName);
+ out.writeInt(flagRes);
+ }
+
+ @Override
+ public CountryMarkerView getMarker() {
+ return new CountryMarkerView(this, abbrevName, flagRes);
+ }
+
+ public CountryMarkerViewOptions abbrevName(String abbrevName) {
+ this.abbrevName = abbrevName;
+ return getThis();
+ }
+
+ public CountryMarkerViewOptions flagRes(int flagRes) {
+ this.flagRes = flagRes;
+ return getThis();
+ }
+
+ public static final Parcelable.Creator<CountryMarkerViewOptions> CREATOR
+ = new Parcelable.Creator<CountryMarkerViewOptions>() {
+ public CountryMarkerViewOptions createFromParcel(Parcel in) {
+ return new CountryMarkerViewOptions(in);
+ }
+
+ public CountryMarkerViewOptions[] newArray(int size) {
+ return new CountryMarkerViewOptions[size];
+ }
+ };
+
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java
new file mode 100644
index 0000000000..60ed6168cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java
@@ -0,0 +1,6 @@
+package com.mapbox.mapboxsdk.testapp.model.constants;
+
+public class AppConstant {
+
+ public final static int STYLE_VERSION = 9;
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
new file mode 100644
index 0000000000..2901060459
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
@@ -0,0 +1,12 @@
+package com.mapbox.mapboxsdk.testapp.model.customlayer;
+
+public class ExampleCustomLayer {
+ static {
+ System.loadLibrary("example-custom-layer");
+ }
+
+ public native static long createContext();
+ public static long InitializeFunction;
+ public static long RenderFunction;
+ public static long DeinitializeFunction;
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java
new file mode 100644
index 0000000000..3c2d1cab5e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java
@@ -0,0 +1,60 @@
+package com.mapbox.mapboxsdk.testapp.model.other;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+import android.widget.EditText;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+/**
+ * Created by antonio on 2/17/16.
+ */
+public class OfflineDownloadRegionDialog extends DialogFragment {
+
+ private final static String LOG_TAG = "OfflineDownloadRegionDialog";
+
+ public interface DownloadRegionDialogListener {
+ void onDownloadRegionDialogPositiveClick(String regionName);
+ }
+
+ DownloadRegionDialogListener mListener;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mListener = (DownloadRegionDialogListener) activity;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Let the user choose a name for the region
+ final EditText regionNameEdit = new EditText(getActivity());
+
+ builder.setTitle("Choose a name for the region")
+ .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
+ .setView(regionNameEdit)
+ .setPositiveButton("Start", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String regionName = regionNameEdit.getText().toString();
+ mListener.onDownloadRegionDialogPositiveClick(regionName);
+ }
+ }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Download cancelled.");
+ }
+ });
+
+ return builder.create();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
new file mode 100644
index 0000000000..e00465a9b1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java
@@ -0,0 +1,51 @@
+package com.mapbox.mapboxsdk.testapp.model.other;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+
+/**
+ * Created by antonio on 2/17/16.
+ */
+public class OfflineListRegionsDialog extends DialogFragment {
+
+ private final static String LOG_TAG = "OfflineListRegionsDialog";
+
+ public final static String ITEMS = "ITEMS";
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Read args
+ Bundle args = getArguments();
+ ArrayList<String> offlineRegionsNames = (args == null ? null : args.getStringArrayList(ITEMS));
+ CharSequence[] items = offlineRegionsNames.toArray(new CharSequence[offlineRegionsNames.size()]);
+
+ builder.setTitle("List of offline regions")
+ .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
+ .setItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Selected item: " + which);
+ }
+ })
+ .setPositiveButton("Accept", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Dialog dismissed");
+ }
+ });
+
+ return builder.create();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java
deleted file mode 100644
index 81b681cc50..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.offline;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.mapbox.mapboxsdk.testapp.R;
-
-/**
- * Created by antonio on 2/17/16.
- */
-public class DownloadRegionDialog extends DialogFragment {
-
- private final static String LOG_TAG = "DownloadRegionDialog";
-
- public interface DownloadRegionDialogListener {
- void onDownloadRegionDialogPositiveClick(String regionName);
- }
-
- DownloadRegionDialogListener mListener;
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mListener = (DownloadRegionDialogListener) activity;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- // Let the user choose a name for the region
- final EditText regionNameEdit = new EditText(getActivity());
-
- builder.setTitle("Choose a name for the region")
- .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
- .setView(regionNameEdit)
- .setPositiveButton("Start", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String regionName = regionNameEdit.getText().toString();
- mListener.onDownloadRegionDialogPositiveClick(regionName);
- }
- }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Log.d(LOG_TAG, "Download cancelled.");
- }
- });
-
- return builder.create();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java
deleted file mode 100644
index 50df71ad00..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.offline;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
-
-import com.mapbox.mapboxsdk.testapp.R;
-
-import java.util.ArrayList;
-
-/**
- * Created by antonio on 2/17/16.
- */
-public class ListRegionsDialog extends DialogFragment {
-
- private final static String LOG_TAG = "ListRegionsDialog";
-
- public final static String ITEMS = "ITEMS";
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- // Read args
- Bundle args = getArguments();
- ArrayList<String> offlineRegionsNames = (args == null ? null : args.getStringArrayList(ITEMS));
- CharSequence[] items = offlineRegionsNames.toArray(new CharSequence[offlineRegionsNames.size()]);
-
- builder.setTitle("List of offline regions")
- .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
- .setItems(items, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Log.d(LOG_TAG, "Selected item: " + which);
- }
- })
- .setPositiveButton("Accept", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Log.d(LOG_TAG, "Dialog dismissed");
- }
- });
-
- return builder.create();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ApiAccess.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ApiAccess.java
deleted file mode 100644
index 20ca2913e2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ApiAccess.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.utils;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-
-/**
- * {@code ApiAccess} provides a method to load the Mapbox access token.
- */
-public final class ApiAccess {
-
- /**
- * <p>
- * Returns the Mapbox access token set in the app resources.
- * </p>
- * It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
- * meta-data value. If not found it will then attempt to load the access token from the
- * {@code res/raw/token.txt} development file.
- *
- * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
- * @return The Mapbox access token or null if not found.
- * @see MapboxConstants#KEY_META_DATA_MANIFEST
- */
- public static String getToken(@NonNull Context context) {
- try {
- // read out AndroidManifest
- PackageManager packageManager = context.getPackageManager();
- ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
- String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
- if (token == null || token.isEmpty()) {
- throw new IllegalArgumentException();
- }
- return token;
- } catch (Exception e) {
- // use fallback on string resource, used for development
- int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
- return tokenResId != 0 ? context.getString(tokenResId) : null;
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java
new file mode 100644
index 0000000000..46552fb7ae
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java
@@ -0,0 +1,27 @@
+package com.mapbox.mapboxsdk.testapp.utils;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+
+import java.util.Hashtable;
+
+public class FontCache {
+
+ private static Hashtable<String, Typeface> fontCache = new Hashtable<>();
+
+ public static Typeface get(String name, Context context) {
+ Typeface tf = fontCache.get(name);
+ if (tf == null) {
+ try {
+ tf = Typeface.createFromAsset(context.getAssets(), name);
+ fontCache.put(name, tf);
+ } catch (Exception e) {
+ Log.e(MapboxConstants.TAG, "Font not found");
+ }
+ }
+ return tf;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ItemClickSupport.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ItemClickSupport.java
new file mode 100644
index 0000000000..51e09f3593
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ItemClickSupport.java
@@ -0,0 +1,95 @@
+package com.mapbox.mapboxsdk.testapp.utils;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class ItemClickSupport {
+ private final RecyclerView mRecyclerView;
+ private OnItemClickListener mOnItemClickListener;
+ private OnItemLongClickListener mOnItemLongClickListener;
+ private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mOnItemClickListener != null) {
+ RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
+ mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
+ }
+ }
+ };
+ private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mOnItemLongClickListener != null) {
+ RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
+ return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
+ }
+ return false;
+ }
+ };
+ private RecyclerView.OnChildAttachStateChangeListener mAttachListener
+ = new RecyclerView.OnChildAttachStateChangeListener() {
+ @Override
+ public void onChildViewAttachedToWindow(View view) {
+ if (mOnItemClickListener != null) {
+ view.setOnClickListener(mOnClickListener);
+ }
+ if (mOnItemLongClickListener != null) {
+ view.setOnLongClickListener(mOnLongClickListener);
+ }
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(View view) {
+
+ }
+ };
+
+ private ItemClickSupport(RecyclerView recyclerView) {
+ mRecyclerView = recyclerView;
+ mRecyclerView.setTag(R.id.item_click_support, this);
+ mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
+ }
+
+ public static ItemClickSupport addTo(RecyclerView view) {
+ ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
+ if (support == null) {
+ support = new ItemClickSupport(view);
+ }
+ return support;
+ }
+
+ public static ItemClickSupport removeFrom(RecyclerView view) {
+ ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
+ if (support != null) {
+ support.detach(view);
+ }
+ return support;
+ }
+
+ public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
+ mOnItemClickListener = listener;
+ return this;
+ }
+
+ public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
+ mOnItemLongClickListener = listener;
+ return this;
+ }
+
+ private void detach(RecyclerView view) {
+ view.removeOnChildAttachStateChangeListener(mAttachListener);
+ view.setTag(R.id.item_click_support, null);
+ }
+
+ public interface OnItemClickListener {
+
+ void onItemClicked(RecyclerView recyclerView, int position, View v);
+ }
+
+ public interface OnItemLongClickListener {
+
+ boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
index 6789b401eb..544e737780 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
@@ -23,7 +23,7 @@ import android.util.Log;
/**
* A utility class to help log timings splits throughout a method call.
* Typical usage is:
- * <p/>
+ *
* <pre>
* TimingLogger timings = new TimingLogger(TAG, "methodA");
* // ... do some work A ...
@@ -34,9 +34,9 @@ import android.util.Log;
* timings.addSplit("work C");
* timings.dumpToLog();
* </pre>
- * <p/>
+ *
* <p>The dumpToLog call would add the following to the log:</p>
- * <p/>
+ *
* <pre>
* D/TAG ( 3459): methodA: begin
* D/TAG ( 3459): methodA: 9 ms, work A
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/rotate_360.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/rotate_360.xml
new file mode 100644
index 0000000000..e3569966d2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/rotate_360.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="rotation"
+ android:valueFrom="0"
+ android:valueTo="360"
+ android:valueType="floatType"/>
+</set> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_down.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_down.xml
new file mode 100644
index 0000000000..d1067b09c2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_down.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="300"
+ android:valueTo="1"/>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="300"
+ android:valueTo="1"/>
+</set> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_up.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_up.xml
new file mode 100644
index 0000000000..b43ed05513
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_up.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="300"
+ android:valueTo="2"/>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="300"
+ android:valueTo="2"/>
+</set> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_us.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_us.png
new file mode 100644
index 0000000000..d09e068fe9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_us.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_a_photo_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_a_photo_black_24dp.xml
new file mode 100644
index 0000000000..3d2ba42f3e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_a_photo_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,4L3,1h2v3h3v2L5,6v3L3,9L3,6L0,6L0,4h3zM6,10L6,7h3L9,4h7l1.83,2L21,6c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2L5,22c-1.1,0 -2,-0.9 -2,-2L3,10h3zM13,19c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5 -5,2.24 -5,5 2.24,5 5,5zM9.8,14c0,1.77 1.43,3.2 3.2,3.2s3.2,-1.43 3.2,-3.2 -1.43,-3.2 -3.2,-3.2 -3.2,1.43 -3.2,3.2z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_blur_on_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_blur_on_24dp.xml
deleted file mode 100644
index b032703e38..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_blur_on_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm-3,0.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm15,5.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm-11,10c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm7,7c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-17c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,5.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm8,0.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm3,8.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM14,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm-4,-12c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,8.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm4,-4.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,-4c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_bug_report_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_bug_report_24dp.xml
deleted file mode 100644
index 909e11f923..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_bug_report_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8H4v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3H20v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1H20V8zm-6,8h-4v-2h4v2zm0,-4h-4v-2h4v2z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
deleted file mode 100644
index 23417034d6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_compare_arrows_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_crop_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_crop_24dp.xml
deleted file mode 100644
index 4f72bed69c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_crop_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M17,15h2V7c0,-1.1 -0.9,-2 -2,-2H9v2h8v8zM7,17V1H5v4H1v2h4v10c0,1.1 0.9,2 2,2h10v4h2v-4h4v-2H7z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions.xml
deleted file mode 100644
index 2c474849a4..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M21.71,11.29l-9,-9c-0.39,-0.39 -1.02,-0.39 -1.41,0l-9,9c-0.39,0.39 -0.39,1.02 0,1.41l9,9c0.39,0.39 1.02,0.39 1.41,0l9,-9c0.39,-0.38 0.39,-1.01 0,-1.41zM14,14.5V12h-4v3H8v-4c0,-0.55 0.45,-1 1,-1h5V7.5l3.5,3.5 -3.5,3.5z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dns_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dns_24dp.xml
deleted file mode 100644
index 5b7b1bce6a..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dns_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dynamic_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dynamic_marker.xml
deleted file mode 100644
index e16f6b317d..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_dynamic_marker.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M7,7h10v3l4,-4 -4,-4v3H5v6h2V7zm10,10H7v-3l-4,4 4,4v-3h12v-6h-2v4zm-4,-2V9h-1l-2,1v1h1.5v4H13z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_center_focus_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_center_focus_24dp.xml
deleted file mode 100644
index 455c18f3d1..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_center_focus_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M5,15H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_none_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_none_24dp.xml
deleted file mode 100644
index 9b16b15571..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_filter_none_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_find_replace_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_find_replace_24dp.xml
deleted file mode 100644
index 26bb56ca5a..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_find_replace_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M11,6c1.38,0 2.63,0.56 3.54,1.46L12,10h6V4l-2.05,2.05C14.68,4.78 12.93,4 11,4c-3.53,0 -6.43,2.61 -6.92,6H6.1c0.46,-2.28 2.48,-4 4.9,-4zm5.64,9.14c0.66,-0.9 1.12,-1.97 1.28,-3.14H15.9c-0.46,2.28 -2.48,4 -4.9,4 -1.38,0 -2.63,-0.56 -3.54,-1.46L10,12H4v6l2.05,-2.05C7.32,17.22 9.07,18 11,18c1.55,0 2.98,-0.51 4.14,-1.36L20,21.49 21.49,20l-4.85,-4.86z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_back_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_back_24dp.xml
deleted file mode 100644
index da636f975c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_back_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M9,7H7v2h2V7zm0,4H7v2h2v-2zm0,-8c-1.11,0 -2,0.9 -2,2h2V3zm4,12h-2v2h2v-2zm6,-12v2h2c0,-1.1 -0.9,-2 -2,-2zm-6,0h-2v2h2V3zM9,17v-2H7c0,1.1 0.89,2 2,2zm10,-4h2v-2h-2v2zm0,-4h2V7h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2zM5,7H3v12c0,1.1 0.89,2 2,2h12v-2H5V7zm10,-2h2V3h-2v2zm0,12h2v-2h-2v2z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_front_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_front_24dp.xml
deleted file mode 100644
index 7d78638595..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_flip_to_front_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm2,4v-2H3c0,1.1 0.89,2 2,2zM3,9h2V7H3v2zm12,12h2v-2h-2v2zm4,-18H9c-1.11,0 -2,0.9 -2,2v10c0,1.1 0.89,2 2,2h10c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,12H9V5h10v10zm-8,6h2v-2h-2v2zm-4,0h2v-2H7v2z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_gps_fixed_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_gps_fixed_24dp.xml
deleted file mode 100644
index a8fa0207e6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_gps_fixed_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#000000"
- android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm8.94,3c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_adapter.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_adapter.xml
deleted file mode 100644
index 0d4c724aa9..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_adapter.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_concurrent.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_concurrent.xml
deleted file mode 100644
index 9b16b15571..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_infowindow_concurrent.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_24dp.xml
new file mode 100644
index 0000000000..944b526c5c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_clear_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_clear_24dp.xml
new file mode 100644
index 0000000000..249f57fc65
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_clear_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19.81,14.99l1.19,-0.92 -1.43,-1.43 -1.19,0.92 1.43,1.43zM19.36,10.27L21,9l-9,-7 -2.91,2.27 7.87,7.88 2.4,-1.88zM3.27,1L2,2.27l4.22,4.22L3,9l1.63,1.27L12,16l2.1,-1.63 1.43,1.43L12,18.54l-7.37,-5.73L3,14.07l9,7 4.95,-3.85L20.73,21 22,19.73 3.27,1z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location.xml
deleted file mode 100644
index 027a644721..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled_24dp.xml
new file mode 100644
index 0000000000..4fedff778b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15L23,13v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zM16.27,17.54C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_map_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_map_padding.xml
deleted file mode 100644
index 60b75a5493..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_map_padding.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_mapboxmap.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_mapboxmap.xml
deleted file mode 100644
index 3482fa9dbc..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_mapboxmap.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12,11.55C9.64,9.35 6.48,8 3,8v11c3.48,0 6.64,1.35 9,3.55 2.36,-2.19 5.52,-3.55 9,-3.55V8c-3.48,0 -6.64,1.35 -9,3.55zM12,8c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3 1.34,3 3,3z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_menu_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_menu_24dp.xml
deleted file mode 100644
index 8c16289f27..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_menu_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M3,18h18v-2H3v2zm0,-5h18v-2H3v2zm0,-7v2h18V6H3z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_my_location_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_my_location_24dp.xml
new file mode 100644
index 0000000000..eb979016bf
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_my_location_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94L23,13v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_navigation_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_navigation_24dp.xml
deleted file mode 100644
index 6ab15dc6b3..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_navigation_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_wallpaper_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_wallpaper_24dp.xml
deleted file mode 100644
index 1b885337da..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_wallpaper_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M4,4h7V2H4c-1.1,0 -2,0.9 -2,2v7h2V4zm6,9l-4,5h12l-3,-4 -2.03,2.71L10,13zm7,-4.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S14,7.67 14,8.5s0.67,1.5 1.5,1.5S17,9.33 17,8.5zM20,2h-7v2h7v7h2V4c0,-1.1 -0.9,-2 -2,-2zm0,18h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2v7zM4,13H2v7c0,1.1 0.9,2 2,2h7v-2H4v-7z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_widgets_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_widgets_24dp.xml
deleted file mode 100644
index 9329bfc677..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_now_widgets_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8H3v8zM3,3v8h8V3H3zm13.66,-1.31L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pin_drop_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pin_drop_24dp.xml
deleted file mode 100644
index 47aff99532..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pin_drop_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M18,8c0,-3.31 -2.69,-6 -6,-6S6,4.69 6,8c0,4.5 6,11 6,11s6,-6.5 6,-11zm-8,0c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.89,2 -2,2c-1.1,0 -2,-0.9 -2,-2zM5,20v2h14v-2H5z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_place_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_place_24dp.xml
deleted file mode 100644
index e778c37c4c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_place_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_refresh_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_refresh_24dp.xml
new file mode 100644
index 0000000000..20cd9a07d8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_refresh_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_tilt.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_tilt.xml
deleted file mode 100644
index d5c67efbb7..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_tilt.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M7.77,6.76L6.23,5.48 0.82,12l5.41,6.52 1.54,-1.28L3.42,12l4.35,-5.24zM7,13h2v-2H7v2zm10,-2h-2v2h2v-2zm-6,2h2v-2h-2v2zm6.77,-7.52l-1.54,1.28L20.58,12l-4.35,5.24 1.54,1.28L23.18,12l-5.41,-6.52z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_transform_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_transform_24dp.xml
deleted file mode 100644
index b8448abb14..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_transform_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M22,18v-2H8V4h2L7,1 4,4h2v2H2v2h4v8c0,1.1 0.9,2 2,2h8v2h-2l3,3 3,-3h-2v-2h4zM10,8h6v6h2V8c0,-1.1 -0.9,-2 -2,-2h-6v2z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_trending_up_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_trending_up_24dp.xml
deleted file mode 100644
index 830f3036f2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_trending_up_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z" />
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/line_divider.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/line_divider.xml
new file mode 100644
index 0000000000..2dc6b4a008
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/line_divider.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <size
+ android:width="1dp"
+ android:height="1dp" />
+
+ <solid android:color="@android:color/darker_gray" />
+
+</shape> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animate_coordinate.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animate_coordinate.xml
deleted file mode 100644
index 73592448b2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animate_coordinate.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <FrameLayout
- android:id="@+id/content_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@+id/toolbar">
-
- <android.support.design.widget.CoordinatorLayout
- android:id="@+id/coordinator_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fab"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/fab_margin"
- android:src="@drawable/ic_animate_coordinates"
- app:backgroundTint="@color/white" />
-
- </android.support.design.widget.CoordinatorLayout>
-
- </FrameLayout>
-
-</RelativeLayout>
-
-
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
index 6a09109ea8..8c025d999c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
@@ -2,6 +2,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
@@ -15,6 +16,7 @@
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ app:style_url="@string/style_light"
android:layout_below="@id/toolbar" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml
deleted file mode 100644
index 0b318d781b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/cameraMapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@+id/toolbar"/>
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/button_camera_move"
- android:id="@+id/cameraMoveButton"
- android:layout_margin="@dimen/fab_margin"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true"
- android:background="@color/white"/>
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/button_camera_ease"
- android:id="@+id/cameraEaseButton"
- android:layout_alignTop="@+id/cameraMoveButton"
- android:layout_centerHorizontal="true"
- android:background="@color/white"/>
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/button_camera_animate"
- android:id="@+id/cameraAnimateButton"
- android:layout_alignBottom="@+id/cameraEaseButton"
- android:layout_alignParentRight="true"
- android:layout_marginRight="@dimen/fab_margin"
- android:background="@color/white"/>
-
-</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
new file mode 100644
index 0000000000..5a5fb5f604
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@+id/toolbar"
+ app:center_latitude="51.50325"
+ app:center_longitude="-0.11968"
+ app:zoom="15" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:orientation="horizontal"
+ android:weightSum="3">
+
+ <Button
+ android:id="@+id/cameraMoveButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_camera_move" />
+
+ <Button
+ android:id="@+id/cameraEaseButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_camera_ease" />
+
+ <Button
+ android:id="@+id/cameraAnimateButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_camera_animate" />
+
+ </LinearLayout>
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml
new file mode 100644
index 0000000000..2e08f79e95
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <android.support.design.widget.CoordinatorLayout
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:style_url="@string/style_mapbox_streets" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_input_24dp"
+ app:backgroundTint="@android:color/white" />
+
+ </android.support.design.widget.CoordinatorLayout>
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_layer.xml
new file mode 100644
index 0000000000..2b52f7d8fd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_layer.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ </android.support.v7.widget.Toolbar>
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <android.support.design.widget.CoordinatorLayout
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_layers_24dp"
+ app:backgroundTint="@android:color/white"
+ />
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
new file mode 100644
index 0000000000..36adfbc6e9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ </android.support.v7.widget.Toolbar>
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <android.support.design.widget.CoordinatorLayout
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabDebug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_marginBottom="82dp"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:src="@drawable/ic_refresh_24dp"
+ app:backgroundTint="@color/accent"
+ app:layout_anchorGravity="top" />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabStyles"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_layers_24dp"
+ app:backgroundTint="@color/primary" />
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geocoder.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geocoder.xml
index d93a6d1eb5..cce6883541 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geocoder.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geocoder.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -15,7 +16,10 @@
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="5" />
+ android:layout_weight="5"
+ app:center_latitude="38.90962"
+ app:center_longitude="-77.04341"
+ app:zoom="15" />
<TextView
android:id="@+id/message"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml
index 0b539daac3..41956385f9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_main.xml
@@ -1,69 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/drawer_layout"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:orientation="vertical">
- <RelativeLayout
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <FrameLayout
- android:id="@+id/content_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@+id/toolbar">
-
- <android.support.design.widget.CoordinatorLayout
- android:id="@+id/coordinator_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mainMapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <TextView
- android:id="@+id/view_fps"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:text="@string/label_fps"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/locationFAB"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/fab_margin"
- android:src="@drawable/ic_gps_fixed_24dp"
- app:backgroundTint="@color/white" />
-
- </android.support.design.widget.CoordinatorLayout>
-
- </FrameLayout>
-
- </RelativeLayout>
-
- <android.support.design.widget.NavigationView
- android:id="@+id/nav_view"
- android:layout_width="wrap_content"
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerView"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="start"
- android:fitsSystemWindows="true"
- app:menu="@menu/menu_drawer" />
+ android:scrollbars="vertical" />
-</android.support.v4.widget.DrawerLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml
index 873e5d18a1..6a90a8eafb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml
@@ -11,6 +11,8 @@
android:background="@color/primary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+
<com.mapbox.mapboxsdk.maps.MapView
android:layout_below="@id/toolbar"
android:id="@+id/manualZoomMapView"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
index ce516f6a1b..a421f21ede 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- tools:context="com.mapbox.mapboxsdk.testapp.MapFragmentActivity">
+ tools:context=".activity.fragment.MapFragmentActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
index aa3c50d65c..f0e635f29c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
@@ -40,8 +40,6 @@
android:layout_width="@dimen/map_padding_right"
android:layout_height="match_parent"
android:layout_gravity="end"
- android:layout_marginLeft="@dimen/map_padding_left"
- android:layout_marginStart="@dimen/map_padding_left"
android:alpha="0.5"
android:layout_marginTop="?attr/actionBarSize"
android:background="@color/mapbox_blue" />
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapboxmap.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapboxmap.xml
deleted file mode 100644
index eccd3ea8eb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapboxmap.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:style_url="@string/style_mapbox_streets"
- app:center_latitude="38.897705003219784"
- app:center_longitude="-77.03655168667463"
- app:zoom="15" />
-
-</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
index ce108ba129..90cb1a90b3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
+ android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
@@ -20,12 +19,32 @@
</android.support.v7.widget.Toolbar>
<com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
+ android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:center_latitude="38.897705003219784"
- app:center_longitude="-77.03655168667463"
+ app:center_latitude="38.87031"
+ android:layout_below="@id/toolbar"
+ app:center_longitude="-77.00897"
app:style_url="@string/style_mapbox_streets"
- app:zoom="15" />
+ app:zoom="10" />
-</LinearLayout>
+ <TextView
+ android:id="@+id/countView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"
+ android:padding="16dp"
+ android:textSize="20sp"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_animate_coordinates" />
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
new file mode 100644
index 0000000000..5d285958f1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"
+ app:center_latitude="38.907192"
+ app:center_longitude="-77.036871"
+ app:style_url="@string/style_mapbox_streets"
+ app:zoom="12" />
+
+ <TextView
+ android:id="@+id/countView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"
+ android:padding="16dp"
+ android:textSize="20sp" />
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml
index 873e5d18a1..611766acb1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -12,9 +13,13 @@
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<com.mapbox.mapboxsdk.maps.MapView
- android:layout_below="@id/toolbar"
- android:id="@+id/manualZoomMapView"
+ android:id="@+id/mapView"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"
+ app:center_latitude="-1.063510"
+ app:center_longitude=" 32.895425"
+ app:style_url="@string/style_satellite_streets"
+ app:zoom="4" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
new file mode 100644
index 0000000000..75218423fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <FrameLayout
+ android:id="@id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.widget.ContentLoadingProgressBar
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+
+ </FrameLayout>
+
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml
new file mode 100644
index 0000000000..af755c72a1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_below="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:orientation="horizontal"
+ android:weightSum="3">
+
+ <Button
+ android:id="@+id/default_user_dot_coloring_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_user_dot_default" />
+
+ <Button
+ android:id="@+id/tint_user_dot_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_user_dot_tint" />
+
+ <Button
+ android:id="@+id/user_accuracy_ring_tint_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_user_accuracy_ring_tint" />
+
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml
new file mode 100644
index 0000000000..e8204117a3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:mapbox="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <android.support.design.widget.CoordinatorLayout
+ android:id="@id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabLocationToggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_my_location_24dp"
+ tools:backgroundTint="@color/primary" />
+
+ </android.support.design.widget.CoordinatorLayout>
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml
index 699fa4be6b..19c30dbb8d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml
@@ -42,7 +42,9 @@
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ app:my_location_foreground_tint="@color/primary"
+ app:my_location_accuracy_tint="@color/primary"
app:style_url="@string/style_mapbox_streets"
app:zoom="15" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polygon.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polygon.xml
new file mode 100644
index 0000000000..1c60deb328
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polygon.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml
index 7875fc2021..8ff9f82b09 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml
@@ -19,6 +19,7 @@
android:layout_below="@+id/toolbar"
app:center_latitude="47.798202"
app:center_longitude="7.573781"
+ app:attribution_tint="@android:color/holo_green_dark"
app:style_url="@string/style_mapbox_streets"
app:zoom="4" />
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
index 39ff128c69..9378900dc3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
@@ -1,15 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/activity_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<android.support.v7.widget.Toolbar
- android:id="@+id/secondToolBar"
+ android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
-</LinearLayout>
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/pressForMarkerMapView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
index 17e0f35729..eb0d0a2245 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
@@ -79,6 +79,10 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
+ app:center_latitude="37.176546"
+ app:center_longitude="-3.599007"
+ app:style_url="@string/style_emerald"
+ app:zoom="15"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
new file mode 100644
index 0000000000..b9701b53e0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@+id/toolbar"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:background="@color/primary" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ app:style_url="@string/style_emerald" />
+
+ </LinearLayout>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_add_a_photo_black_24dp"
+ app:backgroundTint="@android:color/white" />
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml
deleted file mode 100644
index eae320c750..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_tilt.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/tiltMapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:center_latitude="38.90252"
- app:center_longitude="-77.02291"
- app:style_url="@string/style_mapbox_streets"
- app:zoom="11" />
-
-</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
new file mode 100644
index 0000000000..c1ffa7d203
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/text_lat"
+ android:layout_width="72dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:layout_marginRight="4dp"
+ android:gravity="center"
+ android:text="Latitude"
+ android:textColor="@android:color/white" />
+
+ <SeekBar
+ android:id="@+id/seekbar_lat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_toLeftOf="@+id/value_lat"
+ android:layout_toRightOf="@id/text_lat"
+ android:max="360" />
+
+ <TextView
+ android:id="@id/value_lat"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ android:text="-180"
+ android:textColor="@android:color/white" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp">
+
+ <TextView
+ android:id="@+id/text_lon"
+ android:layout_width="72dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:textColor="@android:color/white"
+ android:layout_marginRight="4dp"
+ android:gravity="center"
+ android:text="Longitude" />
+
+ <SeekBar
+ android:id="@+id/seekbar_lon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_toLeftOf="@+id/value_lon"
+ android:layout_toRightOf="@id/text_lon"
+ android:max="360" />
+
+ <TextView
+ android:id="@id/value_lon"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:textColor="@android:color/white"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ android:text="-180" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp">
+
+ <TextView
+ android:id="@+id/text_zoom"
+ android:layout_width="72dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:textColor="@android:color/white"
+ android:layout_marginRight="4dp"
+ android:gravity="center"
+ android:text="Zoom" />
+
+ <SeekBar
+ android:id="@+id/seekbar_zoom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_toLeftOf="@+id/value_zoom"
+ android:layout_toRightOf="@id/text_zoom"
+ android:max="18" />
+
+ <TextView
+ android:id="@id/value_zoom"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="4dp"
+ android:textColor="@android:color/white"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ android:text="18" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp">
+
+ <TextView
+ android:id="@+id/text_bearing"
+ android:textColor="@android:color/white"
+ android:layout_width="72dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:layout_marginRight="4dp"
+ android:gravity="center"
+ android:text="Bearing" />
+
+ <SeekBar
+ android:id="@+id/seekbar_bearing"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_toLeftOf="@+id/value_bearing"
+ android:layout_toRightOf="@id/text_bearing"
+ android:max="360" />
+
+ <TextView
+ android:id="@id/value_bearing"
+ android:layout_width="48dp"
+ android:textColor="@android:color/white"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ android:text="0" />
+
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp">
+
+ <TextView
+ android:id="@+id/text_tilt"
+ android:layout_width="72dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:textColor="@android:color/white"
+ android:layout_marginRight="4dp"
+ android:gravity="center"
+ android:text="Tilt" />
+
+ <SeekBar
+ android:id="@+id/seekbar_tilt"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_toLeftOf="@+id/value_tilt"
+ android:layout_toRightOf="@id/text_tilt"
+ android:max="60" />
+
+ <TextView
+ android:id="@id/value_tilt"
+ android:layout_width="48dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:textColor="@android:color/white"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ android:text="0" />
+
+ </RelativeLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
index 88788fc429..db50720257 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
@@ -7,8 +7,7 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- mapbox:access_token="@string/mapbox_access_token" />
+ android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/map_card"
@@ -22,6 +21,6 @@
android:id="@+id/mini_map"
android:layout_width="100dp"
android:layout_height="100dp"
- mapbox:access_token="@string/mapbox_access_token" />
+ />
</FrameLayout>
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
new file mode 100644
index 0000000000..93af094e3f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingStart="16dp"
+ android:paddingTop="16dp">
+
+ <TextView
+ android:id="@+id/nameView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:text="Dummy headline"
+ android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
+ android:singleLine="true"
+ android:textColor="@android:color/black"
+ android:textSize="16sp" />
+
+ <TextView
+ android:id="@+id/descriptionView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:alpha="0.56"
+ android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
+ android:singleLine="true"
+ android:text="Description is dummy dummy"
+ android:textColor="@android:color/black"
+ android:textSize="14sp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
new file mode 100644
index 0000000000..8978d4e3eb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="48dp">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0.25dp"
+ android:alpha="0.56"
+ android:background="@drawable/line_divider" />
+
+ <TextView
+ android:id="@+id/section_text"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
+ android:alpha="0.54"
+ android:background="@android:color/transparent"
+ android:singleLine="true"
+ android:textColor="@android:color/black"
+ android:textSize="14sp" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
new file mode 100644
index 0000000000..08caf1df66
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@id/imageView"
+ android:layout_width="64dp"
+ android:layout_height="64dp"/>
+
+ <TextView
+ android:id="@id/textView"
+ android:layout_width="wrap_content"
+ android:textColor="@android:color/white"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:layout_centerInParent="true" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
new file mode 100644
index 0000000000..8a3508691a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@color/mapbox_green">
+
+ <TextView
+ android:id="@id/textView"
+ android:layout_width="wrap_content"
+ android:textColor="@android:color/white"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:padding="4dp"
+ android:layout_centerInParent="true" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
deleted file mode 100644
index d04996dc95..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
+++ /dev/null
@@ -1,221 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@+id/mainActivityMenu"
- android:title="@string/navdrawer_menu_title_mainactivity_controls">
- <menu>
- <group>
- <item
- android:id="@+id/action_debug"
- android:icon="@drawable/ic_bug_report_24dp"
- android:title="@string/action_debug" />
-
- <item
- android:id="@+id/action_markers"
- android:icon="@drawable/ic_place_24dp"
- android:title="@string/action_point_annotations" />
-
- <item
- android:id="@+id/action_compass"
- android:icon="@drawable/ic_navigation_24dp"
- android:title="@string/action_compass" />
-
- <item
- android:id="@+id/action_custom_layer"
- android:icon="@drawable/ic_now_wallpaper_24dp"
- android:title="@string/action_custom_layer" />
-
- </group>
- </menu>
- </item>
-
- <group android:id="@+id/stylesSeparator" />
-
- <item
- android:id="@+id/mainActivityStylesMenue"
- android:title="@string/navdrawer_menu_title_mainactivity_styles">
- <menu>
- <group android:id="@+id/stylesGroup">
- <item
- android:id="@+id/actionStyleMapboxStreets"
- android:title="@string/styleMapboxStreets" />
-
- <item
- android:id="@+id/actionStyleEmerald"
- android:title="@string/styleEmerald" />
-
- <item
- android:id="@+id/actionStyleLight"
- android:title="@string/styleLight" />
-
- <item
- android:id="@+id/actionStyleDark"
- android:title="@string/styleDark" />
-
- <item
- android:id="@+id/actionStyleSatellite"
- android:title="@string/styleSatellite" />
-
- <item
- android:id="@+id/actionStyleSatelliteStreets"
- android:title="@string/styleSatelliteStreets" />
-
- </group>
- </menu>
- </item>
-
- <group android:id="@+id/testsSeparator" />
-
- <item
- android:id="@+id/testsMenu"
- android:title="@string/navdrawer_menu_title_individual_tests">
- <menu>
-
- <item
- android:id="@+id/action_offline"
- android:checkable="false"
- android:icon="@drawable/ic_airplanemode_active_black_24dp"
- android:title="@string/action_offline" />
-
- <item
- android:id="@+id/action_mapboxmap"
- android:checkable="false"
- android:icon="@drawable/ic_mapboxmap"
- android:title="@string/action_mapboxmap" />
-
- <item
- android:id="@+id/action_tilt"
- android:checkable="false"
- android:icon="@drawable/ic_tilt"
- android:title="@string/action_tilt" />
-
- <item
- android:id="@+id/action_camera"
- android:checkable="false"
- android:icon="@drawable/ic_transform_24dp"
- android:title="@string/action_camera" />
-
- <item
- android:id="@+id/action_map_fragment"
- android:checkable="false"
- android:icon="@drawable/ic_now_widgets_24dp"
- android:title="@string/action_map_fragment" />
-
- <item
- android:id="@+id/action_support_map_fragment"
- android:checkable="false"
- android:icon="@drawable/ic_now_widgets_24dp"
- android:title="@string/action_map_fragment_support" />
-
- <item
- android:id="@+id/action_info_window"
- android:checkable="false"
- android:icon="@drawable/ic_flip_to_front_24dp"
- android:title="@string/action_info_window" />
-
- <item
- android:id="@+id/action_info_window_concurrent"
- android:checkable="false"
- android:icon="@drawable/ic_flip_to_back_24dp"
- android:title="@string/action_info_window_concurrent" />
-
- <item
- android:id="@+id/action_info_window_adapter"
- android:checkable="false"
- android:icon="@drawable/ic_infowindow_adapter"
- android:title="@string/action_info_window_adapter" />
-
- <item
- android:id="@+id/action_press_for_marker"
- android:checkable="false"
- android:icon="@drawable/ic_pin_drop_24dp"
- android:title="@string/action_press_for_marker" />
-
- <item
- android:id="@+id/action_manual_zoom"
- android:checkable="false"
- android:icon="@drawable/ic_filter_center_focus_24dp"
- android:title="@string/action_manual_zoom" />
-
- <item
- android:id="@+id/action_minmax_zoom"
- android:checkable="false"
- android:icon="@drawable/ic_find_replace_24dp"
- android:title="@string/action_minmax_zoom" />
-
- <item
- android:id="@+id/action_coordinate_change"
- android:checkable="false"
- android:icon="@drawable/ic_animate_coordinates"
- android:title="@string/action_animate_coordinate_change" />
-
- <item
- android:id="@+id/action_visible_bounds"
- android:checkable="false"
- android:icon="@drawable/ic_crop_24dp"
- android:title="@string/action_visible_bounds" />
-
- <item
- android:id="@+id/action_bulk_markers"
- android:checkable="false"
- android:icon="@drawable/ic_blur_on_24dp"
- android:title="@string/action_add_bulk_markers" />
-
- <item
- android:id="@+id/action_user_tracking_mode"
- android:checkable="false"
- android:icon="@drawable/ic_gps_fixed_24dp"
- android:title="@string/activity_user_tracking_mode" />
-
- <item
- android:id="@+id/action_polyline"
- android:checkable="false"
- android:icon="@drawable/ic_trending_up_24dp"
- android:title="@string/activity_polyline" />
-
- <item
- android:id="@+id/action_directions"
- android:checkable="false"
- android:icon="@drawable/ic_directions"
- android:title="@string/activity_directions" />
-
- <item
- android:id="@+id/action_double_mapview"
- android:checkable="false"
- android:icon="@drawable/ic_dns_24dp"
- android:title="@string/action_double_mapview" />
-
- <item
- android:id="@+id/action_geocoder"
- android:checkable="false"
- android:icon="@drawable/ic_directions"
- android:title="@string/activity_geocoder" />
-
- <item
- android:id="@+id/action_scroll_by"
- android:checkable="false"
- android:icon="@drawable/ic_input_24dp"
- android:title="@string/activity_scroll_by" />
-
- <item
- android:id="@+id/action_dynamic_marker"
- android:checkable="false"
- android:icon="@drawable/ic_dynamic_marker"
- android:title="@string/action_dynamic_marker" />
-
- <item
- android:id="@+id/action_map_padding"
- android:checkable="false"
- android:icon="@drawable/ic_map_padding"
- android:title="@string/action_map_padding" />
-
- <item
- android:id="@+id/action_animated_marker"
- android:checkable="false"
- android:icon="@drawable/ic_compare_arrows_black_24dp"
- android:title="@string/action_animated_marker" />
-
- </menu>
- </item>
-</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml
new file mode 100644
index 0000000000..583b760d7c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <group android:checkableBehavior="all">
+ <item
+ android:title="@string/menuitem_title_concurrent_infowindow"
+ android:id="@+id/action_toggle_concurrent_infowindow"
+ app:showAsAction="never"
+ android:checkable="true"/>
+ </group>
+
+</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
new file mode 100644
index 0000000000..0b3a8e797e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/menuItemReset"
+ android:title="@string/menuitem_title_reset"
+ app:showAsAction="always"
+ />
+</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml
new file mode 100644
index 0000000000..dc2add474c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <group android:checkableBehavior="all">
+ <item
+ android:id="@+id/action_toggle_dismissible_tracking"
+ android:checkable="true"
+ android:checked="false"
+ android:title="@string/menuitem_title_tracking_mode_dismiss_on_gesture"
+ app:showAsAction="never" />
+ <item
+ android:id="@+id/action_toggle_dismissible_bearing"
+ android:checkable="true"
+ android:checked="false"
+ android:title="@string/menuitem_title_bearing_mode_dismiss_on_gesture"
+ app:showAsAction="never" />
+ </group>
+
+</menu> \ 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
index ac3b4fc30d..891ab8d7a8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#1E8CAB</color>
- <color name="primaryDark">#166B83</color>
+ <color name="primary_dark">#166B83</color>
<color name="accent">#E55E5E</color>
- <color name="white">#FFFFFF</color>
+ <color name="white">#F9F9F9</color>
+ <color name="mapbox_green">#56B881</color>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index aeda60d478..98ec90c6fd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -6,4 +6,7 @@
<dimen name="map_padding_left">96dp</dimen>
<dimen name="map_padding_bottom">256dp</dimen>
<dimen name="map_padding_right">32dp</dimen>
+ <dimen name="toolbar_shadow">4dp</dimen>
+ <dimen name="locationview_background_drawable_padding">2dp</dimen>
+ <dimen name="locationview_padding_top">350dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml
new file mode 100644
index 0000000000..120bf49fca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="mapView" type="id" />
+ <item name="fab" type="id" />
+ <item name="progress" type="id" />
+ <item name="imageView" type="id" />
+ <item name="textView" type="id" />
+ <item name="toolbar" type="id" />
+ <item name="container" type="id" />
+ <item name="item_click_support" type="id" />
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index bd3d6390ee..a84035aa58 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -1,68 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name">Mapbox GL</string>
+ <string name="app_name">Mapbox Android SDK TestApp</string>
<!-- Test Activities -->
- <string name="activity_mapboxmap">MapboxMap</string>
- <string name="activity_tilt">Map Tilt</string>
- <string name="activity_infowindow_adapter">InfoWindow Adapter</string>
- <string name="activity_map_fragment_suport">Support Map Fragment Activity</string>
- <string name="activity_map_fragment">Map Fragment Activity</string>
- <string name="activity_press_for_marker">Press For Marker Activity</string>
- <string name="activity_marker_in_bulk">Add Bulk Markers Activity</string>
- <string name="activity_manual_zoom">Manual Zoom Activity</string>
- <string name="activity_info_window">InfoWindow Activity</string>
- <string name="activity_info_window_concurrent">InfoWindow Activity (Concurrent)</string>
- <string name="activity_visible_coordinate_bounds">Visible Coordinate Bounds</string>
- <string name="activity_user_tracking_mode">User tracking mode</string>
+ <string name="activity_mapboxmap">MapboxMap Activity</string>
+
+ <!-- Fragment -->
+ <string name="activity_map_fragment_suport">Support Map Fragment</string>
+ <string name="activity_map_fragment">Map Fragment</string>
+
+ <!-- Annotations -->
<string name="activity_add_bulk_markers">Add Markers In Bulk</string>
- <string name="activity_camera">Camera</string>
- <string name="activity_polyline">Polyline Activity</string>
- <string name="activity_animate_coordinate_change">Animate Coordinate Change</string>
- <string name="activity_custom_layer">Custom Layer</string>
- <string name="activity_directions">Directions Activity</string>
- <string name="activity_geocoder">Geocoder Activity</string>
+ <string name="activity_animated_marker">Animated Marker (experimental)</string>
+ <string name="activity_dynamic_marker">Dynamic Marker</string>
+ <string name="activity_polyline">Polyline</string>
+ <string name="activity_polygon">Polygon</string>
+ <string name="activity_press_for_marker">Press Map For Marker</string>
+ <string name="activity_view_marker">View Marker API</string>
+
+ <!-- InfoWindow-->
+ <string name="activity_info_window">Standard InfoWindow example</string>
+ <string name="activity_infowindow_adapter">Custom InfoWindow Adapter</string>
+
+ <!-- Camera -->
+ <string name="activity_camera_animation_types">Animation Types</string>
+ <string name="activity_camera_zoom">Zoom Methods</string>
+ <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string>
+ <string name="activity_camera_position">CameraPosition Method</string>
+ <string name="activity_scroll_by">Scroll By Method</string>
+
+ <!-- API -->
+ <string name="activity_directions">Directions</string>
+ <string name="activity_geocoder">Geocoder</string>
+
+ <!-- Other -->
<string name="activity_double_map">Double Map Activity</string>
- <string name="activity_scroll_by">Scroll By Activity</string>
- <string name="activity_dynamic_marker">Dynamic Marker Activity</string>
- <string name="activity_map_padding">Map Padding Activity</string>
- <string name="activity_offline">Offline Map Activity</string>
-
- <string name="navdrawer_menu_title_mainactivity_controls">Main Activity Controls</string>
- <string name="navdrawer_menu_title_mainactivity_styles">Main Activity Styles</string>
- <string name="navdrawer_menu_title_individual_tests">Individual Tests</string>
- <string name="action_gps">Toggle GPS location</string>
- <string name="action_user_location_tracking">User location tracking</string>
- <string name="action_compass">Toggle compass</string>
- <string name="action_custom_layer">Custom Layer</string>
- <string name="action_debug">Cycle map debug options</string>
- <string name="action_point_annotations">Toggle point annotations</string>
- <string name="action_mapboxmap">MapboxMap</string>
- <string name="action_info_window_adapter">InfoWindow Adapter</string>
- <string name="action_tilt">Tilt</string>
- <string name="action_map_fragment">MapFragment</string>
- <string name="action_map_fragment_support">SupportMapFragment</string>
- <string name="action_press_for_marker">Press For Marker</string>
- <string name="action_manual_zoom">Manual Zoom</string>
- <string name="action_minmax_zoom">Min/Max Zoom</string>
- <string name="action_info_window">InfoWindow</string>
- <string name="action_animate_coordinate_change">Animate Coordinate Change</string>
- <string name="action_info_window_concurrent">InfoWindow (Concurrent)</string>
- <string name="action_add_bulk_markers">Add Markers in bulk</string>
- <string name="action_camera">Camera</string>
- <string name="action_visible_bounds">Set Visible Bounds</string>
+ <string name="activity_snapshot">Snapshot Activity</string>
+ <string name="activity_user_tracking_mode">User tracking mode</string>
+ <string name="activity_user_tracking_customization">User location customization</string>
+ <string name="activity_user_dot_color">User location tint color</string>
+ <string name="activity_user_location_toggle">User location toggle</string>
+ <string name="activity_custom_layer">Custom Layer</string>
+ <string name="activity_map_padding">Map Padding</string>
+ <string name="activity_debug_mode">Debug Mode</string>
+ <string name="activity_offline">Offline Map</string>
+ <string name="activity_minmax_zoom">Min/Max Zoom</string>
+
+ <!-- Description -->
+ <string name="description_user_location_tracking">Tracks the location of the user</string>
+ <string name="description_user_location_customization">Customize the location of the user</string>
+ <string name="description_user_location_dot_color">Customize the user location color</string>
+ <string name="description_user_location_toggle">Toggle location of the user on and off</string>
+ <string name="description_custom_layer">Overlay a custom native layer on the map</string>
+ <string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string>
+ <string name="description_cameraposition">CameraPosition capabilities</string>
+ <string name="description_map_fragment">Showcase MapFragment</string>
+ <string name="description_map_fragment_support">Showcase SupportMapFragment</string>
+ <string name="description_press_for_marker">Add marker to map on long press</string>
+ <string name="description_camera_zoom">Different types of zoom methods</string>
+ <string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
+ <string name="description_info_window">Learn how to handle the InfoWindow</string>
+ <string name="description_info_window_concurrent">InfoWindow example with multiple open</string>
+ <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string>
+ <string name="description_camera_animation_types">Showcase the different animation types</string>
+ <string name="description_visible_bounds">Center the camera around a bounds</string>
+ <string name="description_dynamic_marker">Update position and icon</string>
+ <string name="description_map_padding">Map Padding example</string>
+ <string name="description_debug_mode">Debug Mode</string>
+ <string name="description_offline">Offline Map example</string>
+ <string name="description_animated_marker">Animate the position change of a Marker</string>
+ <string name="description_polyline">Add a polyline to a map</string>
+ <string name="description_polygon">Add a polygon to a map</string>
+ <string name="description_directions">Example with Directions API</string>
+ <string name="description_geocoder">Example with Geocoder API</string>
+ <string name="description_scroll_by">Scroll with pixels in x,y direction</string>
+ <string name="description_snapshot">Example to make a snapshot of the map</string>
+ <string name="description_doublemap">2 maps in a view hierarchy</string>
+ <string name="description_view_marker">Use an Android SDK View as marker</string>
+
+ <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>r
+ <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string>
+ <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string>
+ <string name="menuitem_title_reset">Reset</string>
+
+ <string name="category">category</string>
+ <string name="category_annotation">Annotation</string>
+ <string name="category_camera">Camera</string>
+ <string name="category_custom_layer">Custom Layer</string>
+ <string name="category_directions">Directions</string>
+ <string name="category_fragment">Fragment</string>
+ <string name="category_geocoding">Geocoding</string>
+ <string name="category_imagegenerator">Image Generator</string>
+ <string name="category_infowindow">InfoWindow</string>
+ <string name="category_maplayout">Map Layout</string>
+ <string name="category_offline">Offline</string>
+ <string name="category_userlocation">User Location</string>
+
<string name="action_visible_bounds_explanation">Center map around 2 markers</string>
<string name="action_remove_polylines">Remove polylines</string>
- <string name="action_double_mapview">Double MapView</string>
- <string name="action_dynamic_marker">Dynamic Marker</string>
- <string name="action_map_padding">Map Padding</string>
- <string name="action_offline">Offline Map</string>
- <string name="action_animated_marker">Animated Marker</string>
<string name="button_camera_move">Move</string>
<string name="button_camera_ease">Ease</string>
<string name="button_camera_animate">Animate</string>
+ <string name="button_make_snapshot">Snapshot</string>
+ <string name="button_user_dot_default">Default</string>
+ <string name="button_user_dot_tint">Tint dot</string>
+ <string name="button_user_accuracy_ring_tint">Tint ring</string>
<string name="label_fps">FPS:</string>
@@ -86,14 +130,14 @@
</string-array>
<string-array name="user_tracking_mode">
- <item>Disable tracking</item>
- <item>Follow tracking mode</item>
+ <item>Disabled</item>
+ <item>Follow tracking</item>
</string-array>
<string-array name="user_bearing_mode">
- <item>Disable bearing</item>
- <item>GPS mode</item>
- <item>Compass mode</item>
+ <item>Disabled</item>
+ <item>GPS bearing</item>
+ <item>Compass bearing</item>
<!--<item>Combined mode</item>-->
</string-array>
@@ -103,6 +147,9 @@
<string name="scrollby_y_value">Y: %1$d</string>
<string name="button_download_region">Download region</string>
<string name="button_list_regions">List regions</string>
- <string name="title_activity_animated_marker">AnimatedMarkerActivity</string>
+
+ <string name="dialog_camera_position">Animate to new position</string>
+
+ <string name="geocoder_instructions">Tap Map To Geocode Where Black Marker Is Currently Located</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
index e781e337d6..d01b9d313f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
@@ -5,9 +5,9 @@
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
- <item name="colorPrimaryDark">@color/primaryDark</item>
+ <item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
- <item name="android:windowBackground">@android:color/white</item>
+ <item name="android:windowBackground">@color/white</item>
</style>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
new file mode 100644
index 0000000000..76658c242f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
@@ -0,0 +1,91 @@
+package com.mapbox.mapboxsdk.annotations;
+
+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;
+ 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);
+ }
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
index cc38ca40b8..a55672e86b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
@@ -1,17 +1,17 @@
package com.mapbox.mapboxsdk.annotations;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.os.Parcelable;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
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 {
@@ -70,8 +70,8 @@ public class MarkerTest {
@Test
public void testHashCode() {
- Marker marker = new MarkerOptions().position(new LatLng(10, 12)).getMarker();
- assertEquals("hash code should match", marker.hashCode(), -1946419200);
+ Marker marker = new MarkerOptions().getMarker();
+ assertEquals("hash code should match", marker.hashCode(), 0);
}
@Test
@@ -88,6 +88,35 @@ public class MarkerTest {
}
@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();
@@ -116,4 +145,10 @@ public class MarkerTest {
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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
new file mode 100644
index 0000000000..ed8e4ff323
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
@@ -0,0 +1,210 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.utils.MockParcel;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class MarkerViewTest {
+
+ @Test
+ public void testSanity() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions();
+ assertNotNull("markerOptions should not be null", markerOptions);
+ }
+
+ @Test
+ public void testMarker() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions();
+ assertNotNull("marker should not be null", markerOptions.getMarker());
+ }
+
+ @Test
+ public void testPosition() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng(10, 12));
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals(marker.getPosition(), new LatLng(10, 12));
+ assertEquals(markerOptions.getPosition(), new LatLng(10, 12));
+ }
+
+ @Test
+ public void testSnippet() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().snippet("Mapbox");
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals(marker.getSnippet(), "Mapbox");
+ }
+
+ @Test
+ public void testTitle() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().title("Mapbox");
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals(marker.getTitle(), "Mapbox");
+ assertEquals(markerOptions.getTitle(), "Mapbox");
+ }
+
+ @Test
+ public void testIcon() {
+ Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);
+ Icon icon = IconFactory.recreate("test", bitmap);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().icon(icon);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("Icon should match", icon, marker.getIcon());
+ assertEquals("Icon should match", icon, markerOptions.getIcon());
+ }
+
+ @Test
+ public void testFlat() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().flat(true);
+ MarkerView marker = markerOptions.getMarker();
+ assertTrue("flat should be true", marker.isFlat());
+ }
+
+ @Test
+ public void testFlatDefault() {
+ assertFalse("default value of flat should be false", new MarkerViewOptions().getMarker().isFlat());
+ }
+
+ @Test
+ public void testAnchor() {
+ float anchorU = 1;
+ float anchorV = 1;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().anchor(anchorU, anchorV);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("anchorU should match ", anchorU, marker.getAnchorU(), 0);
+ assertEquals("anchorU should match ", anchorV, marker.getAnchorV(), 0);
+ }
+
+ @Test
+ public void testAnchorDefault() {
+ MarkerView marker = new MarkerViewOptions().getMarker();
+ assertEquals("anchorU should match ", 0.5, marker.getAnchorU(), 0);
+ assertEquals("anchorU should match ", 1, marker.getAnchorV(), 0);
+ }
+
+ @Test
+ public void testInfoWindowAnchor() {
+ float anchorU = 1;
+ float anchorV = 1;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().infoWindowAnchor(anchorU, anchorV);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("anchorU should match ", 1, marker.getInfoWindowAnchorU(), 0);
+ assertEquals("anchorU should match ", 1, marker.getInfoWindowAnchorV(), 0);
+ }
+
+ @Test
+ public void testInfoWindowAnchorDefault() {
+ MarkerView marker = new MarkerViewOptions().getMarker();
+ assertEquals("anchorU should match ", 0.5, marker.getInfoWindowAnchorU(), 0);
+ assertEquals("anchorU should match ", 0, marker.getInfoWindowAnchorV(), 0);
+ }
+
+ @Test
+ public void testSelectAnimRes() {
+ int animatorRes = R.animator.rotate_360;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().selectAnimatorResource(animatorRes);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("select animator resource should match ", animatorRes, marker.getSelectAnimRes(), 0);
+ }
+
+ @Test
+ public void testDeselectAnimRes() {
+ int animatorRes = R.animator.rotate_360;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().deselectAnimatorResource(animatorRes);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("deselect animator resource should match ", animatorRes, marker.getDeselectAnimRes(), 0);
+ }
+
+ @Test
+ public void testRotation() {
+ int rotation = 90;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().rotation(rotation);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("rotation should match ", rotation, marker.getRotation(), 0);
+ }
+
+ @Test
+ public void testVisible() {
+ boolean visible = false;
+ MarkerViewOptions markerOptions = new MarkerViewOptions().visible(visible);
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("visible should match ", visible, marker.isVisible());
+ }
+
+ @Test
+ public void testVisibleDefault() {
+ assertTrue(new MarkerViewOptions().getMarker().isVisible());
+ }
+
+ @Test
+ public void testBuilder() {
+ MarkerView marker = new MarkerViewOptions().title("title").snippet("snippet").position(new LatLng(10, 12)).getMarker();
+ assertEquals(marker.getSnippet(), "snippet");
+
+ assertEquals(marker.getPosition(), new LatLng(10, 12));
+ }
+
+ @Test
+ public void testHashCode() {
+ MarkerView marker = new MarkerViewOptions().getMarker();
+ assertEquals("hash code should match", marker.hashCode(), 0);
+ }
+
+ @Test
+ public void testHashCodeBuilder() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng(10, 12));
+ assertEquals("hash code should match", markerOptions.hashCode(), 0);
+ }
+
+ @Test
+ public void testEquals() {
+ MarkerView markerOne = new MarkerViewOptions().position(new LatLng(0, 0)).getMarker();
+ MarkerView markerTwo = new MarkerViewOptions().position(new LatLng(0, 0)).getMarker();
+ assertEquals(markerOne, markerTwo);
+ }
+
+ @Test
+ public void testEqualsItself() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng(0, 0));
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals("MarkerView should match", marker, marker);
+ assertEquals("MarkerViewOptions should match", markerOptions, markerOptions);
+ }
+
+ @Test
+ public void testNotEquals() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng(0, 0));
+ MarkerView marker = markerOptions.getMarker();
+ assertNotEquals("MarkerViewOptions should match", markerOptions, new Object());
+ assertNotEquals("MarkerView should match", marker, new Object());
+ }
+
+ @Test
+ public void testEqualityBuilder() {
+ MarkerViewOptions markerOne = new MarkerViewOptions().position(new LatLng(0, 0));
+ MarkerViewOptions markerTwo = new MarkerViewOptions().position(new LatLng(0, 0));
+ assertEquals(markerOne, markerTwo);
+ }
+
+ @Test
+ public void testToString() {
+ MarkerView marker = new MarkerViewOptions().position(new LatLng(0, 0)).getMarker();
+ assertEquals(marker.toString(), "MarkerView [position[" + "LatLng [latitude=0.0, longitude=0.0, altitude=0.0]" + "]]");
+ }
+
+ @Test
+ public void testParcelable() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
index 222007df79..a8cca4c3c8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
@@ -1,17 +1,28 @@
package com.mapbox.mapboxsdk.camera;
+import android.content.res.TypedArray;
+import android.os.Parcelable;
+
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MathConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.utils.MathUtils;
+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.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class CameraPositionTest {
+ private static final double DELTA = 1e-15;
+
@Test
public void testSanity() {
LatLng latLng = new LatLng(1, 2);
@@ -20,6 +31,55 @@ public class CameraPositionTest {
}
@Test
+ public void testDefaultTypedArrayBuilder() {
+ TypedArray typedArray = null;
+ CameraPosition cameraPosition = new CameraPosition.Builder(typedArray).build();
+ assertEquals("bearing should match", -1, cameraPosition.bearing, DELTA);
+ assertEquals("latlng should match", null, cameraPosition.target);
+ assertEquals("tilt should match", -1, cameraPosition.tilt, DELTA);
+ assertEquals("zoom should match", -1, cameraPosition.zoom, DELTA);
+ }
+
+ @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.MapView_direction, 0.0f)).thenReturn(bearing);
+ when(typedArray.getFloat(R.styleable.MapView_center_latitude, 0.0f)).thenReturn(latitude);
+ when(typedArray.getFloat(R.styleable.MapView_center_longitude, 0.0f)).thenReturn(longitude);
+ when(typedArray.getFloat(R.styleable.MapView_zoom, 0.0f)).thenReturn(zoom);
+ when(typedArray.getFloat(R.styleable.MapView_tilt, 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 testJniBuilder() {
+ double bearing = 180;
+ double zoom = 12;
+ double latitude = 10;
+ double longitude = 11;
+ double tilt = 44;
+
+ double[] cameraVars = new double[]{latitude, longitude, bearing, tilt, zoom};
+ CameraPosition cameraPosition = new CameraPosition.Builder(cameraVars).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);
@@ -74,4 +134,29 @@ public class CameraPositionTest {
(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);
+ CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9);
+ CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5);
+ CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5);
+ CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5);
+
+ 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);
+ }
+
+ @Test
+ public void testParcelable() {
+ CameraPosition object = new CameraPosition(new LatLng(1, 2), 3, 4, 5);
+ Parcelable parcelable = MockParcel.obtain(object);
+ assertEquals("Parcel should match original object", parcelable, object);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java
new file mode 100644
index 0000000000..078b4184ca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java
@@ -0,0 +1,20 @@
+package com.mapbox.mapboxsdk.constants;
+
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+public class StyleVersionTest {
+
+ private static final double DELTA = 1e-15;
+
+ @Test
+ public void testSanity() {
+ assertEquals("Style version should match, when upgrading, verify that integers.xml is updated",
+ AppConstant.STYLE_VERSION,
+ 9,
+ DELTA);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
index f9cdf29b81..b667940a6f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
@@ -1,6 +1,9 @@
package com.mapbox.mapboxsdk.geometry;
+import android.os.Parcelable;
+
import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;
+import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Before;
import org.junit.Test;
@@ -168,5 +171,13 @@ public class LatLngBoundsTest {
.build());
}
-
-}
+ @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);
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
index 7bf164166c..e184097a43 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
@@ -1,6 +1,8 @@
package com.mapbox.mapboxsdk.geometry;
-import com.mapbox.mapboxsdk.geometry.LatLngSpan;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Test;
@@ -54,4 +56,10 @@ public class LatLngSpanTest {
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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
index e13efb9708..de5dbc5e09 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
@@ -1,18 +1,16 @@
package com.mapbox.mapboxsdk.geometry;
import android.location.Location;
-import android.os.Parcel;
-import com.mapbox.mapboxsdk.geometry.LatLng;
+import android.os.Parcelable;
+
import com.mapbox.mapboxsdk.utils.MockParcel;
+
import org.junit.Test;
-import java.util.Objects;
-import static org.junit.Assert.assertArrayEquals;
+
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.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -164,7 +162,7 @@ public class LatLngTest {
@Test
public void testEqualsItself() {
- LatLng latLng = new LatLng(1,2,3);
+ LatLng latLng = new LatLng(1, 2, 3);
assertEquals("LatLng should match", latLng, latLng);
}
@@ -176,28 +174,9 @@ public class LatLngTest {
@Test
public void testParcelable() {
- LatLng latLng = new LatLng(1, 2, 3);
- Parcel parcel = MockParcel.obtain();
- latLng.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- LatLng parceledLatLng = LatLng.CREATOR.createFromParcel(parcel);
- assertEquals("parcel should match initial object", latLng, parceledLatLng);
- }
-
- @Test
- public void testParcelableArray() {
- LatLng[] latLngs = new LatLng[]{new LatLng(1, 2, 3), new LatLng(1, 2)};
- Parcel parcel = MockParcel.obtain();
- parcel.writeParcelableArray(latLngs, 0);
- parcel.setDataPosition(0);
- LatLng[] parceledLatLngs = (LatLng[]) parcel.readParcelableArray(LatLng.class.getClassLoader());
- assertArrayEquals("parcel should match initial object", latLngs, parceledLatLngs);
- }
-
- @Test
- public void testDescribeContents() {
- LatLng latLng = new LatLng(1.2, 3.4);
- assertEquals("contents should be 0", 0, latLng.describeContents(), DELTA);
+ LatLng latLng = new LatLng(45.0, -185.0);
+ Parcelable parcel = MockParcel.obtain(latLng);
+ assertEquals("parcel should match initial object", latLng, parcel);
}
@Test
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
index bd40221706..5525684601 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
@@ -1,5 +1,9 @@
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;
@@ -48,8 +52,15 @@ public class ProjectedMetersTest {
}
@Test
- public void testToString(){
+ 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);
- assertEquals("toString should match","ProjectedMeters [northing=1.0, easting=1.0]",meters.toString());
+ Parcelable parcel = MockParcel.obtain(meters);
+ assertEquals("parcel should match initial object", meters, parcel);
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
index 0682d0878e..cad268c2ba 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
@@ -1,5 +1,9 @@
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;
@@ -81,4 +85,10 @@ public class VisibleRegionTest {
, 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/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
index 589084d17c..dbde9a4420 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
@@ -1,11 +1,13 @@
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.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Test;
@@ -71,6 +73,12 @@ public class MapboxMapOptionsTest {
}
@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());
@@ -161,6 +169,23 @@ public class MapboxMapOptionsTest {
}
@Test
+ public void testMyLocationForegroundTint() {
+ assertEquals(Color.BLUE, new MapboxMapOptions().myLocationForegroundTintColor(Color.BLUE).getMyLocationForegroundTintColor());
+ }
+
+ @Test
+ public void testMyLocationBackgroundTint() {
+ assertEquals(Color.BLUE, new MapboxMapOptions().myLocationBackgroundTintColor(Color.BLUE).getMyLocationBackgroundTintColor());
+ }
+
+ @Test
+ public void testParceable() {
+ MapboxMapOptions options = new MapboxMapOptions().camera(new CameraPosition.Builder().build()).styleUrl("s").accessToken("a").debugActive(true).compassMargins(new int[]{0, 1, 2, 3});
+ MapboxMapOptions parceled = (MapboxMapOptions) MockParcel.obtain(options);
+ assertEquals(options, parceled);
+ }
+
+ @Test
public void testAccessToken() {
assertNull(new MapboxMapOptions().getAccessToken());
assertEquals("test", new MapboxMapOptions().accessToken("test").getAccessToken());
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
index 1705f44fa4..b69ba4e3f8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -2,7 +2,9 @@ package com.mapbox.mapboxsdk.maps;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.PointF;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.Polygon;
@@ -12,6 +14,7 @@ import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import org.junit.Before;
import org.junit.Test;
@@ -26,6 +29,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -197,7 +201,7 @@ public class MapboxMapTest {
CameraPosition position = new CameraPosition.Builder().bearing(1).tilt(2).zoom(3).target(new LatLng(4, 5)).build();
mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(position));
mMapboxMap.setPadding(0, 0, 0, 0);
- verify(mOnCameraChangeListener, times(2)).onCameraChange(position);
+ verify(mOnCameraChangeListener, times(1)).onCameraChange(position);
}
//
@@ -372,6 +376,58 @@ public class MapboxMapTest {
}
//
+ // Camera - LatLngBounds
+ //
+ @Test
+ public void testLatLngBounds() {
+ LatLng la = new LatLng(34.053940, -118.242622);
+ LatLng ny = new LatLng(40.712730, -74.005953);
+ LatLng centroid = new LatLng(
+ (la.getLatitude() + ny.getLatitude()) / 2,
+ (la.getLongitude() + ny.getLongitude()) / 2);
+
+ Projection projection = mock(Projection.class);
+ when(projection.toScreenLocation(la)).thenReturn(new PointF(20, 20));
+ when(projection.toScreenLocation(ny)).thenReturn(new PointF(100, 100));
+ when(projection.fromScreenLocation(any(PointF.class))).thenReturn(centroid);
+
+ UiSettings uiSettings = mock(UiSettings.class);
+ when(uiSettings.getHeight()).thenReturn(1000f);
+
+ mMapboxMap.setProjection(projection);
+ mMapboxMap.setUiSettings(uiSettings);
+
+ LatLngBounds bounds = new LatLngBounds.Builder().include(la).include(ny).build();
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 1));
+
+ assertEquals("LatLng should be same", centroid, mMapboxMap.getCameraPosition().target);
+ }
+
+
+ //
+ // CameraPositionUpdate - NPX target
+ //
+ @Test
+ public void testCamerePositionUpdateNullTarget() {
+ LatLng latLng = new LatLng(1, 1);
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(null));
+ assertEquals("LatLng should be same", latLng, mMapboxMap.getCameraPosition().target);
+ }
+
+ //
+ // Camera - ScrollBy
+ //
+ @Test
+ public void testScrollBy() {
+ LatLng latLng = new LatLng(1, 1);
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
+ mMapboxMap.moveCamera(CameraUpdateFactory.scrollBy(0, 0));
+ assertEquals("LatLng should be same", latLng, mMapboxMap.getCameraPosition().target);
+ mMapboxMap.moveCamera(CameraUpdateFactory.scrollBy(12, 12));
+ }
+
+ //
// Camera - Zoom
//
@@ -523,7 +579,7 @@ public class MapboxMapTest {
@Test
public void testAddMarkers() {
- List<MarkerOptions> markerList = new ArrayList<>();
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions markerOptions1 = new MarkerOptions().title("a");
MarkerOptions markerOptions2 = new MarkerOptions().title("b");
markerList.add(markerOptions1);
@@ -536,14 +592,14 @@ public class MapboxMapTest {
@Test
public void testAddMarkersEmpty() {
- List<MarkerOptions> markerList = new ArrayList<>();
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
mMapboxMap.addMarkers(markerList);
assertEquals("Markers size should be 0", 0, mMapboxMap.getMarkers().size());
}
@Test
public void testAddMarkersSingleMarker() {
- List<MarkerOptions> markerList = new ArrayList<>();
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions markerOptions = new MarkerOptions().title("a");
markerList.add(markerOptions);
mMapboxMap.addMarkers(markerList);
@@ -686,7 +742,7 @@ public class MapboxMapTest {
@Test
public void testRemoveAnnotations() {
- List<MarkerOptions> markerList = new ArrayList<>();
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions markerOptions1 = new MarkerOptions().title("a");
MarkerOptions markerOptions2 = new MarkerOptions().title("b");
markerList.add(markerOptions1);
@@ -697,8 +753,20 @@ public class MapboxMapTest {
}
@Test
+ public void testClear() {
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a");
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b");
+ markerList.add(markerOptions1);
+ markerList.add(markerOptions2);
+ mMapboxMap.addMarkers(markerList);
+ mMapboxMap.clear();
+ assertTrue("Annotations should be empty", mMapboxMap.getAnnotations().isEmpty());
+ }
+
+ @Test
public void testRemoveAnnotationsByList() {
- List<MarkerOptions> markerList = new ArrayList<>();
+ List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions markerOptions1 = new MarkerOptions().title("a");
MarkerOptions markerOptions2 = new MarkerOptions().title("b");
markerList.add(markerOptions1);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java
new file mode 100644
index 0000000000..71d61a3d4b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java
@@ -0,0 +1,75 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class StyleInitializerTest {
+
+ @InjectMocks
+ Context context = mock(Context.class);
+
+ @InjectMocks
+ Resources resources = mock(Resources.class);
+
+ MapView.StyleInitializer initializer;
+
+ @Before
+ public void beforeTest() {
+ MockitoAnnotations.initMocks(this);
+ when(context.getResources()).thenReturn(resources);
+ when(resources.getInteger(R.integer.style_version)).thenReturn(AppConstant.STYLE_VERSION);
+ initializer = new MapView.StyleInitializer(context);
+ }
+
+ @Test
+ public void testSanity() {
+ assertNotNull("initializer should not be null", initializer);
+ }
+
+ @Test
+ public void testDefaultStyle() {
+ assertTrue(initializer.isDefaultStyle());
+ assertEquals(String.format(Locale.US, "mapbox://styles/mapbox/streets-v%d", AppConstant.STYLE_VERSION), "mapbox://styles/mapbox/streets-v9");
+ }
+
+ @Test
+ public void testUpdateStyle() {
+ String customStyle = "test";
+ initializer.setStyle(customStyle);
+ assertFalse(initializer.isDefaultStyle());
+ assertEquals(customStyle, initializer.getStyle());
+ }
+
+ @Test
+ public void testUpdateStyleNull() {
+ String customStyle = null;
+ initializer.setStyle(customStyle);
+ assertTrue(initializer.isDefaultStyle());
+ assertEquals(String.format(Locale.US, "mapbox://styles/mapbox/streets-v%d", AppConstant.STYLE_VERSION), "mapbox://styles/mapbox/streets-v9");
+ }
+
+ @Test
+ public void testOverrideDefaultStyle() {
+ String customStyle = "test";
+ initializer.setStyle(customStyle, true);
+ assertTrue(initializer.isDefaultStyle());
+ assertEquals(customStyle, initializer.getStyle());
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
index 4af44a3f49..300ed1d73a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.maps;
import android.view.Gravity;
+import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
@@ -9,42 +10,45 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class UiSettingsTest {
@InjectMocks
MapView mMapView = mock(MapView.class);
+ UiSettings uiSettings;
+
+ @Before
+ public void beforeTest() {
+ uiSettings = new UiSettings(mMapView);
+ }
+
@Test
public void testSanity() {
- UiSettings uiSettings = new UiSettings(mMapView);
assertNotNull("uiSettings should not be null", uiSettings);
}
@Test
public void testCompassEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setCompassEnabled(true);
assertEquals("Compass should be enabled", true, uiSettings.isCompassEnabled());
}
@Test
public void testCompassDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setCompassEnabled(false);
assertEquals("Compass should be disabled", false, uiSettings.isCompassEnabled());
}
@Test
public void testCompassGravity() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setCompassGravity(Gravity.LEFT);
assertEquals("Compass gravity should be same", Gravity.LEFT, uiSettings.getCompassGravity());
}
@Test
public void testCompassMargins() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setCompassMargins(1, 2, 3, 4);
assertTrue("Compass margin left should be same", uiSettings.getCompassMarginLeft() == 1);
assertTrue("Compass margin top should be same", uiSettings.getCompassMarginTop() == 2);
@@ -54,28 +58,24 @@ public class UiSettingsTest {
@Test
public void testLogoEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setLogoEnabled(true);
assertEquals("Logo should be enabled", true, uiSettings.isLogoEnabled());
}
@Test
public void testLogoDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setLogoEnabled(false);
assertEquals("Logo should be disabled", false, uiSettings.isLogoEnabled());
}
@Test
public void testLogoGravity() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setLogoGravity(Gravity.RIGHT);
assertEquals("Logo gravity should be same", Gravity.RIGHT, uiSettings.getLogoGravity());
}
@Test
public void testLogoMargins() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setLogoMargins(1, 2, 3, 4);
assertTrue("Compass margin left should be same", uiSettings.getLogoMarginLeft() == 1);
assertTrue("Compass margin top should be same", uiSettings.getLogoMarginTop() == 2);
@@ -85,28 +85,24 @@ public class UiSettingsTest {
@Test
public void testAttributionEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAttributionEnabled(true);
assertEquals("Attribution should be enabled", true, uiSettings.isAttributionEnabled());
}
@Test
public void testAttributionDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAttributionEnabled(false);
assertEquals("Attribution should be disabled", false, uiSettings.isLogoEnabled());
}
@Test
public void testAttributionGravity() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAttributionGravity(Gravity.RIGHT);
assertEquals("Attribution gravity should be same", Gravity.RIGHT, uiSettings.getAttributionGravity());
}
@Test
public void testAttributionMargins() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAttributionMargins(1, 2, 3, 4);
assertTrue("Attribution margin left should be same", uiSettings.getAttributionMarginLeft() == 1);
assertTrue("Attribution margin top should be same", uiSettings.getAttributionMarginTop() == 2);
@@ -116,77 +112,158 @@ public class UiSettingsTest {
@Test
public void testRotateGesturesEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setRotateGesturesEnabled(true);
assertEquals("Rotate gesture should be enabled", true, uiSettings.isRotateGesturesEnabled());
}
@Test
public void testRotateGesturesDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setRotateGesturesEnabled(false);
assertEquals("Rotate gesture should be disabled", false, uiSettings.isRotateGesturesEnabled());
}
@Test
+ public void testRotateGestureChange() {
+ assertEquals("Default state should be true", true, uiSettings.isRotateGestureChangeAllowed());
+ uiSettings.setRotateGestureChangeAllowed(false);
+ assertEquals("State should have been changed", false, uiSettings.isRotateGestureChangeAllowed());
+ }
+
+ @Test
+ public void testRotateGestureChangeAllowed() {
+ uiSettings.setRotateGesturesEnabled(false);
+ assertEquals("Rotate gesture should be false", false, uiSettings.isRotateGesturesEnabled());
+ uiSettings.setRotateGesturesEnabled(true);
+ assertEquals("Rotate gesture should be true", true, uiSettings.isRotateGesturesEnabled());
+ }
+
+ @Test
+ public void testRotateGestureChangeDisallowed() {
+ assertEquals("Rotate gesture should be true", true, uiSettings.isRotateGesturesEnabled());
+ uiSettings.setRotateGestureChangeAllowed(false);
+ uiSettings.setRotateGesturesEnabled(false);
+ assertEquals("Rotate gesture change should be ignored", true, uiSettings.isRotateGesturesEnabled());
+ }
+
+ @Test
public void testTiltGesturesEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setTiltGesturesEnabled(true);
assertEquals("Tilt gesture should be enabled", true, uiSettings.isTiltGesturesEnabled());
}
@Test
public void testTiltGesturesDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setTiltGesturesEnabled(false);
assertEquals("Tilt gesture should be disabled", false, uiSettings.isTiltGesturesEnabled());
}
@Test
+ public void testTiltGestureChange() {
+ assertEquals("Default state should be true", true, uiSettings.isTiltGestureChangeAllowed());
+ uiSettings.setTiltGestureChangeAllowed(false);
+ assertEquals("State should have been changed", false, uiSettings.isTiltGestureChangeAllowed());
+ }
+
+ @Test
+ public void testTiltGestureChangeAllowed() {
+ uiSettings.setTiltGesturesEnabled(false);
+ assertEquals("Tilt gesture should be false", false, uiSettings.isTiltGesturesEnabled());
+ uiSettings.setTiltGesturesEnabled(true);
+ assertEquals("Tilt gesture should be true", true, uiSettings.isTiltGesturesEnabled());
+ }
+
+ @Test
+ public void testTiltGestureChangeDisallowed() {
+ assertEquals("Tilt gesture should be true", true, uiSettings.isTiltGesturesEnabled());
+ uiSettings.setTiltGestureChangeAllowed(false);
+ uiSettings.setTiltGesturesEnabled(false);
+ assertEquals("Tilt gesture change should be ignored", true, uiSettings.isTiltGesturesEnabled());
+ }
+
+ @Test
public void testZoomGesturesEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setZoomGesturesEnabled(true);
assertEquals("Zoom gesture should be enabled", true, uiSettings.isZoomGesturesEnabled());
}
@Test
public void testZoomGesturesDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setZoomGesturesEnabled(false);
assertEquals("Zoom gesture should be disabled", false, uiSettings.isZoomGesturesEnabled());
}
@Test
+ public void testZoomGestureChange() {
+ assertEquals("Default state should be true", true, uiSettings.isZoomGestureChangeAllowed());
+ uiSettings.setZoomGestureChangeAllowed(false);
+ assertEquals("State should have been changed", false, uiSettings.isZoomGestureChangeAllowed());
+ }
+
+ @Test
+ public void testZoomGestureChangeAllowed() {
+ uiSettings.setZoomGesturesEnabled(false);
+ assertEquals("Zoom gesture should be false", false, uiSettings.isZoomGesturesEnabled());
+ uiSettings.setZoomGesturesEnabled(true);
+ assertEquals("Zoom gesture should be true", true, uiSettings.isZoomGesturesEnabled());
+ }
+
+ @Test
+ public void testZoomGestureChangeDisallowed() {
+ assertEquals("Zoom gesture should be true", true, uiSettings.isZoomGesturesEnabled());
+ uiSettings.setZoomGestureChangeAllowed(false);
+ uiSettings.setZoomGesturesEnabled(false);
+ assertEquals("Zooom gesture change should be ignored", true, uiSettings.isZoomGesturesEnabled());
+ }
+
+ @Test
public void testZoomControlsEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setZoomControlsEnabled(true);
assertEquals("Zoom controls should be enabled", true, uiSettings.isZoomControlsEnabled());
}
@Test
public void testZoomControlsDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setZoomControlsEnabled(false);
assertEquals("Zoom controls should be disabled", false, uiSettings.isZoomControlsEnabled());
}
@Test
public void testScrollGesturesEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setScrollGesturesEnabled(true);
assertEquals("Scroll gesture should be enabled", true, uiSettings.isScrollGesturesEnabled());
}
@Test
public void testScrollGesturesDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setScrollGesturesEnabled(false);
assertEquals("Scroll gesture should be disabled", false, uiSettings.isScrollGesturesEnabled());
}
@Test
+ public void testScrollGestureChange() {
+ assertEquals("Default state should be true", true, uiSettings.isScrollGestureChangeAllowed());
+ uiSettings.setScrollGestureChangeAllowed(false);
+ assertEquals("State should have been changed", false, uiSettings.isScrollGestureChangeAllowed());
+ }
+
+ @Test
+ public void testScrollGestureChangeAllowed() {
+ uiSettings.setScrollGesturesEnabled(false);
+ assertEquals("Scroll gesture should be false", false, uiSettings.isScrollGesturesEnabled());
+ uiSettings.setScrollGesturesEnabled(true);
+ assertEquals("Scroll gesture should be true", true, uiSettings.isScrollGesturesEnabled());
+ }
+
+ @Test
+ public void testScrollGestureChangeDisallowed() {
+ assertEquals("Scroll gesture should be true", true, uiSettings.isScrollGesturesEnabled());
+ uiSettings.setScrollGestureChangeAllowed(false);
+ uiSettings.setScrollGesturesEnabled(false);
+ assertEquals("Scroll gesture change should be ignored", true, uiSettings.isScrollGesturesEnabled());
+ }
+
+ @Test
public void testAllGesturesEnabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAllGesturesEnabled(true);
assertEquals("Rotate gesture should be enabled", true, uiSettings.isRotateGesturesEnabled());
assertEquals("Tilt gesture should be enabled", true, uiSettings.isTiltGesturesEnabled());
@@ -196,7 +273,6 @@ public class UiSettingsTest {
@Test
public void testAllGesturesDisabled() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.setAllGesturesEnabled(false);
assertEquals("Rotate gesture should be enabled", false, uiSettings.isRotateGesturesEnabled());
assertEquals("Tilt gesture should be disabled", false, uiSettings.isTiltGesturesEnabled());
@@ -206,8 +282,18 @@ public class UiSettingsTest {
@Test
public void testInvalidate() {
- UiSettings uiSettings = new UiSettings(mMapView);
uiSettings.invalidate();
}
+ @Test
+ public void testHeight() {
+ when(mMapView.getMeasuredHeight()).thenReturn(1);
+ assertEquals("height should be same as mocked instance", 1, uiSettings.getHeight(), 0);
+ }
+
+ @Test
+ public void testWidth() {
+ when(mMapView.getMeasuredWidth()).thenReturn(1);
+ assertEquals("width should be same as mocked instance", 1, uiSettings.getWidth(), 0);
+ }
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
new file mode 100644
index 0000000000..a03eb6acae
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
@@ -0,0 +1,79 @@
+package com.mapbox.mapboxsdk.maps.widgets;
+
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+
+import java.util.Arrays;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class MyLocationViewSettingsTest {
+
+ @InjectMocks
+ MapView mMapView = mock(MapView.class);
+
+ @InjectMocks
+ MyLocationView myLocationView = mock(MyLocationView.class);
+
+ MyLocationViewSettings locationViewSettings;
+
+ @Before
+ public void beforeTest() {
+ locationViewSettings = new MyLocationViewSettings(mMapView, myLocationView);
+ }
+
+ @Test
+ public void testSanity() {
+ assertNotNull("should not be null", locationViewSettings);
+ }
+
+ @Test
+ public void testForegroundDrawables() {
+ Drawable foregroundDrawable = mock(Drawable.class);
+ Drawable foregroundBearingDrawable = mock(Drawable.class);
+ locationViewSettings.setForegroundDrawable(foregroundDrawable, foregroundBearingDrawable);
+ assertEquals("foreground should match", foregroundDrawable, locationViewSettings.getForegroundDrawable());
+ assertEquals("foreground bearing should match", foregroundBearingDrawable, locationViewSettings.getForegroundBearingDrawable());
+ }
+
+ @Test
+ public void testBackgroundDrawable() {
+ Drawable backgroundDrawable = mock(Drawable.class);
+ int[] offset = new int[]{1, 2, 3, 4};
+ locationViewSettings.setBackgroundDrawable(backgroundDrawable, offset);
+ assertEquals("foreground should match", backgroundDrawable, locationViewSettings.getBackgroundDrawable());
+ assertTrue("offsets should match", Arrays.equals(offset, locationViewSettings.getBackgroundOffset()));
+ }
+
+ @Test
+ public void testForegroundTint() {
+ int color = Color.RED;
+ locationViewSettings.setForegroundTintColor(Color.RED);
+ assertEquals("color should match", color, locationViewSettings.getForegroundTintColor());
+ }
+
+ @Test
+ public void testBackgroundTint() {
+ int color = Color.RED;
+ locationViewSettings.setBackgroundTintColor(Color.RED);
+ assertEquals("color should match", color, locationViewSettings.getBackgroundTintColor());
+ }
+
+ @Test
+ public void testEnabled() {
+ assertFalse("initial state should be false", locationViewSettings.isEnabled());
+ locationViewSettings.setEnabled(true);
+ assertTrue("state should be true", locationViewSettings.isEnabled());
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
new file mode 100644
index 0000000000..8be0d2c663
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
@@ -0,0 +1,17 @@
+package com.mapbox.mapboxsdk.telemetry;
+
+import org.junit.Test;
+import okhttp3.internal.Util;
+import static junit.framework.Assert.assertEquals;
+
+public class HttpTransportTest {
+
+ @Test
+ public void testNonAsciiUserAgent() {
+
+ final String swedishUserAgent = "Sveriges Fjäll/1.0/1 MapboxEventsAndroid/4.0.0-SNAPSHOT";
+ final String asciiVersion = "Sveriges Fj?ll/1.0/1 MapboxEventsAndroid/4.0.0-SNAPSHOT";
+
+ assertEquals("asciiVersion and swedishUserAgent should match", asciiVersion, Util.toHumanReadableAscii(swedishUserAgent));
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
index 9badde848e..aa0a4edd13 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
@@ -1,17 +1,24 @@
package com.mapbox.mapboxsdk.utils;
import android.os.Parcel;
-
-import com.mapbox.mapboxsdk.geometry.LatLng;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyByte;
import static org.mockito.Matchers.anyDouble;
+import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
@@ -22,97 +29,227 @@ import static org.mockito.Mockito.when;
public class MockParcel {
- public static Parcel obtain() {
- return new MockParcel().getMockedParcel();
+ public static Parcelable obtain(@NonNull Parcelable object) {
+ return obtain(object, 0);
}
- Parcel mockedParcel;
- int position;
- List<Object> objects;
+ public static Parcelable obtain(@NonNull Parcelable object, int describeContentsValue) {
+ testDescribeContents(object, describeContentsValue);
+ testParcelableArray(object);
+ return testParcelable(object);
+ }
- public Parcel getMockedParcel() {
- return mockedParcel;
+ public static Parcelable testParcelable(@NonNull Parcelable object) {
+ Parcel parcel = ParcelMocker.obtain(object);
+ object.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ try {
+ Field field = object.getClass().getDeclaredField("CREATOR");
+ field.setAccessible(true);
+ Class<?> creatorClass = field.getType();
+ Object fieldValue = field.get(object);
+ Method myMethod = creatorClass.getDeclaredMethod("createFromParcel", Parcel.class);
+ return (Parcelable) myMethod.invoke(fieldValue, parcel);
+ } catch (Exception e) {
+ return null;
+ }
}
- public MockParcel() {
- mockedParcel = mock(Parcel.class);
- objects = new ArrayList<>();
- setupMock();
+ public static void testParcelableArray(@NonNull Parcelable object) {
+ Parcelable[] objects = new Parcelable[]{object};
+ Parcel parcel = ParcelMocker.obtain(objects);
+ parcel.writeParcelableArray(objects, 0);
+ parcel.setDataPosition(0);
+ Parcelable[] parcelableArray = parcel.readParcelableArray(object.getClass().getClassLoader());
+ assertArrayEquals("parcel should match initial object", objects, parcelableArray);
}
- private void setupMock() {
- setupWrites();
- setupReads();
- setupOthers();
+ public static void testDescribeContents(@NonNull Parcelable object, int describeContentsValue) {
+ if (describeContentsValue == 0) {
+ assertEquals("\nExpecting a describeContents() value of 0 for a " + object.getClass().getSimpleName() + " instance." +
+ "\nYou can provide a different value for describeContentValue through the obtain method.",
+ 0,
+ object.describeContents());
+ } else {
+ assertEquals("Expecting a describeContents() value of " + describeContentsValue,
+ describeContentsValue,
+ object.describeContents());
+ }
}
- private void setupWrites() {
- Answer<Void> writeValueAnswer = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object parameter = invocation.getArguments()[0];
- objects.add(parameter);
- return null;
- }
- };
- Answer<Void> writeArrayAnswer = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] parameters = (Object[]) invocation.getArguments()[0];
- objects.add(parameters.length);
- for (Object o : parameters) {
- objects.add(o);
- }
- return null;
- }
- };
+ private static class ParcelMocker {
- doAnswer(writeValueAnswer).when(mockedParcel).writeLong(anyLong());
- doAnswer(writeValueAnswer).when(mockedParcel).writeString(anyString());
- doAnswer(writeValueAnswer).when(mockedParcel).writeDouble(anyDouble());
- doAnswer(writeArrayAnswer).when(mockedParcel).writeParcelableArray(any(LatLng[].class), eq(0));
- }
+ public static Parcel obtain(@NonNull Parcelable target) {
+ Parcel parcel = new ParcelMocker(target).getMockedParcel();
+ target.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return parcel;
+ }
- private void setupReads() {
- when(mockedParcel.readLong()).thenAnswer(new Answer<Long>() {
- @Override
- public Long answer(InvocationOnMock invocation) throws Throwable {
- return (Long) objects.get(position++);
- }
- });
- when(mockedParcel.readString()).thenAnswer(new Answer<String>() {
- @Override
- public String answer(InvocationOnMock invocation) throws Throwable {
- return (String) objects.get(position++);
+ public static Parcel obtain(@NonNull Parcelable[] targets) {
+ if (targets.length == 0) {
+ throw new IllegalArgumentException("The passed argument may not be empty");
}
- });
- when(mockedParcel.readDouble()).thenAnswer(new Answer<Double>() {
- @Override
- public Double answer(InvocationOnMock invocation) throws Throwable {
- return (Double) objects.get(position++);
- }
- });
- when(mockedParcel.readParcelableArray(LatLng.class.getClassLoader())).thenAnswer(new Answer<LatLng[]>() {
- @Override
- public LatLng[] answer(InvocationOnMock invocation) throws Throwable {
- int size = (Integer) objects.get(position++);
- LatLng[] latLngs = LatLng.CREATOR.newArray(size);
- for (int i = 0; i < size; i++) {
- latLngs[i] = (LatLng) objects.get(position++);
+ Parcel parcel = new ParcelMocker(targets[0]).getMockedParcel();
+ parcel.writeParcelableArray(targets, 0);
+ parcel.setDataPosition(0);
+ return parcel;
+ }
+
+ private List<Object> objects;
+ private Object object;
+ private Parcel mockedParcel;
+ private int position;
+
+ private ParcelMocker(Object o) {
+ this.object = o;
+ mockedParcel = mock(Parcel.class);
+ objects = new ArrayList<>();
+ setupMock();
+ }
+
+ private Parcel getMockedParcel() {
+ return mockedParcel;
+ }
+
+ private void setupMock() {
+ setupWrites();
+ setupReads();
+ setupOthers();
+ }
+
+ private void setupWrites() {
+ Answer<Void> writeValueAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object parameter = invocation.getArguments()[0];
+ objects.add(parameter);
+ return null;
}
- return latLngs;
- }
- });
- }
-
- private void setupOthers() {
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- position = ((Integer) invocation.getArguments()[0]);
- return null;
- }
- }).when(mockedParcel).setDataPosition(anyInt());
+ };
+ Answer<Void> writeArrayAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object[] parameters = (Object[]) invocation.getArguments()[0];
+ objects.add(parameters.length);
+ for (Object o : parameters) {
+ objects.add(o);
+ }
+ return null;
+ }
+ };
+ Answer<Void> writeIntArrayAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ int[] parameters = (int[]) invocation.getArguments()[0];
+ if (parameters != null) {
+ objects.add(parameters.length);
+ for (Object o : parameters) {
+ objects.add(o);
+ }
+ } else {
+ objects.add(-1);
+ }
+ return null;
+ }
+ };
+ doAnswer(writeValueAnswer).when(mockedParcel).writeByte(anyByte());
+ doAnswer(writeValueAnswer).when(mockedParcel).writeLong(anyLong());
+ doAnswer(writeValueAnswer).when(mockedParcel).writeString(anyString());
+ doAnswer(writeValueAnswer).when(mockedParcel).writeInt(anyInt());
+ doAnswer(writeIntArrayAnswer).when(mockedParcel).writeIntArray(any(int[].class));
+ doAnswer(writeValueAnswer).when(mockedParcel).writeDouble(anyDouble());
+ doAnswer(writeValueAnswer).when(mockedParcel).writeFloat(anyFloat());
+ doAnswer(writeValueAnswer).when(mockedParcel).writeParcelable(any(Parcelable.class), eq(0));
+ doAnswer(writeArrayAnswer).when(mockedParcel).writeParcelableArray(any(Parcelable[].class), eq(0));
+ }
+
+ private void setupReads() {
+ when(mockedParcel.readInt()).then(new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ return (Integer) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readByte()).thenAnswer(new Answer<Byte>() {
+ @Override
+ public Byte answer(InvocationOnMock invocation) throws Throwable {
+ return (Byte) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readLong()).thenAnswer(new Answer<Long>() {
+ @Override
+ public Long answer(InvocationOnMock invocation) throws Throwable {
+ return (Long) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readString()).thenAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ return (String) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readDouble()).thenAnswer(new Answer<Double>() {
+ @Override
+ public Double answer(InvocationOnMock invocation) throws Throwable {
+ return (Double) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readFloat()).thenAnswer(new Answer<Float>() {
+ @Override
+ public Float answer(InvocationOnMock invocation) throws Throwable {
+ return (Float) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readParcelable(Parcelable.class.getClassLoader())).thenAnswer(new Answer<Parcelable>() {
+ @Override
+ public Parcelable answer(InvocationOnMock invocation) throws Throwable {
+ return (Parcelable) objects.get(position++);
+ }
+ });
+ when(mockedParcel.readParcelableArray(Parcelable.class.getClassLoader())).thenAnswer(new Answer<Object[]>() {
+ @Override
+ public Object[] answer(InvocationOnMock invocation) throws Throwable {
+ int size = (Integer) objects.get(position++);
+ Field field = object.getClass().getDeclaredField("CREATOR");
+ field.setAccessible(true);
+ Class<?> creatorClass = field.getType();
+ Object fieldValue = field.get(object);
+ Method myMethod = creatorClass.getDeclaredMethod("newArray", int.class);
+ Object[] array = (Object[]) myMethod.invoke(fieldValue, size);
+ for (int i = 0; i < size; i++) {
+ array[i] = objects.get(position++);
+ }
+ return array;
+ }
+ });
+ when(mockedParcel.createIntArray()).then(new Answer<int[]>() {
+ @Override
+ public int[] answer(InvocationOnMock invocation) throws Throwable {
+ int size = (Integer) objects.get(position++);
+ if (size == -1) {
+ return null;
+ }
+
+ int[] array = new int[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = (Integer) objects.get(position++);
+ }
+
+ return array;
+ }
+ });
+ }
+
+ private void setupOthers() {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ position = ((Integer) invocation.getArguments()[0]);
+ return null;
+ }
+ }).when(mockedParcel).setDataPosition(anyInt());
+ }
}
-
}
diff --git a/platform/android/README.md b/platform/android/README.md
index 135eb87a93..895cf84688 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -4,14 +4,20 @@
A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Java applications on Android devices.
-This repository is for day-to-day development of the SDK. Building the SDK yourself requires [a number of dependencies and steps](../../INSTALL.md) that are unnecessary for developing production applications. For production applications, please consider installing an official, prebuilt release instead; see the [Mapbox Android SDK website](https://www.mapbox.com/android-sdk/) for installation instructions.
+**To install and use the Mapbox Android SDK in an application, see the [Mapbox Android SDK website](https://www.mapbox.com/android-sdk/).**
-## Installing the SDK
+[![](https://www.mapbox.com/android-sdk/images/splash.jpg)](https://www.mapbox.com/android-sdk/)
-* [Installing the Mapbox Android SDK on Linux](INSTALL_LINUX.md)
-* [Installing the Mapbox Android SDK on OS X](INSTALL_OSX.md)
+## Contributing to the SDK
-## Setting up the Android emulator
+**These instructions are for developers interested in making code-level contributions to the SDK itself. If you instead want to use the SDK in your app, see above.**
+
+Building the SDK yourself requires [a number of dependencies and steps](../../INSTALL.md) that are unnecessary for developing production applications.
+
+* [Contributing on Linux](CONTRIBUTING_LINUX.md)
+* [Contributing on OS X](CONTRIBUTING_OSX.md)
+
+### Setting up the Android emulator
The Mapbox Android SDK requires Android 4.0.3+ (API level 15+).
@@ -35,7 +41,7 @@ In Android Studio, create an x86 AVD (Android Virtual Device):
Now when you run or debug the Android project you will see a window "Choose Device". Select your new AVD from drop down under "Launch emulator". If you select "Use same device for future launches" Android Studio will remember the selection and not ask again.
-## Running Mapbox GL Native on a hardware Android device
+### Running Mapbox GL Native on a hardware Android device
The Mapbox Android SDK requires Android 4.0.3+ (API level 15+).
diff --git a/platform/android/bitrise.yml b/platform/android/bitrise.yml
index 9a7dce6f85..3c06a04675 100644
--- a/platform/android/bitrise.yml
+++ b/platform/android/bitrise.yml
@@ -24,29 +24,17 @@ workflows:
envman add --key SKIPCI --value false
fi
- script:
- title: Install Linux Dependencies
- inputs:
- - content: |-
- #!/bin/bash
-
- apt-get install -y pkg-config
- - script:
- title: Build Android SDK For arm
- inputs:
- - content: |-
- #!/bin/bash
-
- make android -j4
- - is_debug: 'yes'
- - script:
- title: Run Unit Tests
+ title: Build
+ run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- content: |-
#!/bin/bash
-
- cd platform/android
- ./gradlew testReleaseUnitTest --continue
- - is_debug: 'yes'
+ set -eu -o pipefail
+ curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
+ sudo apt-get install -y pkg-config nodejs
+ export BUILDTYPE=Release
+ make android
+ make test-android
- slack:
title: Post to Slack
run_if: '{{enveq "SKIPCI" "false"}}'
@@ -68,47 +56,26 @@ workflows:
scheduled:
steps:
- script:
- title: Install Linux Dependencies
+ title: Build
inputs:
- content: |-
#!/bin/bash
+ set -eu -o pipefail
- apt-get install -y pkg-config python-pip python-dev build-essential
+ curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
+ apt-get install -y pkg-config python-pip python-dev build-essential nodejs
pip install awscli
- - script:
- title: Fetch GPG Secring For SDK Signing
- inputs:
- - content: |-
- #!/bin/bash
aws s3 cp s3://mapbox/android/signing-credentials/secring.gpg platform/android/MapboxGLAndroidSDK/secring.gpg
- opts:
- is_expand: true
- - script:
- title: Inject Signing And Publishing Credentials
- inputs:
- - content: |-
- #!/bin/bash
echo "NEXUS_USERNAME=$PUBLISH_NEXUS_USERNAME
NEXUS_PASSWORD=$PUBLISH_NEXUS_PASSWORD
signing.keyId=$SIGNING_KEYID
signing.password=$SIGNING_PASSWORD
signing.secretKeyRingFile=secring.gpg" >> platform/android/MapboxGLAndroidSDK/gradle.properties
- opts:
- is_expand: true
- - script:
- title: Build Mapbox Android SDK For All ABI
- inputs:
- - content: |-
- #!/bin/bash
+ export BUILDTYPE=Release
make apackage -j4
- - script:
- title: Publish To Maven Central
- inputs:
- - content: |-
- #!/bin/bash
cd platform/android
./gradlew uploadArchives
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index f3a1cc06ba..23aef9d1e6 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -4,10 +4,11 @@
buildscript {
repositories {
jcenter()
+ maven { url 'https://jitpack.io' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.5.0'
- classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
+ classpath 'com.android.tools.build:gradle:2.1.0'
+ classpath 'com.github.JakeWharton:sdk-manager-plugin:220bf7a88a7072df3ed16dc8466fb144f2817070'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -22,5 +23,5 @@ allprojects {
}
task wrapper(type: Wrapper) {
- gradleVersion = '2.10'
+ gradleVersion = '2.12'
}
diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties
index b4ada9d74b..a630852d20 100644
--- a/platform/android/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
-#Fri Jan 15 17:23:43 CET 2016
+#Thu Apr 07 14:21:05 CDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
-distributionSha256Sum=66406247f745fc6f05ab382d3f8d3e120c339f34ef54b86f6dc5f6efc18fbb13
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip
+distributionSha256Sum=e77064981906cd0476ff1e0de3e6fef747bd18e140960f1915cca8ff6c33ab5c
diff --git a/platform/android/mapboxgl-app.gypi b/platform/android/mapboxgl-app.gypi
deleted file mode 100644
index 5e0e653a6f..0000000000
--- a/platform/android/mapboxgl-app.gypi
+++ /dev/null
@@ -1,149 +0,0 @@
-{
- 'includes': [
- '../../gyp/common.gypi',
- ],
- 'targets': [
- { 'target_name': 'android-lib',
- 'product_name': 'mapbox-gl',
- 'type': 'shared_library',
- 'hard_dependency': 1,
-
- 'dependencies': [
- 'mbgl.gyp:core',
- 'mbgl.gyp:platform-<(platform_lib)',
- 'mbgl.gyp:http-<(http_lib)',
- 'mbgl.gyp:asset-<(asset_lib)',
- ],
-
- 'include_dirs': [
- '../src',
- ],
-
- 'sources': [
- './src/native_map_view.cpp',
- './src/jni.cpp',
- './src/attach_env.cpp',
- ],
-
- 'cflags_cc': [
- '<@(boost_cflags)',
- '<@(variant_cflags)',
- '<@(jni.hpp_cflags)',
- ],
- 'libraries': [
- '<@(libpng_static_libs)',
- '<@(libjpeg-turbo_static_libs)',
- '<@(sqlite_static_libs)',
- '<@(libuv_static_libs)',
- '<@(nunicode_static_libs)',
- '<@(libzip_static_libs)',
- ],
- 'variables': {
- 'ldflags': [
- '-llog',
- '-landroid',
- '-lEGL',
- '-lGLESv2',
- '-lstdc++',
- '-latomic',
- '<@(libpng_ldflags)',
- '<@(libjpeg-turbo_ldflags)',
- '<@(sqlite_ldflags)',
- '<@(zlib_ldflags)',
- '<@(libzip_ldflags)',
- ],
- },
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
- }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ],
- },
-
- { 'target_name': 'example-custom-layer-lib',
- 'product_name': 'example-custom-layer',
- 'type': 'shared_library',
- 'hard_dependency': 1,
-
- 'sources': [
- './src/example_custom_layer.cpp',
- ],
-
- 'include_dirs': [
- '../../include',
- ],
-
- 'variables': {
- 'ldflags': [
- '-llog',
- '-landroid',
- '-lEGL',
- '-lGLESv2',
- '-lstdc++',
- '-latomic',
- ],
- },
-
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
- }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ],
- },
-
- { 'target_name': 'androidapp',
- 'type': 'none',
- 'hard_dependency': 1,
-
- 'dependencies': [
- 'android-lib',
- 'example-custom-layer-lib',
- ],
-
- 'variables': {
- 'pwd': '<!(pwd)',
- },
-
- 'copies': [
- {
- 'files': [
- '../../common/ca-bundle.crt',
- '../../platform/default/resources/api_mapbox_com-digicert.der',
- '../../platform/default/resources/api_mapbox_com-geotrust.der',
- '../../platform/default/resources/star_tilestream_net.der',
- ],
- 'destination': '<(pwd)/../platform/android/MapboxGLAndroidSDK/src/main/assets'
- },
- {
- 'files': [
- '<(PRODUCT_DIR)/obj.target'
- ],
- 'destination': '<(pwd)/../platform/android/MapboxGLAndroidSDK/src/main'
- },
- ],
-
- 'actions': [
- {
- 'action_name': 'Strip mapbox library',
- 'inputs': [ '<(PRODUCT_DIR)/lib.target/libmapbox-gl.so' ],
- 'outputs': [ '<(pwd)/../platform/android/MapboxGLAndroidSDK/src/main/jniLibs/$(JNIDIR)/libmapbox-gl.so' ],
- 'action': [ '$(STRIP)', '<@(_inputs)', '-o', '<@(_outputs)' ]
- },
- {
- 'action_name': 'Strip example custom layer library',
- 'inputs': [ '<(PRODUCT_DIR)/lib.target/libexample-custom-layer.so' ],
- 'outputs': [ '<(pwd)/../platform/android/MapboxGLAndroidSDKTestApp/src/main/jniLibs/$(JNIDIR)/libexample-custom-layer.so' ],
- 'action': [ '$(STRIP)', '<@(_inputs)', '-o', '<@(_outputs)' ]
- }
- ],
- },
- ],
-}
diff --git a/platform/android/platform.gyp b/platform/android/platform.gyp
new file mode 100644
index 0000000000..964492a21e
--- /dev/null
+++ b/platform/android/platform.gyp
@@ -0,0 +1,151 @@
+{
+ 'variables': {
+ 'loop_lib': 'android',
+ 'headless_lib': 'none',
+ 'coverage': 0,
+ },
+ 'includes': [
+ '../../mbgl.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'platform-lib',
+ 'product_name': 'mapbox-gl',
+ 'type': 'shared_library',
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'core',
+ ],
+
+ 'include_dirs': [
+ '../default',
+ '../../include',
+ '../../src', # TODO: eliminate
+ ],
+
+ 'sources': [
+ 'src/native_map_view.cpp',
+ 'src/jni.cpp',
+ 'src/attach_env.cpp',
+ 'src/log_android.cpp',
+ 'src/http_file_source.cpp',
+ 'src/asset_file_source.cpp',
+ '../default/thread.cpp',
+ '../default/string_stdlib.cpp',
+ '../default/image.cpp',
+ '../default/png_reader.cpp',
+ '../default/jpeg_reader.cpp',
+ '../default/default_file_source.cpp',
+ '../default/online_file_source.cpp',
+ '../default/mbgl/storage/offline.hpp',
+ '../default/mbgl/storage/offline.cpp',
+ '../default/mbgl/storage/offline_database.hpp',
+ '../default/mbgl/storage/offline_database.cpp',
+ '../default/mbgl/storage/offline_download.hpp',
+ '../default/mbgl/storage/offline_download.cpp',
+ '../default/sqlite3.hpp',
+ '../default/sqlite3.cpp',
+ ],
+
+ 'cflags_cc': [
+ '<@(boost_cflags)',
+ '<@(rapidjson_cflags)',
+ '<@(nunicode_cflags)',
+ '<@(sqlite_cflags)',
+ '<@(jni.hpp_cflags)',
+ '<@(libzip_cflags)',
+ '<@(libpng_cflags)',
+ '<@(libjpeg-turbo_cflags)',
+ ],
+
+ 'link_settings': {
+ 'libraries': [
+ '-llog',
+ '-landroid',
+ '-lEGL',
+ '-lGLESv2',
+ '-lstdc++',
+ '-latomic',
+ '<@(nunicode_static_libs)',
+ '<@(nunicode_ldflags)',
+ '<@(sqlite_static_libs)',
+ '<@(sqlite_ldflags)',
+ '<@(zlib_static_libs)',
+ '<@(zlib_ldflags)',
+ '<@(libzip_static_libs)',
+ '<@(libzip_ldflags)',
+ '<@(libpng_static_libs)',
+ '<@(libpng_ldflags)',
+ '<@(libjpeg-turbo_static_libs)',
+ '<@(libjpeg-turbo_ldflags)',
+ ],
+ },
+ },
+ {
+ 'target_name': 'example-custom-layer-lib',
+ 'product_name': 'example-custom-layer',
+ 'type': 'shared_library',
+ 'hard_dependency': 1,
+
+ 'sources': [
+ './src/example_custom_layer.cpp',
+ ],
+
+ 'include_dirs': [
+ '../../include',
+ ],
+
+ 'link_settings': {
+ 'libraries': [
+ '-llog',
+ '-landroid',
+ '-lEGL',
+ '-lGLESv2',
+ '-lstdc++',
+ '-latomic',
+ ],
+ },
+ },
+ {
+ 'target_name': 'all',
+ 'type': 'none',
+ 'hard_dependency': 1,
+
+ 'dependencies': [
+ 'platform-lib',
+ 'example-custom-layer-lib',
+ ],
+
+ 'variables': {
+ 'pwd': '<!(pwd)',
+ },
+
+ 'copies': [
+ {
+ 'files': [
+ '../../common/ca-bundle.crt',
+ '../default/resources/api_mapbox_com-digicert.der',
+ '../default/resources/api_mapbox_com-geotrust.der',
+ '../default/resources/star_tilestream_net.der',
+ ],
+ 'destination': '<(DEPTH)/platform/android/MapboxGLAndroidSDK/src/main/assets'
+ },
+ ],
+
+ 'actions': [
+ {
+ 'action_name': 'Strip mapbox library',
+ 'inputs': [ '<(PRODUCT_DIR)/lib.target/libmapbox-gl.so' ],
+ 'outputs': [ '<(DEPTH)/platform/android/MapboxGLAndroidSDK/src/main/jniLibs/$(JNIDIR)/libmapbox-gl.so' ],
+ 'action': [ '$(STRIP)', '<@(_inputs)', '-o', '<@(_outputs)' ]
+ },
+ {
+ 'action_name': 'Strip example custom layer library',
+ 'inputs': [ '<(PRODUCT_DIR)/lib.target/libexample-custom-layer.so' ],
+ 'outputs': [ '<(DEPTH)/platform/android//MapboxGLAndroidSDKTestApp/src/main/jniLibs/$(JNIDIR)/libexample-custom-layer.so' ],
+ 'action': [ '$(STRIP)', '<@(_inputs)', '-o', '<@(_outputs)' ]
+ }
+ ],
+ },
+ ],
+}
diff --git a/platform/android/resources/dot-arrow.svg b/platform/android/resources/dot-arrow.svg
new file mode 100644
index 0000000000..4c2841a54b
--- /dev/null
+++ b/platform/android/resources/dot-arrow.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 139.6 219" enable-background="new 0 0 139.6 219" xml:space="preserve">
+<g id="XMLID_11_">
+ <path fill="#39ADCC" d="M66.5,1.3c1.1-1.8,3.7-1.8,4.8,0l9.8,17L94.8,42c-12.9-3.4-41.7-2.9-52,0.5l13.9-24.2L66.5,1.3z"/>
+</g>
+<g id="XMLID_7_">
+ <circle id="XMLID_10_" fill="#39ADCC" cx="69.8" cy="149.2" r="69.8"/>
+</g>
+</svg>
diff --git a/platform/android/scripts/configure.sh b/platform/android/scripts/configure.sh
index 81bdf19e98..2bbc134597 100644
--- a/platform/android/scripts/configure.sh
+++ b/platform/android/scripts/configure.sh
@@ -1,16 +1,19 @@
#!/usr/bin/env bash
-BOOST_VERSION=1.59.0
+UNIQUE_RESOURCE_VERSION=dev
+PROTOZERO_VERSION=1.3.0
+BOOST_VERSION=1.60.0
LIBPNG_VERSION=1.6.20
LIBJPEG_TURBO_VERSION=1.4.2
SQLITE_VERSION=3.9.1
-LIBUV_VERSION=1.7.5
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
LIBZIP_VERSION=0.11.2
-GEOJSONVT_VERSION=3.1.0
-VARIANT_VERSION=1.0
+GEOMETRY_VERSION=0.5.0
+GEOJSONVT_VERSION=4.1.2
+VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
JNI_HPP_VERSION=2.0.0
+EARCUT_VERSION=0.11
export MASON_ANDROID_ABI=${MASON_PLATFORM_VERSION}
diff --git a/platform/android/scripts/debug.sh b/platform/android/scripts/debug.sh
new file mode 100755
index 0000000000..ade9ab2d6f
--- /dev/null
+++ b/platform/android/scripts/debug.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+# Automation of https://github.com/mapbox/mapbox-gl-native/wiki/Android-debugging-with-remote-GDB
+
+export MASON_DIR="`pwd`/.mason"
+export PATH="${MASON_DIR}:${PATH}"
+
+export MASON_ANDROID_ABI=x86
+export MASON_ANDROID_ARCH=x86
+export MASON_ANDROID_PLATFORM=9
+export MASON_NDK_PACKAGE_VERSION=${MASON_ANDROID_ARCH}-${MASON_ANDROID_PLATFORM}-r10e
+
+if [[ $1 == '--prepare' ]]; then
+ mkdir -p ~/.android/debugging/{vendor,system}_lib
+ adb pull /system/lib ~/.android/debugging/system_lib
+ adb pull /vendor/lib ~/.android/debugging/vendor_lib
+ adb pull /system/bin/app_process ~/.android/debugging
+ adb pull /system/bin/app_process32 ~/.android/debugging
+ adb pull /system/bin/linker ~/.android/debugging
+
+ if [[ ${MASON_ANDROID_ABI} == 'x86_64' || ${MASON_ANDROID_ABI} == 'mips64' ]]; then
+ adb pull /system/bin/app_process64 ~/.android/debugging
+ adb pull /system/bin/linker64 ~/.android/debugging
+ fi
+
+ cp `mason prefix android-ndk ${MASON_NDK_PACKAGE_VERSION}`/prebuilt/android-${MASON_ANDROID_ABI}/gdbserver/gdbserver \
+ platform/android/MapboxGLAndroidSDK/src/main/jniLibs/${MASON_ANDROID_ABI}/gdbserver.so
+fi
+
+adb install -rtdg platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk
+adb shell am start -n "com.mapbox.mapboxsdk.testapp/com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity" \
+ -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
+
+adb forward tcp:5039 tcp:5039
+adb shell run-as com.mapbox.mapboxsdk.testapp '/data/data/com.mapbox.mapboxsdk.testapp/lib/gdbserver.so \
+ --attach :5039 `pgrep com.mapbox.mapboxsdk.testapp`' &
+
+export MASON_PLATFORM=android
+export PATH=`mason env PATH`
+`mason env MASON_ANDROID_TOOLCHAIN`-gdb \
+ -ex "target remote :5039" \
+ -ex "set solib-search-path ~/.android/debugging:~/.android/debugging/system_lib:~/.android/debugging/vendor_lib:~/.android/debugging/vendor_lib/egl:./build/android-${MASON_ANDROID_ABI}/Debug/lib.target/"
+
diff --git a/platform/android/scripts/defaults.mk b/platform/android/scripts/defaults.mk
deleted file mode 100644
index 5f049aae48..0000000000
--- a/platform/android/scripts/defaults.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-HEADLESS ?= none
-PLATFORM ?= android
-ASSET ?= zip
-HTTP ?= android
-
-GYP_FLAVOR_SUFFIX=-android
-
-HOST_VERSION ?= arm-v7
-
-ENV = $(shell MASON_ANDROID_ABI=$(HOST_VERSION) ./platform/android/scripts/toolchain.sh)
diff --git a/platform/android/scripts/install.sh b/platform/android/scripts/install.sh
deleted file mode 100755
index e99c3dc75e..0000000000
--- a/platform/android/scripts/install.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-mapbox_time "checkout_mason" \
-git submodule update --init .mason
-
-export MASON_PLATFORM=android
-export MASON_ANDROID_ABI=${ANDROID_ABI}
-
-mapbox_time "android_toolchain" \
-./platform/android/scripts/toolchain.sh
diff --git a/platform/android/scripts/run.sh b/platform/android/scripts/run.sh
deleted file mode 100755
index 9e87298fe9..0000000000
--- a/platform/android/scripts/run.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-# Add Mason to PATH
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-export MASON_PLATFORM=android
-export MASON_ANDROID_ABI=${ANDROID_ABI}
-
-################################################################################
-# Build
-################################################################################
-
-mkdir -p ./platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw
-echo "${MAPBOX_ACCESS_TOKEN}" > ./platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/token.txt
-
-mapbox_time "compile_library" \
-make android-lib HOST_VERSION=${ANDROID_ABI} -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "build_apk" \
-make android HOST_VERSION=${ANDROID_ABI} -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-################################################################################
-# Deploy
-################################################################################
-
-if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- # Install and add awscli to PATH for uploading the results
- mapbox_time "install_awscli" \
- pip install --user awscli
- export PATH="`python -m site --user-base`/bin:${PATH}"
-
- mapbox_time_start "deploy_results"
- echo "Deploying results..."
-
- S3_PREFIX=s3://mapbox/mapbox-gl-native/android/build/${TRAVIS_JOB_NUMBER}
- APK_OUTPUTS=./platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
- JNILIB=`mason env JNIDIR`
-
- # ARM64 does not build APK for now
- if [ ${JNIDIR} != "arm64-v8a" ] ; then
- # Upload either the debug or the release build
- if [ ${BUILDTYPE} == "Debug" ] ; then
- aws s3 cp \
- ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-${JNILIB}-debug.apk \
- ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-debug.apk
- elif [ ${BUILDTYPE} == "Release" ] ; then
- aws s3 cp \
- ${APK_OUTPUTS}/MapboxGLAndroidSDKTestApp-${JNILIB}-release-unsigned.apk \
- ${S3_PREFIX}/MapboxGLAndroidSDKTestApp-release-unsigned.apk
- fi
- fi
-
- mapbox_time_finish
-fi
diff --git a/platform/android/scripts/toolchain.sh b/platform/android/scripts/toolchain.sh
index 67cd0aa476..6eb6a229fe 100755
--- a/platform/android/scripts/toolchain.sh
+++ b/platform/android/scripts/toolchain.sh
@@ -4,7 +4,7 @@ set -e
set -o pipefail
export MASON_PLATFORM=android
-export MASON_ANDROID_ABI=${MASON_ANDROID_ABI:-arm-v7}
+export MASON_ANDROID_ABI=${1:-arm-v7}
export PATH="`pwd`/.mason:${PATH}"
export MASON_DIR="`pwd`/.mason"
diff --git a/platform/android/src/asset_file_source.cpp b/platform/android/src/asset_file_source.cpp
index d2aab30a52..2babe7db41 100644
--- a/platform/android/src/asset_file_source.cpp
+++ b/platform/android/src/asset_file_source.cpp
@@ -35,7 +35,7 @@ struct ZipFileHolder {
namespace mbgl {
-class AssetFileRequest : public FileRequest {
+class AssetFileRequest : public AsyncRequest {
public:
AssetFileRequest(std::unique_ptr<WorkRequest> workRequest_)
: workRequest(std::move(workRequest_)) {
@@ -100,14 +100,14 @@ private:
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource", util::ThreadType::Worker, util::ThreadPriority::Regular},
+ util::ThreadContext{"AssetFileSource"},
root)) {
}
AssetFileSource::~AssetFileSource() = default;
-std::unique_ptr<FileRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
- return std::make_unique<AssetFileRequest>(thread->invokeWithCallback(&Impl::request, callback, resource.url));
+std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
+ return thread->invokeWithCallback(&Impl::request, callback, resource.url);
}
}
diff --git a/platform/android/src/async_task.cpp b/platform/android/src/async_task.cpp
new file mode 100644
index 0000000000..6a9263baff
--- /dev/null
+++ b/platform/android/src/async_task.cpp
@@ -0,0 +1,63 @@
+#include "run_loop_impl.hpp"
+
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/atomic.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <functional>
+
+namespace mbgl {
+namespace util {
+
+class AsyncTask::Impl : public RunLoop::Impl::Runnable {
+public:
+ Impl(std::function<void()>&& fn)
+ : queued(true), task(std::move(fn)) {
+ loop->initRunnable(this);
+ }
+
+ ~Impl() {
+ loop->removeRunnable(this);
+ }
+
+ void maySend() {
+ if (queued) {
+ queued = false;
+ loop->addRunnable(this);
+ }
+ }
+
+ TimePoint dueTime() const override {
+ return due;
+ }
+
+ void runTask() override {
+ loop->removeRunnable(this);
+ queued = true;
+ task();
+ }
+
+private:
+ // Always expired, run immediately.
+ const TimePoint due = Clock::now();
+
+ RunLoop::Impl* loop = reinterpret_cast<RunLoop::Impl*>(RunLoop::getLoopHandle());
+
+ // TODO: Use std::atomic_flag if we ever drop
+ // support for ARMv5
+ util::Atomic<bool> queued;
+ std::function<void()> task;
+};
+
+AsyncTask::AsyncTask(std::function<void()>&& fn)
+ : impl(std::make_unique<Impl>(std::move(fn))) {
+}
+
+AsyncTask::~AsyncTask() = default;
+
+void AsyncTask::send() {
+ impl->maySend();
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp
index 439f9aff1a..516a8f2cd5 100644
--- a/platform/android/src/example_custom_layer.cpp
+++ b/platform/android/src/example_custom_layer.cpp
@@ -1,7 +1,7 @@
#include <jni.h>
#include <GLES2/gl2.h>
-#include <mbgl/style/types.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
static const GLchar * fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }";
@@ -64,7 +64,7 @@ void nativeInitialize(void *context) {
reinterpret_cast<ExampleCustomLayer*>(context)->initialize();
}
-void nativeRender(void *context, const mbgl::CustomLayerRenderParameters& /*parameters*/) {
+void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& /*parameters*/) {
reinterpret_cast<ExampleCustomLayer*>(context)->render();
}
@@ -76,7 +76,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
JNIEnv *env = nullptr;
vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
- jclass customLayerClass = env->FindClass("com/mapbox/mapboxsdk/testapp/layers/ExampleCustomLayer");
+ jclass customLayerClass = env->FindClass("com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer");
JNINativeMethod methods[] = {
{"createContext", "()J", reinterpret_cast<void *>(&nativeCreateContext)}
diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp
new file mode 100644
index 0000000000..97ccdb14b4
--- /dev/null
+++ b/platform/android/src/http_file_source.cpp
@@ -0,0 +1,180 @@
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/util.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/http_header.hpp>
+
+#include <jni/jni.hpp>
+#include "attach_env.hpp"
+
+namespace mbgl {
+
+class HTTPFileSource::Impl {
+public:
+ android::UniqueEnv env { android::AttachEnv() };
+};
+
+class HTTPRequest : public AsyncRequest {
+public:
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };
+
+ HTTPRequest(jni::JNIEnv&, const Resource&, FileSource::Callback);
+ ~HTTPRequest();
+
+ void onFailure(jni::JNIEnv&, int type, jni::String message);
+ void onResponse(jni::JNIEnv&, int code,
+ jni::String etag, jni::String modified,
+ jni::String cacheControl, jni::String expires,
+ jni::Array<jni::jbyte> body);
+
+ static jni::Class<HTTPRequest> javaClass;
+ jni::UniqueObject<HTTPRequest> javaRequest;
+
+private:
+ Resource resource;
+ FileSource::Callback callback;
+ Response response;
+
+ util::AsyncTask async { [this] {
+ // Calling `callback` may result in deleting `this`. Copy data to temporaries first.
+ auto callback_ = callback;
+ auto response_ = response;
+ callback_(response_);
+ } };
+
+ static const int connectionError = 0;
+ static const int temporaryError = 1;
+ static const int permanentError = 2;
+};
+
+jni::Class<HTTPRequest> HTTPRequest::javaClass;
+
+namespace android {
+
+void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
+ HTTPRequest::javaClass = *jni::Class<HTTPRequest>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "mNativePtr",
+ METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
+ METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
+}
+
+} // namespace android
+
+HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, FileSource::Callback callback_)
+ : resource(resource_),
+ callback(callback_) {
+ std::string etagStr;
+ std::string modifiedStr;
+
+ if (resource.priorEtag) {
+ etagStr = *resource.priorEtag;
+ } else if (resource.priorModified) {
+ modifiedStr = util::rfc1123(*resource.priorModified);
+ }
+
+ jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 10);
+
+ static auto constructor =
+ javaClass.GetConstructor<jni::jlong, jni::String, jni::String, jni::String, jni::String>(env);
+
+ javaRequest = javaClass.New(env, constructor,
+ reinterpret_cast<jlong>(this),
+ jni::Make<jni::String>(env, resource.url),
+ jni::Make<jni::String>(env, "MapboxGL/1.0"),
+ jni::Make<jni::String>(env, etagStr),
+ jni::Make<jni::String>(env, modifiedStr)).NewGlobalRef(env);
+}
+
+HTTPRequest::~HTTPRequest() {
+ android::UniqueEnv env = android::AttachEnv();
+
+ static auto cancel = javaClass.GetMethod<void ()>(*env, "cancel");
+
+ javaRequest->Call(*env, cancel);
+}
+
+void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
+ jni::String etag, jni::String modified, jni::String cacheControl,
+ jni::String expires, jni::Array<jni::jbyte> body) {
+ using Error = Response::Error;
+
+ if (etag) {
+ response.etag = jni::Make<std::string>(env, etag);
+ }
+
+ if (modified) {
+ response.modified = util::parseTimestamp(jni::Make<std::string>(env, modified).c_str());
+ }
+
+ if (cacheControl) {
+ response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint();
+ }
+
+ if (expires) {
+ response.expires = util::parseTimestamp(jni::Make<std::string>(env, expires).c_str());
+ }
+
+ if (code == 200) {
+ if (body) {
+ auto data = std::make_shared<std::string>(body.Length(env), char());
+ jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast<jbyte*>(&(*data)[0]));
+ response.data = data;
+ } else {
+ response.data = std::make_shared<std::string>();
+ }
+ } else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) {
+ response.noContent = true;
+ } else if (code == 304) {
+ response.notModified = true;
+ } else if (code == 404) {
+ response.error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
+ } else if (code >= 500 && code < 600) {
+ response.error = std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } + std::to_string(code));
+ } else {
+ response.error = std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(code));
+ }
+
+ async.send();
+}
+
+void HTTPRequest::onFailure(jni::JNIEnv& env, int type, jni::String message) {
+ std::string messageStr = jni::Make<std::string>(env, message);
+
+ using Error = Response::Error;
+
+ switch (type) {
+ case connectionError:
+ response.error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
+ break;
+ case temporaryError:
+ response.error = std::make_unique<Error>(Error::Reason::Server, messageStr);
+ break;
+ default:
+ response.error = std::make_unique<Error>(Error::Reason::Other, messageStr);
+ }
+
+ async.send();
+}
+
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ return std::make_unique<HTTPRequest>(*impl->env, resource, callback);
+}
+
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
+ return 20;
+}
+
+} // namespace mbgl
diff --git a/platform/android/src/http_request_android.cpp b/platform/android/src/http_request_android.cpp
deleted file mode 100644
index 1e0039cdf8..0000000000
--- a/platform/android/src/http_request_android.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/platform/log.hpp>
-
-#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/util.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <jni/jni.hpp>
-#include "attach_env.hpp"
-
-namespace mbgl {
-namespace android {
-
-class HTTPContext : public HTTPContextBase {
-public:
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
- UniqueEnv env { android::AttachEnv() };
-};
-
-class HTTPRequest : public HTTPRequestBase {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/http/HTTPRequest"; };
-
- HTTPRequest(jni::JNIEnv&, const Resource&, Callback);
-
- void cancel() final;
-
- void onFailure(jni::JNIEnv&, int type, jni::String message);
- void onResponse(jni::JNIEnv&, int code,
- jni::String etag, jni::String modified,
- jni::String cacheControl, jni::String expires,
- jni::Array<jni::jbyte> body);
-
- static jni::Class<HTTPRequest> javaClass;
- jni::UniqueObject<HTTPRequest> javaRequest;
-
-private:
- void finish();
-
- std::unique_ptr<Response> response;
- const std::shared_ptr<const Response> existingResponse;
-
- util::AsyncTask async;
-
- static const int connectionError = 0;
- static const int temporaryError = 1;
- static const int permanentError = 2;
-};
-
-jni::Class<HTTPRequest> HTTPRequest::javaClass;
-
-void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
- HTTPRequest::javaClass = *jni::Class<HTTPRequest>::Find(env).NewGlobalRef(env).release();
-
- #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
-
- jni::RegisterNativePeer<HTTPRequest>(env, HTTPRequest::javaClass, "mNativePtr",
- METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
- METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HTTPRequestBase* HTTPContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPRequest(*env, resource, callback);
-}
-
-HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- async([this] { finish(); }) {
- std::string etagStr;
- std::string modifiedStr;
-
- if (resource.priorEtag) {
- etagStr = *resource.priorEtag;
- } else if (resource.priorModified) {
- modifiedStr = util::rfc1123(*resource.priorModified);
- }
-
- jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 10);
-
- static auto constructor =
- javaClass.GetConstructor<jni::jlong, jni::String, jni::String, jni::String, jni::String>(env);
-
- javaRequest = javaClass.New(env, constructor,
- reinterpret_cast<jlong>(this),
- jni::Make<jni::String>(env, resource.url),
- jni::Make<jni::String>(env, "MapboxGL/1.0"),
- jni::Make<jni::String>(env, etagStr),
- jni::Make<jni::String>(env, modifiedStr)).NewGlobalRef(env);
-}
-
-void HTTPRequest::cancel() {
- UniqueEnv env = android::AttachEnv();
-
- static auto cancel = javaClass.GetMethod<void ()>(*env, "cancel");
-
- javaRequest->Call(*env, cancel);
-
- delete this;
-}
-
-void HTTPRequest::finish() {
- assert(response);
- notify(*response);
-
- delete this;
-}
-
-void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
- jni::String etag, jni::String modified, jni::String cacheControl,
- jni::String expires, jni::Array<jni::jbyte> body) {
- response = std::make_unique<Response>();
- using Error = Response::Error;
-
- if (etag) {
- response->etag = jni::Make<std::string>(env, etag);
- }
-
- if (modified) {
- response->modified = util::parseTimePoint(jni::Make<std::string>(env, modified).c_str());
- }
-
- if (cacheControl) {
- response->expires = parseCacheControl(jni::Make<std::string>(env, cacheControl).c_str());
- }
-
- if (expires) {
- response->expires = util::parseTimePoint(jni::Make<std::string>(env, expires).c_str());
- }
-
- if (code == 200) {
- if (body) {
- auto data = std::make_shared<std::string>(body.Length(env), char());
- jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast<jbyte*>(&(*data)[0]));
- response->data = data;
- } else {
- response->data = std::make_shared<std::string>();
- }
- } else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) {
- response->noContent = true;
- } else if (code == 304) {
- response->notModified = true;
- } else if (code == 404) {
- response->error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
- } else if (code >= 500 && code < 600) {
- response->error = std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } + std::to_string(code));
- } else {
- response->error = std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } + std::to_string(code));
- }
-
- async.send();
-}
-
-void HTTPRequest::onFailure(jni::JNIEnv& env, int type, jni::String message) {
- std::string messageStr = jni::Make<std::string>(env, message);
-
- response = std::make_unique<Response>();
- using Error = Response::Error;
-
- switch (type) {
- case connectionError:
- response->error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
- break;
- case temporaryError:
- response->error = std::make_unique<Error>(Error::Reason::Server, messageStr);
- break;
- default:
- response->error = std::make_unique<Error>(Error::Reason::Other, messageStr);
- }
-
- async.send();
-}
-
-} // namespace android
-
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<android::HTTPContext>();
-}
-
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
- return 20;
-}
-
-} // namespace mbgl
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 237fbb99da..e79689d7ad 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -13,14 +13,15 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/annotation/point_annotation.hpp>
-#include <mbgl/annotation/shape_annotation.hpp>
+#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/run_loop.hpp>
#include <jni/jni.hpp>
@@ -139,6 +140,8 @@ jni::jmethodID* offlineRegionStatusConstructorId = nullptr;
jni::jfieldID* offlineRegionStatusDownloadStateId = nullptr;
jni::jfieldID* offlineRegionStatusCompletedResourceCountId = nullptr;
jni::jfieldID* offlineRegionStatusCompletedResourceSizeId = nullptr;
+jni::jfieldID* offlineRegionStatusCompletedTileCountId = nullptr;
+jni::jfieldID* offlineRegionStatusCompletedTileSizeId = nullptr;
jni::jfieldID* offlineRegionStatusRequiredResourceCountId = nullptr;
jni::jfieldID* offlineRegionStatusRequiredResourceCountIsPreciseId = nullptr;
@@ -168,12 +171,12 @@ bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName) {
if (ret != JNI_OK) {
if (ret != JNI_EDETACHED) {
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
- throw new std::runtime_error("GetEnv() failed");
+ throw std::runtime_error("GetEnv() failed");
} else {
ret = vm->AttachCurrentThread(env, &args);
if (ret != JNI_OK) {
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
- throw new std::runtime_error("AttachCurrentThread() failed");
+ throw std::runtime_error("AttachCurrentThread() failed");
}
detach = true;
}
@@ -190,7 +193,7 @@ void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) {
jint ret;
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
- throw new std::runtime_error("DetachCurrentThread() failed");
+ throw std::runtime_error("DetachCurrentThread() failed");
}
}
*env = nullptr;
@@ -245,64 +248,6 @@ jni::jarray<jlong>* std_vector_uint_to_jobject(JNIEnv *env, std::vector<uint32_t
return &jarray;
}
-mbgl::AnnotationSegment annotation_segment_from_latlng_jlist(JNIEnv *env, jni::jobject* jlist) {
- mbgl::AnnotationSegment segment;
-
- NullCheck(*env, jlist);
- jni::jarray<jni::jobject>* jarray =
- reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
-
- NullCheck(*env, jarray);
- std::size_t len = jni::GetArrayLength(*env, *jarray);
-
- segment.reserve(len);
-
- for (std::size_t i = 0; i < len; i++) {
- jni::jobject* latLng = reinterpret_cast<jni::jobject*>(jni::GetObjectArrayElement(*env, *jarray, i));
- NullCheck(*env, latLng);
-
- jdouble latitude = jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId);
-
- segment.push_back(mbgl::LatLng(latitude, longitude));
- jni::DeleteLocalRef(*env, latLng);
- }
-
- jni::DeleteLocalRef(*env, jarray);
- jarray = nullptr;
-
- return segment;
-}
-
-std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> annotation_std_pair_from_polygon_jobject(JNIEnv *env, jni::jobject* polygon) {
- jfloat alpha = jni::GetField<jfloat>(*env, polygon, *polygonAlphaId);
- jint fillColor = jni::GetField<jint>(*env, polygon, *polygonFillColorId);
- jint strokeColor = jni::GetField<jint>(*env, polygon, *polygonStrokeColorId);
-
- int rF = (fillColor >> 16) & 0xFF;
- int gF = (fillColor >> 8) & 0xFF;
- int bF = (fillColor) & 0xFF;
- int aF = (fillColor >> 24) & 0xFF;
-
- int rS = (strokeColor >> 16) & 0xFF;
- int gS = (strokeColor >> 8) & 0xFF;
- int bS = (strokeColor) & 0xFF;
- int aS = (strokeColor >> 24) & 0xFF;
-
- mbgl::ShapeAnnotation::Properties shapeProperties;
- mbgl::FillAnnotationProperties fillProperties;
- fillProperties.opacity = alpha;
- fillProperties.outlineColor = {{ static_cast<float>(rS) / 255.0f, static_cast<float>(gS) / 255.0f, static_cast<float>(bS) / 255.0f, static_cast<float>(aS) / 255.0f }};
- fillProperties.color = {{ static_cast<float>(rF) / 255.0f, static_cast<float>(gF) / 255.0f, static_cast<float>(bF) / 255.0f, static_cast<float>(aF) / 255.0f }};
- shapeProperties.set<mbgl::FillAnnotationProperties>(fillProperties);
-
- jni::jobject* points = jni::GetField<jni::jobject*>(*env, polygon, *polygonPointsId);
- mbgl::AnnotationSegment segment = annotation_segment_from_latlng_jlist(env, points);
- jni::DeleteLocalRef(*env, points);
-
- return std::make_pair(segment, shapeProperties);
-}
-
static std::vector<uint8_t> metadata_from_java(JNIEnv* env, jni::jarray<jbyte>& j) {
mbgl::Log::Debug(mbgl::Event::JNI, "metadata_from_java");
std::size_t length = jni::GetArrayLength(*env, j);
@@ -404,27 +349,6 @@ void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr
nativeMapView->destroySurface();
}
-void nativePause(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativePause");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->pause();
-}
-
-jboolean nativeIsPaused(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeIsPaused");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- return nativeMapView->getMap().isPaused();
-}
-
-void nativeResume(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeResume");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->resume();
-}
-
void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
assert(nativeMapViewPtr != 0);
@@ -432,11 +356,11 @@ void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().update(mbgl::Update::Repaint);
}
-void nativeRenderSync(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeRenderSync");
+void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRender");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().renderSync();
+ nativeMapView->render();
}
void nativeViewResize(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jint width, jint height) {
@@ -496,22 +420,6 @@ jni::jobject* nativeGetClasses(JNIEnv *env, jni::jobject* obj, jlong nativeMapVi
return std_vector_string_to_jobject(env, nativeMapView->getMap().getClasses());
}
-void nativeSetDefaultTransitionDuration(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr,
- jlong duration) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDefaultTransitionDuration");
- assert(nativeMapViewPtr != 0);
- assert(duration >= 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().setDefaultTransitionDuration(mbgl::Milliseconds(duration));
-}
-
-jlong nativeGetDefaultTransitionDuration(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDefaultTransitionDuration");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- return std::chrono::duration_cast<mbgl::Milliseconds>(nativeMapView->getMap().getDefaultTransitionDuration()).count();
-}
-
void nativeSetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleURL");
assert(nativeMapViewPtr != 0);
@@ -519,13 +427,11 @@ void nativeSetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, j
nativeMapView->getMap().setStyleURL(std_string_from_jstring(env, url));
}
-void nativeSetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr,
- jni::jstring* newStyleJson, jni::jstring* base) {
+void nativeSetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* newStyleJson) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleJSON");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().setStyleJSON(std_string_from_jstring(env, newStyleJson),
- std_string_from_jstring(env, base));
+ nativeMapView->getMap().setStyleJSON(std_string_from_jstring(env, newStyleJson));
}
jni::jstring* nativeGetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
@@ -754,24 +660,6 @@ void nativeResetNorth(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().resetNorth();
}
-jlong nativeAddMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* marker) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddMarker");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jni::jobject* position = jni::GetField<jni::jobject*>(*env, marker, *markerPositionId);
- jni::jobject* icon = jni::GetField<jni::jobject*>(*env, marker, *markerIconId);
-
- jni::jstring* jid = reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, icon, *iconIdId));
- std::string id = std_string_from_jstring(env, jid);
-
- jdouble latitude = jni::GetField<jdouble>(*env, position, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, position, *latLngLongitudeId);
-
- // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long.
- return nativeMapView->getMap().addPointAnnotation(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id));
-}
-
void nativeUpdateMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* marker) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdateMarker");
assert(nativeMapViewPtr != 0);
@@ -791,184 +679,133 @@ void nativeUpdateMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr,
jdouble latitude = jni::GetField<jdouble>(*env, position, *latLngLatitudeId);
jdouble longitude = jni::GetField<jdouble>(*env, position, *latLngLongitudeId);
- // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long.
- nativeMapView->getMap().updatePointAnnotation(markerId, mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), iconId));
+ nativeMapView->getMap().updateAnnotation(markerId, mbgl::SymbolAnnotation {
+ mbgl::Point<double>(longitude, latitude),
+ iconId
+ });
}
-jni::jarray<jlong>* nativeAddMarkers(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* jlist) {
+jni::jarray<jlong>* nativeAddMarkers(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddMarkers");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- std::vector<mbgl::PointAnnotation> markers;
-
- NullCheck(*env, jlist);
- jni::jarray<jni::jobject>* jarray =
- reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
-
NullCheck(*env, jarray);
std::size_t len = jni::GetArrayLength(*env, *jarray);
- markers.reserve(len);
+ std::vector<mbgl::AnnotationID> ids;
+ ids.reserve(len);
for (std::size_t i = 0; i < len; i++) {
jni::jobject* marker = jni::GetObjectArrayElement(*env, *jarray, i);
jni::jobject* position = jni::GetField<jni::jobject*>(*env, marker, *markerPositionId);
jni::jobject* icon = jni::GetField<jni::jobject*>(*env, marker, *markerIconId);
- jni::DeleteLocalRef(*env, marker);
-
jni::jstring* jid = reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, icon, *iconIdId));
- jni::DeleteLocalRef(*env, icon);
-
- std::string id = std_string_from_jstring(env, jid);
- jni::DeleteLocalRef(*env, jid);
jdouble latitude = jni::GetField<jdouble>(*env, position, *latLngLatitudeId);
jdouble longitude = jni::GetField<jdouble>(*env, position, *latLngLongitudeId);
- jni::DeleteLocalRef(*env, position);
- markers.emplace_back(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id));
- }
+ ids.push_back(nativeMapView->getMap().addAnnotation(mbgl::SymbolAnnotation {
+ mbgl::Point<double>(longitude, latitude),
+ std_string_from_jstring(env, jid)
+ }));
- jni::DeleteLocalRef(*env, jarray);
+ jni::DeleteLocalRef(*env, position);
+ jni::DeleteLocalRef(*env, jid);
+ jni::DeleteLocalRef(*env, icon);
+ jni::DeleteLocalRef(*env, marker);
+ }
- std::vector<uint32_t> pointAnnotationIDs = nativeMapView->getMap().addPointAnnotations(markers);
- return std_vector_uint_to_jobject(env, pointAnnotationIDs);
+ return std_vector_uint_to_jobject(env, ids);
}
+static mbgl::Color toColor(jint color) {
+ float r = (color >> 16) & 0xFF;
+ float g = (color >> 8) & 0xFF;
+ float b = (color) & 0xFF;
+ float a = (color >> 24) & 0xFF;
+ return {{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f }};
+}
-jlong nativeAddPolyline(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* polyline) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolyline");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jfloat alpha = jni::GetField<jfloat>(*env, polyline, *polylineAlphaId);
- jint color = jni::GetField<jint>(*env, polyline, *polylineColorId);
-
- int r = (color >> 16) & 0xFF;
- int g = (color >> 8) & 0xFF;
- int b = (color) & 0xFF;
- int a = (color >> 24) & 0xFF;
+template <class Geometry>
+Geometry toGeometry(JNIEnv *env, jni::jobject* jlist) {
+ NullCheck(*env, jlist);
+ jni::jarray<jni::jobject>* jarray =
+ reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
+ NullCheck(*env, jarray);
- jfloat width = jni::GetField<jfloat>(*env, polyline, *polylineWidthId);
+ Geometry geometry;
+ geometry.reserve(jni::GetArrayLength(*env, *jarray));
- mbgl::ShapeAnnotation::Properties shapeProperties;
- mbgl::LineAnnotationProperties lineProperties;
- lineProperties.opacity = alpha;
- lineProperties.color = {{ static_cast<float>(r) / 255.0f, static_cast<float>(g) / 255.0f, static_cast<float>(b) / 255.0f, static_cast<float>(a) / 255.0f }};
- lineProperties.width = width;
- shapeProperties.set<mbgl::LineAnnotationProperties>(lineProperties);
+ for (std::size_t i = 0; i < geometry.size(); i++) {
+ jni::jobject* latLng = reinterpret_cast<jni::jobject*>(jni::GetObjectArrayElement(*env, *jarray, i));
+ NullCheck(*env, latLng);
- jni::jobject* points = jni::GetField<jni::jobject*>(*env, polyline, *polylinePointsId);
- mbgl::AnnotationSegment segment = annotation_segment_from_latlng_jlist(env, points);
+ geometry.push_back(mbgl::Point<double>(
+ jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId),
+ jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId)));
- std::vector<mbgl::ShapeAnnotation> shapes;
- shapes.emplace_back(mbgl::AnnotationSegments { segment }, shapeProperties);
+ jni::DeleteLocalRef(*env, latLng);
+ }
- std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
- uint32_t id = shapeAnnotationIDs.at(0);
+ jni::DeleteLocalRef(*env, jarray);
+ jni::DeleteLocalRef(*env, jlist);
- return id;
+ return geometry;
}
-jni::jarray<jlong>* nativeAddPolylines(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* jlist) {
+jni::jarray<jlong>* nativeAddPolylines(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolylines");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- std::vector<mbgl::ShapeAnnotation> shapes;
-
- jni::jarray<jni::jobject>* jarray =
- reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
-
NullCheck(*env, jarray);
std::size_t len = jni::GetArrayLength(*env, *jarray);
- shapes.reserve(len);
+ std::vector<mbgl::AnnotationID> ids;
+ ids.reserve(len);
for (std::size_t i = 0; i < len; i++) {
jni::jobject* polyline = jni::GetObjectArrayElement(*env, *jarray, i);
-
- jfloat alpha = jni::GetField<jfloat>(*env, polyline, *polylineAlphaId);
- jint color = jni::GetField<jint>(*env, polyline, *polylineColorId);
-
- int r = (color >> 16) & 0xFF;
- int g = (color >> 8) & 0xFF;
- int b = (color) & 0xFF;
- int a = (color >> 24) & 0xFF;
-
- jfloat width = jni::GetField<jfloat>(*env, polyline, *polylineWidthId);
-
- mbgl::ShapeAnnotation::Properties shapeProperties;
- mbgl::LineAnnotationProperties lineProperties;
- lineProperties.opacity = alpha;
- lineProperties.color = {{ static_cast<float>(r) / 255.0f, static_cast<float>(g) / 255.0f, static_cast<float>(b) / 255.0f, static_cast<float>(a) / 255.0f }};
- lineProperties.width = width;
- shapeProperties.set<mbgl::LineAnnotationProperties>(lineProperties);
-
jni::jobject* points = jni::GetField<jni::jobject*>(*env, polyline, *polylinePointsId);
- mbgl::AnnotationSegment segment = annotation_segment_from_latlng_jlist(env, points);
- shapes.emplace_back(mbgl::AnnotationSegments { segment }, shapeProperties);
+ mbgl::LineAnnotation annotation { toGeometry<mbgl::LineString<double>>(env, points) };
+ annotation.opacity = jni::GetField<jfloat>(*env, polyline, *polylineAlphaId);
+ annotation.color = toColor(jni::GetField<jint>(*env, polyline, *polylineColorId));
+ annotation.width = jni::GetField<jfloat>(*env, polyline, *polylineWidthId);
+ ids.push_back(nativeMapView->getMap().addAnnotation(annotation));
jni::DeleteLocalRef(*env, polyline);
}
- jni::DeleteLocalRef(*env, jarray);
-
- std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
- return std_vector_uint_to_jobject(env, shapeAnnotationIDs);
+ return std_vector_uint_to_jobject(env, ids);
}
-jlong nativeAddPolygon(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* polygon) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolygon");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- std::vector<mbgl::ShapeAnnotation> shapes;
- std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> segment = annotation_std_pair_from_polygon_jobject(env, polygon);
-
- shapes.emplace_back(mbgl::AnnotationSegments { segment.first }, segment.second);
-
- std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
- uint32_t id = shapeAnnotationIDs.at(0);
- return id;
-}
-
-jni::jarray<jlong>* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* jlist) {
+jni::jarray<jlong>* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolygons");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- std::vector<mbgl::ShapeAnnotation> shapes;
-
- jni::jarray<jni::jobject>* jarray =
- reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
-
NullCheck(*env, jarray);
std::size_t len = jni::GetArrayLength(*env, *jarray);
- shapes.reserve(len);
+
+ std::vector<mbgl::AnnotationID> ids;
+ ids.reserve(len);
for (std::size_t i = 0; i < len; i++) {
jni::jobject* polygon = jni::GetObjectArrayElement(*env, *jarray, i);
+ jni::jobject* points = jni::GetField<jni::jobject*>(*env, polygon, *polygonPointsId);
- std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> segment = annotation_std_pair_from_polygon_jobject(env, polygon);
- shapes.emplace_back(mbgl::AnnotationSegments { segment.first }, segment.second);
+ mbgl::FillAnnotation annotation { mbgl::Polygon<double> { toGeometry<mbgl::LinearRing<double>>(env, points) } };
+ annotation.opacity = jni::GetField<jfloat>(*env, polygon, *polygonAlphaId);
+ annotation.outlineColor = toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId));
+ annotation.color = toColor(jni::GetField<jint>(*env, polygon, *polygonFillColorId));
+ ids.push_back(nativeMapView->getMap().addAnnotation(annotation));
jni::DeleteLocalRef(*env, polygon);
}
- jni::DeleteLocalRef(*env, jarray);
-
- std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
- return std_vector_uint_to_jobject(env, shapeAnnotationIDs);
-}
-
-void nativeRemoveAnnotation(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong annotationId) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveAnnotation");
- assert(nativeMapViewPtr != 0);
- NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().removeAnnotation(static_cast<uint32_t>(annotationId));
+ return std_vector_uint_to_jobject(env, ids);
}
void nativeRemoveAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jlong>* jarray) {
@@ -978,20 +815,14 @@ void nativeRemoveAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapView
NullCheck(*env, jarray);
std::size_t len = jni::GetArrayLength(*env, *jarray);
-
- std::vector<uint32_t> ids;
- ids.reserve(len);
-
auto elements = jni::GetArrayElements(*env, *jarray);
jlong* jids = std::get<0>(elements).get();
for (std::size_t i = 0; i < len; i++) {
if(jids[i] == -1L)
continue;
- ids.push_back(static_cast<uint32_t>(jids[i]));
+ nativeMapView->getMap().removeAnnotation(jids[i]);
}
-
- nativeMapView->getMap().removeAnnotations(ids);
}
jni::jarray<jlong>* nativeGetAnnotationsInBounds(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* latLngBounds_) {
@@ -1052,17 +883,17 @@ void nativeSetVisibleCoordinateBounds(JNIEnv *env, jni::jobject* obj, jlong nati
std::size_t count = jni::GetArrayLength(*env, *coordinates);
mbgl::EdgeInsets mbglInsets = {top, left, bottom, right};
- mbgl::AnnotationSegment segment;
- segment.reserve(count);
+ std::vector<mbgl::LatLng> latLngs;
+ latLngs.reserve(count);
for (std::size_t i = 0; i < count; i++) {
jni::jobject* latLng = jni::GetObjectArrayElement(*env, *coordinates, i);
jdouble latitude = jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId);
jdouble longitude = jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId);
- segment.push_back(mbgl::LatLng(latitude, longitude));
+ latLngs.push_back(mbgl::LatLng(latitude, longitude));
}
- mbgl::CameraOptions cameraOptions = nativeMapView->getMap().cameraForLatLngs(segment, mbglInsets);
+ mbgl::CameraOptions cameraOptions = nativeMapView->getMap().cameraForLatLngs(latLngs, mbglInsets);
if (direction >= 0) {
// convert from degrees to radians
cameraOptions.angle = (-direction * M_PI) / 180;
@@ -1211,7 +1042,7 @@ void nativeJumpTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdoubl
nativeMapView->getMap().jumpTo(options);
}
-void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jlong duration, jdouble pitch, jdouble zoom) {
+void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jlong duration, jdouble pitch, jdouble zoom, jboolean easing) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeEaseTo");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
@@ -1234,6 +1065,11 @@ void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdoubl
mbgl::AnimationOptions animationOptions;
animationOptions.duration.emplace(mbgl::Duration(duration));
+ if (!easing) {
+ // add a linear interpolator instead of easing
+ animationOptions.easing = mbgl::util::UnitBezier(0, 0, 1, 1);
+ }
+
nativeMapView->getMap().easeTo(cameraOptions, animationOptions);
}
@@ -1244,7 +1080,6 @@ void nativeSetContentPadding(JNIEnv *env, jni::jobject* obj,long nativeMapViewPt
nativeMapView->setInsets({top, left, bottom, right});
}
-
void nativeFlyTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jlong duration, jdouble pitch, jdouble zoom) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeFlyTo");
assert(nativeMapViewPtr != 0);
@@ -1275,20 +1110,20 @@ void nativeAddCustomLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr
mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddCustomLayer");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().addCustomLayer(
+ nativeMapView->getMap().addLayer(std::make_unique<mbgl::style::CustomLayer>(
std_string_from_jstring(env, reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, customLayer, *customLayerIdId))),
- reinterpret_cast<mbgl::CustomLayerInitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerInitializeFunctionId)),
- reinterpret_cast<mbgl::CustomLayerRenderFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerRenderFunctionId)),
- reinterpret_cast<mbgl::CustomLayerDeinitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerDeinitializeFunctionId)),
- reinterpret_cast<void*>(jni::GetField<jlong>(*env, customLayer, *customLayerContextId)),
- before ? std_string_from_jstring(env, before).c_str() : nullptr);
+ reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerInitializeFunctionId)),
+ reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerRenderFunctionId)),
+ reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerDeinitializeFunctionId)),
+ reinterpret_cast<void*>(jni::GetField<jlong>(*env, customLayer, *customLayerContextId))),
+ before ? mbgl::optional<std::string>(std_string_from_jstring(env, before)) : mbgl::optional<std::string>());
}
void nativeRemoveCustomLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveCustomLayer");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().removeCustomLayer(std_string_from_jstring(env, id));
+ nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id));
}
// Offline calls begin
@@ -1542,11 +1377,17 @@ void setOfflineRegionObserver(JNIEnv *env, jni::jobject* offlineRegion_, jni::jo
break;
}
+ // Create a new local reference frame (capacity 1 for the NewObject allocation below)
+ // to avoid a local reference table overflow (#4706)
+ jni::UniqueLocalFrame frame = jni::PushLocalFrame(*env2, 1);
+
// Stats object
jni::jobject* jstatus = &jni::NewObject(*env2, *offlineRegionStatusClass, *offlineRegionStatusConstructorId);
jni::SetField<jint>(*env2, jstatus, *offlineRegionStatusDownloadStateId, downloadState);
jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceCountId, status.completedResourceCount);
jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceSizeId, status.completedResourceSize);
+ jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedTileCountId, status.completedTileCount);
+ jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedTileSizeId, status.completedTileSize);
jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountId, status.requiredResourceCount);
jni::SetField<jboolean>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountIsPreciseId, status.requiredResourceCountIsPrecise);
jni::CallMethod<void>(*env2, observerCallback.get(), *offlineRegionObserveronStatusChangedId, jstatus);
@@ -1738,6 +1579,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6);
+ static mbgl::util::RunLoop mainRunLoop;
+
mbgl::android::RegisterNativeHTTPRequest(env);
latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng");
@@ -1831,11 +1674,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeTerminateContext, "(J)V"),
MAKE_NATIVE_METHOD(nativeCreateSurface, "(JLandroid/view/Surface;)V"),
MAKE_NATIVE_METHOD(nativeDestroySurface, "(J)V"),
- MAKE_NATIVE_METHOD(nativePause, "(J)V"),
- MAKE_NATIVE_METHOD(nativeIsPaused, "(J)Z"),
- MAKE_NATIVE_METHOD(nativeResume, "(J)V"),
MAKE_NATIVE_METHOD(nativeUpdate, "(J)V"),
- MAKE_NATIVE_METHOD(nativeRenderSync, "(J)V"),
+ MAKE_NATIVE_METHOD(nativeRender, "(J)V"),
MAKE_NATIVE_METHOD(nativeViewResize, "(JII)V"),
MAKE_NATIVE_METHOD(nativeFramebufferResize, "(JII)V"),
MAKE_NATIVE_METHOD(nativeAddClass, "(JLjava/lang/String;)V"),
@@ -1843,10 +1683,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeHasClass, "(JLjava/lang/String;)Z"),
MAKE_NATIVE_METHOD(nativeSetClasses, "(JLjava/util/List;)V"),
MAKE_NATIVE_METHOD(nativeGetClasses, "(J)Ljava/util/List;"),
- MAKE_NATIVE_METHOD(nativeSetDefaultTransitionDuration, "(JJ)V"),
- MAKE_NATIVE_METHOD(nativeGetDefaultTransitionDuration, "(J)J"),
MAKE_NATIVE_METHOD(nativeSetStyleUrl, "(JLjava/lang/String;)V"),
- MAKE_NATIVE_METHOD(nativeSetStyleJson, "(JLjava/lang/String;Ljava/lang/String;)V"),
+ MAKE_NATIVE_METHOD(nativeSetStyleJson, "(JLjava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeGetStyleJson, "(J)Ljava/lang/String;"),
MAKE_NATIVE_METHOD(nativeSetAccessToken, "(JLjava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeGetAccessToken, "(J)Ljava/lang/String;"),
@@ -1874,14 +1712,10 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeSetBearingXY, "(JDDD)V"),
MAKE_NATIVE_METHOD(nativeGetBearing, "(J)D"),
MAKE_NATIVE_METHOD(nativeResetNorth, "(J)V"),
- MAKE_NATIVE_METHOD(nativeAddMarker, "(JLcom/mapbox/mapboxsdk/annotations/Marker;)J"),
- MAKE_NATIVE_METHOD(nativeAddMarkers, "(JLjava/util/List;)[J"),
- MAKE_NATIVE_METHOD(nativeAddPolyline, "(JLcom/mapbox/mapboxsdk/annotations/Polyline;)J"),
- MAKE_NATIVE_METHOD(nativeAddPolylines, "(JLjava/util/List;)[J"),
- MAKE_NATIVE_METHOD(nativeAddPolygon, "(JLcom/mapbox/mapboxsdk/annotations/Polygon;)J"),
- MAKE_NATIVE_METHOD(nativeAddPolygons, "(JLjava/util/List;)[J"),
+ MAKE_NATIVE_METHOD(nativeAddMarkers, "(J[Lcom/mapbox/mapboxsdk/annotations/Marker;)[J"),
+ MAKE_NATIVE_METHOD(nativeAddPolylines, "(J[Lcom/mapbox/mapboxsdk/annotations/Polyline;)[J"),
+ MAKE_NATIVE_METHOD(nativeAddPolygons, "(J[Lcom/mapbox/mapboxsdk/annotations/Polygon;)[J"),
MAKE_NATIVE_METHOD(nativeUpdateMarker, "(JLcom/mapbox/mapboxsdk/annotations/Marker;)V"),
- MAKE_NATIVE_METHOD(nativeRemoveAnnotation, "(JJ)V"),
MAKE_NATIVE_METHOD(nativeRemoveAnnotations, "(J[J)V"),
MAKE_NATIVE_METHOD(nativeGetAnnotationsInBounds, "(JLcom/mapbox/mapboxsdk/geometry/LatLngBounds;)[J"),
MAKE_NATIVE_METHOD(nativeAddAnnotationIcon, "(JLjava/lang/String;IIF[B)V"),
@@ -1899,7 +1733,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeLatLngForPixel, "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
MAKE_NATIVE_METHOD(nativeGetTopOffsetPixelsForAnnotationSymbol, "(JLjava/lang/String;)D"),
MAKE_NATIVE_METHOD(nativeJumpTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;DD)V"),
- MAKE_NATIVE_METHOD(nativeEaseTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;JDD)V"),
+ MAKE_NATIVE_METHOD(nativeEaseTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;JDDZ)V"),
MAKE_NATIVE_METHOD(nativeFlyTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;JDD)V"),
MAKE_NATIVE_METHOD(nativeAddCustomLayer, "(JLcom/mapbox/mapboxsdk/layers/CustomLayer;Ljava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeRemoveCustomLayer, "(JLjava/lang/String;)V"),
@@ -1982,6 +1816,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
offlineRegionStatusDownloadStateId = &jni::GetFieldID(env, *offlineRegionStatusClass, "downloadState", "I");
offlineRegionStatusCompletedResourceCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedResourceCount", "J");
offlineRegionStatusCompletedResourceSizeId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedResourceSize", "J");
+ offlineRegionStatusCompletedTileCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedTileCount", "J");
+ offlineRegionStatusCompletedTileSizeId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedTileSize", "J");
offlineRegionStatusRequiredResourceCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "requiredResourceCount", "J");
offlineRegionStatusRequiredResourceCountIsPreciseId = &jni::GetFieldID(env, *offlineRegionStatusClass, "requiredResourceCountIsPrecise", "Z");
diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp
index c2f1f5481b..57f84f0ccc 100644
--- a/platform/android/src/jni.hpp
+++ b/platform/android/src/jni.hpp
@@ -1,5 +1,4 @@
-#ifndef MBGL_ANDROID_JNI
-#define MBGL_ANDROID_JNI
+#pragma once
#include <string>
@@ -26,5 +25,3 @@ extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
}
}
-
-#endif
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 58883b2313..0b849976eb 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -26,7 +26,7 @@ void log_egl_string(EGLDisplay display, EGLint name, const char *label) {
if (str == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name,
eglGetError());
- throw new std::runtime_error("eglQueryString() failed");
+ throw std::runtime_error("eglQueryString() failed");
} else {
char buf[513];
for (int len = std::strlen(str), pos = 0; len > 0; len -= 512, pos += 512) {
@@ -42,7 +42,7 @@ void log_gl_string(GLenum name, const char *label) {
if (str == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name,
glGetError());
- throw new std::runtime_error("glGetString() failed");
+ throw std::runtime_error("glGetString() failed");
} else {
char buf[513];
for (int len = std::strlen(reinterpret_cast<const char *>(str)), pos = 0; len > 0;
@@ -54,14 +54,15 @@ void log_gl_string(GLenum name, const char *label) {
}
}
-NativeMapView::NativeMapView(JNIEnv *env, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_)
+NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_)
: mbgl::View(*this),
+ env(env_),
pixelRatio(pixelRatio_),
availableProcessors(availableProcessors_),
totalMemory(totalMemory_) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
- assert(env != nullptr);
+ assert(env_ != nullptr);
assert(obj_ != nullptr);
if (env->GetJavaVM(&vm) < 0) {
@@ -90,8 +91,6 @@ NativeMapView::NativeMapView(JNIEnv *env, jobject obj_, float pixelRatio_, int a
size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f;
map->setSourceTileCacheSize(cacheSize);
-
- map->pause();
}
NativeMapView::~NativeMapView() {
@@ -106,16 +105,10 @@ NativeMapView::~NativeMapView() {
map.reset();
fileSource.reset();
- jint ret;
- JNIEnv *env = nullptr;
- ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
- if (ret == JNI_OK) {
- env->DeleteWeakGlobalRef(obj);
- } else {
- mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
- throw new std::runtime_error("GetEnv() failed");
- }
+ env->DeleteWeakGlobalRef(obj);
+
obj = nullptr;
+ env = nullptr;
vm = nullptr;
}
@@ -134,20 +127,23 @@ std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const {
void NativeMapView::activate() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate");
- assert(vm != nullptr);
+ oldDisplay = eglGetCurrentDisplay();
+ oldReadSurface = eglGetCurrentSurface(EGL_READ);
+ oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
+ oldContext = eglGetCurrentContext();
- renderDetach = attach_jni_thread(vm, &renderEnv, "Map Thread");
+ assert(vm != nullptr);
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
if (!eglMakeCurrent(display, surface, surface, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
+ throw std::runtime_error("eglMakeCurrent() failed");
}
if (!eglSwapInterval(display, 0)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError());
- throw new std::runtime_error("eglSwapInterval() failed");
+ throw std::runtime_error("eglSwapInterval() failed");
}
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
@@ -159,17 +155,21 @@ void NativeMapView::deactivate() {
assert(vm != nullptr);
- if (display != EGL_NO_DISPLAY) {
+ if (oldContext != context && oldContext != EGL_NO_CONTEXT) {
+ if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
+ eglGetError());
+ throw std::runtime_error("eglMakeCurrent() failed");
+ }
+ } else if (display != EGL_NO_DISPLAY) {
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
+ throw std::runtime_error("eglMakeCurrent() failed");
}
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
}
-
- detach_jni_thread(vm, &renderEnv, renderDetach);
}
void NativeMapView::invalidate() {
@@ -178,48 +178,35 @@ void NativeMapView::invalidate() {
assert(vm != nullptr);
assert(obj != nullptr);
- JNIEnv *env = nullptr;
- bool detach = attach_jni_thread(vm, &env, "NativeMapView::invalidate()");
-
env->CallVoidMethod(obj, onInvalidateId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
-
- detach_jni_thread(vm, &env, detach);
}
-void NativeMapView::beforeRender() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::beforeRender()");
+void NativeMapView::render() {
+ activate();
if(sizeChanged){
sizeChanged = false;
glViewport(0, 0, fbWidth, fbHeight);
}
-}
-void NativeMapView::afterRender() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::afterRender()");
-
- assert(vm != nullptr);
- assert(obj != nullptr);
+ map->render();
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
eglGetError());
- throw new std::runtime_error("eglSwapBuffers() failed");
+ throw std::runtime_error("eglSwapBuffers() failed");
}
updateFps();
} else {
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
}
-}
-void NativeMapView::notify() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify()");
- // noop
+ deactivate();
}
mbgl::Map &NativeMapView::getMap() { return *map; }
@@ -243,18 +230,18 @@ void NativeMapView::initializeDisplay() {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
- throw new std::runtime_error("eglGetDisplay() failed");
+ throw std::runtime_error("eglGetDisplay() failed");
}
EGLint major, minor;
if (!eglInitialize(display, &major, &minor)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
- throw new std::runtime_error("eglInitialize() failed");
+ throw std::runtime_error("eglInitialize() failed");
}
if ((major <= 1) && (minor < 3)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
minor);
- throw new std::runtime_error("EGL version is too low");
+ throw std::runtime_error("EGL version is too low");
}
log_egl_string(display, EGL_VENDOR, "Vendor");
@@ -282,29 +269,29 @@ void NativeMapView::initializeDisplay() {
if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
eglGetError());
- throw new std::runtime_error("eglChooseConfig() failed");
+ throw std::runtime_error("eglChooseConfig() failed");
}
if (numConfigs < 1) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
- throw new std::runtime_error("eglChooseConfig() failed");
+ throw std::runtime_error("eglChooseConfig() failed");
}
const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
- throw new std::runtime_error("eglChooseConfig() failed");
+ throw std::runtime_error("eglChooseConfig() failed");
}
config = chooseConfig(configs.get(), numConfigs);
if (config == nullptr) {
mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
- throw new std::runtime_error("No config chosen");
+ throw std::runtime_error("No config chosen");
}
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format);
}
@@ -318,7 +305,7 @@ void NativeMapView::terminateDisplay() {
if (!eglDestroySurface(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
eglGetError());
- throw new std::runtime_error("eglDestroySurface() failed");
+ throw std::runtime_error("eglDestroySurface() failed");
}
surface = EGL_NO_SURFACE;
}
@@ -326,13 +313,13 @@ void NativeMapView::terminateDisplay() {
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
+ throw std::runtime_error("eglMakeCurrent() failed");
}
if (!eglTerminate(display)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
eglGetError());
- throw new std::runtime_error("eglTerminate() failed");
+ throw std::runtime_error("eglTerminate() failed");
}
}
@@ -353,7 +340,7 @@ void NativeMapView::initializeContext() {
if (context == EGL_NO_CONTEXT) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
eglGetError());
- throw new std::runtime_error("eglCreateContext() failed");
+ throw std::runtime_error("eglCreateContext() failed");
}
}
@@ -364,14 +351,14 @@ void NativeMapView::terminateContext() {
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
+ throw std::runtime_error("eglMakeCurrent() failed");
}
if (context != EGL_NO_CONTEXT) {
if (!eglDestroyContext(display, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
eglGetError());
- throw new std::runtime_error("eglDestroyContext() failed");
+ throw std::runtime_error("eglDestroyContext() failed");
}
}
}
@@ -398,21 +385,18 @@ void NativeMapView::createSurface(ANativeWindow *window_) {
if (surface == EGL_NO_SURFACE) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
eglGetError());
- throw new std::runtime_error("eglCreateWindowSurface() failed");
+ throw std::runtime_error("eglCreateWindowSurface() failed");
}
if (!firstTime) {
firstTime = true;
- EGLDisplay oldDisplay = eglGetCurrentDisplay();
- EGLSurface oldReadSurface = eglGetCurrentSurface(EGL_READ);
- EGLSurface oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
- EGLContext oldContext = eglGetCurrentContext();
+ activate();
if (!eglMakeCurrent(display, surface, surface, context)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
+ throw std::runtime_error("eglMakeCurrent() failed");
}
log_gl_string(GL_VENDOR, "Vendor");
@@ -429,35 +413,18 @@ void NativeMapView::createSurface(ANativeWindow *window_) {
return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name));
});
- if (oldDisplay == EGL_NO_DISPLAY) {
- oldDisplay = display;
- }
-
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw new std::runtime_error("eglMakeCurrent() failed");
- }
+ deactivate();
}
-
- resume();
}
void NativeMapView::destroySurface() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
- pause();
-
if (surface != EGL_NO_SURFACE) {
if (!eglDestroySurface(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
eglGetError());
- throw new std::runtime_error("eglDestroySurface() failed");
+ throw std::runtime_error("eglDestroySurface() failed");
}
}
@@ -518,77 +485,77 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
mbgl::Log::Error(mbgl::Event::OpenGL,
"eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
- throw new std::runtime_error("eglGetConfigAttrib() failed");
+ throw std::runtime_error("eglGetConfigAttrib() failed");
}
mbgl::Log::Info(mbgl::Event::OpenGL, "...Caveat: %d", caveat);
@@ -669,42 +636,16 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi
return configId;
}
-void NativeMapView::pause() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause");
-
- if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
- map->pause();
- }
-}
-
-void NativeMapView::resume() {
- mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume");
-
- assert(display != EGL_NO_DISPLAY);
- assert(context != EGL_NO_CONTEXT);
-
- if (surface != EGL_NO_SURFACE) {
- map->resume();
- } else {
- mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready");
- }
-}
-
void NativeMapView::notifyMapChange(mbgl::MapChange change) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");
assert(vm != nullptr);
assert(obj != nullptr);
- JNIEnv *env = nullptr;
- bool detach = attach_jni_thread(vm, &env, "NativeMapView::notifyMapChange()");
-
env->CallVoidMethod(obj, onMapChangedId, change);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
-
- detach_jni_thread(vm, &env, detach);
}
void NativeMapView::enableFps(bool enable) {
@@ -738,15 +679,10 @@ void NativeMapView::updateFps() {
assert(vm != nullptr);
assert(obj != nullptr);
- JNIEnv *env = nullptr;
- bool detach = attach_jni_thread(vm, &env, "NativeMapView::updateFps()");
-
env->CallVoidMethod(obj, onFpsChangedId, fps);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
-
- detach_jni_thread(vm, &env, detach);
}
void NativeMapView::resizeView(int w, int h) {
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index 5926f781ae..40cb012b0b 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -1,5 +1,4 @@
-#ifndef MBGL_ANDROID_NATIVE_MAP_VIEW
-#define MBGL_ANDROID_NATIVE_MAP_VIEW
+#pragma once
#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
@@ -24,10 +23,7 @@ public:
std::array<uint16_t, 2> getFramebufferSize() const override;
void activate() override;
void deactivate() override;
- void notify() override;
void invalidate() override;
- void beforeRender() override;
- void afterRender() override;
void notifyMapChange(mbgl::MapChange) override;
@@ -43,8 +39,7 @@ public:
void createSurface(ANativeWindow *window);
void destroySurface();
- void resume();
- void pause();
+ void render();
void enableFps(bool enable);
void updateFps();
@@ -61,9 +56,16 @@ private:
private:
JavaVM *vm = nullptr;
+ JNIEnv *env = nullptr;
jweak obj = nullptr;
ANativeWindow *window = nullptr;
+
+ EGLDisplay oldDisplay = EGL_NO_DISPLAY;
+ EGLSurface oldReadSurface = EGL_NO_SURFACE;
+ EGLSurface oldDrawSurface = EGL_NO_SURFACE;
+ EGLContext oldContext = EGL_NO_CONTEXT;
+
EGLDisplay display = EGL_NO_DISPLAY;
EGLSurface surface = EGL_NO_SURFACE;
EGLContext context = EGL_NO_CONTEXT;
@@ -88,9 +90,6 @@ private:
int availableProcessors = 0;
size_t totalMemory = 0;
- jboolean renderDetach = false;
- JNIEnv *renderEnv = nullptr;
-
// Ensure these are initialised last
std::unique_ptr<mbgl::DefaultFileSource> fileSource;
std::unique_ptr<mbgl::Map> map;
@@ -98,5 +97,3 @@ private:
};
}
}
-
-#endif
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
new file mode 100644
index 0000000000..7a28a6139c
--- /dev/null
+++ b/platform/android/src/run_loop.cpp
@@ -0,0 +1,213 @@
+#include "run_loop_impl.hpp"
+
+#include <mbgl/util/thread_context.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+#include <android/looper.h>
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <stdexcept>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#define PIPE_OUT 0
+#define PIPE_IN 1
+
+namespace {
+
+using namespace mbgl::util;
+static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
+
+int looperCallbackNew(int fd, int, void* data) {
+ int buffer[1];
+ while (read(fd, buffer, sizeof(buffer)) > 0) {}
+
+ auto loop = reinterpret_cast<ALooper*>(data);
+ ALooper_wake(loop);
+
+ return 1;
+}
+
+int looperCallbackDefault(int fd, int, void* data) {
+ int buffer[1];
+ while (read(fd, buffer, sizeof(buffer)) > 0) {}
+
+ auto runLoop = reinterpret_cast<RunLoop*>(data);
+ runLoop->runOnce();
+
+ return 1;
+}
+
+} // namespace
+
+namespace mbgl {
+namespace util {
+
+RunLoop::Impl::Impl(RunLoop* runLoop, RunLoop::Type type) {
+ using namespace mbgl::android;
+ detach = attach_jni_thread(theJVM, &env, "");
+
+ loop = ALooper_prepare(0);
+ assert(loop);
+
+ ALooper_acquire(loop);
+
+ if (pipe(fds)) {
+ throw std::runtime_error("Failed to create pipe.");
+ }
+
+ if (fcntl(fds[PIPE_OUT], F_SETFL, O_NONBLOCK)) {
+ throw std::runtime_error("Failed to set pipe read end non-blocking.");
+ }
+
+ int ret = 0;
+
+ switch (type) {
+ case Type::New:
+ ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
+ ALOOPER_EVENT_INPUT, looperCallbackNew, loop);
+ break;
+ case Type::Default:
+ ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
+ ALOOPER_EVENT_INPUT, looperCallbackDefault, runLoop);
+ break;
+ }
+
+ if (ret != 1) {
+ throw std::runtime_error("Failed to add file descriptor to Looper.");
+ }
+}
+
+RunLoop::Impl::~Impl() {
+ if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) {
+ throw std::runtime_error("Failed to remove file descriptor from Looper.");
+ }
+
+ if (close(fds[PIPE_IN]) || close(fds[PIPE_OUT])) {
+ throw std::runtime_error("Failed to close file descriptor.");
+ }
+
+ ALooper_release(loop);
+
+ using namespace mbgl::android;
+ detach_jni_thread(theJVM, &env, detach);
+}
+
+void RunLoop::Impl::wake() {
+ if (write(fds[PIPE_IN], "\n", 1) == -1) {
+ throw std::runtime_error("Failed to write to file descriptor.");
+ }
+}
+
+void RunLoop::Impl::addRunnable(Runnable* runnable) {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+
+ if (runnable->iter == runnables.end()) {
+ auto iter = runnables.insert(runnables.end(), runnable);
+ runnable->iter = std::move(iter);
+ }
+
+ wake();
+}
+
+void RunLoop::Impl::removeRunnable(Runnable* runnable) {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+
+ if (runnable->iter != runnables.end()) {
+ runnables.erase(runnable->iter);
+ runnable->iter = runnables.end();
+ }
+}
+
+void RunLoop::Impl::initRunnable(Runnable* runnable) {
+ runnable->iter = runnables.end();
+}
+
+Milliseconds RunLoop::Impl::processRunnables() {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+
+ auto now = Clock::now();
+ auto nextDue = TimePoint::max();
+
+ // O(N) but in the render thread where we get tons
+ // of messages, the size of the list is usually 1~2.
+ for (auto iter = runnables.begin(); iter != runnables.end();) {
+ Runnable* runnable = *(iter++);
+
+ auto const dueTime = runnable->dueTime();
+ if (dueTime <= now) {
+ runnable->runTask();
+ } else {
+ nextDue = std::min(nextDue, dueTime);
+ }
+ }
+
+ if (runnables.empty() || nextDue == TimePoint::max()) {
+ return Milliseconds(-1);
+ } else {
+ return std::chrono::duration_cast<Milliseconds>(nextDue - now);
+ }
+}
+
+RunLoop* RunLoop::Get() {
+ return current.get();
+}
+
+RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) {
+ current.set(this);
+}
+
+RunLoop::~RunLoop() {
+ current.set(nullptr);
+}
+
+LOOP_HANDLE RunLoop::getLoopHandle() {
+ return current.get()->impl.get();
+}
+
+void RunLoop::push(std::shared_ptr<WorkTask> task) {
+ withMutex([&] { queue.push(std::move(task)); });
+ impl->wake();
+}
+
+void RunLoop::run() {
+ MBGL_VERIFY_THREAD(tid);
+
+ impl->running = true;
+
+ int outFd, outEvents;
+ char *outData = nullptr;
+
+ while (impl->running) {
+ process();
+ auto timeout = impl->processRunnables().count();
+ ALooper_pollAll(timeout, &outFd, &outEvents, reinterpret_cast<void**>(&outData));
+ }
+}
+
+void RunLoop::runOnce() {
+ MBGL_VERIFY_THREAD(tid);
+
+ process();
+ impl->processRunnables();
+}
+
+void RunLoop::stop() {
+ impl->running = false;
+ impl->wake();
+}
+
+void RunLoop::addWatch(int, Event, std::function<void(int, Event)>&&) {
+ throw std::runtime_error("Not implemented.");
+}
+
+void RunLoop::removeWatch(int) {
+ throw std::runtime_error("Not implemented.");
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp
new file mode 100644
index 0000000000..d855728b60
--- /dev/null
+++ b/platform/android/src/run_loop_impl.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "jni.hpp"
+
+#include <mbgl/util/atomic.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <list>
+#include <memory>
+#include <mutex>
+
+struct ALooper;
+
+namespace mbgl {
+namespace util {
+
+class RunLoop::Impl {
+public:
+ class Runnable {
+ public:
+ virtual ~Runnable() = default;
+
+ virtual void runTask() = 0;
+ virtual TimePoint dueTime() const = 0;
+
+ std::list<Runnable*>::iterator iter;
+ };
+
+ Impl(RunLoop*, RunLoop::Type);
+ ~Impl();
+
+ void wake();
+
+ void addRunnable(Runnable*);
+ void removeRunnable(Runnable*);
+ void initRunnable(Runnable*);
+
+ Milliseconds processRunnables();
+
+private:
+ friend RunLoop;
+
+ int fds[2];
+
+ JNIEnv *env = nullptr;
+ bool detach = false;
+
+ ALooper* loop = nullptr;
+ util::Atomic<bool> running;
+
+ std::recursive_mutex mtx;
+ std::list<Runnable*> runnables;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/android/src/timer.cpp b/platform/android/src/timer.cpp
new file mode 100644
index 0000000000..741005df23
--- /dev/null
+++ b/platform/android/src/timer.cpp
@@ -0,0 +1,78 @@
+#include "run_loop_impl.hpp"
+
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/timer.hpp>
+
+#include <functional>
+
+namespace mbgl {
+namespace util {
+
+class Timer::Impl : public RunLoop::Impl::Runnable {
+public:
+ Impl() {
+ loop->initRunnable(this);
+ }
+
+ ~Impl() {
+ stop();
+ }
+
+ void start(Duration timeout, Duration repeat_, std::function<void ()>&& task_) {
+ stop();
+
+ repeat = repeat_;
+ task = std::move(task_);
+ due = Clock::now() + timeout;
+
+ loop->addRunnable(this);
+ }
+
+ void stop() {
+ task = nullptr;
+ loop->removeRunnable(this);
+ }
+
+ void reschedule() {
+ if (repeat != Duration::zero()) {
+ due = Clock::now() + repeat;
+ loop->addRunnable(this);
+ } else {
+ stop();
+ }
+ }
+
+ TimePoint dueTime() const override {
+ return due;
+ }
+
+ void runTask() override {
+ task();
+ reschedule();
+ }
+
+private:
+ TimePoint due;
+ Duration repeat;
+
+ RunLoop::Impl* loop = reinterpret_cast<RunLoop::Impl*>(RunLoop::getLoopHandle());
+
+ std::function<void()> task;
+};
+
+Timer::Timer()
+ : impl(std::make_unique<Impl>()) {
+}
+
+Timer::~Timer() = default;
+
+void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) {
+ impl->start(timeout, repeat, std::move(cb));
+}
+
+void Timer::stop() {
+ impl->stop();
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md
index 35032bf5a9..4d7addea0d 100644
--- a/platform/android/tests/docs/UI_TESTS.md
+++ b/platform/android/tests/docs/UI_TESTS.md
@@ -31,15 +31,15 @@ $ ./gradlew 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 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 behaviour,
+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
@@ -59,7 +59,7 @@ You can generate JaCoCo reports from espresso tests by
- running the gradle task `createMockDebugCoverageReport` when executing tests.
## Running Espresso test automatically on AWS Device Farm
-To automatically execute Espresso tests as part of our CI build, we have created a Python [script](https://github.com/mapbox/mapbox-gl-native/blob/aws-devicelab/android/scripts/devicefarm.py).
+To automatically execute Espresso tests as part of our CI build, we have created a Python script called [`devicefarm.py`](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/tests/scripts/devicefarm.py).
This script is responsible for:
- uploading an APK + test APK
diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md
index 15850b8b12..e32a36ef78 100644
--- a/platform/android/tests/docs/UNIT_TESTS.md
+++ b/platform/android/tests/docs/UNIT_TESTS.md
@@ -1,36 +1,80 @@
# Unit tests
-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,
+**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.
-## Running Unit tests locally
-To run Unit tests locally you switch to the Unit Tests build variant, then right click the corresponding test class or method and select "Run ...".
+## Writing unit tests
+Unit tests for an Android project are located in the `test` folder:
-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/platforms/android/java/MapboxGLAndroidSDKTestApp/src/test/java`
-* Click OK to save the new configuration
+<img width="348" alt="screen shot 2016-03-16 at 17 24 31" src="https://cloud.githubusercontent.com/assets/2151639/13829301/9ea62418-eb9c-11e5-8ab3-9d6c6bed80a3.png">
-You can also run the tests from the command line with:
+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(){
+
+ }
+
+}
```
-$ ./gradlew test --continue -p MapboxGLAndroidSDKTestApp
+
+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(){
+
+ }
+
+}
```
-### 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.
+## Running unit tests
+You can run a test locally by right clicking and selecting run:
+
+<img width="322" alt="screen shot 2016-03-16 at 17 46 09" src="https://cloud.githubusercontent.com/assets/2151639/13829762/0877af18-eb9f-11e5-87df-6dfb3be64beb.png">
+
+If you like, you can also run with test coverage enabled. This will show you the following dialog:
+
+<img width="465" alt="screen shot 2016-03-16 at 17 58 34" src="https://cloud.githubusercontent.com/assets/2151639/13830064/d097aeca-eba0-11e5-94bd-e38fd1079937.png">
+
+You can also run the tests from the command line with:
+```
+$ ./gradlew test --continue -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
+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/platform/android/bitrise.yml#L48):
@@ -38,3 +82,11 @@ You can find this gradle command in our [buildscript](https://github.com/mapbox/
```
$ ./gradlew testReleaseUnitTest --continue
```
+
+
+## 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/darwin/README.md b/platform/darwin/README.md
new file mode 100644
index 0000000000..43e229028c
--- /dev/null
+++ b/platform/darwin/README.md
@@ -0,0 +1,10 @@
+# Darwin
+
+The code in the Darwin platform targets Apple platforms but is not specific
+to iOS or OS X. This code is not distributed as an SDK in itself, but is required
+by the [Mapbox iOS SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/ios)
+and [Mapbox OS X SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/osx).
+
+These files depend on the Foundation and Core Foundation frameworks but do not
+depend on iOS- or OS X–specific frameworks, such as UIKit or AppKit. Any
+non-cross-platform code is guarded by TargetConditionals.h macros.
diff --git a/platform/darwin/include/MGLAnnotation.h b/platform/darwin/include/MGLAnnotation.h
deleted file mode 100644
index e4726f9503..0000000000
--- a/platform/darwin/include/MGLAnnotation.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-#import <TargetConditionals.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- The `MGLAnnotation` protocol is used to provide annotation-related information to a map view. To use this protocol, you adopt it in any custom objects that store or represent annotation data. Each object then serves as the source of information about a single map annotation and provides critical information, such as the annotation’s location on the map. Annotation objects do not provide the visual representation of the annotation but typically coordinate (in conjunction with the map view’s delegate) the creation of an appropriate objects to handle the display.
-
- An object that adopts this protocol must implement the `coordinate` property. The other methods of this protocol are optional.
- */
-@protocol MGLAnnotation <NSObject>
-
-#pragma mark Position Attributes
-
-/** The center point (specified as a map coordinate) of the annotation. (required) (read-only) */
-@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
-
-@optional
-
-#pragma mark Title Attributes
-
-/**
- The string containing the annotation’s title.
-
- Although this property is optional, if you support the selection of annotations in your map view, you are expected to provide this property. This string is displayed in the callout for the associated annotation.
- */
-@property (nonatomic, readonly, copy, nullable) NSString *title;
-
-/**
- The string containing the annotation’s subtitle.
-
- This string is displayed in the callout for the associated annotation.
- */
-@property (nonatomic, readonly, copy, nullable) NSString *subtitle;
-
-#if !TARGET_OS_IPHONE
-
-/** The string containing the annotation’s tooltip. */
-@property (nonatomic, readonly, copy, nullable) NSString *toolTip;
-
-#endif
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLGeometry.h b/platform/darwin/include/MGLGeometry.h
deleted file mode 100644
index 8231eed442..0000000000
--- a/platform/darwin/include/MGLGeometry.h
+++ /dev/null
@@ -1,98 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-#import <CoreGraphics/CGBase.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Defines the area spanned by an `MGLCoordinateBounds`. */
-typedef struct MGLCoordinateSpan {
- /** Latitudes spanned by an `MGLCoordinateBounds`. */
- CLLocationDegrees latitudeDelta;
- /** Longitudes spanned by an `MGLCoordinateBounds`. */
- CLLocationDegrees longitudeDelta;
-} MGLCoordinateSpan;
-
-/** Creates a new `MGLCoordinateSpan` from the given latitudinal and longitudinal deltas. */
-NS_INLINE MGLCoordinateSpan MGLCoordinateSpanMake(CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta) {
- MGLCoordinateSpan span;
- span.latitudeDelta = latitudeDelta;
- span.longitudeDelta = longitudeDelta;
- return span;
-}
-
-/** Returns `YES` if the two coordinate spans represent the same latitudinal change and the same longitudinal change. */
-NS_INLINE BOOL MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpan span1, MGLCoordinateSpan span2) {
- return (span1.latitudeDelta == span2.latitudeDelta &&
- span1.longitudeDelta == span2.longitudeDelta);
-}
-
-/** An area of zero width and zero height. */
-extern const MGLCoordinateSpan MGLCoordinateSpanZero;
-
-/** A rectangular area as measured on a two-dimensional map projection. */
-typedef struct MGLCoordinateBounds {
- /** Coordinate at the southwest corner. */
- CLLocationCoordinate2D sw;
- /** Coordinate at the northeast corner. */
- CLLocationCoordinate2D ne;
-} MGLCoordinateBounds;
-
-/** Creates a new `MGLCoordinateBounds` structure from the given southwest and northeast coordinates. */
-NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsMake(CLLocationCoordinate2D sw, CLLocationCoordinate2D ne) {
- MGLCoordinateBounds bounds;
- bounds.sw = sw;
- bounds.ne = ne;
- return bounds;
-}
-
-/** Returns `YES` if the two coordinate bounds are equal to each other. */
-NS_INLINE BOOL MGLCoordinateBoundsEqualToCoordinateBounds(MGLCoordinateBounds bounds1, MGLCoordinateBounds bounds2) {
- return (bounds1.sw.latitude == bounds2.sw.latitude &&
- bounds1.sw.longitude == bounds2.sw.longitude &&
- bounds1.ne.latitude == bounds2.ne.latitude &&
- bounds1.ne.longitude == bounds2.ne.longitude);
-}
-
-/** Returns the area spanned by the coordinate bounds. */
-NS_INLINE MGLCoordinateSpan MGLCoordinateBoundsGetCoordinateSpan(MGLCoordinateBounds bounds) {
- return MGLCoordinateSpanMake(bounds.ne.latitude - bounds.sw.latitude,
- bounds.ne.longitude - bounds.sw.longitude);
-}
-
-/** Returns a coordinate bounds with southwest and northeast coordinates that are offset from those of the source bounds. */
-NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsOffset(MGLCoordinateBounds bounds, MGLCoordinateSpan offset) {
- MGLCoordinateBounds offsetBounds = bounds;
- offsetBounds.sw.latitude += offset.latitudeDelta;
- offsetBounds.sw.longitude += offset.longitudeDelta;
- offsetBounds.ne.latitude += offset.latitudeDelta;
- offsetBounds.ne.longitude += offset.longitudeDelta;
- return offsetBounds;
-}
-
-/** Returns `YES` if the coordinate bounds covers no area.
- Note that a bounds may be empty but have a non-zero coordinate span (e.g., when its northeast point lies due north of its southwest point). */
-NS_INLINE BOOL MGLCoordinateBoundsIsEmpty(MGLCoordinateBounds bounds) {
- MGLCoordinateSpan span = MGLCoordinateBoundsGetCoordinateSpan(bounds);
- return span.latitudeDelta == 0 || span.longitudeDelta == 0;
-}
-
-/** Returns a formatted string for the given coordinate bounds. */
-NS_INLINE NSString *MGLStringFromCoordinateBounds(MGLCoordinateBounds bounds) {
- return [NSString stringWithFormat:@"{ sw = {%.1f, %.1f}, ne = {%.1f, %.1f}}",
- bounds.sw.latitude, bounds.sw.longitude,
- bounds.ne.latitude, bounds.ne.longitude];
-}
-
-/** Returns radians, converted from degrees. */
-NS_INLINE CGFloat MGLRadiansFromDegrees(CLLocationDegrees degrees) {
- return (CGFloat)(degrees * M_PI) / 180;
-}
-
-/** Returns degrees, converted from radians. */
-NS_INLINE CLLocationDegrees MGLDegreesFromRadians(CGFloat radians) {
- return radians * 180 / M_PI;
-}
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLMapCamera.h b/platform/darwin/include/MGLMapCamera.h
deleted file mode 100644
index 3cb902ba52..0000000000
--- a/platform/darwin/include/MGLMapCamera.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreGraphics/CoreGraphics.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** An `MGLMapCamera` object represents a viewpoint from which the user observes some point on an `MGLMapView`. */
-@interface MGLMapCamera : NSObject <NSSecureCoding, NSCopying>
-
-/** Coordinate at the center of the map view. */
-@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
-
-/** Heading measured in degrees clockwise from true north. */
-@property (nonatomic) CLLocationDirection heading;
-
-/** Pitch toward the horizon measured in degrees, with 0 degrees resulting in a two-dimensional map. */
-@property (nonatomic) CGFloat pitch;
-
-/** Meters above ground level. */
-@property (nonatomic) CLLocationDistance altitude;
-
-/** Returns a new camera with all properties set to 0. */
-+ (instancetype)camera;
-
-/**
- Returns a new camera using based on information about the camera’s viewpoint and focus point.
-
- @param centerCoordinate The geographic coordinate on which the map should be centered.
- @param eyeCoordinate The geometric coordinate at which the camera should be situated.
- @param eyeAltitude The altitude (measured in meters) above the map at which the camera should be situated. The altitude may be less than the distance from the camera’s viewpoint to the camera’s focus point.
- */
-+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
- fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
- eyeAltitude:(CLLocationDistance)eyeAltitude;
-
-/**
- Returns a new camera with the given distance, pitch, and heading.
-
- @param centerCoordinate The geographic coordinate on which the map should be centered.
- @param distance The straight-line distance from the viewpoint to the `centerCoordinate`.
- @param pitch The viewing angle of the camera, measured in degrees. A value of `0` results in a camera pointed straight down at the map. Angles greater than `0` result in a camera angled toward the horizon.
- @param heading The camera’s heading, measured in degrees clockwise from true north. A value of `0` means that the top edge of the map view corresponds to true north. The value `90` means the top of the map is pointing due east. The value `180` means the top of the map points due south, and so on.
- */
-+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
- fromDistance:(CLLocationDistance)distance
- pitch:(CGFloat)pitch
- heading:(CLLocationDirection)heading;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLMultiPoint.h b/platform/darwin/include/MGLMultiPoint.h
deleted file mode 100644
index ad9db1e681..0000000000
--- a/platform/darwin/include/MGLMultiPoint.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLShape.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The `MGLMultiPoint` class is an abstract superclass used to define shapes composed of multiple points. You should not create instances of this class directly. Instead, you should create instances of the `MGLPolyline` or `MGLPolygon` classes. However, you can use the method and properties of this class to access information about the specific points associated with the line or polygon. */
-@interface MGLMultiPoint : MGLShape
-
-/** The number of points associated with the shape. (read-only) */
-@property (nonatomic, readonly) NSUInteger pointCount;
-
-/**
- Retrieves one or more coordinates associated with the shape.
-
- @param coords On input, you must provide a C array of structures large enough to hold the desired number of coordinates. On output, this structure contains the requested coordinate data.
- @param range The range of points you want. The `location` field indicates the first point you are requesting, with `0` being the first point, `1` being the second point, and so on. The `length` field indicates the number of points you want. The array in _`coords`_ must be large enough to accommodate the number of requested coordinates.
- */
-- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLOfflinePack.h b/platform/darwin/include/MGLOfflinePack.h
deleted file mode 100644
index 783650590a..0000000000
--- a/platform/darwin/include/MGLOfflinePack.h
+++ /dev/null
@@ -1,201 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLOfflineRegion.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- The state an offline pack is currently in.
- */
-typedef NS_ENUM (NSInteger, MGLOfflinePackState) {
- /**
- It is unknown whether the pack is inactive, active, or complete.
-
- This is the initial state of a pack. The state of a pack becomes known by
- the time the shared `MGLOfflineStorage` object sends the first
- `MGLOfflinePackProgressChangedNotification` about the pack. For inactive
- packs, you must explicitly request a progress update using the
- `-[MGLOfflinePack requestProgress]` method.
-
- An invalid pack always has a state of `MGLOfflinePackStateInvalid`, never
- `MGLOfflinePackStateUnknown`.
- */
- MGLOfflinePackStateUnknown = 0,
- /**
- The pack is incomplete and is not currently downloading.
-
- This is the initial state of a pack that is created using the
- `-[MGLOfflinePack addPackForRegion:withContext:completionHandler:]` method,
- as well as after the `-[MGLOfflinePack suspend]` method is
- called.
- */
- MGLOfflinePackStateInactive = 1,
- /**
- The pack is incomplete and is currently downloading.
-
- This is the state of a pack after the `-[MGLOfflinePack resume]` method is
- called.
- */
- MGLOfflinePackStateActive = 2,
- /**
- The pack has downloaded to completion.
- */
- MGLOfflinePackStateComplete = 3,
- /**
- The pack has been removed using the
- `-[MGLOfflineStorage removePack:withCompletionHandler:]` method. Sending
- any message to the pack will raise an exception.
- */
- MGLOfflinePackStateInvalid = 4,
-};
-
-/**
- A structure containing information about an offline pack’s current download
- progress.
- */
-typedef struct MGLOfflinePackProgress {
- /**
- The number of resources that have been completely downloaded and are ready
- to use offline.
- */
- uint64_t countOfResourcesCompleted;
- /**
- The cumulative size of the downloaded resources on disk, measured in bytes.
- */
- uint64_t countOfBytesCompleted;
- /**
- The minimum number of resources that must be downloaded in order to view
- the pack’s full region without any omissions.
-
- At the beginning of a download, this count is a lower bound; the number of
- expected resources may increase as the download progresses.
- */
- uint64_t countOfResourcesExpected;
- /**
- The maximum number of resources that must be downloaded in order to view
- the pack’s full region without any omissions.
-
- At the beginning of a download, when the exact number of required resources
- is unknown, this field is set to `UINT64_MAX`. Thus this count is always an
- upper bound.
- */
- uint64_t maximumResourcesExpected;
-} MGLOfflinePackProgress;
-
-/**
- An `MGLOfflinePack` represents a collection of resources necessary for viewing
- a region offline to a local database.
-
- To create an instance of `MGLOfflinePack`, use the
- `+[MGLOfflineStorage addPackForRegion:withContext:completionHandler:]` method.
- A pack created using `-[MGLOfflinePack init]` is immediately invalid.
- */
-@interface MGLOfflinePack : NSObject
-
-/**
- The region for which the pack manages resources.
- */
-@property (nonatomic, readonly) id <MGLOfflineRegion> region;
-
-/**
- Arbitrary data stored alongside the downloaded resources.
-
- The context typically holds application-specific information for identifying
- the pack, such as a user-selected name.
- */
-@property (nonatomic, readonly) NSData *context;
-
-/**
- The pack’s current state.
-
- The state of an inactive or completed pack is computed lazily and is set to
- `MGLOfflinePackStateUnknown` by default. To request the pack’s status, use the
- `-requestProgress` method. To get notified when the state becomes known and
- when it changes, observe KVO change notifications on this pack’s `state` key
- path. Alternatively, you can add an observer for
- `MGLOfflinePackProgressChangedNotification`s about this pack that come from the
- default notification center.
- */
-@property (nonatomic, readonly) MGLOfflinePackState state;
-
-/**
- The pack’s current progress.
-
- The progress of an inactive or completed pack is computed lazily, and all its
- fields are set to 0 by default. To request the pack’s progress, use the
- `-requestProgress` method. To get notified when the progress becomes
- known and when it changes, observe KVO change notifications on this pack’s
- `state` key path. Alternatively, you can add an observer for
- `MGLOfflinePackProgressChangedNotification`s about this pack that come from the
- default notification center.
- */
-@property (nonatomic, readonly) MGLOfflinePackProgress progress;
-
-/**
- Resumes downloading if the pack is inactive.
-
- A pack resumes asynchronously. To get notified when this pack resumes, observe
- KVO change notifications on this pack’s `state` key path. Alternatively, you
- can add an observer for `MGLOfflinePackProgressChangedNotification`s about this
- pack that come from the default notification center.
-
- When a pack resumes after being suspended, it may begin by iterating over the
- already downloaded resources. As a result, the `progress` structure’s
- `countOfResourcesCompleted` field may revert to 0 before rapidly returning to
- the level of progress at the time the pack was suspended.
-
- To temporarily suspend downloading, call the `-suspend` method.
- */
-- (void)resume;
-
-/**
- Temporarily stops downloading if the pack is active.
-
- A pack suspends asynchronously. To get notified when this pack resumes, observe
- KVO change notifications on this pack’s `state` key path. Alternatively, you
- can add an observer for `MGLOfflinePackProgressChangedNotification` about this
- pack that come from the default notification center.
-
- If the pack previously reached a higher level of progress before being
- suspended, it may wait to suspend until it returns to that level.
-
- To resume downloading, call the `-resume` method.
- */
-- (void)suspend;
-
-/**
- Request an asynchronous update to the pack’s `state` and `progress` properties.
-
- The state and progress of an inactive or completed pack are computed lazily. If
- you need the state or progress of a pack whose `state` property is currently
- set to `MGLOfflinePackStateUnknown`, observe KVO change notifications on this
- pack’s `state` key path, then call this method. Alternatively, you can add an
- observer for `MGLOfflinePackProgressChangedNotification` about this pack that
- come from the default notification center.
- */
-- (void)requestProgress;
-
-@end
-
-/**
- Methods for round-tripping `MGLOfflinePackProgress` values.
- */
-@interface NSValue (MGLOfflinePackAdditions)
-
-/**
- Creates a new value object containing the given `MGLOfflinePackProgress`
- structure.
-
- @param progress The value for the new object.
- @return A new value object that contains the offline pack progress information.
- */
-+ (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress;
-
-/**
- The `MGLOfflinePackProgress` structure representation of the value.
- */
-@property (readonly) MGLOfflinePackProgress MGLOfflinePackProgressValue;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLOverlay.h b/platform/darwin/include/MGLOverlay.h
deleted file mode 100644
index aa80fe3900..0000000000
--- a/platform/darwin/include/MGLOverlay.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLAnnotation.h"
-#import "MGLGeometry.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- The `MGLOverlay` protocol defines a specific type of annotation that represents both a point and an area on a map. Overlay objects are essentially data objects that contain the geographic data needed to represent the map area. For example, overlays can take the form of common shapes such as rectangles and circles. They can also describe polygons and other complex shapes.
-
- You use overlays to layer more sophisticated content on top of a map view. For example, you could use an overlay to show the boundaries of a national park or trace a bus route along city streets. The Mapbox iOS SDK defines several concrete classes that conform to this protocol and define standard shapes.
-
- Because overlays are also annotations, they have similar usage pattern to annotations. When added to a map view using the `-addOverlay:` method, that view detects whenever the overlay’s defined region intersects the visible portion of the map. At that point, the map view asks its delegate to provide a special overlay view to draw the visual representation of the overlay. If you add an overlay to a map view as an annotation instead, it is treated as an annotation with a single point.
- */
-@protocol MGLOverlay <MGLAnnotation>
-
-/**
- The approximate center point of the overlay area. (required) (read-only)
-
- This point is typically set to the center point of the map’s bounding rectangle. It is used as the anchor point for any callouts displayed for the annotation.
- */
-@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
-
-/**
- The cooordinate rectangle that encompasses the overlay. (required) (read-only)
-
- This property contains the smallest rectangle that completely encompasses the overlay. Implementers of this protocol must set this area when implementing their overlay class, and after setting it, you must not change it.
- */
-@property (nonatomic, readonly) MGLCoordinateBounds overlayBounds;
-
-/**
- Returns a Boolean indicating whether the specified rectangle intersects the receiver’s shape.
-
- You can implement this method to provide more specific bounds checking for an overlay. If you do not implement it, the bounding rectangle is used to detect intersections.
-
- @param overlayBounds The rectangle to intersect with the receiver’s area.
- @return `YES` if any part of the map rectangle intersects the receiver’s shape or `NO` if it does not.
- */
-- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLPointAnnotation.h b/platform/darwin/include/MGLPointAnnotation.h
deleted file mode 100644
index d186cbff18..0000000000
--- a/platform/darwin/include/MGLPointAnnotation.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLShape.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The `MGLPointAnnotation` class defines a concrete annotation object located at a specified point. You can use this class, rather than define your own, in situations where all you want to do is associate a point on the map with a title. */
-@interface MGLPointAnnotation : MGLShape
-
-/** The coordinate point of the annotation, specified as a latitude and longitude. */
-@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLPolygon.h b/platform/darwin/include/MGLPolygon.h
deleted file mode 100644
index c5480264fb..0000000000
--- a/platform/darwin/include/MGLPolygon.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLMultiPoint.h"
-#import "MGLOverlay.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The `MGLPolygon` class represents a shape consisting of one or more points that define a closed polygon. The points are connected end-to-end in the order they are provided. The first and last points are connected to each other to create the closed shape. */
-@interface MGLPolygon : MGLMultiPoint <MGLOverlay>
-
-/**
- Creates and returns an `MGLPolygon` object from the specified set of coordinates.
-
- @param coords The array of coordinates defining the shape. The data in this array is copied to the new object.
- @param count The number of items in the `coords` array.
- @return A new polygon object.
- */
-+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords
- count:(NSUInteger)count;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLPolyline.h b/platform/darwin/include/MGLPolyline.h
deleted file mode 100644
index c28299c7e0..0000000000
--- a/platform/darwin/include/MGLPolyline.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLMultiPoint.h"
-#import "MGLOverlay.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The `MGLPolyline` class represents a shape consisting of one or more points that define connecting line segments. The points are connected end-to-end in the order they are provided. The first and last points are not connected to each other. */
-@interface MGLPolyline : MGLMultiPoint <MGLOverlay>
-
-/**
- Creates and returns an `MGLPolyline` object from the specified set of coordinates.
-
- @param coords The array of coordinates defining the shape. The data in this array is copied to the new object.
- @param count The number of items in the `coords` array.
- @return A new polyline object.
- */
-+ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords
- count:(NSUInteger)count;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLShape.h b/platform/darwin/include/MGLShape.h
deleted file mode 100644
index c1b84816f2..0000000000
--- a/platform/darwin/include/MGLShape.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLAnnotation.h"
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The `MGLShape` class is an abstract class that defines the basic properties for all shape-based annotation objects. This class must be subclassed and cannot be used as is. Subclasses are responsible for defining the geometry of the shape and providing an appropriate value for the coordinate property inherited from the `MGLAnnotation` protocol. */
-@interface MGLShape : NSObject <MGLAnnotation>
-
-/** The title of the shape annotation. The default value of this property is `nil`. */
-@property (nonatomic, copy, nullable) NSString *title;
-
-/** The subtitle of the shape annotation. The default value of this property is `nil`. */
-@property (nonatomic, copy, nullable) NSString *subtitle;
-
-#if !TARGET_OS_IPHONE
-
-/** The tooltip of the shape annotation. The default value of this property is `nil`. */
-@property (nonatomic, copy, nullable) NSString *toolTip;
-
-#endif
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLStyle.h b/platform/darwin/include/MGLStyle.h
deleted file mode 100644
index 65d45dc98e..0000000000
--- a/platform/darwin/include/MGLStyle.h
+++ /dev/null
@@ -1,125 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- A version number identifying the default version of the suite of default styles provided by Mapbox. This version number may be passed into one of the “StyleURLWithVersion” class methods of MGLStyle.
-
- The value of this constant generally corresponds to the latest released version as of the date on which this SDK was published. You can use this constant to ascertain the style used by `MGLMapView` and `MGLTilePyramidOfflineRegion` when no style URL is specified. Consult the <a href="https://www.mapbox.com/api-documentation/#styles">Mapbox Styles API documentation</a> for the most up-to-date style versioning information.
-
- @warning The value of this constant may change in a future release of the SDK. If you use any feature that depends on a specific aspect of a default style – for instance, the minimum zoom level that includes roads – you may use the current value of this constant or the underlying style URL, but do not use the constant itself. Such details may change significantly from version to version.
- */
-static const NSInteger MGLStyleDefaultVersion = 9;
-
-/**
- A collection of convenience methods for creating style URLs of default styles provided by Mapbox. <a href="https://www.mapbox.com/maps/">Learn more about Mapbox default styles</a>.
- */
-@interface MGLStyle : NSObject
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/maps/streets/">Mapbox Streets</a> style.
-
- Streets is a general-purpose style with detailed road and transit networks.
-
- `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style is specified explicitly.
- */
-+ (NSURL *)streetsStyleURL __attribute__((deprecated("Use -streetsStyleURLWithVersion:.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/streets/">Mapbox Streets</a> style.
-
- Streets is a general-purpose style with detailed road and transit networks.
-
- `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style is specified explicitly.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)streetsStyleURLWithVersion:(NSInteger)version;
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/blog/emerald-gl/">Mapbox Emerald</a> style.
-
- Emerald is a tactile style with subtle textures and dramatic hillshading.
- */
-+ (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/outdoors/">Mapbox Outdoors</a> style.
-
- Outdoors is a general-purpose style tailored to outdoor activities.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)outdoorsStyleURLWithVersion:(NSInteger)version;
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Light</a> style.
-
- Light is a subtle, light-colored backdrop for data visualizations.
- */
-+ (NSURL *)lightStyleURL __attribute__((deprecated("Use -lightStyleURLWithVersion:.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Light</a> style.
-
- Light is a subtle, light-colored backdrop for data visualizations.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)lightStyleURLWithVersion:(NSInteger)version;
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Dark</a> style.
-
- Dark is a subtle, dark-colored backdrop for data visualizations.
- */
-+ (NSURL *)darkStyleURL __attribute__((deprecated("Use -darkStyleURLWithVersion:.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Dark</a> style.
-
- Dark is a subtle, dark-colored backdrop for data visualizations.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)darkStyleURLWithVersion:(NSInteger)version;
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite</a> style.
-
- Satellite is high-resolution satellite and aerial imagery.
- */
-+ (NSURL *)satelliteStyleURL __attribute__((deprecated("Use -satelliteStyleURLWithVersion:.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite</a> style.
-
- Satellite is high-resolution satellite and aerial imagery.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version;
-
-/**
- Returns the URL to version 8 of the <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a> style.
-
- Satellite Streets combines the high-resolution satellite and aerial imagery of Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox Streets.
- */
-+ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURLWithVersion:.")));
-
-/**
- Returns the URL to the given version of the <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a> style.
-
- Satellite Streets combines the high-resolution satellite and aerial imagery of Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox Streets.
-
- @param version The style’s latest released version. As of publication, the current version is `9`.
- */
-+ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLTypes.h b/platform/darwin/include/MGLTypes.h
deleted file mode 100644
index ccb496816a..0000000000
--- a/platform/darwin/include/MGLTypes.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#pragma once
-
-#if !__has_feature(nullability)
- #define NS_ASSUME_NONNULL_BEGIN
- #define NS_ASSUME_NONNULL_END
- #define nullable
- #define nonnull
- #define null_resettable
- #define _Nullable
- #define _Nonnull
-#endif
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Indicates an error occurred in the Mapbox SDK. */
-extern NSString * const MGLErrorDomain;
-
-typedef NS_ENUM(NSInteger, MGLErrorCode) {
- MGLErrorCodeUnknown = -1,
- MGLErrorCodeNotFound = 1,
- MGLErrorCodeBadServerResponse = 2,
- MGLErrorCodeConnectionFailed = 3,
-};
-
-/** The mode used to track the user location on the map. */
-typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {
- /** The map does not follow the user location. */
- MGLUserTrackingModeNone = 0,
- /** The map follows the user location. */
- MGLUserTrackingModeFollow,
- /** The map follows the user location and rotates when the heading changes. */
- MGLUserTrackingModeFollowWithHeading,
- /** The map follows the user location and rotates when the course changes. */
- MGLUserTrackingModeFollowWithCourse,
-};
-
-NS_ASSUME_NONNULL_END
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wvariadic-macros"
- #ifndef NS_ARRAY_OF
- // Foundation collection classes adopted lightweight generics in iOS 9.0 and OS X 10.11 SDKs.
- #if __has_feature(objc_generics) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100)
- /** Inserts a type specifier for a pointer to a lightweight generic with the given collection and object classes. Use a `*` for any non-`id` object classes but no `*` for the collection class. */
- #define NS_ARRAY_OF(ObjectClass...) NSArray <ObjectClass>
- #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray <ObjectClass>
- #define NS_SET_OF(ObjectClass...) NSSet <ObjectClass>
- #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet <ObjectClass>
- #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary <ObjectClass>
- #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary <ObjectClass>
- #else
- #define NS_ARRAY_OF(ObjectClass...) NSArray
- #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray
- #define NS_SET_OF(ObjectClass...) NSSet
- #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet
- #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary
- #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary
- #endif
- #endif
-#pragma clang diagnostic pop
diff --git a/platform/darwin/resources/Base.lproj/Foundation.strings b/platform/darwin/resources/Base.lproj/Foundation.strings
new file mode 100644
index 0000000000..9d63dfa697
--- /dev/null
+++ b/platform/darwin/resources/Base.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ o’clock";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ o’clock";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "east";
+
+/* East, short */
+"COMPASS_E_SHORT" = "E";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "east by north";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "EbN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "east by south";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "EbS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "east-northeast";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ENE";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "east-southeast";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ESE";
+
+/* North, long */
+"COMPASS_N_LONG" = "north";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "north by east";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NbE";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "north by west";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NbW";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "northeast";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NE";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "northeast by east";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NEbE";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "northeast by north";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEbN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "north-northeast";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNE";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "north-northwest";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNW";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "northwest";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NW";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "northwest by north";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NWbN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "northwest by west";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NWbW";
+
+/* South, long */
+"COMPASS_S_LONG" = "south";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "south by east";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SbE";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "south by west";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SbW";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "southeast";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SE";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "southeast by east";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SEbE";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "southeast by south";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SEbS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "south-southeast";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSE";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "south-southwest";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSW";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "southwest";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SW";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "southwest by south";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SWbS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "southwest by west";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SWbW";
+
+/* West, long */
+"COMPASS_W_LONG" = "west";
+
+/* West, short */
+"COMPASS_W_SHORT" = "W";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "west by north";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "WbN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "west by south";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "WbS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "west-northwest";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "WNW";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "west-southwest";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "WSW";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d degree(s)";
+
+/* Degrees format, medium: {degrees} */
+"COORD_DEG_MEDIUM" = "%d°";
+
+/* Degrees format, short: {degrees} */
+"COORD_DEG_SHORT" = "%d°";
+
+/* Coordinate format, long: {degrees}{minutes} */
+"COORD_DM_LONG" = "%1$@ and %2$@";
+
+/* Coordinate format, medium: {degrees}{minutes} */
+"COORD_DM_MEDIUM" = "%1$@%2$@";
+
+/* Coordinate format, short: {degrees}{minutes} */
+"COORD_DM_SHORT" = "%1$@%2$@";
+
+/* Coordinate format, long: {degrees}{minutes}{seconds} */
+"COORD_DMS_LONG" = "%1$@, %2$@, and %3$@";
+
+/* Coordinate format, medium: {degrees}{minutes}{seconds} */
+"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@";
+
+/* Coordinate format, short: {degrees}{minutes}{seconds} */
+"COORD_DMS_SHORT" = "%1$@%2$@%3$@";
+
+/* East longitude format, long: {longitude} */
+"COORD_E_LONG" = "%@ east";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ east";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@E";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@ by %2$@";
+
+/* Coordinate pair format, medium: {latitude}, {longitude} */
+"COORD_FMT_MEDIUM" = "%1$@, %2$@";
+
+/* Coordinate pair format, short: {latitude}, {longitude} */
+"COORD_FMT_SHORT" = "%1$@, %2$@";
+
+/* Minutes format, long */
+"COORD_MIN_LONG" = "%d minute(s)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ north";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ north";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ south";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ south";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d second(s)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ west";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ west";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@W";
+
diff --git a/platform/darwin/resources/en.lproj/Foundation.stringsdict b/platform/darwin/resources/en.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..5f23fff93b
--- /dev/null
+++ b/platform/darwin/resources/en.lproj/Foundation.stringsdict
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d degree</string>
+ <key>other</key>
+ <string>%d degrees</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d minute</string>
+ <key>other</key>
+ <string>%d minutes</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d second</string>
+ <key>other</key>
+ <string>%d seconds</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/include/MGLAccountManager.h b/platform/darwin/src/MGLAccountManager.h
index 63440046f7..63440046f7 100644
--- a/platform/darwin/include/MGLAccountManager.h
+++ b/platform/darwin/src/MGLAccountManager.h
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index bfaf9faae9..31baf4e249 100644
--- a/platform/darwin/src/MGLAccountManager.m
+++ b/platform/darwin/src/MGLAccountManager.m
@@ -1,8 +1,6 @@
#import "MGLAccountManager_Private.h"
-#import "MGLMapView.h"
#import "NSBundle+MGLAdditions.h"
#import "NSProcessInfo+MGLAdditions.h"
-#import "NSString+MGLAdditions.h"
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
#import "MGLMapboxEvents.h"
@@ -108,14 +106,6 @@
} else {
NSLog(@"MGLAccountManager is used in a project that doesn’t have Fabric.");
}
-
- // https://github.com/mapbox/mapbox-gl-native/issues/2966
- mgl_linkBundleCategory();
- mgl_linkStringCategory();
- mgl_linkProcessInfoCategory();
-
- // https://github.com/mapbox/mapbox-gl-native/issues/3113
- [MGLMapView class];
}
#endif
diff --git a/platform/darwin/src/MGLAnnotation.h b/platform/darwin/src/MGLAnnotation.h
new file mode 100644
index 0000000000..7293aeec4f
--- /dev/null
+++ b/platform/darwin/src/MGLAnnotation.h
@@ -0,0 +1,61 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+#import <TargetConditionals.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLAnnotation` protocol is used to provide annotation-related information
+ to a map view. To use this protocol, you adopt it in any custom objects that
+ store or represent annotation data. Each object then serves as the source of
+ information about a single map annotation and provides critical information,
+ such as the annotation’s location on the map. Annotation objects do not provide
+ the visual representation of the annotation but typically coordinate (in
+ conjunction with the map view’s delegate) the creation of an appropriate
+ objects to handle the display.
+
+ An object that adopts this protocol must implement the `coordinate` property.
+ The other methods of this protocol are optional.
+ */
+@protocol MGLAnnotation <NSObject>
+
+#pragma mark Position Attributes
+
+/**
+ The center point (specified as a map coordinate) of the annotation. (required)
+ (read-only)
+ */
+@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
+
+@optional
+
+#pragma mark Title Attributes
+
+/**
+ The string containing the annotation’s title.
+
+ Although this property is optional, if you support the selection of annotations
+ in your map view, you are expected to provide this property. This string is
+ displayed in the callout for the associated annotation.
+ */
+@property (nonatomic, readonly, copy, nullable) NSString *title;
+
+/**
+ The string containing the annotation’s subtitle.
+
+ This string is displayed in the callout for the associated annotation.
+ */
+@property (nonatomic, readonly, copy, nullable) NSString *subtitle;
+
+#if !TARGET_OS_IPHONE
+
+/** The string containing the annotation’s tooltip. */
+@property (nonatomic, readonly, copy, nullable) NSString *toolTip;
+
+#endif
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLClockDirectionFormatter.h b/platform/darwin/src/MGLClockDirectionFormatter.h
new file mode 100644
index 0000000000..e467b20e06
--- /dev/null
+++ b/platform/darwin/src/MGLClockDirectionFormatter.h
@@ -0,0 +1,45 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLClockDirectionFormatter` class provides properly formatted descriptions
+ of headings relative to the user, known as <i>clock positions</i>. For
+ example, a value of `90` may be formatted as “3 o’clock”, depending on the
+ locale.
+
+ Use this class to create localized heading strings when displaying directions
+ relative to the user’s current location and heading. To format a direction
+ irrespective of the user’s orientation, use `MGLCompassDirectionFormatter`
+ instead.
+ */
+@interface MGLClockDirectionFormatter : NSFormatter
+
+/**
+ The unit style used by this formatter.
+
+ This property defaults to `NSFormattingUnitStyleMedium`.
+ */
+@property (nonatomic) NSFormattingUnitStyle unitStyle;
+
+/**
+ Returns a clock position string for the provided value.
+
+ @param direction The heading, measured in degrees, where 0° means “straight
+ ahead” and 90° means “directly to your right”.
+ @return The clock position string appropriately formatted for the receiver’s
+ locale.
+ */
+- (NSString *)stringFromDirection:(CLLocationDirection)direction;
+
+/**
+ This method is not supported for the `MGLDirectionFormatter` class.
+ */
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLClockDirectionFormatter.m b/platform/darwin/src/MGLClockDirectionFormatter.m
new file mode 100644
index 0000000000..fd67968e65
--- /dev/null
+++ b/platform/darwin/src/MGLClockDirectionFormatter.m
@@ -0,0 +1,59 @@
+#import "MGLClockDirectionFormatter.h"
+
+#import "NSBundle+MGLAdditions.h"
+
+#define wrap(value, min, max) \
+ (fmod((fmod((value - min), (max - min)) + (max - min)), (max - min)) + min)
+
+@implementation MGLClockDirectionFormatter {
+ NSNumberFormatter *_numberFormatter;
+}
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _unitStyle = NSFormattingUnitStyleMedium;
+ _numberFormatter = [[NSNumberFormatter alloc] init];
+ _numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+ }
+ return self;
+}
+
+- (NSString *)stringFromDirection:(CLLocationDirection)direction {
+ NSInteger hour = round(wrap(direction, 0, 360) / 360 * 12);
+ if (hour == 0) {
+ hour = 12;
+ }
+ NSString *format;
+ switch (self.unitStyle) {
+ case NSFormattingUnitStyleShort:
+ format = NSLocalizedStringWithDefaultValue(@"CLOCK_FMT_SHORT", @"Foundation", nil, @"%@:00", @"Clock position format, short: {hours}:00");
+ break;
+
+ case NSFormattingUnitStyleMedium:
+ format = NSLocalizedStringWithDefaultValue(@"CLOCK_FMT_MEDIUM", @"Foundation", nil, @"%@ o’clock", @"Clock position format, medium: {hours} o’clock");
+
+ break;
+
+ case NSFormattingUnitStyleLong:
+ format = NSLocalizedStringWithDefaultValue(@"CLOCK_FMT_LONG", @"Foundation", nil, @"%@ o’clock", @"Clock position format, long: {hours} o’clock");
+ break;
+
+ default:
+ break;
+ }
+ return [NSString stringWithFormat:format, [_numberFormatter stringFromNumber:@(hour)]];
+}
+
+- (nullable NSString *)stringForObjectValue:(id)obj {
+ if (![obj isKindOfClass:[NSValue class]]) {
+ return nil;
+ }
+ return [self stringFromDirection:[obj doubleValue]];
+}
+
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error {
+ NSAssert(NO, @"-getObjectValue:forString:errorDescription: has not been implemented");
+ return NO;
+}
+
+@end
diff --git a/platform/darwin/src/MGLCompassDirectionFormatter.h b/platform/darwin/src/MGLCompassDirectionFormatter.h
new file mode 100644
index 0000000000..d7c3113291
--- /dev/null
+++ b/platform/darwin/src/MGLCompassDirectionFormatter.h
@@ -0,0 +1,42 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLCompassDirectionFormatter` class provides properly formatted
+ descriptions of absolute headings. For example, a value of `90` may be
+ formatted as “east”, depending on the locale.
+
+ Use this class to create localized heading strings when displaying directions
+ irrespective of the user’s current location. To format a direction relative to
+ the user’s current location, use `MGLCompassDirectionFormatter` instead.
+ */
+@interface MGLCompassDirectionFormatter : NSFormatter
+
+/**
+ The unit style used by this formatter.
+
+ This property defaults to `NSFormattingUnitStyleMedium`.
+ */
+@property (nonatomic) NSFormattingUnitStyle unitStyle;
+
+/**
+ Returns a heading string for the provided value.
+
+ @param direction The heading, measured in degrees, where 0° means “due north”
+ and 90° means “due east”.
+ @return The heading string appropriately formatted for the formatter’s locale.
+ */
+- (NSString *)stringFromDirection:(CLLocationDirection)direction;
+
+/**
+ This method is not supported for the `MGLDirectionFormatter` class.
+ */
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLCompassDirectionFormatter.m b/platform/darwin/src/MGLCompassDirectionFormatter.m
new file mode 100644
index 0000000000..c46fe9e4d5
--- /dev/null
+++ b/platform/darwin/src/MGLCompassDirectionFormatter.m
@@ -0,0 +1,124 @@
+#import "MGLCompassDirectionFormatter.h"
+
+#import "NSBundle+MGLAdditions.h"
+
+#define wrap(value, min, max) \
+ (fmod((fmod((value - min), (max - min)) + (max - min)), (max - min)) + min)
+
+@implementation MGLCompassDirectionFormatter
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _unitStyle = NSFormattingUnitStyleMedium;
+ }
+ return self;
+}
+
+- (NSString *)stringFromDirection:(CLLocationDirection)direction {
+ static NS_ARRAY_OF(NSString *) *shortStrings;
+ static NS_ARRAY_OF(NSString *) *longStrings;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ shortStrings = @[
+ NSLocalizedStringWithDefaultValue(@"COMPASS_N_SHORT", @"Foundation", nil, @"N", @"North, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NbE_SHORT", @"Foundation", nil, @"NbE", @"North by east, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NNE_SHORT", @"Foundation", nil, @"NNE", @"North-northeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NEbN_SHORT", @"Foundation", nil, @"NEbN", @"Northeast by north, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NE_SHORT", @"Foundation", nil, @"NE", @"Northeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NEbE_SHORT", @"Foundation", nil, @"NEbE", @"Northeast by east, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_ENE_SHORT", @"Foundation", nil, @"ENE", @"East-northeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_EbN_SHORT", @"Foundation", nil, @"EbN", @"East by north, short"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_E_SHORT", @"Foundation", nil, @"E", @"East, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_EbS_SHORT", @"Foundation", nil, @"EbS", @"East by south, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_ESE_SHORT", @"Foundation", nil, @"ESE", @"East-southeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SEbE_SHORT", @"Foundation", nil, @"SEbE", @"Southeast by east, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SE_SHORT", @"Foundation", nil, @"SE", @"Southeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SEbS_SHORT", @"Foundation", nil, @"SEbS", @"Southeast by south, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SSE_SHORT", @"Foundation", nil, @"SSE", @"South-southeast, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SbE_SHORT", @"Foundation", nil, @"SbE", @"South by east, short"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_S_SHORT", @"Foundation", nil, @"S", @"South, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SbW_SHORT", @"Foundation", nil, @"SbW", @"South by west, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SSW_SHORT", @"Foundation", nil, @"SSW", @"South-southwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SWbS_SHORT", @"Foundation", nil, @"SWbS", @"Southwest by south, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SW_SHORT", @"Foundation", nil, @"SW", @"Southwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SWbW_SHORT", @"Foundation", nil, @"SWbW", @"Southwest by west, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WSW_SHORT", @"Foundation", nil, @"WSW", @"West-southwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WbS_SHORT", @"Foundation", nil, @"WbS", @"West by south, short"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_W_SHORT", @"Foundation", nil, @"W", @"West, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WbN_SHORT", @"Foundation", nil, @"WbN", @"West by north, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WNW_SHORT", @"Foundation", nil, @"WNW", @"West-northwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NWbW_SHORT", @"Foundation", nil, @"NWbW", @"Northwest by west, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NW_SHORT", @"Foundation", nil, @"NW", @"Northwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NWbN_SHORT", @"Foundation", nil, @"NWbN", @"Northwest by north, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NNW_SHORT", @"Foundation", nil, @"NNW", @"North-northwest, short"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NbW_SHORT", @"Foundation", nil, @"NbW", @"North by west, short"),
+ ];
+
+ longStrings = @[
+ NSLocalizedStringWithDefaultValue(@"COMPASS_N_LONG", @"Foundation", nil, @"north", @"North, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NbE_LONG", @"Foundation", nil, @"north by east", @"North by east, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NNE_LONG", @"Foundation", nil, @"north-northeast", @"North-northeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NEbN_LONG", @"Foundation", nil, @"northeast by north", @"Northeast by north, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NE_LONG", @"Foundation", nil, @"northeast", @"Northeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NEbE_LONG", @"Foundation", nil, @"northeast by east", @"Northeast by east, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_ENE_LONG", @"Foundation", nil, @"east-northeast", @"East-northeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_EbN_LONG", @"Foundation", nil, @"east by north", @"East by north, long"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_E_LONG", @"Foundation", nil, @"east", @"East, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_EbS_LONG", @"Foundation", nil, @"east by south", @"East by south, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_ESE_LONG", @"Foundation", nil, @"east-southeast", @"East-southeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SEbE_LONG", @"Foundation", nil, @"southeast by east", @"Southeast by east, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SE_LONG", @"Foundation", nil, @"southeast", @"Southeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SEbS_LONG", @"Foundation", nil, @"southeast by south", @"Southeast by south, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SSE_LONG", @"Foundation", nil, @"south-southeast", @"South-southeast, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SbE_LONG", @"Foundation", nil, @"south by east", @"South by east, long"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_S_LONG", @"Foundation", nil, @"south", @"South, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SbW_LONG", @"Foundation", nil, @"south by west", @"South by west, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SSW_LONG", @"Foundation", nil, @"south-southwest", @"South-southwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SWbS_LONG", @"Foundation", nil, @"southwest by south", @"Southwest by south, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SW_LONG", @"Foundation", nil, @"southwest", @"Southwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_SWbW_LONG", @"Foundation", nil, @"southwest by west", @"Southwest by west, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WSW_LONG", @"Foundation", nil, @"west-southwest", @"West-southwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WbS_LONG", @"Foundation", nil, @"west by south", @"West by south, long"),
+
+ NSLocalizedStringWithDefaultValue(@"COMPASS_W_LONG", @"Foundation", nil, @"west", @"West, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WbN_LONG", @"Foundation", nil, @"west by north", @"West by north, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_WNW_LONG", @"Foundation", nil, @"west-northwest", @"West-northwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NWbW_LONG", @"Foundation", nil, @"northwest by west", @"Northwest by west, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NW_LONG", @"Foundation", nil, @"northwest", @"Northwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NWbN_LONG", @"Foundation", nil, @"northwest by north", @"Northwest by north, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NNW_LONG", @"Foundation", nil, @"north-northwest", @"North-northwest, long"),
+ NSLocalizedStringWithDefaultValue(@"COMPASS_NbW_LONG", @"Foundation", nil, @"north by west", @"North by west, long"),
+ ];
+
+ NSAssert(shortStrings.count == longStrings.count, @"Long and short compass direction string arrays must have the same size.");
+ });
+
+ NSInteger cardinalPoint = wrap(round(wrap(direction, 0, 360) / 360 * shortStrings.count), 0, shortStrings.count);
+ switch (self.unitStyle) {
+ case NSFormattingUnitStyleShort:
+ return shortStrings[cardinalPoint];
+
+ case NSFormattingUnitStyleMedium:
+ case NSFormattingUnitStyleLong:
+ return longStrings[cardinalPoint];
+ }
+}
+
+- (nullable NSString *)stringForObjectValue:(id)obj {
+ if (![obj isKindOfClass:[NSValue class]]) {
+ return nil;
+ }
+ return [self stringFromDirection:[obj doubleValue]];
+}
+
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error {
+ NSAssert(NO, @"-getObjectValue:forString:errorDescription: has not been implemented");
+ return NO;
+}
+
+@end
diff --git a/platform/darwin/src/MGLCoordinateFormatter.h b/platform/darwin/src/MGLCoordinateFormatter.h
new file mode 100644
index 0000000000..3759576d2a
--- /dev/null
+++ b/platform/darwin/src/MGLCoordinateFormatter.h
@@ -0,0 +1,55 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLCoordinateFormatter` class provides properly formatted descriptions of
+ geographic coordinate pairs. Use this class to create localized coordinate
+ strings when displaying location information to users.
+ */
+@interface MGLCoordinateFormatter : NSFormatter
+
+/**
+ Determines whether the output may contain minutes of arc when nonzero.
+
+ The default value of this property is `YES`, causing the receiver to include
+ minutes of arc in its output. If `allowsSeconds` is `YES`, this property is
+ ignored and the output always includes minutes of arc.
+ */
+@property (nonatomic) BOOL allowsMinutes;
+
+/**
+ Determines whether the output may contain seconds of arc when nonzero.
+
+ The default value of this property is `YES`, causing the receiver to include
+ seconds of arc in its output.
+ */
+@property (nonatomic) BOOL allowsSeconds;
+
+/**
+ The unit style used by this formatter.
+
+ The default value of this property is `NSFormattingUnitStyleMedium`.
+ */
+@property (nonatomic) NSFormattingUnitStyle unitStyle;
+
+/**
+ Returns a coordinate string for the provided value.
+
+ @param coordinate The coordinate’s value.
+ @return The coordinate string appropriately formatted for the formatter’s
+ locale.
+ */
+- (NSString *)stringFromCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ This method is not supported for the `MGLCoordinateFormatter` class.
+ */
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLCoordinateFormatter.m b/platform/darwin/src/MGLCoordinateFormatter.m
new file mode 100644
index 0000000000..682f771faa
--- /dev/null
+++ b/platform/darwin/src/MGLCoordinateFormatter.m
@@ -0,0 +1,126 @@
+#import "MGLCoordinateFormatter.h"
+
+#import "NSBundle+MGLAdditions.h"
+#import "NSValue+MGLAdditions.h"
+
+@implementation MGLCoordinateFormatter
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _allowsMinutes = YES;
+ _allowsSeconds = YES;
+ _unitStyle = NSFormattingUnitStyleMedium;
+ }
+ return self;
+}
+
+- (NSString *)stringFromCoordinate:(CLLocationCoordinate2D)coordinate {
+ NSString *positiveLatitudeFormat;
+ NSString *negativeLatitudeFormat;
+ NSString *positiveLongitudeFormat;
+ NSString *negativeLongitudeFormat;
+ NSString *stringFormat;
+ switch (self.unitStyle) {
+ case NSFormattingUnitStyleShort:
+ positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_SHORT", @"Foundation", nil, @"%@N", @"North latitude format, short: {latitude}");
+ negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_SHORT", @"Foundation", nil, @"%@S", @"South latitude format, short: {latitude}");
+ positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_SHORT", @"Foundation", nil, @"%@E", @"East longitude format, short: {longitude}");
+ negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_SHORT", @"Foundation", nil, @"%@W", @"West longitude format, short: {longitude}");
+ stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_SHORT", @"Foundation", nil, @"%@, %@", @"Coordinate pair format, short: {latitude}, {longitude}");
+ break;
+
+ case NSFormattingUnitStyleMedium:
+ positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_MEDIUM", @"Foundation", nil, @"%@ north", @"North latitude format, medium: {latitude}");
+ negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_MEDIUM", @"Foundation", nil, @"%@ south", @"South latitude format, medium: {latitude}");
+ positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_MEDIUM", @"Foundation", nil, @"%@ east", @"East longitude format, medium: {longitude}");
+ negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_MEDIUM", @"Foundation", nil, @"%@ west", @"West longitude format, medium: {longitude}");
+ stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_MEDIUM", @"Foundation", nil, @"%@, %@", @"Coordinate pair format, medium: {latitude}, {longitude}");
+ break;
+
+ case NSFormattingUnitStyleLong:
+ positiveLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_N_LONG", @"Foundation", nil, @"%@ north", @"North latitude format, long: {latitude}");
+ negativeLatitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_S_LONG", @"Foundation", nil, @"%@ south", @"South latitude format, long: {latitude}");
+ positiveLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_E_LONG", @"Foundation", nil, @"%@ east", @"East longitude format, long: {longitude}");
+ negativeLongitudeFormat = NSLocalizedStringWithDefaultValue(@"COORD_W_LONG", @"Foundation", nil, @"%@ west", @"West longitude format, long: {longitude}");
+ stringFormat = NSLocalizedStringWithDefaultValue(@"COORD_FMT_LONG", @"Foundation", nil, @"%@ by %@", @"Coordinate pair format, long: {latitude}, {longitude}");
+ break;
+ }
+ NSString *latitudeString = [self stringFromLocationDegrees:coordinate.latitude
+ positiveFormat:positiveLatitudeFormat
+ negativeFormat:negativeLatitudeFormat];
+ NSString *longitudeString = [self stringFromLocationDegrees:coordinate.longitude
+ positiveFormat:positiveLongitudeFormat
+ negativeFormat:negativeLongitudeFormat];
+ return [NSString stringWithFormat:stringFormat, latitudeString, longitudeString];
+}
+
+- (NSString *)stringFromLocationDegrees:(CLLocationDegrees)degrees positiveFormat:(NSString *)positiveFormat negativeFormat:(NSString *)negativeFormat {
+ CLLocationDegrees minutes = (fabs(degrees) - floor(fabs(degrees))) * 60;
+ CLLocationDegrees seconds = (minutes - floor(minutes)) * 60;
+
+ NSString *degreesFormat;
+ NSString *minutesFormat;
+ NSString *secondsFormat;
+ NSString *degreesMinutesFormat;
+ NSString *degreesMinutesSecondsFormat;
+ switch (self.unitStyle) {
+ case NSFormattingUnitStyleShort:
+ degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_SHORT", @"Foundation", nil, @"%d°", @"Degrees format, short: {degrees}");
+ minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_SHORT", @"Foundation", nil, @"%d′", @"Minutes format, short: {minutes}");
+ secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_SHORT", @"Foundation", nil, @"%d″", @"Seconds format, short: {seconds}");
+ degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_SHORT", @"Foundation", nil, @"%@%@", @"Coordinate format, short: {degrees}{minutes}");
+ degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_SHORT", @"Foundation", nil, @"%@%@%@", @"Coordinate format, short: {degrees}{minutes}{seconds}");
+ break;
+
+ case NSFormattingUnitStyleMedium:
+ degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_MEDIUM", @"Foundation", nil, @"%d°", @"Degrees format, medium: {degrees}");
+ minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_MEDIUM", @"Foundation", nil, @"%d′", @"Minutes format, medium: {minutes}");
+ secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_MEDIUM", @"Foundation", nil, @"%d″", @"Seconds format, medium: {seconds}");
+ degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_MEDIUM", @"Foundation", nil, @"%@%@", @"Coordinate format, medium: {degrees}{minutes}");
+ degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_MEDIUM", @"Foundation", nil, @"%@%@%@", @"Coordinate format, medium: {degrees}{minutes}{seconds}");
+ break;
+
+ case NSFormattingUnitStyleLong:
+ degreesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DEG_LONG", @"Foundation", nil, @"%d degree(s)", @"Degrees format, long");
+ minutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_MIN_LONG", @"Foundation", nil, @"%d minute(s)", @"Minutes format, long");
+ secondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_SEC_LONG", @"Foundation", nil, @"%d second(s)", @"Seconds format, long");
+ degreesMinutesFormat = NSLocalizedStringWithDefaultValue(@"COORD_DM_LONG", @"Foundation", nil, @"%@ and %@", @"Coordinate format, long: {degrees}{minutes}");
+ degreesMinutesSecondsFormat = NSLocalizedStringWithDefaultValue(@"COORD_DMS_LONG", @"Foundation", nil, @"%@, %@, and %@", @"Coordinate format, long: {degrees}{minutes}{seconds}");
+ break;
+ }
+
+ NSString *degreesString = [NSString stringWithFormat:degreesFormat, (int)floor(fabs(degrees))];
+
+ NSString *string;
+ if (trunc(seconds) > 0 && self.allowsSeconds) {
+ NSString *minutesString = [NSString stringWithFormat:minutesFormat, (int)floor(minutes)];
+ NSString *secondsString = [NSString stringWithFormat:secondsFormat, (int)round(seconds)];
+ string = [NSString stringWithFormat:degreesMinutesSecondsFormat,
+ degreesString, minutesString, secondsString];
+ } else if (trunc(minutes) > 0 && self.allowsMinutes) {
+ NSString *minutesString = [NSString stringWithFormat:minutesFormat, (int)round(minutes)];
+ string = [NSString stringWithFormat:degreesMinutesFormat,
+ degreesString, minutesString];
+ } else {
+ string = [NSString stringWithFormat:degreesFormat, (int)round(fabs(degrees))];
+ }
+
+ if (degrees == 0) {
+ return string;
+ }
+ return [NSString stringWithFormat:degrees > 0 ? positiveFormat : negativeFormat, string];
+}
+
+- (nullable NSString *)stringForObjectValue:(id)obj {
+ if (![obj isKindOfClass:[NSValue class]]) {
+ return nil;
+ }
+ return [self stringFromCoordinate:[obj MGLCoordinateValue]];
+}
+
+- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error {
+ NSAssert(NO, @"-getObjectValue:forString:errorDescription: has not been implemented");
+ return NO;
+}
+
+@end
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
new file mode 100644
index 0000000000..69cff6b054
--- /dev/null
+++ b/platform/darwin/src/MGLFeature.h
@@ -0,0 +1,137 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLPolyline.h"
+#import "MGLPolygon.h"
+#import "MGLPointAnnotation.h"
+#import "MGLShapeCollection.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLFeature` protocol is used to provide details about geographic features
+ contained in a map view’s
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
+ Each concrete subclass of `MGLShape` in turn has a subclass that conforms to
+ this protocol.
+
+ Typically, you do not create feature objects yourself but rather obtain them
+ using `-[MGLMapView visibleFeaturesAtPoint:]` and related methods. Each feature
+ object associates a shape with an identifier and attributes as specified by the
+ source. Like ordinary `MGLAnnotation` objects, some kinds of `MGLFeature`
+ objects can also be added to a map view using `-[MGLMapView addAnnotations:]`
+ and related methods.
+ */
+@protocol MGLFeature <MGLAnnotation>
+
+/**
+ An object that uniquely identifies the feature in its containing
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+
+ The value of this property is currently always an `NSNumber` object but may in
+ the future be an instance of another class, such as `NSString`.
+
+ The identifier corresponds to the
+ <a href="https://github.com/mapbox/vector-tile-spec/tree/master/2.1#42-features">feature identifier</a>
+ (`id`) in the tile source. If the source does not specify the feature’s
+ identifier, the value of this property is `nil`.
+
+ For details about the identifiers used in most Mapbox-provided styles, consult
+ the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
+ layer reference.
+ */
+@property (nonatomic, copy, nullable, readonly) id identifier;
+
+/**
+ A dictionary of attributes for this feature specified by the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+
+ The keys and values of this dictionary are determined by the tile source. In
+ the tile source, each attribute name is a string, while each attribute value
+ may be a null value, Boolean value, integer, floating-point number, or string.
+ These data types are mapped to instances of the following Foundation classes:
+
+ <table>
+ <thead>
+ <tr><th>In the tile source</th><th>In this dictionary</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>Null</td> <td><code>NSNull</code></td></tr>
+ <tr><td>Boolean</td> <td><code>NSNumber</code> (use the <code>boolValue</code> property)</td></tr>
+ <tr><td>Integer</td> <td><code>NSNumber</code> (use the <code>unsignedLongLongValue</code> or <code>longLongValue</code> property)</td></tr>
+ <tr><td>Floating-point number</td> <td><code>NSNumber</code> (use the <code>doubleValue</code> property)</td></tr>
+ <tr><td>String</td> <td><code>NSString</code></td></tr>
+ </tbody>
+ </table>
+
+ For details about the attribute names and values found in Mapbox-provided
+ vector tile sources, consult the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>
+ and
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>
+ layer references.
+ */
+@property (nonatomic, copy, readonly) NS_DICTIONARY_OF(NSString *, id) *attributes;
+
+/**
+ Returns the feature attribute for the given attribute name.
+
+ See the `attributes` property’s documentation for details on keys and values
+ associated with this method.
+ */
+- (nullable id)attributeForKey:(NSString *)key;
+
+@end
+
+/**
+ The `MGLPointFeature` class represents a point in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLPointFeature : MGLPointAnnotation <MGLFeature>
+@end
+
+/**
+ The `MGLPolylineFeature` class represents a polyline in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLPolylineFeature : MGLPolyline <MGLFeature>
+@end
+
+/**
+ The `MGLPolygonFeature` class represents a polygon in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLPolygonFeature : MGLPolygon <MGLFeature>
+@end
+
+/**
+ The `MGLMultiPointFeature` class represents a multipoint in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLMultiPointFeature : MGLMultiPoint <MGLFeature>
+@end
+
+/**
+ The `MGLMultiPolylineFeature` class represents a multipolyline in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLMultiPolylineFeature : MGLMultiPolyline <MGLFeature>
+@end
+
+/**
+ The `MGLMultiPolygonFeature` class represents a multipolygon in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLMultiPolygonFeature : MGLMultiPolygon <MGLFeature>
+@end
+
+/**
+ The `MGLShapeCollectionFeature` class represents a shape collection in a
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
+ */
+@interface MGLShapeCollectionFeature : MGLShapeCollection <MGLFeature>
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
new file mode 100644
index 0000000000..777b296303
--- /dev/null
+++ b/platform/darwin/src/MGLFeature.mm
@@ -0,0 +1,269 @@
+#import "MGLFeature_Private.h"
+
+#import "MGLPointAnnotation.h"
+#import "MGLPolyline.h"
+#import "MGLPolygon.h"
+
+#import "MGLMultiPoint_Private.h"
+
+#import <mbgl/util/geometry.hpp>
+
+@protocol MGLFeaturePrivate <MGLFeature>
+
+@property (nonatomic, copy, nullable, readwrite) id identifier;
+@property (nonatomic, copy, readwrite) NS_DICTIONARY_OF(NSString *, id) *attributes;
+
+@end
+
+@interface MGLPointFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLPointFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLPolylineFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLPolylineFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLPolygonFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLPolygonFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLMultiPointFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLMultiPointFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLMultiPolylineFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLMultiPolylineFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLMultiPolygonFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLMultiPolygonFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+@interface MGLShapeCollectionFeature () <MGLFeaturePrivate>
+@end
+
+@implementation MGLShapeCollectionFeature
+
+@synthesize identifier;
+@synthesize attributes;
+
+- (id)attributeForKey:(NSString *)key {
+ return self.attributes[key];
+}
+
+@end
+
+/**
+ Recursively transforms a C++ type into the corresponding Foundation type.
+ */
+class PropertyValueEvaluator {
+public:
+ id operator()(const std::nullptr_t &) const {
+ return [NSNull null];
+ }
+
+ id operator()(const bool &value) const {
+ return value ? @YES : @NO;
+ }
+
+ id operator()(const uint64_t &value) const {
+ return @(value);
+ }
+
+ id operator()(const int64_t &value) const {
+ return @(value);
+ }
+
+ id operator()(const double &value) const {
+ return @(value);
+ }
+
+ id operator()(const std::string &value) const {
+ return @(value.c_str());
+ }
+
+ id operator()(const std::vector<mbgl::Value> &values) const {
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:values.size()];
+ for (const auto &v : values) {
+ [objects addObject:mbgl::Value::visit(v, *this)];
+ }
+ return objects;
+ }
+
+ id operator()(const std::unordered_map<std::string, mbgl::Value> &items) const {
+ NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:items.size()];
+ for (auto &item : items) {
+ attributes[@(item.first.c_str())] = mbgl::Value::visit(item.second, *this);
+ }
+ return attributes;
+ }
+};
+
+/**
+ Transforms an `mbgl::geometry::geometry` type into an instance of the
+ corresponding Objective-C geometry class.
+ */
+template <typename T>
+class GeometryEvaluator {
+public:
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::Point<T> &geometry) const {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.coordinate = toLocationCoordinate2D(geometry);
+ return feature;
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::LineString<T> &geometry) const {
+ std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
+ return [MGLPolylineFeature polylineWithCoordinates:&coordinates[0] count:coordinates.size()];
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::Polygon<T> &geometry) const {
+ return toShape<MGLPolygonFeature>(geometry);
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::MultiPoint<T> &geometry) const {
+ std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(geometry);
+ return [[MGLMultiPointFeature alloc] initWithCoordinates:&coordinates[0] count:coordinates.size()];
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::MultiLineString<T> &geometry) const {
+ NSMutableArray *polylines = [NSMutableArray arrayWithCapacity:geometry.size()];
+ for (auto &lineString : geometry) {
+ std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(lineString);
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:&coordinates[0] count:coordinates.size()];
+ [polylines addObject:polyline];
+ }
+
+ return [MGLMultiPolylineFeature multiPolylineWithPolylines:polylines];
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mbgl::MultiPolygon<T> &geometry) const {
+ NSMutableArray *polygons = [NSMutableArray arrayWithCapacity:geometry.size()];
+ for (auto &polygon : geometry) {
+ [polygons addObject:toShape(polygon)];
+ }
+
+ return [MGLMultiPolygonFeature multiPolygonWithPolygons:polygons];
+ }
+
+ MGLShape <MGLFeaturePrivate> * operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
+ NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:collection.size()];
+ for (auto &geometry : collection) {
+ // This is very much like the transformation that happens in MGLFeaturesFromMBGLFeatures(), but these are raw geometries with no associated feature IDs or attributes.
+ MGLShape <MGLFeaturePrivate> *shape = mapbox::geometry::geometry<T>::visit(geometry, *this);
+ [shapes addObject:shape];
+ }
+ return [MGLShapeCollectionFeature shapeCollectionWithShapes:shapes];
+ }
+
+private:
+ static CLLocationCoordinate2D toLocationCoordinate2D(const mbgl::Point<T> &point) {
+ return CLLocationCoordinate2DMake(point.y, point.x);
+ }
+
+ static std::vector<CLLocationCoordinate2D> toLocationCoordinates2D(const std::vector<mbgl::Point<T>> &points) {
+ std::vector<CLLocationCoordinate2D> coordinates;
+ coordinates.reserve(points.size());
+ std::transform(points.begin(), points.end(), std::back_inserter(coordinates), toLocationCoordinate2D);
+ return coordinates;
+ }
+
+ template<typename U = MGLPolygon>
+ static U *toShape(const mbgl::Polygon<T> &geometry) {
+ auto &linearRing = geometry.front();
+ std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(linearRing);
+ NSMutableArray *innerPolygons;
+ if (geometry.size() > 1) {
+ innerPolygons = [NSMutableArray arrayWithCapacity:geometry.size() - 1];
+ for (auto iter = geometry.begin() + 1; iter != geometry.end(); iter++) {
+ auto &innerRing = *iter;
+ std::vector<CLLocationCoordinate2D> coordinates = toLocationCoordinates2D(innerRing);
+ MGLPolygon *innerPolygon = [MGLPolygon polygonWithCoordinates:&coordinates[0] count:coordinates.size()];
+ [innerPolygons addObject:innerPolygon];
+ }
+ }
+
+ return [U polygonWithCoordinates:&coordinates[0] count:coordinates.size() interiorPolygons:innerPolygons];
+ }
+};
+
+NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) {
+ NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
+ for (const auto &feature : features) {
+ NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:feature.properties.size()];
+ for (auto &pair : feature.properties) {
+ auto &value = pair.second;
+ PropertyValueEvaluator evaluator;
+ attributes[@(pair.first.c_str())] = mbgl::Value::visit(value, evaluator);
+ }
+
+ GeometryEvaluator<double> evaluator;
+ MGLShape <MGLFeaturePrivate> *shape = mapbox::geometry::geometry<double>::visit(feature.geometry, evaluator);
+ if (feature.id) {
+ shape.identifier = @(*feature.id);
+ }
+ shape.attributes = attributes;
+ [shapes addObject:shape];
+ }
+ return shapes;
+}
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
new file mode 100644
index 0000000000..fbc7f88559
--- /dev/null
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -0,0 +1,11 @@
+#import "MGLFeature.h"
+#import "MGLShape.h"
+
+#import <mbgl/util/geo.hpp>
+#import <mbgl/util/feature.hpp>
+
+/**
+ Returns an array of `MGLFeature` objects converted from the given vector of
+ vector tile features.
+ */
+NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features);
diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h
new file mode 100644
index 0000000000..ca99eaf16f
--- /dev/null
+++ b/platform/darwin/src/MGLGeometry.h
@@ -0,0 +1,129 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+#import <CoreGraphics/CGBase.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Defines the area spanned by an `MGLCoordinateBounds`. */
+typedef struct MGLCoordinateSpan {
+ /** Latitudes spanned by an `MGLCoordinateBounds`. */
+ CLLocationDegrees latitudeDelta;
+ /** Longitudes spanned by an `MGLCoordinateBounds`. */
+ CLLocationDegrees longitudeDelta;
+} MGLCoordinateSpan;
+
+/**
+ Creates a new `MGLCoordinateSpan` from the given latitudinal and longitudinal
+ deltas.
+ */
+NS_INLINE MGLCoordinateSpan MGLCoordinateSpanMake(CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta) {
+ MGLCoordinateSpan span;
+ span.latitudeDelta = latitudeDelta;
+ span.longitudeDelta = longitudeDelta;
+ return span;
+}
+
+/**
+ Returns `YES` if the two coordinate spans represent the same latitudinal change
+ and the same longitudinal change.
+ */
+NS_INLINE BOOL MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpan span1, MGLCoordinateSpan span2) {
+ return (span1.latitudeDelta == span2.latitudeDelta &&
+ span1.longitudeDelta == span2.longitudeDelta);
+}
+
+/** An area of zero width and zero height. */
+extern const MGLCoordinateSpan MGLCoordinateSpanZero;
+
+/** A rectangular area as measured on a two-dimensional map projection. */
+typedef struct MGLCoordinateBounds {
+ /** Coordinate at the southwest corner. */
+ CLLocationCoordinate2D sw;
+ /** Coordinate at the northeast corner. */
+ CLLocationCoordinate2D ne;
+} MGLCoordinateBounds;
+
+/**
+ Creates a new `MGLCoordinateBounds` structure from the given southwest and
+ northeast coordinates.
+ */
+NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsMake(CLLocationCoordinate2D sw, CLLocationCoordinate2D ne) {
+ MGLCoordinateBounds bounds;
+ bounds.sw = sw;
+ bounds.ne = ne;
+ return bounds;
+}
+
+/** Returns `YES` if the two coordinate bounds are equal to each other. */
+NS_INLINE BOOL MGLCoordinateBoundsEqualToCoordinateBounds(MGLCoordinateBounds bounds1, MGLCoordinateBounds bounds2) {
+ return (bounds1.sw.latitude == bounds2.sw.latitude &&
+ bounds1.sw.longitude == bounds2.sw.longitude &&
+ bounds1.ne.latitude == bounds2.ne.latitude &&
+ bounds1.ne.longitude == bounds2.ne.longitude);
+}
+
+/** Returns `YES` if the coordinate is within the coordinate bounds. */
+NS_INLINE BOOL MGLCoordinateInCoordinateBounds(CLLocationCoordinate2D coordinate, MGLCoordinateBounds bounds) {
+ return (coordinate.latitude >= bounds.sw.latitude &&
+ coordinate.latitude <= bounds.ne.latitude &&
+ coordinate.longitude >= bounds.sw.longitude &&
+ coordinate.longitude <= bounds.ne.longitude);
+}
+
+/** Returns the area spanned by the coordinate bounds. */
+NS_INLINE MGLCoordinateSpan MGLCoordinateBoundsGetCoordinateSpan(MGLCoordinateBounds bounds) {
+ return MGLCoordinateSpanMake(bounds.ne.latitude - bounds.sw.latitude,
+ bounds.ne.longitude - bounds.sw.longitude);
+}
+
+/**
+ Returns a coordinate bounds with southwest and northeast coordinates that are
+ offset from those of the source bounds.
+ */
+NS_INLINE MGLCoordinateBounds MGLCoordinateBoundsOffset(MGLCoordinateBounds bounds, MGLCoordinateSpan offset) {
+ MGLCoordinateBounds offsetBounds = bounds;
+ offsetBounds.sw.latitude += offset.latitudeDelta;
+ offsetBounds.sw.longitude += offset.longitudeDelta;
+ offsetBounds.ne.latitude += offset.latitudeDelta;
+ offsetBounds.ne.longitude += offset.longitudeDelta;
+ return offsetBounds;
+}
+
+/**
+ Returns `YES` if the coordinate bounds covers no area.
+
+ @note A bounds may be empty but have a non-zero coordinate span (e.g., when its
+ northeast point lies due north of its southwest point).
+ */
+NS_INLINE BOOL MGLCoordinateBoundsIsEmpty(MGLCoordinateBounds bounds) {
+ MGLCoordinateSpan span = MGLCoordinateBoundsGetCoordinateSpan(bounds);
+ return span.latitudeDelta == 0 || span.longitudeDelta == 0;
+}
+
+/** Returns a formatted string for the given coordinate bounds. */
+NS_INLINE NSString *MGLStringFromCoordinateBounds(MGLCoordinateBounds bounds) {
+ return [NSString stringWithFormat:@"{ sw = {%.1f, %.1f}, ne = {%.1f, %.1f}}",
+ bounds.sw.latitude, bounds.sw.longitude,
+ bounds.ne.latitude, bounds.ne.longitude];
+}
+
+/** Returns radians, converted from degrees. */
+NS_INLINE CGFloat MGLRadiansFromDegrees(CLLocationDegrees degrees) {
+ return (CGFloat)(degrees * M_PI) / 180;
+}
+
+/** Returns degrees, converted from radians. */
+NS_INLINE CLLocationDegrees MGLDegreesFromRadians(CGFloat radians) {
+ return radians * 180 / M_PI;
+}
+
+/**
+ Methods for round-tripping Mapbox geometry structure values.
+ */
+@interface NSValue (MGLGeometryAdditions)
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h
index 72123827df..fc57460128 100644
--- a/platform/darwin/src/MGLGeometry_Private.h
+++ b/platform/darwin/src/MGLGeometry_Private.h
@@ -5,8 +5,8 @@
#import <UIKit/UIKit.h>
#endif
-#import <mbgl/map/map.hpp>
#import <mbgl/util/geo.hpp>
+#import <mbgl/util/geometry.hpp>
/// Returns the smallest rectangle that contains both the given rectangle and
/// the given point.
@@ -16,6 +16,10 @@ NS_INLINE mbgl::LatLng MGLLatLngFromLocationCoordinate2D(CLLocationCoordinate2D
return mbgl::LatLng(coordinate.latitude, coordinate.longitude);
}
+NS_INLINE mbgl::Point<double> MGLPointFromLocationCoordinate2D(CLLocationCoordinate2D coordinate) {
+ return mbgl::Point<double>(coordinate.longitude, coordinate.latitude);
+}
+
NS_INLINE CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng) {
return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude);
}
@@ -30,11 +34,6 @@ NS_INLINE mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBo
MGLLatLngFromLocationCoordinate2D(coordinateBounds.ne));
}
-NS_INLINE BOOL MGLCoordinateInCoordinateBounds(CLLocationCoordinate2D coordinate, MGLCoordinateBounds coordinateBounds) {
- mbgl::LatLngBounds bounds = MGLLatLngBoundsFromCoordinateBounds(coordinateBounds);
- return bounds.contains(MGLLatLngFromLocationCoordinate2D(coordinate));
-}
-
#if TARGET_OS_IPHONE
NS_INLINE mbgl::EdgeInsets MGLEdgeInsetsFromNSEdgeInsets(UIEdgeInsets insets) {
return { insets.top, insets.left, insets.bottom, insets.right };
diff --git a/platform/darwin/src/MGLMapCamera.h b/platform/darwin/src/MGLMapCamera.h
new file mode 100644
index 0000000000..4b50c32b2f
--- /dev/null
+++ b/platform/darwin/src/MGLMapCamera.h
@@ -0,0 +1,71 @@
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ An `MGLMapCamera` object represents a viewpoint from which the user observes
+ some point on an `MGLMapView`.
+ */
+@interface MGLMapCamera : NSObject <NSSecureCoding, NSCopying>
+
+/** Coordinate at the center of the map view. */
+@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
+
+/** Heading measured in degrees clockwise from true north. */
+@property (nonatomic) CLLocationDirection heading;
+
+/**
+ Pitch toward the horizon measured in degrees, with 0 degrees resulting in a
+ two-dimensional map.
+ */
+@property (nonatomic) CGFloat pitch;
+
+/** Meters above ground level. */
+@property (nonatomic) CLLocationDistance altitude;
+
+/** Returns a new camera with all properties set to 0. */
++ (instancetype)camera;
+
+/**
+ Returns a new camera using based on information about the camera’s viewpoint
+ and focus point.
+
+ @param centerCoordinate The geographic coordinate on which the map should be
+ centered.
+ @param eyeCoordinate The geometric coordinate at which the camera should be
+ situated.
+ @param eyeAltitude The altitude (measured in meters) above the map at which the
+ camera should be situated. The altitude may be less than the distance from
+ the camera’s viewpoint to the camera’s focus point.
+ */
++ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
+ eyeAltitude:(CLLocationDistance)eyeAltitude;
+
+/**
+ Returns a new camera with the given distance, pitch, and heading.
+
+ @param centerCoordinate The geographic coordinate on which the map should be
+ centered.
+ @param distance The straight-line distance from the viewpoint to the
+ `centerCoordinate`.
+ @param pitch The viewing angle of the camera, measured in degrees. A value of
+ `0` results in a camera pointed straight down at the map. Angles greater
+ than `0` result in a camera angled toward the horizon.
+ @param heading The camera’s heading, measured in degrees clockwise from true
+ north. A value of `0` means that the top edge of the map view corresponds to
+ true north. The value `90` means the top of the map is pointing due east.
+ The value `180` means the top of the map points due south, and so on.
+ */
++ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
+ fromDistance:(CLLocationDistance)distance
+ pitch:(CGFloat)pitch
+ heading:(CLLocationDirection)heading;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h
new file mode 100644
index 0000000000..2d6b327086
--- /dev/null
+++ b/platform/darwin/src/MGLMultiPoint.h
@@ -0,0 +1,42 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLShape.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLMultiPoint` class is an abstract superclass used to define shapes
+ composed of multiple points. You should not create instances of this class
+ directly. Instead, you should create instances of the `MGLPolyline` or
+ `MGLPolygon` classes. However, you can use the method and properties of this
+ class to access information about the specific points associated with the line
+ or polygon.
+ */
+@interface MGLMultiPoint : MGLShape
+
+/** The array of coordinates associated with the shape. */
+@property (nonatomic, readonly) CLLocationCoordinate2D *coordinates NS_RETURNS_INNER_POINTER;
+
+/** The number of coordinates associated with the shape. (read-only) */
+@property (nonatomic, readonly) NSUInteger pointCount;
+
+/**
+ Retrieves one or more coordinates associated with the shape.
+
+ @param coords On input, you must provide a C array of structures large enough
+ to hold the desired number of coordinates. On output, this structure
+ contains the requested coordinate data.
+ @param range The range of points you want. The `location` field indicates the
+ first point you are requesting, with `0` being the first point, `1` being
+ the second point, and so on. The `length` field indicates the number of
+ points you want. The array in _`coords`_ must be large enough to accommodate
+ the number of requested coordinates.
+ */
+- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index a864b7bce7..6084535d05 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -14,7 +14,6 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
@implementation MGLMultiPoint
{
- CLLocationCoordinate2D *_coords;
size_t _count;
MGLCoordinateBounds _bounds;
}
@@ -27,13 +26,13 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
if (self)
{
_count = count;
- _coords = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D));
+ _coordinates = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D));
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
for (NSUInteger i = 0; i < _count; i++)
{
- _coords[i] = coords[i];
+ _coordinates[i] = coords[i];
bounds.extend(mbgl::LatLng(coords[i].latitude, coords[i].longitude));
}
@@ -45,7 +44,7 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
- (void)dealloc
{
- free(_coords);
+ free(_coordinates);
}
- (CLLocationCoordinate2D)coordinate
@@ -59,7 +58,7 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
assert(_count > 0);
- return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude);
+ return CLLocationCoordinate2DMake(_coordinates[0].latitude, _coordinates[0].longitude);
}
- (NSUInteger)pointCount
@@ -89,7 +88,7 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
for (NSUInteger i = range.location; i < range.location + range.length; i++)
{
- coords[index] = _coords[i];
+ coords[index] = _coordinates[i];
index++;
}
}
@@ -104,28 +103,9 @@ mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
return MGLLatLngBoundsFromCoordinateBounds(_bounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
}
-- (void)addShapeAnnotationObjectToCollection:(std::vector<mbgl::ShapeAnnotation> &)shapes withDelegate:(id <MGLMultiPointDelegate>)delegate {
- NSUInteger count = self.pointCount;
- if (count == 0) {
- return;
- }
-
- CLLocationCoordinate2D *coordinates = (CLLocationCoordinate2D *)malloc(count * sizeof(CLLocationCoordinate2D));
- NSAssert(coordinates, @"Unable to allocate annotation with %lu points", (unsigned long)count);
- [self getCoordinates:coordinates range:NSMakeRange(0, count)];
-
- mbgl::AnnotationSegment segment;
- segment.reserve(count);
- for (NSUInteger i = 0; i < count; i++) {
- segment.push_back(MGLLatLngFromLocationCoordinate2D(coordinates[i]));
- }
- free(coordinates);
- shapes.emplace_back(mbgl::AnnotationSegments {{ segment }},
- [self shapeAnnotationPropertiesObjectWithDelegate:delegate]);
-}
-
-- (mbgl::ShapeAnnotation::Properties)shapeAnnotationPropertiesObjectWithDelegate:(__unused id <MGLMultiPointDelegate>)delegate {
- return mbgl::ShapeAnnotation::Properties();
+- (mbgl::Annotation)annotationObjectWithDelegate:(__unused id <MGLMultiPointDelegate>)delegate {
+ NSAssert(NO, @"Cannot add an annotation from an instance of %@", NSStringFromClass([self class]));
+ return mbgl::SymbolAnnotation({mbgl::Point<double>()});
}
- (NSString *)description
diff --git a/platform/darwin/src/MGLMultiPoint_Private.h b/platform/darwin/src/MGLMultiPoint_Private.h
index c1f1fa1584..aa52a02fcb 100644
--- a/platform/darwin/src/MGLMultiPoint_Private.h
+++ b/platform/darwin/src/MGLMultiPoint_Private.h
@@ -3,7 +3,7 @@
#import "MGLGeometry.h"
#import "MGLTypes.h"
-#import <mbgl/annotation/shape_annotation.hpp>
+#import <mbgl/annotation/annotation.hpp>
#import <vector>
#import <CoreGraphics/CoreGraphics.h>
@@ -21,11 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds;
-/** Adds a shape annotation to the given vector by asking the delegate for style values. */
-- (void)addShapeAnnotationObjectToCollection:(std::vector<mbgl::ShapeAnnotation> &)shapes withDelegate:(id <MGLMultiPointDelegate>)delegate;
-
-/** Constructs a shape annotation properties object by asking the delegate for style values. */
-- (mbgl::ShapeAnnotation::Properties)shapeAnnotationPropertiesObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate;
+/** Constructs a shape annotation object, asking the delegate for style values. */
+- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate;
@end
diff --git a/platform/darwin/src/MGLOfflinePack.h b/platform/darwin/src/MGLOfflinePack.h
new file mode 100644
index 0000000000..3d4422da33
--- /dev/null
+++ b/platform/darwin/src/MGLOfflinePack.h
@@ -0,0 +1,190 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLOfflineRegion.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The state an offline pack is currently in.
+ */
+typedef NS_ENUM (NSInteger, MGLOfflinePackState) {
+ /**
+ It is unknown whether the pack is inactive, active, or complete.
+
+ This is the initial state of a pack. The state of a pack becomes known by
+ the time the shared `MGLOfflineStorage` object sends the first
+ `MGLOfflinePackProgressChangedNotification` about the pack. For inactive
+ packs, you must explicitly request a progress update using the
+ `-[MGLOfflinePack requestProgress]` method.
+
+ An invalid pack always has a state of `MGLOfflinePackStateInvalid`, never
+ `MGLOfflinePackStateUnknown`.
+ */
+ MGLOfflinePackStateUnknown = 0,
+ /**
+ The pack is incomplete and is not currently downloading.
+
+ This is the initial state of a pack that is created using the
+ `-[MGLOfflinePack addPackForRegion:withContext:completionHandler:]` method,
+ as well as after the `-[MGLOfflinePack suspend]` method is
+ called.
+ */
+ MGLOfflinePackStateInactive = 1,
+ /**
+ The pack is incomplete and is currently downloading.
+
+ This is the state of a pack after the `-[MGLOfflinePack resume]` method is
+ called.
+ */
+ MGLOfflinePackStateActive = 2,
+ /**
+ The pack has downloaded to completion.
+ */
+ MGLOfflinePackStateComplete = 3,
+ /**
+ The pack has been removed using the
+ `-[MGLOfflineStorage removePack:withCompletionHandler:]` method. Sending
+ any message to the pack will raise an exception.
+ */
+ MGLOfflinePackStateInvalid = 4,
+};
+
+/**
+ A structure containing information about an offline pack’s current download
+ progress.
+ */
+typedef struct MGLOfflinePackProgress {
+ /**
+ The number of resources, including tiles, that have been completely
+ downloaded and are ready to use offline.
+ */
+ uint64_t countOfResourcesCompleted;
+ /**
+ The cumulative size of the downloaded resources on disk, including tiles,
+ measured in bytes.
+ */
+ uint64_t countOfBytesCompleted;
+ /**
+ The number of tiles that have been completely downloaded and are ready
+ to use offline.
+ */
+ uint64_t countOfTilesCompleted;
+ /**
+ The cumulative size of the downloaded tiles on disk, measured in bytes.
+ */
+ uint64_t countOfTileBytesCompleted;
+ /**
+ The minimum number of resources that must be downloaded in order to view
+ the pack’s full region without any omissions.
+
+ At the beginning of a download, this count is a lower bound; the number of
+ expected resources may increase as the download progresses.
+ */
+ uint64_t countOfResourcesExpected;
+ /**
+ The maximum number of resources that must be downloaded in order to view
+ the pack’s full region without any omissions.
+
+ At the beginning of a download, when the exact number of required resources
+ is unknown, this field is set to `UINT64_MAX`. Thus this count is always an
+ upper bound.
+ */
+ uint64_t maximumResourcesExpected;
+} MGLOfflinePackProgress;
+
+/**
+ An `MGLOfflinePack` represents a collection of resources necessary for viewing
+ a region offline to a local database.
+
+ To create an instance of `MGLOfflinePack`, use the
+ `+[MGLOfflineStorage addPackForRegion:withContext:completionHandler:]` method.
+ A pack created using `-[MGLOfflinePack init]` is immediately invalid.
+ */
+@interface MGLOfflinePack : NSObject
+
+/**
+ The region for which the pack manages resources.
+ */
+@property (nonatomic, readonly) id <MGLOfflineRegion> region;
+
+/**
+ Arbitrary data stored alongside the downloaded resources.
+
+ The context typically holds application-specific information for identifying
+ the pack, such as a user-selected name.
+ */
+@property (nonatomic, readonly) NSData *context;
+
+/**
+ The pack’s current state.
+
+ The state of an inactive or completed pack is computed lazily and is set to
+ `MGLOfflinePackStateUnknown` by default. To request the pack’s status, use the
+ `-requestProgress` method. To get notified when the state becomes known and
+ when it changes, observe KVO change notifications on this pack’s `state` key
+ path. Alternatively, you can add an observer for
+ `MGLOfflinePackProgressChangedNotification`s about this pack that come from the
+ default notification center.
+ */
+@property (nonatomic, readonly) MGLOfflinePackState state;
+
+/**
+ The pack’s current progress.
+
+ The progress of an inactive or completed pack is computed lazily, and all its
+ fields are set to 0 by default. To request the pack’s progress, use the
+ `-requestProgress` method. To get notified when the progress becomes
+ known and when it changes, observe KVO change notifications on this pack’s
+ `state` key path. Alternatively, you can add an observer for
+ `MGLOfflinePackProgressChangedNotification`s about this pack that come from the
+ default notification center.
+ */
+@property (nonatomic, readonly) MGLOfflinePackProgress progress;
+
+/**
+ Resumes downloading if the pack is inactive.
+
+ A pack resumes asynchronously. To get notified when this pack resumes, observe
+ KVO change notifications on this pack’s `state` key path. Alternatively, you
+ can add an observer for `MGLOfflinePackProgressChangedNotification`s about this
+ pack that come from the default notification center.
+
+ When a pack resumes after being suspended, it may begin by iterating over the
+ already downloaded resources. As a result, the `progress` structure’s
+ `countOfResourcesCompleted` field may revert to 0 before rapidly returning to
+ the level of progress at the time the pack was suspended.
+
+ To temporarily suspend downloading, call the `-suspend` method.
+ */
+- (void)resume;
+
+/**
+ Temporarily stops downloading if the pack is active.
+
+ A pack suspends asynchronously. To get notified when this pack resumes, observe
+ KVO change notifications on this pack’s `state` key path. Alternatively, you
+ can add an observer for `MGLOfflinePackProgressChangedNotification` about this
+ pack that come from the default notification center.
+
+ If the pack previously reached a higher level of progress before being
+ suspended, it may wait to suspend until it returns to that level.
+
+ To resume downloading, call the `-resume` method.
+ */
+- (void)suspend;
+
+/**
+ Request an asynchronous update to the pack’s `state` and `progress` properties.
+
+ The state and progress of an inactive or completed pack are computed lazily. If
+ you need the state or progress of a pack whose `state` property is currently
+ set to `MGLOfflinePackStateUnknown`, observe KVO change notifications on this
+ pack’s `state` key path, then call this method. Alternatively, you can add an
+ observer for `MGLOfflinePackProgressChangedNotification` about this pack that
+ come from the default notification center.
+ */
+- (void)requestProgress;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm
index 9775f1cfca..59406074e6 100644
--- a/platform/darwin/src/MGLOfflinePack.mm
+++ b/platform/darwin/src/MGLOfflinePack.mm
@@ -5,7 +5,6 @@
#import "MGLTilePyramidOfflineRegion.h"
#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/util/string.hpp>
/**
Assert that the current offline pack is valid.
@@ -154,6 +153,8 @@ private:
MGLOfflinePackProgress progress;
progress.countOfResourcesCompleted = status.completedResourceCount;
progress.countOfBytesCompleted = status.completedResourceSize;
+ progress.countOfTilesCompleted = status.completedTileCount;
+ progress.countOfTileBytesCompleted = status.completedTileSize;
progress.countOfResourcesExpected = status.requiredResourceCount;
progress.maximumResourcesExpected = status.requiredResourceCountIsPrecise ? status.requiredResourceCount : UINT64_MAX;
self.progress = progress;
@@ -203,17 +204,3 @@ void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) {
[pack.delegate offlinePack:pack didReceiveMaximumAllowedMapboxTiles:limit];
});
}
-
-@implementation NSValue (MGLOfflinePackAdditions)
-
-+ (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress {
- return [NSValue value:&progress withObjCType:@encode(MGLOfflinePackProgress)];
-}
-
-- (MGLOfflinePackProgress)MGLOfflinePackProgressValue {
- MGLOfflinePackProgress progress;
- [self getValue:&progress];
- return progress;
-}
-
-@end
diff --git a/platform/darwin/include/MGLOfflineRegion.h b/platform/darwin/src/MGLOfflineRegion.h
index cf98e253bd..cf98e253bd 100644
--- a/platform/darwin/include/MGLOfflineRegion.h
+++ b/platform/darwin/src/MGLOfflineRegion.h
diff --git a/platform/darwin/include/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index 20cc521f2e..20cc521f2e 100644
--- a/platform/darwin/include/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 89bf050249..a59fb69943 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -5,6 +5,7 @@
#import "MGLOfflinePack_Private.h"
#import "MGLOfflineRegion_Private.h"
#import "MGLTilePyramidOfflineRegion.h"
+#import "NSValue+MGLAdditions.h"
#include <mbgl/util/string.hpp>
@@ -63,12 +64,6 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
NSString *cachePath = cacheURL ? cacheURL.path : @"";
- // Avoid backing up the offline cache onto iCloud, because it can be
- // redownloaded. Ideally, we’d even put the ambient cache in Caches, so
- // it can be reclaimed by the system when disk space runs low. But
- // unfortunately it has to live in the same file as offline resources.
- [cacheURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
-
// Move the offline cache from v3.2.0-beta.1 to a location that can also
// be used for ambient caching.
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
@@ -91,7 +86,13 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
}
_mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [NSBundle mainBundle].resourceURL.path.UTF8String);
-
+
+ // Avoid backing up the offline cache onto iCloud, because it can be
+ // redownloaded. Ideally, we’d even put the ambient cache in Caches, so
+ // it can be reclaimed by the system when disk space runs low. But
+ // unfortunately it has to live in the same file as offline resources.
+ [cacheURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
+
// Observe for changes to the global access token (and find out the current one).
[[MGLAccountManager sharedManager] addObserver:self
forKeyPath:@"accessToken"
diff --git a/platform/darwin/src/MGLOverlay.h b/platform/darwin/src/MGLOverlay.h
new file mode 100644
index 0000000000..ba03b19737
--- /dev/null
+++ b/platform/darwin/src/MGLOverlay.h
@@ -0,0 +1,67 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLAnnotation.h"
+#import "MGLGeometry.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLOverlay` protocol defines a specific type of annotation that represents
+ both a point and an area on a map. Overlay objects are essentially data objects
+ that contain the geographic data needed to represent the map area. For example,
+ overlays can take the form of common shapes such as rectangles and circles.
+ They can also describe polygons and other complex shapes.
+
+ You use overlays to layer more sophisticated content on top of a map view. For
+ example, you could use an overlay to show the boundaries of a national park or
+ trace a bus route along city streets. This SDK defines several concrete classes
+ that conform to this protocol and define standard shapes.
+
+ Because overlays are also annotations, they have similar usage pattern to
+ annotations. When added to a map view using the `-addOverlay:` method, that
+ view detects whenever the overlay’s defined region intersects the visible
+ portion of the map. At that point, the map view asks its delegate to provide a
+ special overlay view to draw the visual representation of the overlay. If you
+ add an overlay to a map view as an annotation instead, it is treated as an
+ annotation with a single point.
+ */
+@protocol MGLOverlay <MGLAnnotation>
+
+/**
+ The approximate center point of the overlay area. (required) (read-only)
+
+ This point is typically set to the center point of the map’s bounding
+ rectangle. It is used as the anchor point for any callouts displayed for the
+ annotation.
+ */
+@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
+
+/**
+ The cooordinate rectangle that encompasses the overlay. (required) (read-only)
+
+ This property contains the smallest rectangle that completely encompasses the
+ overlay. Implementers of this protocol must set this area when implementing
+ their overlay class, and after setting it, you must not change it.
+ */
+@property (nonatomic, readonly) MGLCoordinateBounds overlayBounds;
+
+/**
+ Returns a Boolean indicating whether the specified rectangle intersects the
+ receiver’s shape.
+
+ You can implement this method to provide more specific bounds checking for an
+ overlay. If you do not implement it, the bounding rectangle is used to detect
+ intersections.
+
+ @param overlayBounds The rectangle to intersect with the receiver’s area.
+ @return `YES` if any part of the map rectangle intersects the receiver’s shape
+ or `NO` if it does not.
+ */
+- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPointAnnotation.h b/platform/darwin/src/MGLPointAnnotation.h
new file mode 100644
index 0000000000..13afcab717
--- /dev/null
+++ b/platform/darwin/src/MGLPointAnnotation.h
@@ -0,0 +1,25 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLShape.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLPointAnnotation` class defines a concrete annotation object located at
+ a specified point. You can use this class, rather than define your own, in
+ situations where all you want to do is associate a point on the map with a
+ title.
+ */
+@interface MGLPointAnnotation : MGLShape
+
+/**
+ The coordinate point of the annotation, specified as a latitude and longitude.
+ */
+@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h
new file mode 100644
index 0000000000..3d5b36abb6
--- /dev/null
+++ b/platform/darwin/src/MGLPolygon.h
@@ -0,0 +1,84 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLMultiPoint.h"
+#import "MGLOverlay.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLPolygon` class represents a shape consisting of one or more points that
+ define a closed polygon. The points are connected end-to-end in the order they
+ are provided. The first and last points are connected to each other to create
+ the closed shape.
+ */
+@interface MGLPolygon : MGLMultiPoint <MGLOverlay>
+
+/**
+ The array of polygons nested inside the receiver.
+
+ The area occupied by any interior polygons is excluded from the overall shape.
+ Interior polygons should not overlap. An interior polygon should not have
+ interior polygons of its own.
+
+ If there are no interior polygons, the value of this property is `nil`.
+ */
+@property (nonatomic, nullable, readonly) NS_ARRAY_OF(MGLPolygon *) *interiorPolygons;
+
+/**
+ Creates and returns an `MGLPolygon` object from the specified set of
+ coordinates.
+
+ @param coords The array of coordinates defining the shape. The data in this
+ array is copied to the new object.
+ @param count The number of items in the `coords` array.
+ @return A new polygon object.
+ */
++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;
+
+/**
+ Creates and returns an `MGLPolygon` object from the specified set of
+ coordinates and interior polygons.
+
+ @param coords The array of coordinates defining the shape. The data in this
+ array is copied to the new object.
+ @param count The number of items in the `coords` array.
+ @param interiorPolygons An array of `MGLPolygon` objects that define regions
+ excluded from the overall shape. If this array is `nil` or empty, the shape
+ is considered to have no interior polygons.
+ @return A new polygon object.
+ */
++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NS_ARRAY_OF(MGLPolygon *) *)interiorPolygons;
+
+@end
+
+/**
+ The `MGLMultiPolygon` class represents a shape consisting of one or more
+ polygons that do not overlap. For example, you would use an `MGLMultiPolygon`
+ object to represent an atoll together with an island in the atoll’s lagoon:
+ the atoll itself would be one `MGLPolygon` object, while the inner island would
+ be another.
+
+ @note `MGLMultiPolygon` objects cannot be added to a map view using
+ `-[MGLMapView addAnnotations:]` and related methods.
+ */
+@interface MGLMultiPolygon : MGLShape <MGLOverlay>
+
+/**
+ An array of polygons forming the multipolygon.
+ */
+@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolygon *) *polygons;
+
+/**
+ Creates and returns a multipolygon object consisting of the given polygons.
+
+ @param polygons The array of polygons defining the shape.
+ @return A new multipolygon object.
+ */
++ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index 5019385cb2..c009d9e3d6 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -1,28 +1,90 @@
#import "MGLPolygon.h"
#import "MGLMultiPoint_Private.h"
+#import "MGLGeometry_Private.h"
@implementation MGLPolygon
@dynamic overlayBounds;
-+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords
- count:(NSUInteger)count
-{
- return [[self alloc] initWithCoordinates:coords count:count];
-}
-
-- (mbgl::ShapeAnnotation::Properties)shapeAnnotationPropertiesObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
- mbgl::ShapeAnnotation::Properties shapeProperties = [super shapeAnnotationPropertiesObjectWithDelegate:delegate];
-
- mbgl::FillAnnotationProperties fillProperties;
- fillProperties.opacity = [delegate alphaForShapeAnnotation:self];
- fillProperties.outlineColor = [delegate strokeColorForShapeAnnotation:self];
- fillProperties.color = [delegate fillColorForPolygonAnnotation:self];
-
- shapeProperties.set<mbgl::FillAnnotationProperties>(fillProperties);
-
- return shapeProperties;
++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
+ return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
+}
+
++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
+ return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
+}
+
+- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
+ if (self = [super initWithCoordinates:coords count:count]) {
+ if (interiorPolygons.count) {
+ _interiorPolygons = interiorPolygons;
+ }
+ }
+ return self;
+}
+
+- (mbgl::LinearRing<double>)ring {
+ NSUInteger count = self.pointCount;
+ CLLocationCoordinate2D *coordinates = self.coordinates;
+
+ mbgl::LinearRing<double> result;
+ result.reserve(self.pointCount);
+ for (NSUInteger i = 0; i < count; i++) {
+ result.push_back(mbgl::Point<double>(coordinates[i].longitude, coordinates[i].latitude));
+ }
+ return result;
+}
+
+- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
+ mbgl::Polygon<double> geometry;
+ geometry.push_back(self.ring);
+ for (MGLPolygon *polygon in self.interiorPolygons) {
+ geometry.push_back(polygon.ring);
+ }
+
+ mbgl::FillAnnotation annotation { geometry };
+ annotation.opacity = [delegate alphaForShapeAnnotation:self];
+ annotation.outlineColor = [delegate strokeColorForShapeAnnotation:self];
+ annotation.color = [delegate fillColorForPolygonAnnotation:self];
+
+ return annotation;
+}
+
+@end
+
+@interface MGLMultiPolygon ()
+
+@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolygon *) *polygons;
+
+@end
+
+@implementation MGLMultiPolygon {
+ MGLCoordinateBounds _overlayBounds;
+}
+
+@synthesize overlayBounds = _overlayBounds;
+
++ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons {
+ return [[self alloc] initWithPolygons:polygons];
+}
+
+- (instancetype)initWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons {
+ if (self = [super init]) {
+ _polygons = polygons;
+
+ mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
+
+ for (MGLPolygon *polygon in _polygons) {
+ bounds.extend(MGLLatLngBoundsFromCoordinateBounds(polygon.overlayBounds));
+ }
+ _overlayBounds = MGLCoordinateBoundsFromLatLngBounds(bounds);
+ }
+ return self;
+}
+
+- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
+ return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
}
@end
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
new file mode 100644
index 0000000000..78d9649751
--- /dev/null
+++ b/platform/darwin/src/MGLPolyline.h
@@ -0,0 +1,60 @@
+#import <Foundation/Foundation.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLMultiPoint.h"
+#import "MGLOverlay.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLPolyline` class represents a shape consisting of one or more points
+ that define connecting line segments. The points are connected end-to-end in
+ the order they are provided. The first and last points are not connected to
+ each other.
+ */
+@interface MGLPolyline : MGLMultiPoint <MGLOverlay>
+
+/**
+ Creates and returns an `MGLPolyline` object from the specified set of
+ coordinates.
+
+ @param coords The array of coordinates defining the shape. The data in this
+ array is copied to the new object.
+ @param count The number of items in the `coords` array.
+ @return A new polyline object.
+ */
++ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords
+ count:(NSUInteger)count;
+
+@end
+
+/**
+ The `MGLMultiPolyline` class represents a shape consisting of one or more
+ polylines. For example, you could use an `MGLMultiPolyline` object to represent
+ both sides of a divided highway (dual carriageway), excluding the median
+ (central reservation): each carriageway would be a distinct `MGLPolyline`
+ object.
+
+ @note `MGLMultiPolyline` objects cannot be added to a map view using
+ `-[MGLMapView addAnnotations:]` and related methods.
+ */
+@interface MGLMultiPolyline : MGLShape <MGLOverlay>
+
+/**
+ An array of polygons forming the multipolyline.
+ */
+@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolyline *) *polylines;
+
+/**
+ Creates and returns a multipolyline object consisting of the given polylines.
+
+ @param polylines The array of polylines defining the shape.
+ @return A new multipolyline object.
+ */
++ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index f560a571bc..15ea5a0952 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -1,6 +1,7 @@
#import "MGLPolyline.h"
#import "MGLMultiPoint_Private.h"
+#import "MGLGeometry_Private.h"
@implementation MGLPolyline
@@ -12,17 +13,58 @@
return [[self alloc] initWithCoordinates:coords count:count];
}
-- (mbgl::ShapeAnnotation::Properties)shapeAnnotationPropertiesObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
- mbgl::ShapeAnnotation::Properties shapeProperties = [super shapeAnnotationPropertiesObjectWithDelegate:delegate];
-
- mbgl::LineAnnotationProperties lineProperties;
- lineProperties.opacity = [delegate alphaForShapeAnnotation:self];
- lineProperties.color = [delegate strokeColorForShapeAnnotation:self];
- lineProperties.width = [delegate lineWidthForPolylineAnnotation:self];
-
- shapeProperties.set<mbgl::LineAnnotationProperties>(lineProperties);
-
- return shapeProperties;
+- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
+ NSUInteger count = self.pointCount;
+ CLLocationCoordinate2D *coordinates = self.coordinates;
+
+ mbgl::LineString<double> geometry;
+ geometry.reserve(self.pointCount);
+ for (NSUInteger i = 0; i < count; i++) {
+ geometry.push_back(mbgl::Point<double>(coordinates[i].longitude, coordinates[i].latitude));
+ }
+
+ mbgl::LineAnnotation annotation { geometry };
+ annotation.opacity = [delegate alphaForShapeAnnotation:self];
+ annotation.color = [delegate strokeColorForShapeAnnotation:self];
+ annotation.width = [delegate lineWidthForPolylineAnnotation:self];
+
+ return annotation;
+}
+
+@end
+
+@interface MGLMultiPolyline ()
+
+@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolyline *) *polylines;
+
+@end
+
+@implementation MGLMultiPolyline {
+ MGLCoordinateBounds _overlayBounds;
+}
+
+@synthesize overlayBounds = _overlayBounds;
+
++ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines {
+ return [[self alloc] initWithPolylines:polylines];
+}
+
+- (instancetype)initWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines {
+ if (self = [super init]) {
+ _polylines = polylines;
+
+ mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
+
+ for (MGLPolyline *polyline in _polylines) {
+ bounds.extend(MGLLatLngBoundsFromCoordinateBounds(polyline.overlayBounds));
+ }
+ _overlayBounds = MGLCoordinateBoundsFromLatLngBounds(bounds);
+ }
+ return self;
+}
+
+- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds {
+ return MGLLatLngBoundsFromCoordinateBounds(_overlayBounds).intersects(MGLLatLngBoundsFromCoordinateBounds(overlayBounds));
}
@end
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
new file mode 100644
index 0000000000..40c92fc32d
--- /dev/null
+++ b/platform/darwin/src/MGLShape.h
@@ -0,0 +1,41 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLAnnotation.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLShape` class is an abstract class that defines the basic properties for
+ all shape-based annotation objects. This class must be subclassed and cannot be
+ used as is. Subclasses are responsible for defining the geometry of the shape
+ and providing an appropriate value for the coordinate property inherited from
+ the `MGLAnnotation` protocol.
+ */
+@interface MGLShape : NSObject <MGLAnnotation>
+
+/**
+ The title of the shape annotation. The default value of this property is `nil`.
+ */
+@property (nonatomic, copy, nullable) NSString *title;
+
+/**
+ The subtitle of the shape annotation. The default value of this property is
+ `nil`.
+ */
+@property (nonatomic, copy, nullable) NSString *subtitle;
+
+#if !TARGET_OS_IPHONE
+
+/**
+ The tooltip of the shape annotation. The default value of this property is
+ `nil`.
+ */
+@property (nonatomic, copy, nullable) NSString *toolTip;
+
+#endif
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h
new file mode 100644
index 0000000000..a617223ea7
--- /dev/null
+++ b/platform/darwin/src/MGLShapeCollection.h
@@ -0,0 +1,35 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLShape.h"
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLShapeCollection` class represents a shape consisting of one or more
+ distinct but related shapes that are instances of `MGLShape`. The constituent
+ shapes can be a mixture of different kinds of shapes.
+
+ @note `MGLShapeCollection` objects cannot be added to a map view using
+ `-[MGLMapView addAnnotations:]` and related methods.
+ */
+@interface MGLShapeCollection : MGLShape
+
+/**
+ An array of shapes forming the shape collection.
+ */
+@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape *) *shapes;
+
+/**
+ Creates and returns a shape collection consisting of the given shapes.
+
+ @param shapes The array of shapes defining the shape collection. The data in
+ this array is copied to the new object.
+ @return A new shape collection object.
+ */
++ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLShapeCollection.m b/platform/darwin/src/MGLShapeCollection.m
new file mode 100644
index 0000000000..5d42b5a51c
--- /dev/null
+++ b/platform/darwin/src/MGLShapeCollection.m
@@ -0,0 +1,21 @@
+#import "MGLShapeCollection.h"
+
+@implementation MGLShapeCollection
+
++ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes {
+ return [[self alloc] initWithShapes:shapes];
+}
+
+- (instancetype)initWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes {
+ if (self = [super init]) {
+ NSAssert(shapes.count, @"Cannot create an empty shape collection");
+ _shapes = shapes.copy;
+ }
+ return self;
+}
+
+- (CLLocationCoordinate2D)coordinate {
+ return _shapes.firstObject.coordinate;
+}
+
+@end
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
new file mode 100644
index 0000000000..8577522b96
--- /dev/null
+++ b/platform/darwin/src/MGLStyle.h
@@ -0,0 +1,165 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A version number identifying the default version of the suite of default styles
+ provided by Mapbox. This version number may be passed into one of the
+ “StyleURLWithVersion” class methods of MGLStyle.
+
+ The value of this constant generally corresponds to the latest released version
+ as of the date on which this SDK was published. You can use this constant to
+ ascertain the style used by `MGLMapView` and `MGLTilePyramidOfflineRegion` when
+ no style URL is specified. Consult the
+ <a href="https://www.mapbox.com/api-documentation/#styles">Mapbox Styles API documentation</a>
+ for the most up-to-date style versioning information.
+
+ @warning The value of this constant may change in a future release of the SDK.
+ If you use any feature that depends on a specific aspect of a default style
+ – for instance, the minimum zoom level that includes roads – you may use the
+ current value of this constant or the underlying style URL, but do not use
+ the constant itself. Such details may change significantly from version to
+ version.
+ */
+static const NSInteger MGLStyleDefaultVersion = 9;
+
+/**
+ A collection of convenience methods for creating style URLs of default styles
+ provided by Mapbox.
+ <a href="https://www.mapbox.com/maps/">Learn more about Mapbox default styles</a>.
+ */
+@interface MGLStyle : NSObject
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/maps/streets/">Mapbox Streets</a> style.
+
+ Streets is a general-purpose style with detailed road and transit networks.
+
+ `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style
+ is specified explicitly.
+ */
++ (NSURL *)streetsStyleURL __attribute__((deprecated("Use -streetsStyleURLWithVersion:.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/streets/">Mapbox Streets</a> style.
+
+ Streets is a general-purpose style with detailed road and transit networks.
+
+ `MGLMapView` and `MGLTilePyramidOfflineRegion` use Mapbox Streets when no style
+ is specified explicitly.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)streetsStyleURLWithVersion:(NSInteger)version;
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/blog/emerald-gl/">Mapbox Emerald</a> style.
+
+ Emerald is a tactile style with subtle textures and dramatic hillshading.
+ */
++ (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/outdoors/">Mapbox Outdoors</a> style.
+
+ Outdoors is a general-purpose style tailored to outdoor activities.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)outdoorsStyleURLWithVersion:(NSInteger)version;
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Light</a> style.
+
+ Light is a subtle, light-colored backdrop for data visualizations.
+ */
++ (NSURL *)lightStyleURL __attribute__((deprecated("Use -lightStyleURLWithVersion:.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Light</a> style.
+
+ Light is a subtle, light-colored backdrop for data visualizations.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)lightStyleURLWithVersion:(NSInteger)version;
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Dark</a> style.
+
+ Dark is a subtle, dark-colored backdrop for data visualizations.
+ */
++ (NSURL *)darkStyleURL __attribute__((deprecated("Use -darkStyleURLWithVersion:.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/light-dark/">Mapbox Dark</a> style.
+
+ Dark is a subtle, dark-colored backdrop for data visualizations.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)darkStyleURLWithVersion:(NSInteger)version;
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite</a> style.
+
+ Satellite is high-resolution satellite and aerial imagery.
+ */
++ (NSURL *)satelliteStyleURL __attribute__((deprecated("Use -satelliteStyleURLWithVersion:.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite</a> style.
+
+ Satellite is high-resolution satellite and aerial imagery.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version;
+
+/**
+ Returns the URL to version 8 of the
+ <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a>
+ style.
+
+ Satellite Streets combines the high-resolution satellite and aerial imagery of
+ Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox
+ Streets.
+ */
++ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURLWithVersion:.")));
+
+/**
+ Returns the URL to the given version of the
+ <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a>
+ style.
+
+ Satellite Streets combines the high-resolution satellite and aerial imagery of
+ Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox
+ Streets.
+
+ @param version The style’s latest released version. As of publication, the
+ current version is `9`.
+ */
++ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/include/MGLTilePyramidOfflineRegion.h b/platform/darwin/src/MGLTilePyramidOfflineRegion.h
index 4e9f394e74..4e9f394e74 100644
--- a/platform/darwin/include/MGLTilePyramidOfflineRegion.h
+++ b/platform/darwin/src/MGLTilePyramidOfflineRegion.h
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
new file mode 100644
index 0000000000..746cb686c0
--- /dev/null
+++ b/platform/darwin/src/MGLTypes.h
@@ -0,0 +1,64 @@
+#import <Foundation/Foundation.h>
+
+#pragma once
+
+#if !__has_feature(nullability)
+ #define NS_ASSUME_NONNULL_BEGIN
+ #define NS_ASSUME_NONNULL_END
+ #define nullable
+ #define nonnull
+ #define null_resettable
+ #define _Nullable
+ #define _Nonnull
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Indicates an error occurred in the Mapbox SDK. */
+extern NSString * const MGLErrorDomain;
+
+/** Error constants for the Mapbox SDK. */
+typedef NS_ENUM(NSInteger, MGLErrorCode) {
+ /** An unknown error occurred. */
+ MGLErrorCodeUnknown = -1,
+ /** The resource could not be found. */
+ MGLErrorCodeNotFound = 1,
+ /** The connection received an invalid server response. */
+ MGLErrorCodeBadServerResponse = 2,
+ /** An attempt to establish a connection failed. */
+ MGLErrorCodeConnectionFailed = 3,
+};
+
+/** The mode used to track the user location on the map. */
+typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {
+ /** The map does not follow the user location. */
+ MGLUserTrackingModeNone = 0,
+ /** The map follows the user location. */
+ MGLUserTrackingModeFollow,
+ /** The map follows the user location and rotates when the heading changes. */
+ MGLUserTrackingModeFollowWithHeading,
+ /** The map follows the user location and rotates when the course changes. */
+ MGLUserTrackingModeFollowWithCourse,
+};
+
+NS_ASSUME_NONNULL_END
+
+#ifndef NS_ARRAY_OF
+ // Foundation collection classes adopted lightweight generics in iOS 9.0 and OS X 10.11 SDKs.
+ #if __has_feature(objc_generics) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100)
+ /** Inserts a type specifier for a pointer to a lightweight generic with the given collection and object classes. Use a `*` for any non-`id` object classes but no `*` for the collection class. */
+ #define NS_ARRAY_OF(ObjectClass...) NSArray <ObjectClass>
+ #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray <ObjectClass>
+ #define NS_SET_OF(ObjectClass...) NSSet <ObjectClass>
+ #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet <ObjectClass>
+ #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary <ObjectClass>
+ #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary <ObjectClass>
+ #else
+ #define NS_ARRAY_OF(ObjectClass...) NSArray
+ #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray
+ #define NS_SET_OF(ObjectClass...) NSSet
+ #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet
+ #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary
+ #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary
+ #endif
+#endif
diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h
index 52ef7fbfa7..1fc9e8b896 100644
--- a/platform/darwin/src/NSBundle+MGLAdditions.h
+++ b/platform/darwin/src/NSBundle+MGLAdditions.h
@@ -4,7 +4,28 @@
NS_ASSUME_NONNULL_BEGIN
-void mgl_linkBundleCategory();
+// Strings in the SDK targets must be retrieved from the framework bundle rather
+// than the main bundle, which is usually the application bundle. Redefining
+// these macros ensures that the framework bundle’s string tables are used at
+// runtime yet tools like genstrings and Xcode can still find the localizable
+// string identifiers. (genstrings has an -s option that would allow us to
+// define our own macros, but Xcode’s Export Localization feature lacks support
+// for it.)
+//
+// As a consequence of this approach, this header must be included in all SDK
+// files that include localizable strings.
+
+#undef NSLocalizedString
+#define NSLocalizedString(key, comment) \
+ [[NSBundle mgl_frameworkBundle] localizedStringForKey:(key) value:@"" table:nil]
+
+#undef NSLocalizedStringFromTable
+#define NSLocalizedStringFromTable(key, tbl, comment) \
+ [[NSBundle mgl_frameworkBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
+
+#undef NSLocalizedStringWithDefaultValue
+#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
+ [[NSBundle mgl_frameworkBundle] localizedStringForKey:(key) value:(val) table:(tbl)]
@interface NSBundle (MGLAdditions)
diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m
index e1f3e7c720..76d9cc0db7 100644
--- a/platform/darwin/src/NSBundle+MGLAdditions.m
+++ b/platform/darwin/src/NSBundle+MGLAdditions.m
@@ -2,8 +2,6 @@
#import "MGLAccountManager.h"
-void mgl_linkBundleCategory() {}
-
@implementation NSBundle (MGLAdditions)
+ (instancetype)mgl_frameworkBundle {
diff --git a/platform/darwin/src/NSProcessInfo+MGLAdditions.h b/platform/darwin/src/NSProcessInfo+MGLAdditions.h
index ea231d4e6a..1dc1439d53 100644
--- a/platform/darwin/src/NSProcessInfo+MGLAdditions.h
+++ b/platform/darwin/src/NSProcessInfo+MGLAdditions.h
@@ -1,7 +1,5 @@
#import <Foundation/Foundation.h>
-void mgl_linkProcessInfoCategory();
-
@interface NSProcessInfo (MGLAdditions)
/**
diff --git a/platform/darwin/src/NSProcessInfo+MGLAdditions.m b/platform/darwin/src/NSProcessInfo+MGLAdditions.m
index 3da2bf73ed..293f49538b 100644
--- a/platform/darwin/src/NSProcessInfo+MGLAdditions.m
+++ b/platform/darwin/src/NSProcessInfo+MGLAdditions.m
@@ -1,7 +1,5 @@
#import "NSProcessInfo+MGLAdditions.h"
-void mgl_linkProcessInfoCategory() {}
-
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
static NSString * const MGLIBDesignablesAgentProcessName = @"IBDesignablesAgentCocoaTouch";
#elif TARGET_OS_MAC
diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h
index 6064f8b40f..3001624d32 100644
--- a/platform/darwin/src/NSString+MGLAdditions.h
+++ b/platform/darwin/src/NSString+MGLAdditions.h
@@ -4,8 +4,6 @@
NS_ASSUME_NONNULL_BEGIN
-void mgl_linkStringCategory();
-
@interface NSString (MGLAdditions)
/** Returns the receiver if non-empty or nil if empty. */
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index b94a5f0198..969886651b 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -1,7 +1,5 @@
#import "NSString+MGLAdditions.h"
-void mgl_linkStringCategory() {}
-
@implementation NSString (MGLAdditions)
- (nullable NSString *)mgl_stringOrNilIfEmpty
diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h
new file mode 100644
index 0000000000..4a97c8e115
--- /dev/null
+++ b/platform/darwin/src/NSValue+MGLAdditions.h
@@ -0,0 +1,75 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLGeometry.h"
+#import "MGLOfflinePack.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Methods for round-tripping values for Mapbox-defined types.
+ */
+@interface NSValue (MGLAdditions)
+
+#pragma mark Working with Geographic Coordinate Values
+
+/**
+ Creates a new value object containing the specified Core Location geographic
+ coordinate structure.
+
+ @param coordinate The value for the new object.
+ @return A new value object that contains the geographic coordinate information.
+ */
++ (instancetype)valueWithMGLCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ The Core Location geographic coordinate structure representation of the value.
+ */
+@property (readonly) CLLocationCoordinate2D MGLCoordinateValue;
+
+/**
+ Creates a new value object containing the specified Mapbox coordinate span
+ structure.
+
+ @param span The value for the new object.
+ @return A new value object that contains the coordinate span information.
+ */
++ (instancetype)valueWithMGLCoordinateSpan:(MGLCoordinateSpan)span;
+
+/**
+ The Mapbox coordinate span structure representation of the value.
+ */
+@property (readonly) MGLCoordinateSpan MGLCoordinateSpanValue;
+
+/**
+ Creates a new value object containing the specified Mapbox coordinate bounds
+ structure.
+
+ @param bounds The value for the new object.
+ @return A new value object that contains the coordinate bounds information.
+ */
++ (instancetype)valueWithMGLCoordinateBounds:(MGLCoordinateBounds)bounds;
+
+/**
+ The Mapbox coordinate bounds structure representation of the value.
+ */
+@property (readonly) MGLCoordinateBounds MGLCoordinateBoundsValue;
+
+#pragma mark Working with Offline Map Values
+
+/**
+ Creates a new value object containing the given `MGLOfflinePackProgress`
+ structure.
+
+ @param progress The value for the new object.
+ @return A new value object that contains the offline pack progress information.
+ */
++ (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress;
+
+/**
+ The `MGLOfflinePackProgress` structure representation of the value.
+ */
+@property (readonly) MGLOfflinePackProgress MGLOfflinePackProgressValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m
new file mode 100644
index 0000000000..0d2128bea8
--- /dev/null
+++ b/platform/darwin/src/NSValue+MGLAdditions.m
@@ -0,0 +1,49 @@
+#import "NSValue+MGLAdditions.h"
+
+@implementation NSValue (MGLAdditions)
+
+#pragma mark Geometry
+
++ (instancetype)valueWithMGLCoordinate:(CLLocationCoordinate2D)coordinate {
+ return [self valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)];
+}
+
+- (CLLocationCoordinate2D)MGLCoordinateValue {
+ CLLocationCoordinate2D coordinate;
+ [self getValue:&coordinate];
+ return coordinate;
+}
+
++ (instancetype)valueWithMGLCoordinateSpan:(MGLCoordinateSpan)span {
+ return [self valueWithBytes:&span objCType:@encode(MGLCoordinateSpan)];
+}
+
+- (MGLCoordinateSpan)MGLCoordinateSpanValue {
+ MGLCoordinateSpan span;
+ [self getValue:&span];
+ return span;
+}
+
++ (instancetype)valueWithMGLCoordinateBounds:(MGLCoordinateBounds)bounds {
+ return [self valueWithBytes:&bounds objCType:@encode(MGLCoordinateBounds)];
+}
+
+- (MGLCoordinateBounds)MGLCoordinateBoundsValue {
+ MGLCoordinateBounds bounds;
+ [self getValue:&bounds];
+ return bounds;
+}
+
+#pragma mark Offline maps
+
++ (NSValue *)valueWithMGLOfflinePackProgress:(MGLOfflinePackProgress)progress {
+ return [NSValue value:&progress withObjCType:@encode(MGLOfflinePackProgress)];
+}
+
+- (MGLOfflinePackProgress)MGLOfflinePackProgressValue {
+ MGLOfflinePackProgress progress;
+ [self getValue:&progress];
+ return progress;
+}
+
+@end
diff --git a/platform/darwin/src/application_root.mm b/platform/darwin/src/application_root.mm
deleted file mode 100644
index d4702c7ec5..0000000000
--- a/platform/darwin/src/application_root.mm
+++ /dev/null
@@ -1,18 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#include <mbgl/platform/platform.hpp>
-
-namespace mbgl {
-namespace platform {
-
-// Returns the path to the root folder of the application.
-const std::string &applicationRoot() {
- static const std::string root = []() -> std::string {
- NSString *path = [[[NSBundle mainBundle] resourceURL] path];
- return {[path cStringUsingEncoding : NSUTF8StringEncoding],
- [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]};
- }();
- return root;
-}
-}
-}
diff --git a/platform/darwin/src/async_task.cpp b/platform/darwin/src/async_task.cpp
new file mode 100644
index 0000000000..513629726b
--- /dev/null
+++ b/platform/darwin/src/async_task.cpp
@@ -0,0 +1,71 @@
+#include <mbgl/util/async_task.hpp>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <atomic>
+
+namespace mbgl {
+namespace util {
+
+class AsyncTask::Impl {
+public:
+ Impl(std::function<void()>&& fn)
+ : task(std::move(fn)),
+ loop(CFRunLoopGetCurrent()) {
+ CFRunLoopSourceContext context = {
+ 0,
+ this,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ perform
+ };
+ source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
+ CFRunLoopAddSource(loop, source, kCFRunLoopDefaultMode);
+ }
+
+ ~Impl() {
+ CFRunLoopSourceInvalidate(source);
+ CFRelease(source);
+ }
+
+ void maySend() {
+ if (!queued.test_and_set()) {
+ CFRunLoopSourceSignal(source);
+ CFRunLoopWakeUp(loop);
+ }
+ }
+
+ void runTask() {
+ queued.clear();
+ task();
+ }
+
+private:
+ static void perform(void* info) {
+ reinterpret_cast<Impl*>(info)->runTask();
+ }
+
+ std::function<void()> task;
+ std::atomic_flag queued = ATOMIC_FLAG_INIT;
+
+ CFRunLoopRef loop;
+ CFRunLoopSourceRef source;
+};
+
+AsyncTask::AsyncTask(std::function<void()>&& fn)
+ : impl(std::make_unique<Impl>(std::move(fn))) {
+}
+
+AsyncTask::~AsyncTask() = default;
+
+void AsyncTask::send() {
+ impl->maySend();
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/darwin/src/headless_view_cgl.cpp b/platform/darwin/src/headless_view_cgl.cpp
new file mode 100644
index 0000000000..a4f809a250
--- /dev/null
+++ b/platform/darwin/src/headless_view_cgl.cpp
@@ -0,0 +1,113 @@
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace mbgl {
+
+gl::glProc HeadlessView::initializeExtension(const char* name) {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void* symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<gl::glProc>(symbol);
+}
+
+void HeadlessView::createContext() {
+ CGLError error = CGLCreateContext(display->pixelFormat, NULL, &glContext);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Error creating GL context object:") + CGLErrorString(error) + "\n");
+ }
+
+ error = CGLEnable(glContext, kCGLCEMPEngine);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + CGLErrorString(error) + "\n");
+ }
+}
+
+void HeadlessView::destroyContext() {
+ CGLDestroyContext(glContext);
+}
+
+void HeadlessView::resizeFramebuffer() {
+ const unsigned int w = dimensions[0] * pixelRatio;
+ const unsigned int h = dimensions[1] * pixelRatio;
+
+ // Create depth/stencil buffer
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
+
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
+
+ GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ std::string error("Couldn't create framebuffer: ");
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
+ default: error += "other"; break;
+ }
+ throw std::runtime_error(error);
+ }
+
+ MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
+}
+
+void HeadlessView::clearBuffers() {
+ assert(active);
+
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+
+ if (fbo) {
+ MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
+ fbo = 0;
+ }
+
+ if (fboColor) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
+ fboColor = 0;
+ }
+
+ if (fboDepthStencil) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
+ fboDepthStencil = 0;
+ }
+}
+
+void HeadlessView::activateContext() {
+ CGLError error = CGLSetCurrentContext(glContext);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Switching OpenGL context failed:") + CGLErrorString(error) + "\n");
+ }
+}
+
+void HeadlessView::deactivateContext() {
+ CGLError error = CGLSetCurrentContext(nullptr);
+ if (error != kCGLNoError) {
+ throw std::runtime_error(std::string("Removing OpenGL context failed:") + CGLErrorString(error) + "\n");
+ }
+}
+
+} // namespace mbgl
diff --git a/platform/darwin/src/headless_view_eagl.mm b/platform/darwin/src/headless_view_eagl.mm
new file mode 100644
index 0000000000..a1f335fc6e
--- /dev/null
+++ b/platform/darwin/src/headless_view_eagl.mm
@@ -0,0 +1,103 @@
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+
+#include <OpenGLES/EAGL.h>
+
+namespace mbgl {
+
+gl::glProc HeadlessView::initializeExtension(const char* name) {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void* symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<gl::glProc>(symbol);
+}
+
+void HeadlessView::createContext() {
+ glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ if (glContext == nil) {
+ throw std::runtime_error("Error creating GL context object");
+ }
+ [reinterpret_cast<EAGLContext*>(glContext) retain];
+ reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES;
+}
+
+void HeadlessView::destroyContext() {
+ [reinterpret_cast<EAGLContext*>(glContext) release];
+ glContext = nil;
+}
+
+void HeadlessView::resizeFramebuffer() {
+ const unsigned int w = dimensions[0] * pixelRatio;
+ const unsigned int h = dimensions[1] * pixelRatio;
+
+ // Create depth/stencil buffer
+ MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboDepthStencil));
+ MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboDepthStencil));
+ MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+
+ MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboColor));
+ MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboColor));
+ MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+
+ MBGL_CHECK_ERROR(glGenFramebuffers(1, &fbo));
+ MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo));
+
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fboColor));
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil));
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil));
+
+ GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ std::string error("Couldn't create framebuffer: ");
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: (error += "incomplete attachment"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error += "incomplete missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: error += "incomplete dimensions"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
+ default: error += "other"; break;
+ }
+ throw std::runtime_error(error);
+ }
+
+ MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
+}
+
+void HeadlessView::clearBuffers() {
+ assert(active);
+
+ MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+
+ if (fbo) {
+ MBGL_CHECK_ERROR(glDeleteFramebuffers(1, &fbo));
+ fbo = 0;
+ }
+
+ if (fboColor) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboColor));
+ fboColor = 0;
+ }
+
+ if (fboDepthStencil) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboDepthStencil));
+ fboDepthStencil = 0;
+ }
+}
+
+void HeadlessView::activateContext() {
+ [EAGLContext setCurrentContext:reinterpret_cast<EAGLContext*>(glContext)];
+}
+
+void HeadlessView::deactivateContext() {
+ [EAGLContext setCurrentContext:nil];
+}
+
+} // namespace mbgl
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm
new file mode 100644
index 0000000000..eb751258c8
--- /dev/null
+++ b/platform/darwin/src/http_file_source.mm
@@ -0,0 +1,320 @@
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+#include <mbgl/util/http_header.hpp>
+#include <mbgl/util/async_task.hpp>
+
+#include <mbgl/util/version.hpp>
+
+#import <Foundation/Foundation.h>
+
+#include <mutex>
+
+@interface MBGLBundleCanary : NSObject
+@end
+
+@implementation MBGLBundleCanary
+@end
+
+namespace mbgl {
+
+// Data that is shared between the requesting thread and the thread running the completion handler.
+class HTTPRequestShared {
+public:
+ HTTPRequestShared(Response& response_, util::AsyncTask& async_)
+ : response(response_),
+ async(async_) {
+ }
+
+ void notify(const Response& response_) {
+ std::lock_guard<std::mutex> lock(mutex);
+ if (!cancelled) {
+ response = response_;
+ async.send();
+ }
+ }
+
+ void cancel() {
+ std::lock_guard<std::mutex> lock(mutex);
+ cancelled = true;
+ }
+
+private:
+ std::mutex mutex;
+ bool cancelled = false;
+
+ Response& response;
+ util::AsyncTask& async;
+};
+
+class HTTPRequest : public AsyncRequest {
+public:
+ HTTPRequest(FileSource::Callback callback_)
+ : shared(std::make_shared<HTTPRequestShared>(response, async)),
+ callback(callback_) {
+ }
+
+ ~HTTPRequest() override {
+ shared->cancel();
+ if (task) {
+ [task cancel];
+ }
+ }
+
+ std::shared_ptr<HTTPRequestShared> shared;
+ NSURLSessionDataTask* task = nil;
+
+private:
+ FileSource::Callback callback;
+ Response response;
+
+ util::AsyncTask async { [this] {
+ // Calling `callback` may result in deleting `this`. Copy data to temporaries first.
+ auto callback_ = callback;
+ auto response_ = response;
+ callback_(response_);
+ } };
+};
+
+class HTTPFileSource::Impl {
+public:
+ Impl() {
+ @autoreleasepool {
+ NSURLSessionConfiguration* sessionConfig =
+ [NSURLSessionConfiguration defaultSessionConfiguration];
+ sessionConfig.timeoutIntervalForResource = 30;
+ sessionConfig.HTTPMaximumConnectionsPerHost = 8;
+ sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ sessionConfig.URLCache = nil;
+
+ session = [NSURLSession sessionWithConfiguration:sessionConfig];
+
+ userAgent = getUserAgent();
+
+ accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
+ }
+ }
+
+ NSURLSession* session = nil;
+ NSString* userAgent = nil;
+ NSInteger accountType = 0;
+
+private:
+ NSString* getUserAgent() const;
+ NSBundle* getSDKBundle() const;
+};
+
+NSString *HTTPFileSource::Impl::getUserAgent() const {
+ NSMutableArray *userAgentComponents = [NSMutableArray array];
+
+ NSBundle *appBundle = [NSBundle mainBundle];
+ if (appBundle) {
+ NSString *appName = appBundle.infoDictionary[@"CFBundleName"];
+ [userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@",
+ appName.length ? appName : appBundle.infoDictionary[@"CFBundleIdentifier"],
+ appBundle.infoDictionary[@"CFBundleShortVersionString"]]];
+ } else {
+ [userAgentComponents addObject:[NSProcessInfo processInfo].processName];
+ }
+
+ NSBundle *sdkBundle = HTTPFileSource::Impl::getSDKBundle();
+ if (sdkBundle) {
+ NSString *versionString = sdkBundle.infoDictionary[@"MGLSemanticVersionString"];
+ if (!versionString) {
+ versionString = sdkBundle.infoDictionary[@"CFBundleShortVersionString"];
+ }
+ if (versionString) {
+ [userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@",
+ sdkBundle.infoDictionary[@"CFBundleName"], versionString]];
+ }
+ }
+
+ // Avoid %s here because it inserts hidden bidirectional markers on OS X when the system
+ // language is set to a right-to-left language.
+ [userAgentComponents addObject:[NSString stringWithFormat:@"MapboxGL/%@ (%@)",
+ CFSTR(MBGL_VERSION_STRING), CFSTR(MBGL_VERSION_REV)]];
+
+ NSString *systemName = @"Darwin";
+#if TARGET_OS_IPHONE
+ systemName = @"iOS";
+#elif TARGET_OS_MAC
+ systemName = @"OS X";
+#elif TARGET_OS_WATCH
+ systemName = @"watchOS";
+#elif TARGET_OS_TV
+ systemName = @"tvOS";
+#endif
+#if TARGET_OS_SIMULATOR
+ systemName = [systemName stringByAppendingString:@" Simulator"];
+#endif
+ NSString *systemVersion = nil;
+ if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) {
+ NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion;
+ systemVersion = [NSString stringWithFormat:@"%ld.%ld.%ld",
+ (long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion];
+ }
+ if (systemVersion) {
+ [userAgentComponents addObject:[NSString stringWithFormat:@"%@/%@", systemName, systemVersion]];
+ }
+
+ NSString *cpu = nil;
+#if TARGET_CPU_X86
+ cpu = @"x86";
+#elif TARGET_CPU_X86_64
+ cpu = @"x86_64";
+#elif TARGET_CPU_ARM
+ cpu = @"arm";
+#elif TARGET_CPU_ARM64
+ cpu = @"arm64";
+#endif
+ if (cpu) {
+ [userAgentComponents addObject:[NSString stringWithFormat:@"(%@)", cpu]];
+ }
+
+ return [userAgentComponents componentsJoinedByString:@" "];
+}
+
+NSBundle *HTTPFileSource::Impl::getSDKBundle() const {
+ NSBundle *bundle = [NSBundle bundleForClass:[MBGLBundleCanary class]];
+ if (bundle && ![bundle.infoDictionary[@"CFBundlePackageType"] isEqualToString:@"FMWK"]) {
+ // For static frameworks, the class is contained in the application bundle rather than the
+ // framework bundle.
+ bundle = [NSBundle bundleWithPath:[bundle.privateFrameworksPath
+ stringByAppendingPathComponent:@"Mapbox.framework"]];
+ }
+ return bundle;
+}
+
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
+ return 20;
+}
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ auto request = std::make_unique<HTTPRequest>(callback);
+ auto shared = request->shared; // Explicit copy so that it also gets copied into the completion handler block below.
+
+ @autoreleasepool {
+ NSURL* url = [NSURL URLWithString:@(resource.url.c_str())];
+ if (impl->accountType == 0 &&
+ ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
+ NSString* absoluteString = [url.absoluteString
+ stringByAppendingFormat:(url.query ? @"&%@" : @"?%@"), @"events=true"];
+ url = [NSURL URLWithString:absoluteString];
+ }
+
+ NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
+ if (resource.priorEtag) {
+ [req addValue:@(resource.priorEtag->c_str())
+ forHTTPHeaderField:@"If-None-Match"];
+ } else if (resource.priorModified) {
+ [req addValue:@(util::rfc1123(*resource.priorModified).c_str())
+ forHTTPHeaderField:@"If-Modified-Since"];
+ }
+
+ [req addValue:impl->userAgent forHTTPHeaderField:@"User-Agent"];
+
+ request->task = [impl->session
+ dataTaskWithRequest:req
+ completionHandler:^(NSData* data, NSURLResponse* res, NSError* error) {
+ if (error && [error code] == NSURLErrorCancelled) {
+ return;
+ }
+
+ Response response;
+ using Error = Response::Error;
+
+ if (error) {
+ if (data) {
+ response.data =
+ std::make_shared<std::string>((const char*)[data bytes], [data length]);
+ }
+
+ switch ([error code]) {
+ case NSURLErrorBadServerResponse: // 5xx errors
+ response.error = std::make_unique<Error>(
+ Error::Reason::Server, [[error localizedDescription] UTF8String]);
+ break;
+
+ case NSURLErrorNetworkConnectionLost:
+ case NSURLErrorCannotFindHost:
+ case NSURLErrorCannotConnectToHost:
+ case NSURLErrorDNSLookupFailed:
+ case NSURLErrorNotConnectedToInternet:
+ case NSURLErrorInternationalRoamingOff:
+ case NSURLErrorCallIsActive:
+ case NSURLErrorDataNotAllowed:
+ case NSURLErrorTimedOut:
+ response.error = std::make_unique<Error>(
+ Error::Reason::Connection, [[error localizedDescription] UTF8String]);
+ break;
+
+ default:
+ response.error = std::make_unique<Error>(
+ Error::Reason::Other, [[error localizedDescription] UTF8String]);
+ break;
+ }
+ } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
+ const long responseCode = [(NSHTTPURLResponse *)res statusCode];
+
+ NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
+ NSString *cache_control = [headers objectForKey:@"Cache-Control"];
+ if (cache_control) {
+ response.expires = http::CacheControl::parse([cache_control UTF8String]).toTimePoint();
+ }
+
+ NSString *expires = [headers objectForKey:@"Expires"];
+ if (expires) {
+ response.expires = util::parseTimestamp([expires UTF8String]);
+ }
+
+ NSString *last_modified = [headers objectForKey:@"Last-Modified"];
+ if (last_modified) {
+ response.modified = util::parseTimestamp([last_modified UTF8String]);
+ }
+
+ NSString *etag = [headers objectForKey:@"ETag"];
+ if (etag) {
+ response.etag = std::string([etag UTF8String]);
+ }
+
+ if (responseCode == 200) {
+ response.data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
+ } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
+ response.noContent = true;
+ } else if (responseCode == 304) {
+ response.notModified = true;
+ } else if (responseCode == 404) {
+ response.error =
+ std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
+ } else if (responseCode >= 500 && responseCode < 600) {
+ response.error =
+ std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
+ std::to_string(responseCode));
+ } else {
+ response.error =
+ std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
+ std::to_string(responseCode));
+ }
+ } else {
+ // This should never happen.
+ response.error = std::make_unique<Error>(Error::Reason::Other,
+ "Response class is not NSHTTPURLResponse");
+ }
+
+ shared->notify(response);
+ }];
+
+ [request->task resume];
+ }
+
+ return std::move(request);
+}
+
+}
diff --git a/platform/darwin/src/http_request_nsurl.mm b/platform/darwin/src/http_request_nsurl.mm
deleted file mode 100644
index e58441e4d2..0000000000
--- a/platform/darwin/src/http_request_nsurl.mm
+++ /dev/null
@@ -1,267 +0,0 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-
-#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/run_loop.hpp>
-
-#import <Foundation/Foundation.h>
-
-#include <map>
-#include <cassert>
-#include <mutex>
-
-namespace mbgl {
-
-class HTTPNSURLContext;
-
-class HTTPNSURLRequest : public HTTPRequestBase {
-public:
- HTTPNSURLRequest(HTTPNSURLContext*, Resource, Callback);
- ~HTTPNSURLRequest();
-
- void cancel() final;
-
-private:
- static std::unique_ptr<Response> handleResult(NSData *data, NSURLResponse *res, NSError *error, Resource);
- void handleResponse();
-
- HTTPNSURLContext *context = nullptr;
- std::shared_ptr<std::pair<bool, std::mutex>> cancelled;
- NSURLSessionDataTask *task = nullptr;
- std::unique_ptr<Response> response;
- util::AsyncTask async;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class HTTPNSURLContext : public HTTPContextBase {
-public:
- HTTPNSURLContext();
- ~HTTPNSURLContext();
-
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
-
- NSURLSession *session = nil;
- NSString *userAgent = nil;
- NSInteger accountType = 0;
-};
-
-HTTPNSURLContext::HTTPNSURLContext() {
- @autoreleasepool {
- NSURLSessionConfiguration* sessionConfig =
- [NSURLSessionConfiguration defaultSessionConfiguration];
- sessionConfig.timeoutIntervalForResource = 30;
- sessionConfig.HTTPMaximumConnectionsPerHost = 8;
- sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
- sessionConfig.URLCache = nil;
-
- session = [NSURLSession sessionWithConfiguration:sessionConfig];
- [session retain];
-
- // Write user agent string
- userAgent = @"MapboxGL";
-
- accountType = [[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"];
- }
-}
-
-HTTPNSURLContext::~HTTPNSURLContext() {
- [session release];
- session = nullptr;
-
- [userAgent release];
- userAgent = nullptr;
-}
-
-HTTPRequestBase* HTTPNSURLContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPNSURLRequest(this, resource, callback);
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HTTPNSURLRequest::HTTPNSURLRequest(HTTPNSURLContext* context_,
- Resource resource_,
- Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- context(context_),
- async([this] { handleResponse(); }) {
-
- // Ensure that a stack-allocated std::shared_ptr gets copied into the Objective-C
- // block used as the completion handler below. Objective-C will implicitly copy captured
- // stack variables. For member variable access it will implicitly copy the this pointer.
- // That wouldn't work here because we need the block to have its own shared_ptr.
- auto cancelled_ = cancelled = std::make_shared<std::pair<bool, std::mutex>>();
- cancelled->first = false;
-
- @autoreleasepool {
- NSURL* url = [NSURL URLWithString:@(resource.url.c_str())];
- if (context->accountType == 0 &&
- ([url.host isEqualToString:@"mapbox.com"] || [url.host hasSuffix:@".mapbox.com"])) {
- NSString* absoluteString = [url.absoluteString
- stringByAppendingFormat:(url.query ? @"&%@" : @"?%@"), @"events=true"];
- url = [NSURL URLWithString:absoluteString];
- }
-
- NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
- if (resource.priorEtag) {
- [req addValue:@(resource.priorEtag->c_str())
- forHTTPHeaderField:@"If-None-Match"];
- } else if (resource.priorModified) {
- [req addValue:@(util::rfc1123(*resource.priorModified).c_str())
- forHTTPHeaderField:@"If-Modified-Since"];
- }
-
- [req addValue:context->userAgent forHTTPHeaderField:@"User-Agent"];
-
- task = [context->session
- dataTaskWithRequest:req
- completionHandler:^(NSData* data, NSURLResponse* res, NSError* error) {
- std::unique_ptr<Response> response_ = HTTPNSURLRequest::handleResult(data, res, error, resource_);
- std::lock_guard<std::mutex> lock(cancelled_->second);
- if (!cancelled_->first) {
- response = std::move(response_);
- async.send();
- }
- }];
- [task retain];
- [task resume];
- }
-}
-
-HTTPNSURLRequest::~HTTPNSURLRequest() {
- assert(!task);
-}
-
-void HTTPNSURLRequest::handleResponse() {
- if (task) {
- [task release];
- task = nullptr;
- }
-
- assert(response);
- notify(*response);
-
- delete this;
-}
-
-void HTTPNSURLRequest::cancel() {
- if (task) {
- [task cancel];
- [task release];
- task = nullptr;
- }
-
- {
- std::lock_guard<std::mutex> lock(cancelled->second);
- cancelled->first = true;
- }
-
- // The lock is in place to enforce that `async` is not accessed if the request has been
- // cancelled. Therefore it's not necessary to hold the lock beyond setting cancelled to
- // true, and in fact it's unsafe to so: if this is the last remaining shared reference,
- // `delete this` will destroy the mutex. If the lock was held, it would then be orphaned.
-
- delete this;
-}
-
-std::unique_ptr<Response> HTTPNSURLRequest::handleResult(NSData *data, NSURLResponse *res, NSError *error, Resource resource) {
- std::unique_ptr<Response> response = std::make_unique<Response>();
- using Error = Response::Error;
-
- if (error) {
- if ([error code] == NSURLErrorCancelled) {
- response.reset();
-
- } else {
- if (data) {
- response->data =
- std::make_shared<std::string>((const char*)[data bytes], [data length]);
- }
-
- switch ([error code]) {
- case NSURLErrorBadServerResponse: // 5xx errors
- response->error = std::make_unique<Error>(
- Error::Reason::Server, [[error localizedDescription] UTF8String]);
- break;
-
- case NSURLErrorNetworkConnectionLost:
- case NSURLErrorCannotFindHost:
- case NSURLErrorCannotConnectToHost:
- case NSURLErrorDNSLookupFailed:
- case NSURLErrorNotConnectedToInternet:
- case NSURLErrorInternationalRoamingOff:
- case NSURLErrorCallIsActive:
- case NSURLErrorDataNotAllowed:
- case NSURLErrorTimedOut:
- response->error = std::make_unique<Error>(
- Error::Reason::Connection, [[error localizedDescription] UTF8String]);
- break;
-
- default:
- response->error = std::make_unique<Error>(
- Error::Reason::Other, [[error localizedDescription] UTF8String]);
- break;
- }
- }
- } else if ([res isKindOfClass:[NSHTTPURLResponse class]]) {
- const long responseCode = [(NSHTTPURLResponse *)res statusCode];
-
- NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
- NSString *cache_control = [headers objectForKey:@"Cache-Control"];
- if (cache_control) {
- response->expires = parseCacheControl([cache_control UTF8String]);
- }
-
- NSString *expires = [headers objectForKey:@"Expires"];
- if (expires) {
- response->expires = util::parseTimePoint([expires UTF8String]);
- }
-
- NSString *last_modified = [headers objectForKey:@"Last-Modified"];
- if (last_modified) {
- response->modified = util::parseTimePoint([last_modified UTF8String]);
- }
-
- NSString *etag = [headers objectForKey:@"ETag"];
- if (etag) {
- response->etag = std::string([etag UTF8String]);
- }
-
- if (responseCode == 200) {
- response->data = std::make_shared<std::string>((const char *)[data bytes], [data length]);
- } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
- response->noContent = true;
- } else if (responseCode == 304) {
- response->notModified = true;
- } else if (responseCode == 404) {
- response->error =
- std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
- } else if (responseCode >= 500 && responseCode < 600) {
- response->error =
- std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- } else {
- response->error =
- std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- }
- } else {
- // This should never happen.
- response->error = std::make_unique<Error>(Error::Reason::Other,
- "Response class is not NSHTTPURLResponse");
- }
-
- return response;
-}
-
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<HTTPNSURLContext>();
-}
-
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
- return 20;
-}
-
-}
diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp
new file mode 100644
index 0000000000..6e9a1c50b0
--- /dev/null
+++ b/platform/darwin/src/run_loop.cpp
@@ -0,0 +1,51 @@
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace mbgl {
+namespace util {
+
+static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
+static RunLoop mainRunLoop;
+
+class RunLoop::Impl {
+public:
+ std::unique_ptr<AsyncTask> async;
+};
+
+RunLoop* RunLoop::Get() {
+ assert(current.get());
+ return current.get();
+}
+
+RunLoop::RunLoop(Type)
+ : impl(std::make_unique<Impl>()) {
+ current.set(this);
+ impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
+}
+
+RunLoop::~RunLoop() {
+ current.set(nullptr);
+}
+
+void RunLoop::push(std::shared_ptr<WorkTask> task) {
+ withMutex([&] { queue.push(std::move(task)); });
+ impl->async->send();
+}
+
+void RunLoop::run() {
+ CFRunLoopRun();
+}
+
+void RunLoop::runOnce() {
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+}
+
+void RunLoop::stop() {
+ invoke([&] { CFRunLoopStop(CFRunLoopGetCurrent()); });
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/darwin/src/timer.cpp b/platform/darwin/src/timer.cpp
new file mode 100644
index 0000000000..e9e58d86c6
--- /dev/null
+++ b/platform/darwin/src/timer.cpp
@@ -0,0 +1,60 @@
+#include <mbgl/util/timer.hpp>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+namespace mbgl {
+namespace util {
+
+CFTimeInterval toCFTimeInterval(Duration duration) {
+ return std::chrono::duration<CFTimeInterval>(duration).count();
+}
+
+CFAbsoluteTime toCFAbsoluteTime(Duration duration) {
+ return CFAbsoluteTimeGetCurrent() + toCFTimeInterval(duration);
+}
+
+class Timer::Impl {
+public:
+ Impl(Duration timeout, Duration repeat, std::function<void()>&& fn)
+ : task(std::move(fn)),
+ loop(CFRunLoopGetCurrent()) {
+ CFRunLoopTimerContext context = {
+ 0,
+ this,
+ nullptr,
+ nullptr,
+ nullptr
+ };
+ timer = CFRunLoopTimerCreate(kCFAllocatorDefault, toCFAbsoluteTime(timeout), toCFTimeInterval(repeat), 0, 0, perform, &context);
+ CFRunLoopAddTimer(loop, timer, kCFRunLoopDefaultMode);
+ }
+
+ ~Impl() {
+ CFRunLoopRemoveTimer(loop, timer, kCFRunLoopDefaultMode);
+ CFRelease(timer);
+ }
+
+private:
+ static void perform(CFRunLoopTimerRef, void* info) {
+ reinterpret_cast<Impl*>(info)->task();
+ }
+
+ std::function<void()> task;
+
+ CFRunLoopRef loop;
+ CFRunLoopTimerRef timer;
+};
+
+Timer::Timer() = default;
+Timer::~Timer() = default;
+
+void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) {
+ impl = std::make_unique<Impl>(timeout, repeat, std::move(cb));
+}
+
+void Timer::stop() {
+ impl.reset();
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/darwin/test/MGLClockDirectionFormatterTests.m b/platform/darwin/test/MGLClockDirectionFormatterTests.m
new file mode 100644
index 0000000000..a020ed88b2
--- /dev/null
+++ b/platform/darwin/test/MGLClockDirectionFormatterTests.m
@@ -0,0 +1,74 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+static NSString * const MGLTestLocaleIdentifier = @"en-US";
+
+@interface MGLClockDirectionFormatterTests : XCTestCase
+
+@end
+
+@implementation MGLClockDirectionFormatterTests
+
+- (void)testClockDirections {
+ MGLClockDirectionFormatter *shortFormatter = [[MGLClockDirectionFormatter alloc] init];
+ shortFormatter.unitStyle = NSFormattingUnitStyleShort;
+
+ MGLClockDirectionFormatter *mediumFormatter = [[MGLClockDirectionFormatter alloc] init];
+
+ MGLClockDirectionFormatter *longFormatter = [[MGLClockDirectionFormatter alloc] init];
+ longFormatter.unitStyle = NSFormattingUnitStyleLong;
+
+ CLLocationDirection direction;
+
+ direction = -90;
+ XCTAssertEqualObjects(@"9:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"9 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"9 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 0;
+ XCTAssertEqualObjects(@"12:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 45;
+ XCTAssertEqualObjects(@"2:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"2 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"2 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 90;
+ XCTAssertEqualObjects(@"3:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"3 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"3 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 180;
+ XCTAssertEqualObjects(@"6:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"6 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"6 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 270;
+ XCTAssertEqualObjects(@"9:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"9 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"9 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 359.34951805867024;
+ XCTAssertEqualObjects(@"12:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 360;
+ XCTAssertEqualObjects(@"12:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 360.1;
+ XCTAssertEqualObjects(@"12:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [longFormatter stringFromDirection:direction]);
+
+ direction = 720;
+ XCTAssertEqualObjects(@"12:00", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"12 o’clock", [longFormatter stringFromDirection:direction]);
+}
+
+@end
diff --git a/platform/darwin/test/MGLCompassDirectionFormatterTests.m b/platform/darwin/test/MGLCompassDirectionFormatterTests.m
new file mode 100644
index 0000000000..bba0317ebf
--- /dev/null
+++ b/platform/darwin/test/MGLCompassDirectionFormatterTests.m
@@ -0,0 +1,88 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+@interface MGLCompassDirectionFormatterTests : XCTestCase
+
+@end
+
+@implementation MGLCompassDirectionFormatterTests
+
+- (void)testCompassDirections {
+ MGLCompassDirectionFormatter *shortFormatter = [[MGLCompassDirectionFormatter alloc] init];
+ shortFormatter.unitStyle = NSFormattingUnitStyleShort;
+
+ MGLCompassDirectionFormatter *mediumFormatter = [[MGLCompassDirectionFormatter alloc] init];
+ XCTAssertEqual(mediumFormatter.unitStyle, NSFormattingUnitStyleMedium, @"Unit style should be medium by default.");
+
+ MGLCompassDirectionFormatter *longFormatter = [[MGLCompassDirectionFormatter alloc] init];
+ longFormatter.unitStyle = NSFormattingUnitStyleLong;
+
+ CLLocationDirection direction;
+
+ direction = -45;
+ XCTAssertEqualObjects(@"NW", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"northwest", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"northwest", [longFormatter stringFromDirection:direction]);
+
+ direction = 0;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+
+ direction = 1;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+
+ direction = 10;
+ XCTAssertEqualObjects(@"NbE", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north by east", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north by east", [longFormatter stringFromDirection:direction]);
+
+ direction = 20;
+ XCTAssertEqualObjects(@"NNE", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north-northeast", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north-northeast", [longFormatter stringFromDirection:direction]);
+
+ direction = 45;
+ XCTAssertEqualObjects(@"NE", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"northeast", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"northeast", [longFormatter stringFromDirection:direction]);
+
+ direction = 90;
+ XCTAssertEqualObjects(@"E", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"east", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"east", [longFormatter stringFromDirection:direction]);
+
+ direction = 180;
+ XCTAssertEqualObjects(@"S", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"south", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"south", [longFormatter stringFromDirection:direction]);
+
+ direction = 270;
+ XCTAssertEqualObjects(@"W", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"west", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"west", [longFormatter stringFromDirection:direction]);
+
+ direction = 359.34951805867024;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+
+ direction = 360;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+
+ direction = 360.1;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+
+ direction = 720;
+ XCTAssertEqualObjects(@"N", [shortFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [mediumFormatter stringFromDirection:direction]);
+ XCTAssertEqualObjects(@"north", [longFormatter stringFromDirection:direction]);
+}
+
+@end
diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m
new file mode 100644
index 0000000000..6a6c7a3b2e
--- /dev/null
+++ b/platform/darwin/test/MGLCoordinateFormatterTests.m
@@ -0,0 +1,48 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+@interface MGLCoordinateFormatterTests : XCTestCase
+
+@end
+
+@implementation MGLCoordinateFormatterTests
+
+- (void)testStrings {
+ MGLCoordinateFormatter *shortFormatter = [[MGLCoordinateFormatter alloc] init];
+ shortFormatter.unitStyle = NSFormattingUnitStyleShort;
+ XCTAssertTrue(shortFormatter.allowsSeconds, @"Arcseconds should be allowed by default.");
+ XCTAssertTrue(shortFormatter.allowsMinutes, @"Arcminutes should be allowed by default.");
+
+ MGLCoordinateFormatter *mediumFormatter = [[MGLCoordinateFormatter alloc] init];
+ XCTAssertEqual(mediumFormatter.unitStyle, NSFormattingUnitStyleMedium, @"Unit style should be medium by default.");
+
+ MGLCoordinateFormatter *longFormatter = [[MGLCoordinateFormatter alloc] init];
+ longFormatter.unitStyle = NSFormattingUnitStyleLong;
+
+ CLLocationCoordinate2D coordinate;
+
+ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
+ XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°54′48″N, 77°1′57″W");
+ XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°54′48″ north, 77°1′57″ west");
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west");
+
+ shortFormatter.allowsSeconds = NO;
+ mediumFormatter.allowsSeconds = NO;
+ longFormatter.allowsSeconds = NO;
+
+ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
+ XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°55′N, 77°2′W");
+ XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°55′ north, 77°2′ west");
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west");
+
+ shortFormatter.allowsMinutes = NO;
+ mediumFormatter.allowsMinutes = NO;
+ longFormatter.allowsMinutes = NO;
+
+ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
+ XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W");
+ XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° west");
+ XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west");
+}
+
+@end
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
new file mode 100644
index 0000000000..6cf038d4fb
--- /dev/null
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -0,0 +1,160 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#import "../../darwin/src/MGLFeature_Private.h"
+
+@interface MGLFeatureTests : XCTestCase
+
+@end
+
+@implementation MGLFeatureTests
+
+- (void)testGeometryConversion {
+ std::vector<mbgl::Feature> features;
+
+ mapbox::geometry::point<double> point = { -90.066667, 29.95 };
+ features.emplace_back(point);
+
+ mapbox::geometry::line_string<double> lineString = {
+ { -84.516667, 39.1 },
+ { -90.066667, 29.95 },
+ };
+ features.emplace_back(lineString);
+
+ mapbox::geometry::polygon<double> polygon = {
+ {
+ { 1, 1 },
+ { 4, 1 },
+ { 4, 4 },
+ { 1, 4 },
+ },
+ {
+ { 2, 2 },
+ { 3, 2 },
+ { 3, 3 },
+ { 2, 3 },
+ },
+ };
+ features.emplace_back(polygon);
+
+ NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features);
+ XCTAssertEqual(shapes.count, 3, @"All features should be converted into shapes");
+
+ MGLPointFeature *pointShape = (MGLPointFeature *)shapes[0];
+ XCTAssertTrue([pointShape isKindOfClass:[MGLPointFeature class]]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:pointShape.coordinate],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
+
+ MGLPolylineFeature *polylineShape = (MGLPolylineFeature *)shapes[1];
+ XCTAssertTrue([polylineShape isKindOfClass:[MGLPolylineFeature class]]);
+ XCTAssertEqual(polylineShape.pointCount, 2);
+ CLLocationCoordinate2D polylineCoordinates[2];
+ [polylineShape getCoordinates:polylineCoordinates range:NSMakeRange(0, polylineShape.pointCount)];
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polylineCoordinates[0]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(39.1, -84.516667)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polylineCoordinates[1]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(29.95, -90.066667)]);
+
+ MGLPolygonFeature *polygonShape = (MGLPolygonFeature *)shapes[2];
+ XCTAssertTrue([polygonShape isKindOfClass:[MGLPolygonFeature class]]);
+ XCTAssertEqual(polygonShape.pointCount, 4);
+ CLLocationCoordinate2D *polygonCoordinates = polygonShape.coordinates;
+ XCTAssertNotEqual(polygonCoordinates, nil);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[0]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(1, 1)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[1]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(1, 4)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[2]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 4)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[3]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 1)]);
+ NS_ARRAY_OF(MGLPolygon *) *interiorPolygons = polygonShape.interiorPolygons;
+ XCTAssertEqual(interiorPolygons.count, 1);
+ MGLPolygon *interiorPolygon = interiorPolygons.firstObject;
+ XCTAssertEqual(interiorPolygon.pointCount, 4);
+ CLLocationCoordinate2D interiorPolygonCoordinates[4];
+ [interiorPolygon getCoordinates:interiorPolygonCoordinates range:NSMakeRange(0, interiorPolygon.pointCount)];
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:interiorPolygonCoordinates[0]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(2, 2)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:interiorPolygonCoordinates[1]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(2, 3)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:interiorPolygonCoordinates[2]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(3, 3)]);
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:interiorPolygonCoordinates[3]],
+ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(3, 2)]);
+}
+
+- (void)testPropertyConversion {
+ std::vector<mbgl::Feature> features;
+
+ mapbox::geometry::point<double> point = { -90.066667, 29.95 };
+ mbgl::Feature pointFeature(point);
+ pointFeature.id = UINT64_MAX;
+ pointFeature.properties["null"] = nullptr;
+ pointFeature.properties["bool"] = true;
+ pointFeature.properties["unsigned int"] = UINT64_MAX;
+ pointFeature.properties["int"] = INT64_MIN;
+ pointFeature.properties["double"] = DBL_MAX;
+ pointFeature.properties["string"] = std::string("🚏");
+ std::vector<bool> vector;
+ vector.push_back(true);
+ vector.push_back(false);
+ vector.push_back(true);
+ features.push_back(pointFeature);
+
+ NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features);
+ XCTAssertEqual(shapes.count, 1, @"All features should be converted into shapes");
+
+ MGLShape <MGLFeature> *shape = shapes.firstObject;
+ XCTAssertTrue([shape conformsToProtocol:@protocol(MGLFeature)]);
+ XCTAssertTrue([shape isKindOfClass:[MGLShape class]]);
+
+ NSNumber *identifier = shape.identifier;
+ XCTAssertTrue([identifier isKindOfClass:[NSNumber class]], @"Feature identifier should be NSNumber");
+ XCTAssertEqual(strcmp(identifier.objCType, @encode(uint64_t)), 0, @"Feature identifier should be 64-bit unsigned integer");
+
+ NSNull *null = [shape attributeForKey:@"null"];
+ XCTAssertNotNil(null);
+ XCTAssertTrue([null isKindOfClass:[NSNull class]]);
+ XCTAssertEqual(null, shape.attributes[@"null"]);
+
+ NSNumber *boolean = [shape attributeForKey:@"bool"];
+ XCTAssertNotNil(boolean);
+ XCTAssertTrue([boolean isKindOfClass:[NSNumber class]]);
+#if (TARGET_OS_IPHONE && __LP64__) || TARGET_OS_WATCH
+ XCTAssertEqual(strcmp(boolean.objCType, @encode(char)), 0, @"Boolean property should be converted to bool NSNumber");
+#else
+ XCTAssertEqual(strcmp(boolean.objCType, @encode(BOOL)), 0, @"Boolean property should be converted to bool NSNumber");
+#endif
+ XCTAssertTrue(boolean.boolValue);
+ XCTAssertEqual(boolean, shape.attributes[@"bool"]);
+
+ NSNumber *unsignedInteger = [shape attributeForKey:@"unsigned int"];
+ XCTAssertNotNil(unsignedInteger);
+ XCTAssertTrue([unsignedInteger isKindOfClass:[NSNumber class]]);
+ XCTAssertEqual(strcmp(unsignedInteger.objCType, @encode(uint64_t)), 0, @"Unsigned integer property should be converted to unsigned long long NSNumber");
+ XCTAssertEqual(unsignedInteger.unsignedLongLongValue, UINT64_MAX);
+ XCTAssertEqual(unsignedInteger, shape.attributes[@"unsigned int"]);
+
+ NSNumber *integer = [shape attributeForKey:@"int"];
+ XCTAssertNotNil(integer);
+ XCTAssertTrue([integer isKindOfClass:[NSNumber class]]);
+ XCTAssertEqual(strcmp(integer.objCType, @encode(int64_t)), 0, @"Integer property should be converted to long long NSNumber");
+ XCTAssertEqual(integer.longLongValue, INT64_MIN);
+ XCTAssertEqual(integer, shape.attributes[@"int"]);
+
+ NSNumber *floatingPointNumber = [shape attributeForKey:@"double"];
+ XCTAssertNotNil(floatingPointNumber);
+ XCTAssertTrue([floatingPointNumber isKindOfClass:[NSNumber class]]);
+ XCTAssertEqual(strcmp(floatingPointNumber.objCType, @encode(double)), 0, @"Floating-point number property should be converted to double NSNumber");
+ XCTAssertEqual(floatingPointNumber.doubleValue, DBL_MAX);
+ XCTAssertEqual(floatingPointNumber, shape.attributes[@"double"]);
+
+ NSString *string = [shape attributeForKey:@"string"];
+ XCTAssertNotNil(string);
+ XCTAssertTrue([string isKindOfClass:[NSString class]]);
+ XCTAssertEqualObjects(string, @"🚏");
+ XCTAssertEqual(string, shape.attributes[@"string"]);
+}
+
+@end
diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm
new file mode 100644
index 0000000000..b15916e6fa
--- /dev/null
+++ b/platform/darwin/test/MGLGeometryTests.mm
@@ -0,0 +1,102 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#import "../../darwin/src/MGLGeometry_Private.h"
+
+@interface MGLGeometryTests : XCTestCase
+@end
+
+@implementation MGLGeometryTests
+
+- (void)testCoordinateBoundsIsEmpty {
+ MGLCoordinateBounds emptyBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 0));
+ XCTAssertTrue(MGLCoordinateBoundsIsEmpty(emptyBounds));
+ XCTAssertFalse(MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpanZero, MGLCoordinateBoundsGetCoordinateSpan(emptyBounds)));
+}
+
+- (void)testAngleConversions {
+ XCTAssertEqualWithAccuracy(-180, MGLDegreesFromRadians(-M_PI), 5);
+ XCTAssertEqual(0, MGLDegreesFromRadians(0));
+ XCTAssertEqualWithAccuracy(45, MGLDegreesFromRadians(M_PI_4), 5);
+ XCTAssertEqualWithAccuracy(90, MGLDegreesFromRadians(M_PI_2), 5);
+ XCTAssertEqualWithAccuracy(180, MGLDegreesFromRadians(M_PI), 5);
+ XCTAssertEqualWithAccuracy(360, MGLDegreesFromRadians(2 * M_PI), 5);
+ XCTAssertEqualWithAccuracy(720, MGLDegreesFromRadians(4 * M_PI), 5);
+
+ XCTAssertEqualWithAccuracy(-360, MGLDegreesFromRadians(MGLRadiansFromDegrees(-360)), 4);
+ XCTAssertEqualWithAccuracy(-180, MGLDegreesFromRadians(MGLRadiansFromDegrees(-180)), 5);
+ XCTAssertEqualWithAccuracy(-90, MGLDegreesFromRadians(MGLRadiansFromDegrees(-90)), 5);
+ XCTAssertEqualWithAccuracy(-45, MGLDegreesFromRadians(MGLRadiansFromDegrees(-45)), 5);
+ XCTAssertEqualWithAccuracy(0, MGLDegreesFromRadians(MGLRadiansFromDegrees(0)), 5);
+ XCTAssertEqualWithAccuracy(45, MGLDegreesFromRadians(MGLRadiansFromDegrees(45)), 5);
+ XCTAssertEqualWithAccuracy(90, MGLDegreesFromRadians(MGLRadiansFromDegrees(90)), 5);
+ XCTAssertEqualWithAccuracy(180, MGLDegreesFromRadians(MGLRadiansFromDegrees(180)), 5);
+ XCTAssertEqualWithAccuracy(360, MGLDegreesFromRadians(MGLRadiansFromDegrees(360)), 4);
+}
+
+- (void)testAltitudeConversions {
+ CGSize tallSize = CGSizeMake(600, 1200);
+ CGSize midSize = CGSizeMake(600, 800);
+ CGSize shortSize = CGSizeMake(600, 400);
+
+ XCTAssertEqualWithAccuracy(1800, MGLAltitudeForZoomLevel(MGLZoomLevelForAltitude(1800, 0, 0, midSize), 0, 0, midSize), 1);
+ XCTAssertLessThan(MGLZoomLevelForAltitude(1800, 0, 0, midSize), MGLZoomLevelForAltitude(1800, 0, 0, tallSize));
+ XCTAssertGreaterThan(MGLZoomLevelForAltitude(1800, 0, 0, midSize), MGLZoomLevelForAltitude(1800, 0, 0, shortSize));
+
+ XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 0, 0, midSize), 0, 0, midSize), 3);
+ XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 0, 0, midSize), 0, 0, midSize), 3);
+
+ XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 0, 40, midSize), 0, 40, midSize), 3);
+ XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 0, 40, midSize), 0, 40, midSize), 3);
+
+ XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 60, 40, midSize), 60, 40, midSize), 3);
+ XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 60, 40, midSize), 60, 40, midSize), 3);
+}
+
+- (void)testGeometryBoxing {
+ CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239);
+ CLLocationCoordinate2D roundTrippedCoordinate = [NSValue valueWithMGLCoordinate:coordinate].MGLCoordinateValue;
+
+ XCTAssertEqual(coordinate.latitude, roundTrippedCoordinate.latitude, @"Latitude should round-trip.");
+ XCTAssertEqual(coordinate.longitude, roundTrippedCoordinate.longitude, @"Longitude should round-trip.");
+
+ MGLCoordinateSpan span = MGLCoordinateSpanMake(4.383333333333335, -4.299999999999997);
+ MGLCoordinateSpan roundTrippedSpan = [NSValue valueWithMGLCoordinateSpan:span].MGLCoordinateSpanValue;
+
+ XCTAssertEqual(span.latitudeDelta, roundTrippedSpan.latitudeDelta, @"Latitude delta should round-trip.");
+ XCTAssertEqual(span.longitudeDelta, roundTrippedSpan.longitudeDelta, @"Longitude delta should round-trip.");
+
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(38.9131982, -77.0325453144239),
+ CLLocationCoordinate2DMake(37.7757368, -122.4135302));
+ MGLCoordinateBounds roundTrippedBounds = [NSValue valueWithMGLCoordinateBounds:bounds].MGLCoordinateBoundsValue;
+
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.sw],
+ [NSValue valueWithMGLCoordinate:roundTrippedBounds.sw],
+ @"Southwest should round-trip.");
+ XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:bounds.ne],
+ [NSValue valueWithMGLCoordinate:roundTrippedBounds.ne],
+ @"Northeast should round-trip.");
+}
+
+- (void)testCoordinateInCoordinateBounds {
+ CLLocationCoordinate2D ne = CLLocationCoordinate2DMake(41, -111);
+ CLLocationCoordinate2D sw = CLLocationCoordinate2DMake(45, -104);
+ MGLCoordinateBounds wyoming = MGLCoordinateBoundsMake(ne, sw);
+
+ CLLocationCoordinate2D centerOfWyoming = CLLocationCoordinate2DMake(43, -107.5);
+
+ XCTAssertTrue(MGLCoordinateInCoordinateBounds(ne, wyoming));
+ XCTAssertTrue(MGLCoordinateInCoordinateBounds(sw, wyoming));
+ XCTAssertTrue(MGLCoordinateInCoordinateBounds(centerOfWyoming, wyoming));
+
+ CLLocationCoordinate2D australia = CLLocationCoordinate2DMake(-25, 135);
+ CLLocationCoordinate2D brazil = CLLocationCoordinate2DMake(-12, -50);
+ CLLocationCoordinate2D china = CLLocationCoordinate2DMake(35, 100);
+
+ XCTAssertFalse(MGLCoordinateInCoordinateBounds(australia, wyoming));
+ XCTAssertFalse(MGLCoordinateInCoordinateBounds(brazil, wyoming));
+ XCTAssertFalse(MGLCoordinateInCoordinateBounds(china, wyoming));
+ XCTAssertFalse(MGLCoordinateInCoordinateBounds(kCLLocationCoordinate2DInvalid, wyoming));
+}
+
+@end
diff --git a/platform/darwin/test/MGLOfflinePackTests.m b/platform/darwin/test/MGLOfflinePackTests.m
new file mode 100644
index 0000000000..fa231ba005
--- /dev/null
+++ b/platform/darwin/test/MGLOfflinePackTests.m
@@ -0,0 +1,40 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+@interface MGLOfflinePackTests : XCTestCase
+
+@end
+
+@implementation MGLOfflinePackTests
+
+- (void)testInvalidation {
+ MGLOfflinePack *invalidPack = [[MGLOfflinePack alloc] init];
+
+ XCTAssertEqual(invalidPack.state, MGLOfflinePackStateInvalid, @"Offline pack should be invalid when initialized independently of MGLOfflineStorage.");
+
+ XCTAssertThrowsSpecificNamed(invalidPack.region, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its region.");
+ XCTAssertThrowsSpecificNamed(invalidPack.context, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its context.");
+ XCTAssertThrowsSpecificNamed([invalidPack resume], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being resumed.");
+ XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being suspended.");
+}
+
+- (void)testProgressBoxing {
+ MGLOfflinePackProgress progress = {
+ .countOfResourcesCompleted = 3,
+ .countOfResourcesExpected = 2,
+ .countOfBytesCompleted = 7,
+ .countOfTilesCompleted = 1,
+ .countOfTileBytesCompleted = 6,
+ .maximumResourcesExpected = UINT64_MAX,
+ };
+ MGLOfflinePackProgress roundTrippedProgress = [NSValue valueWithMGLOfflinePackProgress:progress].MGLOfflinePackProgressValue;
+
+ XCTAssertEqual(progress.countOfResourcesCompleted, roundTrippedProgress.countOfResourcesCompleted, @"Completed resources should round-trip.");
+ XCTAssertEqual(progress.countOfResourcesExpected, roundTrippedProgress.countOfResourcesExpected, @"Expected resources should round-trip.");
+ XCTAssertEqual(progress.countOfBytesCompleted, roundTrippedProgress.countOfBytesCompleted, @"Completed bytes should round-trip.");
+ XCTAssertEqual(progress.countOfTilesCompleted, roundTrippedProgress.countOfTilesCompleted, @"Completed tiles should round-trip.");
+ XCTAssertEqual(progress.countOfTileBytesCompleted, roundTrippedProgress.countOfTileBytesCompleted, @"Completed tile bytes should round-trip.");
+ XCTAssertEqual(progress.maximumResourcesExpected, roundTrippedProgress.maximumResourcesExpected, @"Maximum expected resources should round-trip.");
+}
+
+@end
diff --git a/platform/darwin/test/MGLOfflineRegionTests.m b/platform/darwin/test/MGLOfflineRegionTests.m
new file mode 100644
index 0000000000..bb467dd35b
--- /dev/null
+++ b/platform/darwin/test/MGLOfflineRegionTests.m
@@ -0,0 +1,32 @@
+#import <Mapbox/Mapbox.h>
+
+#import <XCTest/XCTest.h>
+
+@interface MGLOfflineRegionTests : XCTestCase
+
+@end
+
+@implementation MGLOfflineRegionTests
+
+- (void)testStyleURLs {
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(kCLLocationCoordinate2DInvalid, kCLLocationCoordinate2DInvalid);
+ MGLTilePyramidOfflineRegion *region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:nil bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX];
+ XCTAssertEqualObjects(region.styleURL, [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion], @"Streets isn’t the default style.");
+
+ NSURL *localURL = [NSURL URLWithString:@"beautiful.style"];
+ XCTAssertThrowsSpecificNamed([[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:localURL bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX], NSException, @"Invalid style URL", @"No exception raised when initializing region with a local file URL as the style URL.");
+}
+
+- (void)testEquality {
+ MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(kCLLocationCoordinate2DInvalid, kCLLocationCoordinate2DInvalid);
+ MGLTilePyramidOfflineRegion *original = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:[MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion] bounds:bounds fromZoomLevel:5 toZoomLevel:10];
+ MGLTilePyramidOfflineRegion *copy = [original copy];
+ XCTAssertEqualObjects(original, copy, @"Tile pyramid region should be equal to its copy.");
+
+ XCTAssertEqualObjects(original.styleURL, copy.styleURL, @"Style URL has changed.");
+ XCTAssert(MGLCoordinateBoundsEqualToCoordinateBounds(original.bounds, copy.bounds), @"Bounds have changed.");
+ XCTAssertEqual(original.minimumZoomLevel, original.minimumZoomLevel, @"Minimum zoom level has changed.");
+ XCTAssertEqual(original.maximumZoomLevel, original.maximumZoomLevel, @"Maximum zoom level has changed.");
+}
+
+@end
diff --git a/platform/darwin/test/MGLOfflineStorageTests.m b/platform/darwin/test/MGLOfflineStorageTests.m
new file mode 100644
index 0000000000..415039c527
--- /dev/null
+++ b/platform/darwin/test/MGLOfflineStorageTests.m
@@ -0,0 +1,146 @@
+#import <Mapbox/Mapbox.h>
+
+#import <XCTest/XCTest.h>
+
+@interface MGLOfflineStorageTests : XCTestCase
+
+@end
+
+@implementation MGLOfflineStorageTests
+
+- (void)testSharedObject {
+ XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage], [MGLOfflineStorage sharedOfflineStorage], @"There should only be one shared offline storage object.");
+}
+
+// This test needs to come first so it can test the initial loading of packs.
+- (void)testAAALoadPacks {
+ [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
+ NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
+ return changeKind = NSKeyValueChangeSetting;
+ }];
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ XCTAssertNotNil([MGLOfflineStorage sharedOfflineStorage].packs, @"Shared offline storage object should have a non-nil collection of packs by this point.");
+}
+
+- (void)testAddPack {
+ NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count;
+
+ NSURL *styleURL = [MGLStyle lightStyleURLWithVersion:8];
+ /// Somewhere near Grape Grove, Ohio, United States.
+ MGLCoordinateBounds bounds = {
+ { .latitude = 39.70358155855172, .longitude = -83.69506472545841 },
+ { .latitude = 39.703818870225376, .longitude = -83.69420641857361 },
+ };
+ double zoomLevel = 20;
+ MGLTilePyramidOfflineRegion *region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds fromZoomLevel:zoomLevel toZoomLevel:zoomLevel];
+
+ NSString *nameKey = @"Name";
+ NSString *name = @"🍇 Grape Grove";
+
+ NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
+ nameKey: name,
+ }];
+
+ __block MGLOfflinePack *pack;
+ [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
+ NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
+ NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
+ return changeKind == NSKeyValueChangeInsertion && indices.count == 1;
+ }];
+ XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"];
+ [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) {
+ XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
+ XCTAssertEqual(completionHandlerPack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state.");
+ pack = completionHandlerPack;
+ [additionCompletionHandlerExpectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks.");
+
+ XCTAssertEqual(pack, [MGLOfflineStorage sharedOfflineStorage].packs.lastObject, @"Pack should be appended to end of packs array.");
+
+ XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
+
+ NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
+ XCTAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
+ XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
+ XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
+
+ XCTAssertEqual(pack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state.");
+
+ [self keyValueObservingExpectationForObject:pack keyPath:@"state" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
+ NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
+ MGLOfflinePackState state = [change[NSKeyValueChangeNewKey] integerValue];
+ return changeKind == NSKeyValueChangeSetting && state == MGLOfflinePackStateInactive;
+ }];
+ [self expectationForNotification:MGLOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) {
+ MGLOfflinePack *notificationPack = notification.object;
+ XCTAssert([notificationPack isKindOfClass:[MGLOfflinePack class]], @"Object of notification should be an MGLOfflinePack.");
+
+ NSDictionary *userInfo = notification.userInfo;
+ XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary.");
+
+ NSNumber *stateNumber = userInfo[MGLOfflinePackStateUserInfoKey];
+ XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber.");
+ XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state.");
+
+ NSValue *progressValue = userInfo[MGLOfflinePackProgressUserInfoKey];
+ XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue.");
+ XCTAssertEqualObjects(progressValue, [NSValue valueWithMGLOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress.");
+
+ return notificationPack == pack && pack.state == MGLOfflinePackStateInactive;
+ }];
+ [pack requestProgress];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testBackupExclusion {
+ NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:nil
+ create:NO
+ error:nil];
+ // Unit tests don't use the main bundle; use com.mapbox.ios.sdk instead.
+ NSString *bundleIdentifier = [NSBundle bundleForClass:[MGLMapView class]].bundleIdentifier;
+ cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
+ XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache directory should exist.");
+
+ NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
+ XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Cache database should exist.");
+
+ NSError *error = nil;
+ NSNumber *exclusionFlag = nil;
+ [cacheURL getResourceValue:&exclusionFlag
+ forKey:NSURLIsExcludedFromBackupKey
+ error:&error];
+ XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for cache database.");
+ XCTAssertNil(error, @"No errors should be returned when checking backup exclusion flag.");
+}
+
+- (void)testRemovePack {
+ NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count;
+
+ MGLOfflinePack *pack = [MGLOfflineStorage sharedOfflineStorage].packs.lastObject;
+ XCTAssertNotNil(pack, @"Added pack should still exist.");
+
+ [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
+ NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
+ NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
+ return changeKind = NSKeyValueChangeRemoval && indices.count == 1;
+ }];
+ XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"];
+ [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
+ XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler.");
+ [completionHandlerExpectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously.");
+
+ XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack.");
+}
+
+@end
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
new file mode 100644
index 0000000000..07e012edc6
--- /dev/null
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -0,0 +1,105 @@
+#import "MGLStyle.h"
+
+#import "NSBundle+MGLAdditions.h"
+
+#import <mbgl/util/default_styles.hpp>
+
+#import <XCTest/XCTest.h>
+#import <objc/runtime.h>
+
+@interface MGLStyleTests : XCTestCase
+@end
+
+@implementation MGLStyleTests
+
+- (void)testUnversionedStyleURLs {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ XCTAssertEqualObjects([MGLStyle streetsStyleURL].absoluteString, @"mapbox://styles/mapbox/streets-v8");
+ XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8");
+ XCTAssertEqualObjects([MGLStyle lightStyleURL].absoluteString, @"mapbox://styles/mapbox/light-v8");
+ XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @"mapbox://styles/mapbox/dark-v8");
+ XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-v8");
+ XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8");
+#pragma clang diagnostic pop
+}
+
+- (void)testVersionedStyleURLs {
+ // Test that all the default styles have publicly-declared MGLStyle class
+ // methods and that the URLs all have the right values.
+ XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::streets.url));
+ XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/streets-v99");
+ XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::outdoors.url));
+ XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/outdoors-v99");
+ XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::light.url));
+ XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/light-v99");
+ XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::dark.url));
+ XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/dark-v99");
+ XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::satellite.url));
+ XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-v99");
+ XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url));
+ XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-streets-v99");
+
+ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
+ "MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
+}
+
+- (void)testStyleURLDeclarations {
+ // Make sure this test is comprehensive.
+ const unsigned numImplicitArgs = 2 /* _cmd, self */;
+ unsigned numMethods = 0;
+ Method *methods = class_copyMethodList(object_getClass([MGLStyle class]), &numMethods);
+ unsigned numVersionedMethods = 0;
+ for (NSUInteger i = 0; i < numMethods; i++) {
+ Method method = methods[i];
+ SEL selector = method_getName(method);
+ NSString *name = @(sel_getName(selector));
+ unsigned numArgs = method_getNumberOfArguments(method);
+ if ([name hasSuffix:@"StyleURL"]) {
+ XCTAssertEqual(numArgs, numImplicitArgs, @"Unversioned style URL method should have no parameters, but it has %u.", numArgs - numImplicitArgs);
+ } else if ([name hasSuffix:@"StyleURLWithVersion:"]) {
+ XCTAssertEqual(numArgs, numImplicitArgs + 1, @"Versioned style URL method should have one parameter, but it has %u.", numArgs - numImplicitArgs);
+ numVersionedMethods++;
+ } else {
+ XCTAssertEqual([name rangeOfString:@"URL"].location, NSNotFound, @"MGLStyle style URL method %@ is malformed.", name);
+ }
+ }
+ XCTAssertEqual(mbgl::util::default_styles::numOrderedStyles, numVersionedMethods,
+ @"There are %lu default styles but MGLStyleTests only provides versioned style URL methods for %u of them.",
+ mbgl::util::default_styles::numOrderedStyles, numVersionedMethods);
+
+ // Test that all the versioned style methods are in the public header.
+ NSString *styleHeader = self.stringWithContentsOfStyleHeader;
+
+ NSError *versionedMethodError;
+ NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*;)RE");
+ NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError];
+ XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods.");
+ NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
+ XCTAssertEqual(numVersionedMethodDeclarations, numVersionedMethods);
+
+ // Test that “current version is” statements are present and current for all versioned style methods.
+ NSError *versionError;
+ NSString *versionExpressionString = @(R"RE(current version is `(\d+)`)RE");
+ NSRegularExpression *versionExpression = [NSRegularExpression regularExpressionWithPattern:versionExpressionString options:0 error:&versionError];
+ XCTAssertNil(versionError, @"Error compiling regular expression to search for current version statements.");
+ NSUInteger numVersionDeclarations = [versionExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
+ XCTAssertEqual(numVersionDeclarations, numVersionedMethods);
+ [versionExpression enumerateMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
+ XCTAssertEqual(result.numberOfRanges, 2, @"Regular expression should have one capture group.");
+ NSString *version = [styleHeader substringWithRange:[result rangeAtIndex:1]];
+ XCTAssertEqual([version integerValue], MGLStyleDefaultVersion, @"Versioned style URL method should document current version as %ld, not %ld.", MGLStyleDefaultVersion, version.integerValue);
+ }];
+}
+
+- (NSString *)stringWithContentsOfStyleHeader {
+ NSURL *styleHeaderURL = [[[NSBundle mgl_frameworkBundle].bundleURL
+ URLByAppendingPathComponent:@"Headers" isDirectory:YES]
+ URLByAppendingPathComponent:@"MGLStyle.h"];
+ NSError *styleHeaderError;
+ NSString *styleHeader = [NSString stringWithContentsOfURL:styleHeaderURL usedEncoding:nil error:&styleHeaderError];
+ XCTAssertNil(styleHeaderError, @"Error getting contents of MGLStyle.h.");
+ return styleHeader;
+}
+
+@end
diff --git a/platform/default/application_root.cpp b/platform/default/application_root.cpp
deleted file mode 100644
index 6669a049a4..0000000000
--- a/platform/default/application_root.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <mbgl/platform/platform.hpp>
-
-#include <uv.h>
-#include <libgen.h>
-
-namespace mbgl {
-namespace platform {
-
-// Returns the path to the root folder of the application.
-const std::string &applicationRoot() {
- static const std::string root = []() -> std::string {
- size_t max = 0;
- std::string dir;
- do {
- // Gradually increase the length of the string in case the path was truncated.
- max += 256;
- dir.resize(max);
- uv_exepath(const_cast<char *>(dir.data()), &max);
- } while (max == dir.size());
- dir.resize(max - 1);
- dir = dirname(const_cast<char *>(dir.c_str()));
- return dir;
- }();
- return root;
-}
-
-}
-}
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 2573966c72..3a47f349fe 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -12,15 +12,6 @@
namespace mbgl {
-class AssetFileRequest : public FileRequest {
-public:
- AssetFileRequest(std::unique_ptr<WorkRequest> workRequest_)
- : workRequest(std::move(workRequest_)) {
- }
-
- std::unique_ptr<WorkRequest> workRequest;
-};
-
class AssetFileSource::Impl {
public:
Impl(const std::string& root_)
@@ -66,14 +57,14 @@ private:
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource", util::ThreadType::Worker, util::ThreadPriority::Regular},
+ util::ThreadContext{"AssetFileSource"},
root)) {
}
AssetFileSource::~AssetFileSource() = default;
-std::unique_ptr<FileRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
- return std::make_unique<AssetFileRequest>(thread->invokeWithCallback(&Impl::request, callback, resource.url));
+std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, Callback callback) {
+ return thread->invokeWithCallback(&Impl::request, callback, resource.url);
}
} // namespace mbgl
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index 36839b2381..9d369210f8 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -25,29 +25,6 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- class Task {
- public:
- Task(Resource resource, FileSource::Callback callback, DefaultFileSource::Impl* impl) {
- auto offlineResponse = impl->offlineDatabase.get(resource);
-
- Resource revalidation = resource;
-
- if (offlineResponse) {
- revalidation.priorModified = offlineResponse->modified;
- revalidation.priorExpires = offlineResponse->expires;
- revalidation.priorEtag = offlineResponse->etag;
- callback(*offlineResponse);
- }
-
- onlineRequest = impl->onlineFileSource.request(revalidation, [=] (Response onlineResponse) {
- impl->offlineDatabase.put(revalidation, onlineResponse);
- callback(onlineResponse);
- });
- }
-
- std::unique_ptr<FileRequest> onlineRequest;
- };
-
Impl(const std::string& cachePath, uint64_t maximumCacheSize)
: offlineDatabase(cachePath, maximumCacheSize) {
}
@@ -104,11 +81,39 @@ public:
getDownload(regionID).setState(state);
}
- void request(FileRequest* req, Resource resource, Callback callback) {
- tasks[req] = std::make_unique<Task>(resource, callback, this);
+ void request(AsyncRequest* req, Resource resource, Callback callback) {
+ Resource revalidation = resource;
+
+ const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires;
+ if (!hasPrior || resource.necessity == Resource::Optional) {
+ auto offlineResponse = offlineDatabase.get(resource);
+
+ if (resource.necessity == Resource::Optional && !offlineResponse) {
+ // Ensure there's always a response that we can send, so the caller knows that
+ // there's no optional data available in the cache.
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Not found in offline database");
+ }
+
+ if (offlineResponse) {
+ revalidation.priorModified = offlineResponse->modified;
+ revalidation.priorExpires = offlineResponse->expires;
+ revalidation.priorEtag = offlineResponse->etag;
+ callback(*offlineResponse);
+ }
+ }
+
+ if (resource.necessity == Resource::Required) {
+ tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) {
+ this->offlineDatabase.put(revalidation, onlineResponse);
+ callback(onlineResponse);
+ });
+ }
}
- void cancel(FileRequest* req) {
+ void cancel(AsyncRequest* req) {
tasks.erase(req);
}
@@ -132,14 +137,14 @@ private:
OfflineDatabase offlineDatabase;
OnlineFileSource onlineFileSource;
- std::unordered_map<FileRequest*, std::unique_ptr<Task>> tasks;
+ std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
};
DefaultFileSource::DefaultFileSource(const std::string& cachePath,
const std::string& assetRoot,
uint64_t maximumCacheSize)
- : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadType::Unknown, util::ThreadPriority::Low},
+ : thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadPriority::Low},
cachePath, maximumCacheSize)),
assetFileSource(std::make_unique<AssetFileSource>(assetRoot)) {
}
@@ -154,20 +159,20 @@ std::string DefaultFileSource::getAccessToken() const {
return thread->invokeSync<std::string>(&Impl::getAccessToken);
}
-std::unique_ptr<FileRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
- class DefaultFileRequest : public FileRequest {
+std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) {
+ class DefaultFileRequest : public AsyncRequest {
public:
DefaultFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<DefaultFileSource::Impl>& thread_)
: thread(thread_),
workRequest(thread.invokeWithCallback(&DefaultFileSource::Impl::request, callback_, this, resource_)) {
}
- ~DefaultFileRequest() {
+ ~DefaultFileRequest() override {
thread.invoke(&DefaultFileSource::Impl::cancel, this);
}
util::Thread<DefaultFileSource::Impl>& thread;
- std::unique_ptr<WorkRequest> workRequest;
+ std::unique_ptr<AsyncRequest> workRequest;
};
if (isAssetURL(resource.url)) {
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 790b0a4dd7..67e9748a70 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -1,7 +1,7 @@
#include <mbgl/platform/default/glfw_view.hpp>
-#include <mbgl/annotation/point_annotation.hpp>
-#include <mbgl/annotation/shape_annotation.hpp>
+#include <mbgl/annotation/annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
+#include <mbgl/style/transition_options.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/gl_values.hpp>
#include <mbgl/gl/gl_helper.hpp>
@@ -9,6 +9,7 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
+#include <mbgl/map/camera.hpp>
#include <cassert>
#include <cstdlib>
@@ -94,6 +95,8 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("- Press `N` to reset north\n");
printf("- Press `R` to toggle any available `night` style class\n");
printf("- Press `Z` to cycle through north orientations\n");
+ printf("- Prezz `X` to cycle through the viewport modes\n");
+ printf("- Press `A` to cycle through Mapbox offices in the world + dateline monument\n");
printf("\n");
printf("- Press `1` through `6` to add increasing numbers of point annotations for testing\n");
printf("- Press `7` through `0` to add increasing numbers of shape annotations for testing\n");
@@ -137,20 +140,17 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
if (!mods)
view->map->resetPosition();
break;
- case GLFW_KEY_C:
- view->toggleClipMasks();
- break;
case GLFW_KEY_S:
if (view->changeStyleCallback)
view->changeStyleCallback();
break;
case GLFW_KEY_R:
if (!mods) {
- view->map->setDefaultTransitionDuration(mbgl::Milliseconds(300));
+ static const mbgl::style::TransitionOptions transition { { mbgl::Milliseconds(300) } };
if (view->map->hasClass("night")) {
- view->map->removeClass("night");
+ view->map->removeClass("night", transition);
} else {
- view->map->addClass("night");
+ view->map->addClass("night", transition);
}
}
break;
@@ -167,6 +167,26 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
case GLFW_KEY_P: {
view->addRandomCustomPointAnnotations(1);
} break;
+ case GLFW_KEY_A: {
+ // XXX Fix precision loss in flyTo:
+ // https://github.com/mapbox/mapbox-gl-native/issues/4298
+ static const std::vector<mbgl::LatLng> places = {
+ mbgl::LatLng { -16.796665, -179.999983 }, // Dateline monument
+ mbgl::LatLng { 12.9810542, 77.6345551 }, // Mapbox Bengaluru, India
+ mbgl::LatLng { -13.15607,-74.21773 }, // Mapbox Peru
+ mbgl::LatLng { 37.77572, -122.4158818 }, // Mapbox SF, USA
+ mbgl::LatLng { 38.91318,-77.03255 }, // Mapbox DC, USA
+ };
+ static size_t nextPlace = 0;
+ mbgl::CameraOptions cameraOptions;
+ cameraOptions.center = places[nextPlace++];
+ cameraOptions.zoom = 20;
+ cameraOptions.pitch = 30;
+
+ mbgl::AnimationOptions animationOptions(mbgl::Seconds(10));
+ view->map->flyTo(cameraOptions, animationOptions);
+ nextPlace = nextPlace % places.size();
+ } break;
}
}
@@ -187,14 +207,11 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
}
}
-mbgl::LatLng GLFWView::makeRandomPoint() const {
- const auto nw = map->latLngForPixel({ 0, 0 });
- const auto se = map->latLngForPixel({ double(width), double(height) });
-
- const double lon = nw.longitude + (se.longitude - nw.longitude) * (double(std::rand()) / RAND_MAX);
- const double lat = se.latitude + (nw.latitude - se.latitude) * (double(std::rand()) / RAND_MAX);
-
- return { lat, lon };
+mbgl::Point<double> GLFWView::makeRandomPoint() const {
+ const double x = width * double(std::rand()) / RAND_MAX;
+ const double y = height * double(std::rand()) / RAND_MAX;
+ mbgl::LatLng latLng = map->latLngForPixel({ x, y });
+ return { latLng.longitude, latLng.latitude };
}
std::shared_ptr<const mbgl::SpriteImage>
@@ -236,65 +253,35 @@ void GLFWView::nextOrientation() {
}
}
-void GLFWView::toggleClipMasks() {
- showClipMasks = !showClipMasks;
- map->update(mbgl::Update::Repaint);
-}
-
void GLFWView::addRandomCustomPointAnnotations(int count) {
- std::vector<mbgl::PointAnnotation> points;
-
for (int i = 0; i < count; i++) {
static int spriteID = 1;
const auto name = std::string{ "marker-" } + mbgl::util::toString(spriteID++);
map->addAnnotationIcon(name, makeSpriteImage(22, 22, 1));
spriteIDs.push_back(name);
- points.emplace_back(makeRandomPoint(), name);
+ annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), name }));
}
-
- auto newIDs = map->addPointAnnotations(points);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::addRandomPointAnnotations(int count) {
- std::vector<mbgl::PointAnnotation> points;
-
for (int i = 0; i < count; i++) {
- points.emplace_back(makeRandomPoint(), "default_marker");
+ annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), "default_marker" }));
}
-
- auto newIDs = map->addPointAnnotations(points);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::addRandomShapeAnnotations(int count) {
- std::vector<mbgl::ShapeAnnotation> shapes;
-
- mbgl::FillAnnotationProperties properties;
- properties.opacity = .1;
-
for (int i = 0; i < count; i++) {
- mbgl::AnnotationSegment triangle;
- triangle.push_back(makeRandomPoint());
- triangle.push_back(makeRandomPoint());
- triangle.push_back(makeRandomPoint());
-
- mbgl::AnnotationSegments segments;
- segments.push_back(triangle);
-
- shapes.emplace_back(segments, properties);
+ mbgl::Polygon<double> triangle;
+ triangle.push_back({ makeRandomPoint(), makeRandomPoint(), makeRandomPoint() });
+ annotationIDs.push_back(map->addAnnotation(mbgl::FillAnnotation { triangle, .1 }));
}
-
- auto newIDs = map->addShapeAnnotations(shapes);
- annotationIDs.insert(annotationIDs.end(), newIDs.begin(), newIDs.end());
}
void GLFWView::clearAnnotations() {
- if (annotationIDs.empty()) {
- return;
+ for (const auto& id : annotationIDs) {
+ map->removeAnnotation(id);
}
- map->removeAnnotations(annotationIDs);
annotationIDs.clear();
}
@@ -326,7 +313,7 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset)
scale = 1.0 / scale;
}
- view->map->scaleBy(scale, { view->lastX, view->lastY });
+ view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY });
}
void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
@@ -363,9 +350,9 @@ void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modi
double now = glfwGetTime();
if (now - view->lastClick < 0.4 /* ms */) {
if (modifiers & GLFW_MOD_SHIFT) {
- view->map->scaleBy(0.5, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
+ view->map->scaleBy(0.5, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
} else {
- view->map->scaleBy(2.0, { view->lastX, view->lastY }, mbgl::Milliseconds(500));
+ view->map->scaleBy(2.0, mbgl::ScreenCoordinate { view->lastX, view->lastY }, mbgl::Milliseconds(500));
}
}
view->lastClick = now;
@@ -396,18 +383,39 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) {
}
void GLFWView::run() {
- while (!glfwWindowShouldClose(window)) {
- glfwWaitEvents();
- const bool dirty = !clean.test_and_set();
+ auto callback = [&] {
+ if (glfwWindowShouldClose(window)) {
+ runLoop.stop();
+ return;
+ }
+
+ glfwPollEvents();
+
if (dirty) {
const double started = glfwGetTime();
- map->renderSync();
+
+ glfwMakeContextCurrent(window);
+ glViewport(0, 0, fbWidth, fbHeight);
+
+ map->render();
+
+ glfwSwapBuffers(window);
+
report(1000 * (glfwGetTime() - started));
if (benchmark) {
map->update(mbgl::Update::Repaint);
}
+
+ dirty = false;
}
- }
+ };
+
+ frameTick.start(mbgl::Duration::zero(), mbgl::Milliseconds(1000 / 60), callback);
+#if defined(__APPLE__)
+ while (!glfwWindowShouldClose(window)) runLoop.run();
+#else
+ runLoop.run();
+#endif
}
float GLFWView::getPixelRatio() const {
@@ -430,70 +438,11 @@ void GLFWView::deactivate() {
glfwMakeContextCurrent(nullptr);
}
-void GLFWView::notify() {
- glfwPostEmptyEvent();
-}
-
void GLFWView::invalidate() {
- clean.clear();
+ dirty = true;
glfwPostEmptyEvent();
}
-void GLFWView::beforeRender() {
- // This is called from the map thread but `width` and `height`
- // can be accessed with no race because the main thread is blocked
- // when we render. This will be more straightforward when we move
- // rendering to the main thread.
- glViewport(0, 0, fbWidth, fbHeight);
-}
-
-void GLFWView::afterRender() {
- if (showClipMasks) {
- renderClipMasks();
- }
-
- glfwSwapBuffers(window);
-}
-
-void GLFWView::renderClipMasks() {
- // Read the stencil buffer
- auto pixels = std::make_unique<uint8_t[]>(fbWidth * fbHeight);
- glReadPixels(0, // GLint x
- 0, // GLint y
- fbWidth, // GLsizei width
- fbHeight, // GLsizei height
- GL_STENCIL_INDEX, // GLenum format
- GL_UNSIGNED_BYTE, // GLenum type
- pixels.get() // GLvoid * data
- );
-
- // Scale the Stencil buffer to cover the entire color space.
- auto it = pixels.get();
- auto end = it + fbWidth * fbHeight;
- const auto factor = 255.0f / *std::max_element(it, end);
- for (; it != end; ++it) {
- *it *= factor;
- }
-
- using namespace mbgl::gl;
- Preserve<PixelZoom> pixelZoom;
- Preserve<RasterPos> rasterPos;
- Preserve<StencilTest> stencilTest;
- Preserve<DepthTest> depthTest;
- Preserve<Program> program;
- Preserve<ColorMask> colorMask;
-
- MBGL_CHECK_ERROR(glPixelZoom(1.0f, 1.0f));
- MBGL_CHECK_ERROR(glRasterPos2f(-1.0f, -1.0f));
- MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
- MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
- MBGL_CHECK_ERROR(glUseProgram(0));
- MBGL_CHECK_ERROR(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
- MBGL_CHECK_ERROR(glWindowPos2i(0, 0));
-
- MBGL_CHECK_ERROR(glDrawPixels(fbWidth, fbHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get()));
-}
-
void GLFWView::report(float duration) {
frames++;
frameTime += duration;
diff --git a/platform/default/headless_display.cpp b/platform/default/headless_display.cpp
index fccc91c727..8b9f3fe04b 100644
--- a/platform/default/headless_display.cpp
+++ b/platform/default/headless_display.cpp
@@ -16,7 +16,6 @@ HeadlessDisplay::HeadlessDisplay() {
CGLPixelFormatAttribute attributes[] = {
kCGLPFAOpenGLProfile,
static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
- kCGLPFAAccelerated,
static_cast<CGLPixelFormatAttribute>(0)
};
diff --git a/platform/default/headless_view.cpp b/platform/default/headless_view.cpp
index 7fd3c37f3a..13ea78a709 100644
--- a/platform/default/headless_view.cpp
+++ b/platform/default/headless_view.cpp
@@ -1,163 +1,34 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/headless_display.hpp>
-#include <mbgl/platform/log.hpp>
-#include <stdexcept>
-#include <sstream>
-#include <string>
-#include <cstring>
#include <cassert>
-#include <utility>
-
-#ifdef MBGL_USE_CGL
-#include <CoreFoundation/CoreFoundation.h>
-#elif MBGL_USE_GLX
-#include <GL/glx.h>
-#endif
+#include <cstring>
namespace mbgl {
HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height)
- : display(std::make_shared<HeadlessDisplay>()), pixelRatio(pixelRatio_) {
- resize(width, height);
+ : display(std::make_shared<HeadlessDisplay>())
+ , pixelRatio(pixelRatio_)
+ , dimensions({{ width, height }})
+ , needsResize(true) {
}
HeadlessView::HeadlessView(std::shared_ptr<HeadlessDisplay> display_,
float pixelRatio_,
uint16_t width,
uint16_t height)
- : display(std::move(display_)), pixelRatio(pixelRatio_) {
- resize(width, height);
+ : display(std::move(display_))
+ , pixelRatio(pixelRatio_)
+ , dimensions({{ width, height }})
+ , needsResize(true) {
}
-void HeadlessView::loadExtensions() {
- if (extensionsLoaded) {
- return;
- }
-
-#ifdef MBGL_USE_CGL
- gl::InitializeExtensions([](const char * name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void* symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<gl::glProc>(symbol);
- });
-#endif
-
-#ifdef MBGL_USE_GLX
- gl::InitializeExtensions([](const char * name) {
- return glXGetProcAddress(reinterpret_cast<const GLubyte *>(name));
- });
-#endif
-
- extensionsLoaded = true;
-}
-
-void HeadlessView::createContext() {
- if (!display) {
- throw std::runtime_error("Display is not set");
- }
-
-#if MBGL_USE_CGL
- CGLError error = CGLCreateContext(display->pixelFormat, NULL, &glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error creating GL context object:") + CGLErrorString(error) + "\n");
- }
-
- error = CGLEnable(glContext, kCGLCEMPEngine);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") + CGLErrorString(error) + "\n");
- }
-#endif
-
-#if MBGL_USE_GLX
- xDisplay = display->xDisplay;
- fbConfigs = display->fbConfigs;
-
- if (!glContext) {
- // Try to create a legacy context
- glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
- if (glContext) {
- if (!glXIsDirect(xDisplay, glContext)) {
- Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
- glXDestroyContext(xDisplay, glContext);
- glContext = 0;
- }
- }
- }
-
- if (glContext == 0) {
- throw std::runtime_error("Error creating GL context object.");
- }
-
- // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
- // activate the context.
- int pbufferAttributes[] = {
- GLX_PBUFFER_WIDTH, 8,
- GLX_PBUFFER_HEIGHT, 8,
- None
- };
- glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
-#endif
-}
-
-bool HeadlessView::isActive() const {
- return std::this_thread::get_id() == thread;
-}
-
-void HeadlessView::resizeFramebuffer() {
- assert(isActive());
-
- if (!needsResize) return;
-
+HeadlessView::~HeadlessView() {
+ activate();
clearBuffers();
+ deactivate();
- const unsigned int w = dimensions[0] * pixelRatio;
- const unsigned int h = dimensions[1] * pixelRatio;
-
- // Create depth/stencil buffer
- MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
- MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
-
- MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
- MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
- MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
-
- MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
- MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
-
- MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
- MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
-
- GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
-
- if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
- std::string error("Couldn't create framebuffer: ");
- switch (status) {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
- case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
- default: error += "other"; break;
- }
- throw std::runtime_error(error);
- }
-
- MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
-
- needsResize = false;
+ destroyContext();
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
@@ -170,7 +41,7 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) {
}
PremultipliedImage HeadlessView::readStillImage() {
- assert(isActive());
+ assert(active);
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
@@ -178,7 +49,7 @@ PremultipliedImage HeadlessView::readStillImage() {
PremultipliedImage image { w, h };
MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
- const int stride = image.stride();
+ const auto stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t* rgba = image.data.get();
for (int i = 0, j = h - 1; i < j; i++, j--) {
@@ -190,50 +61,6 @@ PremultipliedImage HeadlessView::readStillImage() {
return image;
}
-void HeadlessView::clearBuffers() {
- assert(isActive());
-
- MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
-
- if (fbo) {
- MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
- fbo = 0;
- }
-
- if (fboColor) {
- MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
- fboColor = 0;
- }
-
- if (fboDepthStencil) {
- MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
- fboDepthStencil = 0;
- }
-}
-
-HeadlessView::~HeadlessView() {
- activate();
- clearBuffers();
- deactivate();
-
-#if MBGL_USE_CGL
- CGLDestroyContext(glContext);
-#endif
-
-#if MBGL_USE_GLX
- if (glxPbuffer) {
- glXDestroyPbuffer(xDisplay, glxPbuffer);
- glxPbuffer = 0;
- }
-
- glXDestroyContext(xDisplay, glContext);
-#endif
-}
-
-void HeadlessView::notify() {
- // no-op
-}
-
float HeadlessView::getPixelRatio() const {
return pixelRatio;
}
@@ -248,61 +75,36 @@ std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const {
}
void HeadlessView::activate() {
- if (thread != std::thread::id()) {
- throw std::runtime_error("OpenGL context was already current");
- }
- thread = std::this_thread::get_id();
+ active = true;
if (!glContext) {
+ if (!display) {
+ throw std::runtime_error("Display is not set");
+ }
createContext();
}
-#if MBGL_USE_CGL
- CGLError error = CGLSetCurrentContext(glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Switching OpenGL context failed:") + CGLErrorString(error) + "\n");
- }
-#endif
+ activateContext();
-#if MBGL_USE_GLX
- if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) {
- throw std::runtime_error("Switching OpenGL context failed.\n");
+ if (!extensionsLoaded) {
+ gl::InitializeExtensions(initializeExtension);
+ extensionsLoaded = true;
}
-#endif
- loadExtensions();
+ if (needsResize) {
+ clearBuffers();
+ resizeFramebuffer();
+ needsResize = false;
+ }
}
void HeadlessView::deactivate() {
- if (thread == std::thread::id()) {
- throw std::runtime_error("OpenGL context was not current");
- }
- thread = std::thread::id();
-
-#if MBGL_USE_CGL
- CGLError error = CGLSetCurrentContext(nullptr);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Removing OpenGL context failed:") + CGLErrorString(error) + "\n");
- }
-#endif
-
-#if MBGL_USE_GLX
- if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) {
- throw std::runtime_error("Removing OpenGL context failed.\n");
- }
-#endif
+ deactivateContext();
+ active = false;
}
void HeadlessView::invalidate() {
- // no-op
-}
-
-void HeadlessView::beforeRender() {
- resizeFramebuffer();
-}
-
-void HeadlessView::afterRender() {
- // no-op
+ assert(false);
}
} // namespace mbgl
diff --git a/platform/default/headless_view_glx.cpp b/platform/default/headless_view_glx.cpp
new file mode 100644
index 0000000000..3b719ab43a
--- /dev/null
+++ b/platform/default/headless_view_glx.cpp
@@ -0,0 +1,128 @@
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <cassert>
+
+#include <GL/glx.h>
+
+namespace mbgl {
+
+gl::glProc HeadlessView::initializeExtension(const char* name) {
+ return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
+}
+
+void HeadlessView::createContext() {
+ xDisplay = display->xDisplay;
+ fbConfigs = display->fbConfigs;
+
+ if (!glContext) {
+ // Try to create a legacy context
+ glContext = glXCreateNewContext(xDisplay, fbConfigs[0], GLX_RGBA_TYPE, None, True);
+ if (glContext) {
+ if (!glXIsDirect(xDisplay, glContext)) {
+ Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
+ glXDestroyContext(xDisplay, glContext);
+ glContext = 0;
+ }
+ }
+ }
+
+ if (glContext == 0) {
+ throw std::runtime_error("Error creating GL context object.");
+ }
+
+ // Create a dummy pbuffer. We will render to framebuffers anyway, but we need a pbuffer to
+ // activate the context.
+ int pbufferAttributes[] = {
+ GLX_PBUFFER_WIDTH, 8,
+ GLX_PBUFFER_HEIGHT, 8,
+ None
+ };
+ glxPbuffer = glXCreatePbuffer(xDisplay, fbConfigs[0], pbufferAttributes);
+}
+
+void HeadlessView::destroyContext() {
+ if (glxPbuffer) {
+ glXDestroyPbuffer(xDisplay, glxPbuffer);
+ glxPbuffer = 0;
+ }
+
+ glXDestroyContext(xDisplay, glContext);
+}
+
+void HeadlessView::resizeFramebuffer() {
+ const unsigned int w = dimensions[0] * pixelRatio;
+ const unsigned int h = dimensions[1] * pixelRatio;
+
+ // Create depth/stencil buffer
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
+ MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
+
+ MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
+
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
+ MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
+
+ GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
+
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ std::string error("Couldn't create framebuffer: ");
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
+ default: error += "other"; break;
+ }
+ throw std::runtime_error(error);
+ }
+
+ MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
+}
+
+void HeadlessView::clearBuffers() {
+ assert(active);
+
+ MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+
+ if (fbo) {
+ MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
+ fbo = 0;
+ }
+
+ if (fboColor) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
+ fboColor = 0;
+ }
+
+ if (fboDepthStencil) {
+ MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
+ fboDepthStencil = 0;
+ }
+}
+
+void HeadlessView::activateContext() {
+ if (!glXMakeContextCurrent(xDisplay, glxPbuffer, glxPbuffer, glContext)) {
+ throw std::runtime_error("Switching OpenGL context failed.\n");
+ }
+}
+
+void HeadlessView::deactivateContext() {
+ if (!glXMakeContextCurrent(xDisplay, 0, 0, nullptr)) {
+ throw std::runtime_error("Removing OpenGL context failed.\n");
+ }
+}
+
+} // namespace mbgl
diff --git a/platform/default/http_file_source.cpp b/platform/default/http_file_source.cpp
new file mode 100644
index 0000000000..e83ecfbfc9
--- /dev/null
+++ b/platform/default/http_file_source.cpp
@@ -0,0 +1,406 @@
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <mbgl/util/util.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/timer.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/http_header.hpp>
+
+#include <curl/curl.h>
+
+#include <queue>
+#include <map>
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+
+static void handleError(CURLMcode code) {
+ if (code != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code));
+ }
+}
+
+static void handleError(CURLcode code) {
+ if (code != CURLE_OK) {
+ throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code));
+ }
+}
+
+namespace mbgl {
+
+class HTTPFileSource::Impl {
+public:
+ Impl();
+ ~Impl();
+
+ static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
+ static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
+ static void onTimeout(HTTPFileSource::Impl *context);
+
+ void perform(curl_socket_t s, util::RunLoop::Event event);
+ CURL *getHandle();
+ void returnHandle(CURL *handle);
+ void checkMultiInfo();
+
+ // Used as the CURL timer function to periodically check for socket updates.
+ util::Timer timeout;
+
+ // CURL multi handle that we use to request multiple URLs at the same time, without having to
+ // block and spawn threads.
+ CURLM *multi = nullptr;
+
+ // CURL share handles are used for sharing session state (e.g.)
+ CURLSH *share = nullptr;
+
+ // A queue that we use for storing resuable CURL easy handles to avoid creating and destroying
+ // them all the time.
+ std::queue<CURL *> handles;
+};
+
+class HTTPRequest : public AsyncRequest {
+public:
+ HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
+ ~HTTPRequest();
+
+ void handleResult(CURLcode code);
+
+private:
+ static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
+ static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp);
+
+ HTTPFileSource::Impl* context = nullptr;
+ Resource resource;
+ FileSource::Callback callback;
+
+ // Will store the current response.
+ std::shared_ptr<std::string> data;
+ std::unique_ptr<Response> response;
+
+ CURL *handle = nullptr;
+ curl_slist *headers = nullptr;
+
+ char error[CURL_ERROR_SIZE] = { 0 };
+};
+
+HTTPFileSource::Impl::Impl() {
+ if (curl_global_init(CURL_GLOBAL_ALL)) {
+ throw std::runtime_error("Could not init cURL");
+ }
+
+ share = curl_share_init();
+
+ multi = curl_multi_init();
+ handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handleSocket));
+ handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, this));
+ handleError(curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, startTimeout));
+ handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this));
+}
+
+HTTPFileSource::Impl::~Impl() {
+ while (!handles.empty()) {
+ curl_easy_cleanup(handles.front());
+ handles.pop();
+ }
+
+ curl_multi_cleanup(multi);
+ multi = nullptr;
+
+ curl_share_cleanup(share);
+ share = nullptr;
+
+ timeout.stop();
+}
+
+CURL *HTTPFileSource::Impl::getHandle() {
+ if (!handles.empty()) {
+ auto handle = handles.front();
+ handles.pop();
+ return handle;
+ } else {
+ return curl_easy_init();
+ }
+}
+
+void HTTPFileSource::Impl::returnHandle(CURL *handle) {
+ curl_easy_reset(handle);
+ handles.push(handle);
+}
+
+void HTTPFileSource::Impl::checkMultiInfo() {
+ CURLMsg *message = nullptr;
+ int pending = 0;
+
+ while ((message = curl_multi_info_read(multi, &pending))) {
+ switch (message->msg) {
+ case CURLMSG_DONE: {
+ HTTPRequest *baton = nullptr;
+ curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
+ assert(baton);
+ baton->handleResult(message->data.result);
+ } break;
+
+ default:
+ // This should never happen, because there are no other message types.
+ throw std::runtime_error("CURLMsg returned unknown message type");
+ }
+ }
+}
+
+void HTTPFileSource::Impl::perform(curl_socket_t s, util::RunLoop::Event events) {
+ int flags = 0;
+
+ if (events == util::RunLoop::Event::Read) {
+ flags |= CURL_CSELECT_IN;
+ }
+ if (events == util::RunLoop::Event::Write) {
+ flags |= CURL_CSELECT_OUT;
+ }
+
+
+ int running_handles = 0;
+ curl_multi_socket_action(multi, s, flags, &running_handles);
+ checkMultiInfo();
+}
+
+int HTTPFileSource::Impl::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
+ void * /* socketp */) {
+ assert(userp);
+ auto context = reinterpret_cast<Impl *>(userp);
+
+ switch (action) {
+ case CURL_POLL_IN: {
+ using namespace std::placeholders;
+ util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Read,
+ std::bind(&Impl::perform, context, _1, _2));
+ break;
+ }
+ case CURL_POLL_OUT: {
+ using namespace std::placeholders;
+ util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Write,
+ std::bind(&Impl::perform, context, _1, _2));
+ break;
+ }
+ case CURL_POLL_REMOVE:
+ util::RunLoop::Get()->removeWatch(s);
+ break;
+ default:
+ throw std::runtime_error("Unhandled CURL socket action");
+ }
+
+ return 0;
+}
+
+void HTTPFileSource::Impl::onTimeout(Impl *context) {
+ int running_handles;
+ CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
+ if (error != CURLM_OK) {
+ throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
+ }
+ context->checkMultiInfo();
+}
+
+int HTTPFileSource::Impl::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
+ assert(userp);
+ auto context = reinterpret_cast<Impl *>(userp);
+
+ if (timeout_ms < 0) {
+ // A timeout of 0 ms means that the timer will invoked in the next loop iteration.
+ timeout_ms = 0;
+ }
+
+ context->timeout.stop();
+ context->timeout.start(mbgl::Milliseconds(timeout_ms), Duration::zero(),
+ std::bind(&Impl::onTimeout, context));
+
+ return 0;
+}
+
+HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, const Resource& resource_, FileSource::Callback callback_)
+ : context(context_),
+ resource(resource_),
+ callback(callback_),
+ handle(context->getHandle()) {
+
+ // If there's already a response, set the correct etags/modified headers to make sure we are
+ // getting a 304 response if possible. This avoids redownloading unchanged data.
+ if (resource.priorEtag) {
+ const std::string header = std::string("If-None-Match: ") + *resource.priorEtag;
+ headers = curl_slist_append(headers, header.c_str());
+ } else if (resource.priorModified) {
+ const std::string time =
+ std::string("If-Modified-Since: ") + util::rfc1123(*resource.priorModified);
+ headers = curl_slist_append(headers, time.c_str());
+ }
+
+ if (headers) {
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+ }
+
+ handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
+ handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
+ handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
+ handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
+ handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
+ handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
+ handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this));
+ handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
+ handleError(curl_easy_setopt(handle, CURLOPT_HEADERDATA, this));
+#if LIBCURL_VERSION_NUM >= ((7) << 16 | (21) << 8 | 6) // Renamed in 7.21.6
+ handleError(curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"));
+#else
+ handleError(curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate"));
+#endif
+ handleError(curl_easy_setopt(handle, CURLOPT_USERAGENT, "MapboxGL/1.0"));
+ handleError(curl_easy_setopt(handle, CURLOPT_SHARE, context->share));
+
+ // Start requesting the information.
+ handleError(curl_multi_add_handle(context->multi, handle));
+}
+
+HTTPRequest::~HTTPRequest() {
+ handleError(curl_multi_remove_handle(context->multi, handle));
+ context->returnHandle(handle);
+ handle = nullptr;
+
+ if (headers) {
+ curl_slist_free_all(headers);
+ headers = nullptr;
+ }
+}
+
+// This function is called when we have new data for a request. We just append it to the string
+// containing the previous data.
+size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
+ assert(userp);
+ auto impl = reinterpret_cast<HTTPRequest *>(userp);
+
+ if (!impl->data) {
+ impl->data = std::make_shared<std::string>();
+ }
+
+ impl->data->append((char *)contents, size * nmemb);
+ return size * nmemb;
+}
+
+// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!)
+// header string. If the data buffer contains the header string at the beginning, it returns
+// the length of the header string == begin of the value, otherwise it returns npos.
+// The comparison of the header is ASCII-case-insensitive.
+size_t headerMatches(const char *const header, const char *const buffer, const size_t length) {
+ const size_t headerLength = strlen(header);
+ if (length < headerLength) {
+ return std::string::npos;
+ }
+ size_t i = 0;
+ while (i < length && i < headerLength && std::tolower(buffer[i]) == std::tolower(header[i])) {
+ i++;
+ }
+ return i == headerLength ? i : std::string::npos;
+}
+
+size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
+ assert(userp);
+ auto baton = reinterpret_cast<HTTPRequest *>(userp);
+
+ if (!baton->response) {
+ baton->response = std::make_unique<Response>();
+ }
+
+ const size_t length = size * nmemb;
+ size_t begin = std::string::npos;
+ if ((begin = headerMatches("last-modified: ", buffer, length)) != std::string::npos) {
+ // Always overwrite the modification date; We might already have a value here from the
+ // Date header, but this one is more accurate.
+ const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
+ baton->response->modified = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
+ } else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) {
+ baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
+ } else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
+ const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
+ baton->response->expires = http::CacheControl::parse(value.c_str()).toTimePoint();
+ } else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
+ const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
+ baton->response->expires = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
+ }
+
+ return length;
+}
+
+void HTTPRequest::handleResult(CURLcode code) {
+ // Make sure a response object exists in case we haven't got any headers or content.
+ if (!response) {
+ response = std::make_unique<Response>();
+ }
+
+ using Error = Response::Error;
+
+ // Add human-readable error code
+ if (code != CURLE_OK) {
+ switch (code) {
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ case CURLE_COULDNT_RESOLVE_HOST:
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_OPERATION_TIMEDOUT:
+
+ response->error = std::make_unique<Error>(
+ Error::Reason::Connection, std::string{ curl_easy_strerror(code) } + ": " + error);
+ break;
+
+ default:
+ response->error = std::make_unique<Error>(
+ Error::Reason::Other, std::string{ curl_easy_strerror(code) } + ": " + error);
+ break;
+ }
+ } else {
+ long responseCode = 0;
+ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
+
+ if (responseCode == 200) {
+ if (data) {
+ response->data = std::move(data);
+ } else {
+ response->data = std::make_shared<std::string>();
+ }
+ } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
+ response->noContent = true;
+ } else if (responseCode == 304) {
+ response->notModified = true;
+ } else if (responseCode == 404) {
+ response->error =
+ std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
+ } else if (responseCode >= 500 && responseCode < 600) {
+ response->error =
+ std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
+ util::toString(responseCode));
+ } else {
+ response->error =
+ std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
+ util::toString(responseCode));
+ }
+ }
+
+ // Calling `callback` may result in deleting `this`. Copy data to temporaries first.
+ auto callback_ = callback;
+ auto response_ = *response;
+ callback_(response_);
+}
+
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
+ return std::make_unique<HTTPRequest>(impl.get(), resource, callback);
+}
+
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
+ return 20;
+}
+
+} // namespace mbgl
diff --git a/platform/default/http_request_curl.cpp b/platform/default/http_request_curl.cpp
deleted file mode 100644
index 58c574fee1..0000000000
--- a/platform/default/http_request_curl.cpp
+++ /dev/null
@@ -1,549 +0,0 @@
-#include <mbgl/storage/http_context_base.hpp>
-#include <mbgl/storage/http_request_base.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/platform/log.hpp>
-
-#include <mbgl/util/util.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/timer.hpp>
-#include <mbgl/util/chrono.hpp>
-
-#include <curl/curl.h>
-
-#ifdef __ANDROID__
-#include <mbgl/android/jni.hpp>
-#include <zip.h>
-#include <openssl/ssl.h>
-#endif
-
-#include <queue>
-#include <map>
-#include <cassert>
-#include <cstring>
-#include <cstdio>
-
-void handleError(CURLMcode code) {
- if (code != CURLM_OK) {
- throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(code));
- }
-}
-
-void handleError(CURLcode code) {
- if (code != CURLE_OK) {
- throw std::runtime_error(std::string("CURL easy error: ") + curl_easy_strerror(code));
- }
-}
-
-namespace mbgl {
-
-class HTTPCURLRequest;
-
-class HTTPCURLContext : public HTTPContextBase {
- MBGL_STORE_THREAD(tid)
-
-public:
- HTTPCURLContext();
- ~HTTPCURLContext();
-
- HTTPRequestBase* createRequest(const Resource&, HTTPRequestBase::Callback) final;
-
- static int handleSocket(CURL *handle, curl_socket_t s, int action, void *userp, void *socketp);
- static int startTimeout(CURLM *multi, long timeout_ms, void *userp);
- static void onTimeout(HTTPCURLContext *context);
-
- void perform(curl_socket_t s, util::RunLoop::Event event);
- CURL *getHandle();
- void returnHandle(CURL *handle);
- void checkMultiInfo();
-
- // Used as the CURL timer function to periodically check for socket updates.
- util::Timer timeout;
-
- // CURL multi handle that we use to request multiple URLs at the same time, without having to
- // block and spawn threads.
- CURLM *multi = nullptr;
-
- // CURL share handles are used for sharing session state (e.g.)
- CURLSH *share = nullptr;
-
- // A queue that we use for storing resuable CURL easy handles to avoid creating and destroying
- // them all the time.
- std::queue<CURL *> handles;
-};
-
-class HTTPCURLRequest : public HTTPRequestBase {
- MBGL_STORE_THREAD(tid)
-
-public:
- HTTPCURLRequest(HTTPCURLContext*, const Resource&, Callback);
- ~HTTPCURLRequest();
-
- void cancel() final;
-
- void handleResult(CURLcode code);
-
-private:
- static size_t headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp);
- static size_t writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp);
-
- HTTPCURLContext *context = nullptr;
-
- // Will store the current response.
- std::shared_ptr<std::string> data;
- std::unique_ptr<Response> response;
-
- // In case of revalidation requests, this will store the old response.
- const std::shared_ptr<const Response> existingResponse;
-
- CURL *handle = nullptr;
- curl_slist *headers = nullptr;
-
- char error[CURL_ERROR_SIZE];
-};
-
-// -------------------------------------------------------------------------------------------------
-
-HTTPCURLContext::HTTPCURLContext() {
- if (curl_global_init(CURL_GLOBAL_ALL)) {
- throw std::runtime_error("Could not init cURL");
- }
-
- share = curl_share_init();
-
- multi = curl_multi_init();
- handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handleSocket));
- handleError(curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, this));
- handleError(curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, startTimeout));
- handleError(curl_multi_setopt(multi, CURLMOPT_TIMERDATA, this));
-}
-
-HTTPCURLContext::~HTTPCURLContext() {
- while (!handles.empty()) {
- curl_easy_cleanup(handles.front());
- handles.pop();
- }
-
- curl_multi_cleanup(multi);
- multi = nullptr;
-
- curl_share_cleanup(share);
- share = nullptr;
-
- timeout.stop();
-}
-
-HTTPRequestBase* HTTPCURLContext::createRequest(const Resource& resource, HTTPRequestBase::Callback callback) {
- return new HTTPCURLRequest(this, resource, callback);
-}
-
-CURL *HTTPCURLContext::getHandle() {
- if (!handles.empty()) {
- auto handle = handles.front();
- handles.pop();
- return handle;
- } else {
- return curl_easy_init();
- }
-}
-
-void HTTPCURLContext::returnHandle(CURL *handle) {
- curl_easy_reset(handle);
- handles.push(handle);
-}
-
-void HTTPCURLContext::checkMultiInfo() {
- MBGL_VERIFY_THREAD(tid);
- CURLMsg *message = nullptr;
- int pending = 0;
-
- while ((message = curl_multi_info_read(multi, &pending))) {
- switch (message->msg) {
- case CURLMSG_DONE: {
- HTTPCURLRequest *baton = nullptr;
- curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, (char *)&baton);
- assert(baton);
- baton->handleResult(message->data.result);
- } break;
-
- default:
- // This should never happen, because there are no other message types.
- throw std::runtime_error("CURLMsg returned unknown message type");
- }
- }
-}
-
-void HTTPCURLContext::perform(curl_socket_t s, util::RunLoop::Event events) {
- MBGL_VERIFY_THREAD(tid);
-
- int flags = 0;
-
- if (events == util::RunLoop::Event::Read) {
- flags |= CURL_CSELECT_IN;
- }
- if (events == util::RunLoop::Event::Write) {
- flags |= CURL_CSELECT_OUT;
- }
-
-
- int running_handles = 0;
- curl_multi_socket_action(multi, s, flags, &running_handles);
- checkMultiInfo();
-}
-
-int HTTPCURLContext::handleSocket(CURL * /* handle */, curl_socket_t s, int action, void *userp,
- void * /* socketp */) {
- assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
-
- switch (action) {
- case CURL_POLL_IN: {
- using namespace std::placeholders;
- util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Read,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
- break;
- }
- case CURL_POLL_OUT: {
- using namespace std::placeholders;
- util::RunLoop::Get()->addWatch(s, util::RunLoop::Event::Write,
- std::bind(&HTTPCURLContext::perform, context, _1, _2));
- break;
- }
- case CURL_POLL_REMOVE:
- util::RunLoop::Get()->removeWatch(s);
- break;
- default:
- throw std::runtime_error("Unhandled CURL socket action");
- }
-
- return 0;
-}
-
-void HTTPCURLContext::onTimeout(HTTPCURLContext *context) {
- MBGL_VERIFY_THREAD(context->tid);
- int running_handles;
- CURLMcode error = curl_multi_socket_action(context->multi, CURL_SOCKET_TIMEOUT, 0, &running_handles);
- if (error != CURLM_OK) {
- throw std::runtime_error(std::string("CURL multi error: ") + curl_multi_strerror(error));
- }
- context->checkMultiInfo();
-}
-
-int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *userp) {
- assert(userp);
- auto context = reinterpret_cast<HTTPCURLContext *>(userp);
- MBGL_VERIFY_THREAD(context->tid);
- if (timeout_ms < 0) {
- // A timeout of 0 ms means that the timer will invoked in the next loop iteration.
- timeout_ms = 0;
- }
- context->timeout.stop();
- context->timeout.start(mbgl::Milliseconds(timeout_ms), Duration::zero(),
- std::bind(&HTTPCURLContext::onTimeout, context));
-
- return 0;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-#ifdef __ANDROID__
-
-// This function is called to load the CA bundle
-// from http://curl.haxx.se/libcurl/c/cacertinmem.html¯
-static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm */) {
-
- int error = 0;
- struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error);
- if (apk == nullptr) {
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- struct zip_file *apkFile = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE);
- if (apkFile == nullptr) {
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- struct zip_stat stat;
- if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- if (stat.size > std::numeric_limits<int>::max()) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- const auto pem = std::make_unique<char[]>(stat.size);
-
- if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // get a pointer to the X509 certificate store (which may be empty!)
- X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
- if (store == nullptr) {
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // get a BIO
- BIO *bio = BIO_new_mem_buf(pem.get(), static_cast<int>(stat.size));
- if (bio == nullptr) {
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // use it to read the PEM formatted certificate from memory into an X509
- // structure that SSL can use
- X509 *cert = nullptr;
- while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) {
- if (cert == nullptr) {
- BIO_free(bio);
- bio = nullptr;
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- // add our certificate to this store
- if (X509_STORE_add_cert(store, cert) == 0) {
- X509_free(cert);
- cert = nullptr;
- BIO_free(bio);
- bio = nullptr;
- store = nullptr;
- return CURLE_SSL_CACERT_BADFILE;
- }
-
- X509_free(cert);
- cert = nullptr;
- }
-
- // decrease reference counts
- BIO_free(bio);
- bio = nullptr;
-
- zip_fclose(apkFile);
- apkFile = nullptr;
- zip_close(apk);
- apk = nullptr;
-
- // all set to go
- return CURLE_OK;
-}
-#endif
-
-HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_)
- : HTTPRequestBase(resource_, callback_),
- context(context_),
- handle(context->getHandle()) {
- // Zero out the error buffer.
- memset(error, 0, sizeof(error));
-
- // If there's already a response, set the correct etags/modified headers to make sure we are
- // getting a 304 response if possible. This avoids redownloading unchanged data.
- if (resource.priorEtag) {
- const std::string header = std::string("If-None-Match: ") + *resource.priorEtag;
- headers = curl_slist_append(headers, header.c_str());
- } else if (resource.priorModified) {
- const std::string time =
- std::string("If-Modified-Since: ") + util::rfc1123(*resource.priorModified);
- headers = curl_slist_append(headers, time.c_str());
- }
-
- if (headers) {
- curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
- }
-
- handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
- handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
-#ifdef __ANDROID__
- handleError(curl_easy_setopt(handle, CURLOPT_SSLCERTTYPE, "PEM"));
- handleError(curl_easy_setopt(handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function));
-#else
- handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
-#endif
- handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
- handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
- handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
- handleError(curl_easy_setopt(handle, CURLOPT_WRITEDATA, this));
- handleError(curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback));
- handleError(curl_easy_setopt(handle, CURLOPT_HEADERDATA, this));
-#if LIBCURL_VERSION_NUM >= ((7) << 16 | (21) << 8 | 6) // Renamed in 7.21.6
- handleError(curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"));
-#else
- handleError(curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate"));
-#endif
- handleError(curl_easy_setopt(handle, CURLOPT_USERAGENT, "MapboxGL/1.0"));
- handleError(curl_easy_setopt(handle, CURLOPT_SHARE, context->share));
-
- // Start requesting the information.
- handleError(curl_multi_add_handle(context->multi, handle));
-}
-
-HTTPCURLRequest::~HTTPCURLRequest() {
- MBGL_VERIFY_THREAD(tid);
-
- handleError(curl_multi_remove_handle(context->multi, handle));
- context->returnHandle(handle);
- handle = nullptr;
-
- if (headers) {
- curl_slist_free_all(headers);
- headers = nullptr;
- }
-}
-
-void HTTPCURLRequest::cancel() {
- delete this;
-}
-
-// This function is called when we have new data for a request. We just append it to the string
-// containing the previous data.
-size_t HTTPCURLRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {
- assert(userp);
- auto impl = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(impl->tid);
-
- if (!impl->data) {
- impl->data = std::make_shared<std::string>();
- }
-
- impl->data->append((char *)contents, size * nmemb);
- return size * nmemb;
-}
-
-// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!)
-// header string. If the data buffer contains the header string at the beginning, it returns
-// the length of the header string == begin of the value, otherwise it returns npos.
-// The comparison of the header is ASCII-case-insensitive.
-size_t headerMatches(const char *const header, const char *const buffer, const size_t length) {
- const size_t headerLength = strlen(header);
- if (length < headerLength) {
- return std::string::npos;
- }
- size_t i = 0;
- while (i < length && i < headerLength && std::tolower(buffer[i]) == std::tolower(header[i])) {
- i++;
- }
- return i == headerLength ? i : std::string::npos;
-}
-
-size_t HTTPCURLRequest::headerCallback(char *const buffer, const size_t size, const size_t nmemb, void *userp) {
- assert(userp);
- auto baton = reinterpret_cast<HTTPCURLRequest *>(userp);
- MBGL_VERIFY_THREAD(baton->tid);
-
- if (!baton->response) {
- baton->response = std::make_unique<Response>();
- }
-
- const size_t length = size * nmemb;
- size_t begin = std::string::npos;
- if ((begin = headerMatches("last-modified: ", buffer, length)) != std::string::npos) {
- // Always overwrite the modification date; We might already have a value here from the
- // Date header, but this one is more accurate.
- const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->modified = SystemClock::from_time_t(curl_getdate(value.c_str(), nullptr));
- } else if ((begin = headerMatches("etag: ", buffer, length)) != std::string::npos) {
- baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
- } else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
- const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = parseCacheControl(value.c_str());
- } else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
- const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = SystemClock::from_time_t(curl_getdate(value.c_str(), nullptr));
- }
-
- return length;
-}
-
-void HTTPCURLRequest::handleResult(CURLcode code) {
- MBGL_VERIFY_THREAD(tid);
-
- if (cancelled) {
- // In this case, it doesn't make sense to even process the response even further since
- // the request was canceled anyway.
- delete this;
- return;
- }
-
- // Make sure a response object exists in case we haven't got any headers or content.
- if (!response) {
- response = std::make_unique<Response>();
- }
-
- using Error = Response::Error;
-
- // Add human-readable error code
- if (code != CURLE_OK) {
- switch (code) {
- case CURLE_COULDNT_RESOLVE_PROXY:
- case CURLE_COULDNT_RESOLVE_HOST:
- case CURLE_COULDNT_CONNECT:
- case CURLE_OPERATION_TIMEDOUT:
-
- response->error = std::make_unique<Error>(
- Error::Reason::Connection, std::string{ curl_easy_strerror(code) } + ": " + error);
- break;
-
- default:
- response->error = std::make_unique<Error>(
- Error::Reason::Other, std::string{ curl_easy_strerror(code) } + ": " + error);
- break;
- }
- } else {
- long responseCode = 0;
- curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
-
- if (responseCode == 200) {
- if (data) {
- response->data = std::move(data);
- } else {
- response->data = std::make_shared<std::string>();
- }
- } else if (responseCode == 204 || (responseCode == 404 && resource.kind == Resource::Kind::Tile)) {
- response->noContent = true;
- } else if (responseCode == 304) {
- response->notModified = true;
- } else if (responseCode == 404) {
- response->error =
- std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
- } else if (responseCode >= 500 && responseCode < 600) {
- response->error =
- std::make_unique<Error>(Error::Reason::Server, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- } else {
- response->error =
- std::make_unique<Error>(Error::Reason::Other, std::string{ "HTTP status code " } +
- std::to_string(responseCode));
- }
- }
-
- // Actually return the response.
- notify(*response);
- delete this;
-}
-
-std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext() {
- return std::make_unique<HTTPCURLContext>();
-}
-
-uint32_t HTTPContextBase::maximumConcurrentRequests() {
- return 20;
-}
-
-} // namespace mbgl
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 71fb5414b3..988d2f4a4e 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -4,20 +4,24 @@
#include <png.h>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-variable"
-// Check png library version.
-const static bool png_version_check = []() {
+template<size_t max, typename... Args>
+inline static std::string sprintf(const char *msg, Args... args) {
+ char res[max];
+ int len = snprintf(res, sizeof(res), msg, args...);
+ return std::string(res, len);
+}
+
+const static bool png_version_check __attribute__((unused)) = []() {
const png_uint_32 version = png_access_version_number();
if (version != PNG_LIBPNG_VER) {
- throw std::runtime_error(mbgl::util::sprintf<96>(
+ throw std::runtime_error(sprintf<96>(
"libpng version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d",
PNG_LIBPNG_VER / 10000, (PNG_LIBPNG_VER / 100) % 100, PNG_LIBPNG_VER % 100,
version / 10000, (version / 100) % 100, version % 100));
}
return true;
}();
-#pragma GCC diagnostic pop
+
namespace mbgl {
std::string encodePNG(const PremultipliedImage& pre) {
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index 931e079771..d8e0357ae2 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -1,6 +1,6 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tile_cover.hpp>
-#include <mbgl/source/source_info.hpp>
+#include <mbgl/util/tileset.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
@@ -23,20 +23,20 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
-std::vector<TileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
- double minZ = std::max<double>(coveringZoomLevel(minZoom, type, tileSize), info.minZoom);
- double maxZ = std::min<double>(coveringZoomLevel(maxZoom, type, tileSize), info.maxZoom);
+std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
+ double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), tileset.minZoom);
+ double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), tileset.maxZoom);
assert(minZ >= 0);
assert(maxZ >= 0);
assert(minZ < std::numeric_limits<uint8_t>::max());
assert(maxZ < std::numeric_limits<uint8_t>::max());
- std::vector<TileID> result;
+ std::vector<CanonicalTileID> result;
for (uint8_t z = minZ; z <= maxZ; z++) {
- for (const auto& tile : mbgl::tileCover(bounds, z, z)) {
- result.push_back(tile.normalized());
+ for (const auto& tile : util::tileCover(bounds, z)) {
+ result.emplace_back(tile.canonical);
}
}
diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 0b8dec01bf..3193909294 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -4,7 +4,6 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
-#include <mbgl/map/tile_id.hpp>
#include <mbgl/platform/log.hpp>
#include "sqlite3.hpp"
@@ -158,7 +157,7 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
}
if (evict_ && !evict(size)) {
- Log::Warning(Event::Database, "Unable to make space for entry");
+ Log::Debug(Event::Database, "Unable to make space for entry");
return { false, 0 };
}
@@ -182,7 +181,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
Statement accessedStmt = getStatement(
"UPDATE resources SET accessed = ?1 WHERE url = ?2");
- accessedStmt->bind(1, SystemClock::now());
+ accessedStmt->bind(1, util::now());
accessedStmt->bind(2, resource.url);
accessedStmt->run();
@@ -202,8 +201,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
uint64_t size = 0;
response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<SystemTimePoint>>(1);
- response.modified = stmt->get<optional<SystemTimePoint>>(2);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.modified = stmt->get<optional<Timestamp>>(2);
optional<std::string> data = stmt->get<optional<std::string>>(3);
if (!data) {
@@ -230,7 +229,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
" expires = ?2 "
"WHERE url = ?3 ");
- update->bind(1, SystemClock::now());
+ update->bind(1, util::now());
update->bind(2, response.expires);
update->bind(3, resource.url);
update->run();
@@ -239,6 +238,10 @@ bool OfflineDatabase::putResource(const Resource& resource,
// We can't use REPLACE because it would change the id value.
+ // Begin an immediate-mode transaction to ensure that two writers do not attempt
+ // to INSERT a resource at the same moment.
+ Transaction transaction(*db, Transaction::Immediate);
+
Statement update = getStatement(
"UPDATE resources "
"SET kind = ?1, "
@@ -254,7 +257,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
update->bind(2, response.etag);
update->bind(3, response.expires);
update->bind(4, response.modified);
- update->bind(5, SystemClock::now());
+ update->bind(5, util::now());
update->bind(8, resource.url);
if (response.noContent) {
@@ -267,6 +270,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
update->run();
if (db->changes() != 0) {
+ transaction.commit();
return false;
}
@@ -279,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
insert->bind(3, response.etag);
insert->bind(4, response.expires);
insert->bind(5, response.modified);
- insert->bind(6, SystemClock::now());
+ insert->bind(6, util::now());
if (response.noContent) {
insert->bind(7, nullptr);
@@ -290,6 +294,8 @@ bool OfflineDatabase::putResource(const Resource& resource,
}
insert->run();
+ transaction.commit();
+
return true;
}
@@ -303,7 +309,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
" AND y = ?5 "
" AND z = ?6 ");
- accessedStmt->bind(1, SystemClock::now());
+ accessedStmt->bind(1, util::now());
accessedStmt->bind(2, tile.urlTemplate);
accessedStmt->bind(3, tile.pixelRatio);
accessedStmt->bind(4, tile.x);
@@ -335,8 +341,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
uint64_t size = 0;
response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<SystemTimePoint>>(1);
- response.modified = stmt->get<optional<SystemTimePoint>>(2);
+ response.expires = stmt->get<optional<Timestamp>>(1);
+ response.modified = stmt->get<optional<Timestamp>>(2);
optional<std::string> data = stmt->get<optional<std::string>>(3);
if (!data) {
@@ -367,7 +373,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND y = ?6 "
" AND z = ?7 ");
- update->bind(1, SystemClock::now());
+ update->bind(1, util::now());
update->bind(2, response.expires);
update->bind(3, tile.urlTemplate);
update->bind(4, tile.pixelRatio);
@@ -380,6 +386,10 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// We can't use REPLACE because it would change the id value.
+ // Begin an immediate-mode transaction to ensure that two writers do not attempt
+ // to INSERT a resource at the same moment.
+ Transaction transaction(*db, Transaction::Immediate);
+
Statement update = getStatement(
"UPDATE tiles "
"SET modified = ?1, "
@@ -397,7 +407,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
update->bind(1, response.modified);
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, SystemClock::now());
+ update->bind(4, util::now());
update->bind(7, tile.urlTemplate);
update->bind(8, tile.pixelRatio);
update->bind(9, tile.x);
@@ -414,6 +424,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
update->run();
if (db->changes() != 0) {
+ transaction.commit();
return false;
}
@@ -429,7 +440,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
insert->bind(6, response.modified);
insert->bind(7, response.etag);
insert->bind(8, response.expires);
- insert->bind(9, SystemClock::now());
+ insert->bind(9, util::now());
if (response.noContent) {
insert->bind(10, nullptr);
@@ -440,6 +451,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
}
insert->run();
+ transaction.commit();
+
return true;
}
@@ -594,26 +607,37 @@ OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) {
OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) {
OfflineRegionStatus result;
- Statement stmt = getStatement(
- "SELECT COUNT(*), SUM(size) FROM ( "
- " SELECT LENGTH(data) as size "
- " FROM region_resources, resources "
- " WHERE region_id = ?1 "
- " AND resource_id = resources.id "
- " UNION ALL "
- " SELECT LENGTH(data) as size "
- " FROM region_tiles, tiles "
- " WHERE region_id = ?1 "
- " AND tile_id = tiles.id "
- ") ");
+ std::tie(result.completedResourceCount, result.completedResourceSize)
+ = getCompletedResourceCountAndSize(regionID);
+ std::tie(result.completedTileCount, result.completedTileSize)
+ = getCompletedTileCountAndSize(regionID);
+
+ result.completedResourceCount += result.completedTileCount;
+ result.completedResourceSize += result.completedTileSize;
+
+ return result;
+}
+std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) {
+ Statement stmt = getStatement(
+ "SELECT COUNT(*), SUM(LENGTH(data)) "
+ "FROM region_resources, resources "
+ "WHERE region_id = ?1 "
+ "AND resource_id = resources.id ");
stmt->bind(1, regionID);
stmt->run();
+ return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
+}
- result.completedResourceCount = stmt->get<int64_t>(0);
- result.completedResourceSize = stmt->get<int64_t>(1);
-
- return result;
+std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) {
+ Statement stmt = getStatement(
+ "SELECT COUNT(*), SUM(LENGTH(data)) "
+ "FROM region_tiles, tiles "
+ "WHERE region_id = ?1 "
+ "AND tile_id = tiles.id ");
+ stmt->bind(1, regionID);
+ stmt->run();
+ return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
}
template <class T>
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 1e77d560d4..1706c6ba5a 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -1,5 +1,4 @@
-#ifndef MBGL_OFFLINE_DATABASE
-#define MBGL_OFFLINE_DATABASE
+#pragma once
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/offline.hpp>
@@ -92,6 +91,9 @@ private:
// Return value is true iff the resource was previously unused by any other regions.
bool markUsed(int64_t regionID, const Resource&);
+ std::pair<int64_t, int64_t> getCompletedResourceCountAndSize(int64_t regionID);
+ std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID);
+
const std::string path;
std::unique_ptr<::mapbox::sqlite::Database> db;
std::unordered_map<const char *, std::unique_ptr<::mapbox::sqlite::Statement>> statements;
@@ -108,5 +110,3 @@ private:
};
} // namespace mbgl
-
-#endif
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index cda00bf8df..11ca862925 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -3,8 +3,7 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/style/style_parser.hpp>
-#include <mbgl/layer/symbol_layer.hpp>
+#include <mbgl/style/parser.hpp>
#include <mbgl/text/glyph.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/mapbox.hpp>
@@ -47,7 +46,7 @@ void OfflineDownload::setState(OfflineRegionDownloadState state) {
observer->statusChanged(status);
}
-std::vector<Resource> OfflineDownload::spriteResources(const StyleParser& parser) const {
+std::vector<Resource> OfflineDownload::spriteResources(const style::Parser& parser) const {
std::vector<Resource> result;
if (!parser.spriteURL.empty()) {
@@ -58,7 +57,7 @@ std::vector<Resource> OfflineDownload::spriteResources(const StyleParser& parser
return result;
}
-std::vector<Resource> OfflineDownload::glyphResources(const StyleParser& parser) const {
+std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parser) const {
std::vector<Resource> result;
if (!parser.glyphURL.empty()) {
@@ -72,11 +71,11 @@ std::vector<Resource> OfflineDownload::glyphResources(const StyleParser& parser)
return result;
}
-std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const SourceInfo& info) const {
+std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
std::vector<Resource> result;
- for (const auto& tile : definition.tileCover(type, tileSize, info)) {
- result.push_back(Resource::tile(info.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
+ for (const auto& tile : definition.tileCover(type, tileSize, tileset)) {
+ result.push_back(Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
}
return result;
@@ -95,7 +94,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
return result;
}
- StyleParser parser;
+ style::Parser parser;
parser.parse(*styleResponse->data);
result.requiredResourceCountIsPrecise = true;
@@ -104,14 +103,14 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
switch (source->type) {
case SourceType::Vector:
case SourceType::Raster:
- if (source->getInfo()) {
- result.requiredResourceCount += tileResources(source->type, source->tileSize, *source->getInfo()).size();
+ if (source->getTileset()) {
+ result.requiredResourceCount += tileResources(source->type, source->tileSize, *source->getTileset()).size();
} else {
result.requiredResourceCount += 1;
optional<Response> sourceResponse = offlineDatabase.get(Resource::source(source->url));
if (sourceResponse) {
result.requiredResourceCount += tileResources(source->type, source->tileSize,
- *StyleParser::parseTileJSON(*sourceResponse->data, source->url, source->type, source->tileSize)).size();
+ *style::parseTileJSON(*sourceResponse->data, source->url, source->type, source->tileSize)).size();
} else {
result.requiredResourceCountIsPrecise = false;
}
@@ -145,7 +144,7 @@ void OfflineDownload::activateDownload() {
ensureResource(Resource::style(definition.styleURL), [&] (Response styleResponse) {
status.requiredResourceCountIsPrecise = true;
- StyleParser parser;
+ style::Parser parser;
parser.parse(*styleResponse.data);
for (const auto& source : parser.sources) {
@@ -156,14 +155,14 @@ void OfflineDownload::activateDownload() {
switch (type) {
case SourceType::Vector:
case SourceType::Raster:
- if (source->getInfo()) {
- ensureTiles(type, tileSize, *source->getInfo());
+ if (source->getTileset()) {
+ ensureTiles(type, tileSize, *source->getTileset());
} else {
status.requiredResourceCountIsPrecise = false;
requiredSourceURLs.insert(url);
ensureResource(Resource::source(url), [=] (Response sourceResponse) {
- ensureTiles(type, tileSize, *StyleParser::parseTileJSON(*sourceResponse.data, url, type, tileSize));
+ ensureTiles(type, tileSize, *style::parseTileJSON(*sourceResponse.data, url, type, tileSize));
requiredSourceURLs.erase(url);
if (requiredSourceURLs.empty()) {
@@ -196,11 +195,10 @@ void OfflineDownload::activateDownload() {
}
void OfflineDownload::deactivateDownload() {
- workRequests.clear();
- fileRequests.clear();
+ requests.clear();
}
-void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const SourceInfo& info) {
+void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Tileset& info) {
for (const auto& resource : tileResources(type, tileSize, info)) {
ensureResource(resource);
}
@@ -209,9 +207,9 @@ void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Sour
void OfflineDownload::ensureResource(const Resource& resource, std::function<void (Response)> callback) {
status.requiredResourceCount++;
- auto workRequestsIt = workRequests.insert(workRequests.begin(), nullptr);
+ auto workRequestsIt = requests.insert(requests.begin(), nullptr);
*workRequestsIt = util::RunLoop::Get()->invokeCancellable([=] () {
- workRequests.erase(workRequestsIt);
+ requests.erase(workRequestsIt);
optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource);
if (offlineResponse) {
@@ -221,6 +219,10 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
status.completedResourceCount++;
status.completedResourceSize += offlineResponse->second;
+ if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileSize += offlineResponse->second;
+ }
+
observer->statusChanged(status);
if (status.complete()) {
@@ -234,21 +236,25 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
return;
}
- auto fileRequestsIt = fileRequests.insert(fileRequests.begin(), nullptr);
+ auto fileRequestsIt = requests.insert(requests.begin(), nullptr);
*fileRequestsIt = onlineFileSource.request(resource, [=] (Response onlineResponse) {
if (onlineResponse.error) {
observer->responseError(*onlineResponse.error);
return;
}
- fileRequests.erase(fileRequestsIt);
+ requests.erase(fileRequestsIt);
if (callback) {
callback(onlineResponse);
}
status.completedResourceCount++;
- status.completedResourceSize += offlineDatabase.putRegionResource(id, resource, onlineResponse);
+ uint64_t resourceSize = offlineDatabase.putRegionResource(id, resource, onlineResponse);
+ status.completedResourceSize += resourceSize;
+ if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileSize += resourceSize;
+ }
observer->statusChanged(status);
diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp
index be8e90b251..1a0d7536d8 100644
--- a/platform/default/mbgl/storage/offline_download.hpp
+++ b/platform/default/mbgl/storage/offline_download.hpp
@@ -1,7 +1,6 @@
- #pragma once
+#pragma once
#include <mbgl/storage/offline.hpp>
-#include <mbgl/style/types.hpp>
#include <list>
#include <set>
@@ -11,13 +10,14 @@ namespace mbgl {
class OfflineDatabase;
class FileSource;
-class WorkRequest;
-class FileRequest;
+class AsyncRequest;
class Resource;
class Response;
-class SourceInfo;
-class StyleParser;
-class Source;
+class Tileset;
+
+namespace style {
+class Parser;
+}
/**
* Coordinates the request and storage of all resources for an offline region.
@@ -38,9 +38,9 @@ private:
void activateDownload();
void deactivateDownload();
- std::vector<Resource> spriteResources(const StyleParser&) const;
- std::vector<Resource> glyphResources(const StyleParser&) const;
- std::vector<Resource> tileResources(SourceType, uint16_t, const SourceInfo&) const;
+ std::vector<Resource> spriteResources(const style::Parser&) const;
+ std::vector<Resource> glyphResources(const style::Parser&) const;
+ std::vector<Resource> tileResources(SourceType, uint16_t, const Tileset&) const;
/*
* Ensure that the resource is stored in the database, requesting it if necessary.
@@ -48,7 +48,7 @@ private:
* is deactivated, all in progress requests are cancelled.
*/
void ensureResource(const Resource&, std::function<void (Response)> = {});
- void ensureTiles(SourceType, uint16_t, const SourceInfo&);
+ void ensureTiles(SourceType, uint16_t, const Tileset&);
bool checkTileCountLimit(const Resource& resource);
int64_t id;
@@ -57,8 +57,7 @@ private:
FileSource& onlineFileSource;
OfflineRegionStatus status;
std::unique_ptr<OfflineRegionObserver> observer;
- std::list<std::unique_ptr<WorkRequest>> workRequests;
- std::list<std::unique_ptr<FileRequest>> fileRequests;
+ std::list<std::unique_ptr<AsyncRequest>> requests;
std::set<std::string> requiredSourceURLs;
};
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index 944bcd56d5..a4ac2c2b2b 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -1,5 +1,5 @@
#include <mbgl/storage/online_file_source.hpp>
-#include <mbgl/storage/http_context_base.hpp>
+#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/storage/response.hpp>
@@ -22,20 +22,20 @@
namespace mbgl {
-class OnlineFileRequestImpl : public util::noncopyable {
+class OnlineFileRequest : public AsyncRequest {
public:
using Callback = std::function<void (Response)>;
- OnlineFileRequestImpl(FileRequest*, const Resource&, Callback, OnlineFileSource::Impl&);
- ~OnlineFileRequestImpl();
+ OnlineFileRequest(const Resource&, Callback, OnlineFileSource::Impl&);
+ ~OnlineFileRequest();
- void networkIsReachableAgain(OnlineFileSource::Impl&);
- void schedule(OnlineFileSource::Impl&, optional<SystemTimePoint> expires);
- void completed(OnlineFileSource::Impl&, Response);
+ void networkIsReachableAgain();
+ void schedule(optional<Timestamp> expires);
+ void completed(Response);
- FileRequest* key;
+ OnlineFileSource::Impl& impl;
Resource resource;
- HTTPRequestBase* request = nullptr;
+ std::unique_ptr<AsyncRequest> request;
util::Timer timer;
Callback callback;
@@ -52,8 +52,7 @@ public:
class OnlineFileSource::Impl {
public:
- // Dummy parameter is a workaround for a gcc 4.9 bug.
- Impl(int) {
+ Impl() {
NetworkStatus::Subscribe(&reachability);
}
@@ -61,16 +60,16 @@ public:
NetworkStatus::Unsubscribe(&reachability);
}
- void request(FileRequest* key, Resource resource, Callback callback) {
- allRequests[key] = std::make_unique<OnlineFileRequestImpl>(key, resource, callback, *this);
+ void add(OnlineFileRequest* request) {
+ allRequests.insert(request);
}
- void cancel(FileRequest* key) {
- allRequests.erase(key);
- if (activeRequests.erase(key)) {
+ void remove(OnlineFileRequest* request) {
+ allRequests.erase(request);
+ if (activeRequests.erase(request)) {
activatePendingRequest();
} else {
- auto it = pendingRequestsMap.find(key);
+ auto it = pendingRequestsMap.find(request);
if (it != pendingRequestsMap.end()) {
pendingRequestsList.erase(it->second);
pendingRequestsMap.erase(it);
@@ -78,30 +77,30 @@ public:
}
}
- void activateOrQueueRequest(OnlineFileRequestImpl* impl) {
- assert(allRequests.find(impl->key) != allRequests.end());
- assert(activeRequests.find(impl->key) == activeRequests.end());
- assert(!impl->request);
+ void activateOrQueueRequest(OnlineFileRequest* request) {
+ assert(allRequests.find(request) != allRequests.end());
+ assert(activeRequests.find(request) == activeRequests.end());
+ assert(!request->request);
- if (activeRequests.size() >= HTTPContextBase::maximumConcurrentRequests()) {
- queueRequest(impl);
+ if (activeRequests.size() >= HTTPFileSource::maximumConcurrentRequests()) {
+ queueRequest(request);
} else {
- activateRequest(impl);
+ activateRequest(request);
}
}
- void queueRequest(OnlineFileRequestImpl* impl) {
- auto it = pendingRequestsList.insert(pendingRequestsList.end(), impl->key);
- pendingRequestsMap.emplace(impl->key, std::move(it));
+ void queueRequest(OnlineFileRequest* request) {
+ auto it = pendingRequestsList.insert(pendingRequestsList.end(), request);
+ pendingRequestsMap.emplace(request, std::move(it));
}
- void activateRequest(OnlineFileRequestImpl* impl) {
- activeRequests.insert(impl->key);
- impl->request = httpContext->createRequest(impl->resource, [=] (Response response) {
- impl->request = nullptr;
- activeRequests.erase(impl->key);
+ void activateRequest(OnlineFileRequest* request) {
+ activeRequests.insert(request);
+ request->request = httpFileSource.request(request->resource, [=] (Response response) {
+ activeRequests.erase(request);
activatePendingRequest();
- impl->completed(*this, response);
+ request->request.reset();
+ request->completed(response);
});
}
@@ -110,20 +109,18 @@ public:
return;
}
- FileRequest* key = pendingRequestsList.front();
+ OnlineFileRequest* request = pendingRequestsList.front();
pendingRequestsList.pop_front();
- pendingRequestsMap.erase(key);
+ pendingRequestsMap.erase(request);
- auto it = allRequests.find(key);
- assert(it != allRequests.end());
- activateRequest(it->second.get());
+ activateRequest(request);
}
private:
void networkIsReachableAgain() {
- for (auto& req : allRequests) {
- req.second->networkIsReachableAgain(*this);
+ for (auto& request : allRequests) {
+ request->networkIsReachableAgain();
}
}
@@ -138,23 +135,22 @@ private:
* Requests in any state are in `allRequests`. Requests in the pending state are in
* `pendingRequests`. Requests in the active state are in `activeRequests`.
*/
- std::unordered_map<FileRequest*, std::unique_ptr<OnlineFileRequestImpl>> allRequests;
- std::list<FileRequest*> pendingRequestsList;
- std::unordered_map<FileRequest*, std::list<FileRequest*>::iterator> pendingRequestsMap;
- std::unordered_set<FileRequest*> activeRequests;
+ std::unordered_set<OnlineFileRequest*> allRequests;
+ std::list<OnlineFileRequest*> pendingRequestsList;
+ std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap;
+ std::unordered_set<OnlineFileRequest*> activeRequests;
- const std::unique_ptr<HTTPContextBase> httpContext { HTTPContextBase::createContext() };
+ HTTPFileSource httpFileSource;
util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
};
OnlineFileSource::OnlineFileSource()
- : thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{ "OnlineFileSource", util::ThreadType::Unknown, util::ThreadPriority::Low }, 0)) {
+ : impl(std::make_unique<Impl>()) {
}
OnlineFileSource::~OnlineFileSource() = default;
-std::unique_ptr<FileRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
+std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) {
Resource res = resource;
switch (resource.kind) {
@@ -183,40 +179,25 @@ std::unique_ptr<FileRequest> OnlineFileSource::request(const Resource& resource,
break;
}
- class OnlineFileRequest : public FileRequest {
- public:
- OnlineFileRequest(Resource resource_, FileSource::Callback callback_, util::Thread<OnlineFileSource::Impl>& thread_)
- : thread(thread_),
- workRequest(thread.invokeWithCallback(&OnlineFileSource::Impl::request, callback_, this, resource_)) {
- }
-
- ~OnlineFileRequest() {
- thread.invoke(&OnlineFileSource::Impl::cancel, this);
- }
-
- util::Thread<OnlineFileSource::Impl>& thread;
- std::unique_ptr<WorkRequest> workRequest;
- };
-
- return std::make_unique<OnlineFileRequest>(res, callback, *thread);
+ return std::make_unique<OnlineFileRequest>(res, callback, *impl);
}
-OnlineFileRequestImpl::OnlineFileRequestImpl(FileRequest* key_, const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl)
- : key(key_),
+OnlineFileRequest::OnlineFileRequest(const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl_)
+ : impl(impl_),
resource(resource_),
callback(std::move(callback_)) {
+ impl.add(this);
+
// Force an immediate first request if we don't have an expiration time.
if (resource.priorExpires) {
- schedule(impl, resource.priorExpires);
+ schedule(resource.priorExpires);
} else {
- schedule(impl, SystemClock::now());
+ schedule(util::now());
}
}
-OnlineFileRequestImpl::~OnlineFileRequestImpl() {
- if (request) {
- request->cancel();
- }
+OnlineFileRequest::~OnlineFileRequest() {
+ impl.remove(this);
}
static Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, uint32_t failedRequests) {
@@ -233,20 +214,20 @@ static Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, u
}
}
-static Duration expirationTimeout(optional<SystemTimePoint> expires, uint32_t expiredRequests) {
+static Duration expirationTimeout(optional<Timestamp> expires, uint32_t expiredRequests) {
if (expiredRequests) {
return Seconds(1 << std::min(expiredRequests - 1, 31u));
} else if (expires) {
- return std::max(SystemDuration::zero(), *expires - SystemClock::now());
+ return std::max(Seconds::zero(), *expires - util::now());
} else {
return Duration::max();
}
}
-SystemTimePoint interpolateExpiration(const SystemTimePoint& current,
- optional<SystemTimePoint> prior,
- bool& expired) {
- auto now = SystemClock::now();
+Timestamp interpolateExpiration(const Timestamp& current,
+ optional<Timestamp> prior,
+ bool& expired) {
+ auto now = util::now();
if (current > now) {
return current;
}
@@ -275,10 +256,10 @@ SystemTimePoint interpolateExpiration(const SystemTimePoint& current,
// Assume that either the client or server clock is wrong and
// try to interpolate a valid expiration date (from the client POV)
// observing a minimum timeout.
- return now + std::max<SystemDuration>(delta, util::CLOCK_SKEW_RETRY_TIMEOUT);
+ return now + std::max<Seconds>(delta, util::CLOCK_SKEW_RETRY_TIMEOUT);
}
-void OnlineFileRequestImpl::schedule(OnlineFileSource::Impl& impl, optional<SystemTimePoint> expires) {
+void OnlineFileRequest::schedule(optional<Timestamp> expires) {
if (request) {
// There's already a request in progress; don't start another one.
return;
@@ -307,7 +288,7 @@ void OnlineFileRequestImpl::schedule(OnlineFileSource::Impl& impl, optional<Syst
});
}
-void OnlineFileRequestImpl::completed(OnlineFileSource::Impl& impl, Response response) {
+void OnlineFileRequest::completed(Response response) {
// If we didn't get various caching headers in the response, continue using the
// previous values. Otherwise, update the previous values to the new values.
@@ -345,15 +326,20 @@ void OnlineFileRequestImpl::completed(OnlineFileSource::Impl& impl, Response res
failedRequestReason = Response::Error::Reason::Success;
}
- callback(response);
- schedule(impl, response.expires);
+ schedule(response.expires);
+
+ // Calling the callback may result in `this` being deleted. It needs to be done last,
+ // and needs to make a local copy of the callback to ensure that it remains valid for
+ // the duration of the call.
+ auto callback_ = callback;
+ callback_(response);
}
-void OnlineFileRequestImpl::networkIsReachableAgain(OnlineFileSource::Impl& impl) {
+void OnlineFileRequest::networkIsReachableAgain() {
// We need all requests to fail at least once before we are going to start retrying
// them, and we only immediately restart request that failed due to connection issues.
if (failedRequestReason == Response::Error::Reason::Connection) {
- schedule(impl, SystemClock::now());
+ schedule(util::now());
}
}
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 2b9bbbeb46..096596ee2e 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -83,8 +83,8 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
- unsigned width = 0;
- unsigned height = 0;
+ png_uint_32 width = 0;
+ png_uint_32 height = 0;
int bit_depth = 0;
int color_type = 0;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index bf0bbfc683..b6db71a752 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -7,10 +7,7 @@
#include <chrono>
#include <experimental/optional>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-variable"
-// Check sqlite3 library version.
-const static bool sqliteVersionCheck = []() {
+const static bool sqliteVersionCheck __attribute__((unused)) = []() {
if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) {
char message[96];
snprintf(message, 96,
@@ -21,7 +18,6 @@ const static bool sqliteVersionCheck = []() {
return true;
}();
-#pragma GCC diagnostic pop
namespace mapbox {
namespace sqlite {
@@ -222,7 +218,9 @@ void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool ret
bindBlob(offset, value.data(), value.size(), retain);
}
-template <> void Statement::bind(int offset, std::chrono::system_clock::time_point value) {
+template <>
+void Statement::bind(
+ int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) {
assert(stmt);
check(sqlite3_bind_int64(stmt, offset, std::chrono::system_clock::to_time_t(value)));
}
@@ -235,7 +233,10 @@ template <> void Statement::bind(int offset, optional<std::string> value) {
}
}
-template <> void Statement::bind(int offset, optional<std::chrono::system_clock::time_point> value) {
+template <>
+void Statement::bind(
+ int offset,
+ optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) {
if (!value) {
bind(offset, nullptr);
} else {
@@ -287,9 +288,12 @@ template <> std::vector<uint8_t> Statement::get(int offset) {
return { begin, end };
}
-template <> std::chrono::system_clock::time_point Statement::get(int offset) {
+template <>
+std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
+Statement::get(int offset) {
assert(stmt);
- return std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt, offset));
+ return std::chrono::time_point_cast<std::chrono::seconds>(
+ std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt, offset)));
}
template <> optional<int64_t> Statement::get(int offset) {
@@ -319,12 +323,15 @@ template <> optional<std::string> Statement::get(int offset) {
}
}
-template <> optional<std::chrono::system_clock::time_point> Statement::get(int offset) {
+template <>
+optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>
+Statement::get(int offset) {
assert(stmt);
if (sqlite3_column_type(stmt, offset) == SQLITE_NULL) {
- return optional<std::chrono::system_clock::time_point>();
+ return {};
} else {
- return get<std::chrono::system_clock::time_point>(offset);
+ return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>(
+ offset);
}
}
@@ -338,5 +345,40 @@ void Statement::clearBindings() {
sqlite3_clear_bindings(stmt);
}
+Transaction::Transaction(Database& db_, Mode mode)
+ : db(db_) {
+ switch (mode) {
+ case Deferred:
+ db.exec("BEGIN DEFERRED TRANSACTION");
+ break;
+ case Immediate:
+ db.exec("BEGIN IMMEDIATE TRANSACTION");
+ break;
+ case Exclusive:
+ db.exec("BEGIN EXCLUSIVE TRANSACTION");
+ break;
+ }
+}
+
+Transaction::~Transaction() {
+ if (needRollback) {
+ try {
+ rollback();
+ } catch (...) {
+ // Ignore failed rollbacks in destructor.
+ }
+ }
+}
+
+void Transaction::commit() {
+ needRollback = false;
+ db.exec("COMMIT TRANSACTION");
+}
+
+void Transaction::rollback() {
+ needRollback = false;
+ db.exec("ROLLBACK TRANSACTION");
+}
+
} // namespace sqlite
} // namespace mapbox
diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp
index abe83a2d44..57ee18e9f3 100644
--- a/platform/default/sqlite3.hpp
+++ b/platform/default/sqlite3.hpp
@@ -88,5 +88,29 @@ private:
sqlite3_stmt *stmt = nullptr;
};
+class Transaction {
+private:
+ Transaction(const Transaction&) = delete;
+ Transaction(Transaction&&) = delete;
+ Transaction& operator=(const Transaction&) = delete;
+
+public:
+ enum Mode {
+ Deferred,
+ Immediate,
+ Exclusive
+ };
+
+ Transaction(Database&, Mode = Deferred);
+ ~Transaction();
+
+ void commit();
+ void rollback();
+
+private:
+ Database& db;
+ bool needRollback = true;
+};
+
}
}
diff --git a/platform/default/webp_reader.cpp b/platform/default/webp_reader.cpp
index 37d23da110..5b0eaf4741 100644
--- a/platform/default/webp_reader.cpp
+++ b/platform/default/webp_reader.cpp
@@ -15,8 +15,11 @@ PremultipliedImage decodeWebP(const uint8_t* data, size_t size) {
throw std::runtime_error("failed to retrieve WebP basic header information");
}
- std::unique_ptr<uint8_t[]> webp(WebPDecodeRGBA(data, size, &width, &height));
- if (!webp) {
+ int stride = width * 4;
+ size_t webpSize = stride * height;
+ auto webp = std::make_unique<uint8_t[]>(webpSize);
+
+ if (!WebPDecodeRGBAInto(data, size, webp.get(), webpSize, stride)) {
throw std::runtime_error("failed to decode WebP data");
}
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index c0eaa9b7f5..2b34571d1c 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -1,6 +1,38 @@
# Changelog for Mapbox iOS SDK
-Mapbox welcomes participation and contributions from everyone. If you’d like to do so please see [CONTRIBUTING.md](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
+Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
+
+## master
+
+- Applications linking against the SDK static framework no longer need to add `-ObjC` to the Other Linker Flags (`OTHER_LDFLAGS`) build setting. If you previously added this flag solely for this SDK, removing the flag may potentially reduce the overall size of your application. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+- Removed the `armv7s` slice from the SDK to reduce its size. iPhone 5 and iPhone 5c automatically use the `armv7` slice instead. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+- MGLPointAnnotation and custom MGLAnnotation implementations (but not MGLMultiPoint) can be backed by an MGLAnnotationView instead of an MGLAnnotationImage. MGLAnnotationView is a subclass of UIView, so you can use Core Animation and other familiar technologies with it. To associate an MGLAnnotation with an MGLAnnotationView, implement `-mapView:viewForAnnotation:` in your MGLMapViewDelegate class. ([#4801](https://github.com/mapbox/mapbox-gl-native/pull/4801))
+- The user dot now moves smoothly between user location updates while user location tracking is disabled. ([#1582](https://github.com/mapbox/mapbox-gl-native/pull/1582))
+- An MGLAnnotation can be relocated by changing its `coordinate` property in a KVO-compliant way. An MGLMultiPoint cannot be relocated. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
+- Setting the `image` property of an MGLAnnotationImage to `nil` resets it to the default red pin image and reclaims resources that can be used to customize additional annotations. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
+- Added methods to MGLMapView for obtaining the underlying map data rendered by the current style, along with additional classes to represent complex geometry in that data. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+- An MGLPolygon can now have interior polygons, representing holes knocked out of the overall shape. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+- `MGLOfflinePackProgress` now indicates how many tiles have been downloaded and how much space they take up. ([#4874](https://github.com/mapbox/mapbox-gl-native/pull/4874))
+- The compass, user dot, and visible annotations are now accessible to VoiceOver users. ([#1496](https://github.com/mapbox/mapbox-gl-native/pull/1496))
+- Fixed an issue (speculatively) where the tile cache could be included in iCloud backups. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124))
+- Improved performance viewing regions with large landcover polygons when viewing a style that uses the Mapbox Streets source. ([#2444](https://github.com/mapbox/mapbox-gl-native/pull/2444))
+- Fixed a memory leak when using raster resources. ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
+- The SDK is now localizable. No localizations are currently provided, other than English, but if you need a particular localization, you can install the SDK manually and drop a .lproj folder into the framework. ([#4783](https://github.com/mapbox/mapbox-gl-native/pull/4783))
+- Fixed an issue preventing KVO change notifications from being generated on MGLMapView’s `userTrackingMode` key path when `-setUserTrackingMode:animated:` is called. ([#4724](https://github.com/mapbox/mapbox-gl-native/pull/4724))
+- Rendering now occurs on the main thread, fixing a hang when calling `-[MGLMapView styleURL]` before the map view has fully loaded or while the application is in the background. ([#2909](https://github.com/mapbox/mapbox-gl-native/pull/2909))
+- Improved responsiveness when zooming in then immediately panning around. ([#4595](https://github.com/mapbox/mapbox-gl-native/pull/4595))
+- Fixed a crash setting MGLMapView’s `userLocationVerticalAlignment` property before a user location update has occurred. ([#5274](https://github.com/mapbox/mapbox-gl-native/issues/5274))
+- Added category methods on NSValue for converting to and from the structure types defined in MGLGeometry.h. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
+- Added NSFormatter subclasses for converting geographic coordinates and directions into display strings. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
+- Added a new method, `-[MGLMapView cameraThatFitsCoordinateBounds:]`, to get a camera that you can pass into `-setCamera:` that fits the given coordinate bounds. ([#4790](https://github.com/mapbox/mapbox-gl-native/pull/4790))
+- Added a `-reloadStyle:` action to MGLMapView to force a reload of the current style. ([#4728](https://github.com/mapbox/mapbox-gl-native/pull/4728))
+- A more specific user agent string is now sent with style and tile requests. ([#4012](https://github.com/mapbox/mapbox-gl-native/pull/4012))
+- Mapbox Telemetry is automatically disabled while the host application is running in the iOS Simulator. ([#4726](https://github.com/mapbox/mapbox-gl-native/pull/4726))
+- Suppressed “Unable to make space for entry” console spew. ([#4708](https://github.com/mapbox/mapbox-gl-native/pull/4708))
+- Removed unused SVG files from the SDK’s resource bundle. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+- Deprecated `-[MGLMapView emptyMemoryCache]`. ([#4725](https://github.com/mapbox/mapbox-gl-native/pull/4725))
+- Added `MGLCoordinateInCoordinateBounds()`, a function that tests whether or not a coordinate is in a given bounds. ([#5053](https://github.com/mapbox/mapbox-gl-native/pull/5053))
+- Added a new option to `MGLMapDebugMaskOptions`, `MGLMapDebugWireframesMask`, that shows wireframes instead of the usual rendered output. ([#4359](https://github.com/mapbox/mapbox-gl-native/pull/4359))
## 3.2.3
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index 15693eadb6..d771f7541b 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -2,55 +2,117 @@
This document explains how to build the Mapbox iOS SDK from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox iOS SDK.
-### Requirements
+## Requirements
-The Mapbox iOS SDK and iosapp demo application build against the iOS 7.0 SDK and require Xcode on a computer running OS X.
+The Mapbox iOS SDK and iosapp demo application build against the iOS 7.0 SDK. The SDK is intended to run on iOS 7.0 and above, while iosapp is intended to run on iOS 8.0 and above due to the use of a dynamic framework. Both require Xcode on a computer running OS X.
-### Build
+## Building the SDK
-[Install core dependencies](../../INSTALL.md), then run
+Make sure that you have the [core dependencies](../../INSTALL.md) installed.
- make iproj
+Create and open an Xcode workspace that includes both the SDK source and some Objective-C test applications by running:
-Which will create and open an Xcode project that can build the entire library from source, as well as an Objective-C test app. Before building, use the scheme picker button in the toolbar to change the scheme from “All (ios project)” to “iosapp” and the destination from “My Mac” to one of the simulators or connected devices listed in the menu.
+```bash
+make iproj
+```
-If you don't have an Apple Developer account, change the destination from "My Mac" to a simulator such as "iPhone 6" before you run and build the app.
+### Xcode schemes
-`ipackage` is only one of several available `make` build targets. Others include:
+Before building, use the scheme picker button in the toolbar to change the scheme to **iosapp** and the destination to one of the simulators or connected devices listed in the menu.
-* `make ipackage` builds both dynamic and static frameworks in the Release configuration for devices and the iOS Simulator.
+**iosapp** is only one of several shared schemes. Others include:
+
+* **CI** builds exactly what gets built for continuous integration on Bitrise.
+* **bench** is a simple benchmarking application. For more consistent results between runs, run `platform/ios/benchmark/assets/{glyphs,tiles}/download.sh` to download any necessary resources.
+* **dynamic** builds the SDK as a dynamic framework.
+* **static** builds the SDK as a static library and separate resource bundle.
+* **dynamic+static** is a combination of the **dynamic** and **static** schemes.
+
+If you don’t have an Apple Developer account, change the destination to a simulator such as “iPhone 6s” before you run and build the app.
+
+### Packaging builds
+
+Install [jazzy](https://github.com/realm/jazzy) for generating API documentation:
+
+```bash
+[sudo] gem install jazzy
+```
+
+Build and package the SDK by using one of the following commands:
+
+* `make ipackage` builds both dynamic and static frameworks in the Debug configuration for devices and the iOS Simulator.
+* `make iframework` builds a dynamic framework in the Debug configuration for devices and the iOS Simulator. The CocoaPods pod downloads the output of this target.
* `make ipackage-sim` builds a dynamic framework in the Debug configuration for the iOS simulator. This is the fastest target.
-* `make ipackage-strip` builds both dynamic and static frameworks in the Release configuration, stripped of debug symbols, for devices and the iOS Simulator.
-* `make iframework` builds a dynamic framework in the Release configuration for devices and the iOS Simulator. The CocoaPods pod downloads the output of this target.
+* `make ipackage-strip` builds both dynamic and static frameworks in the Debug configuration, stripped of debug symbols, for devices and the iOS Simulator.
+* `make ifabric` builds a special static framework for compatibility with the Fabric Mac application.
You can customize the build output by passing the following arguments into the `make` invocation:
-* `BITCODE=NO` builds without Bitcode support.
+* `BUILDTYPE=Release` will optimize for distribution. Defaults to `Debug`.
* `BUILD_DEVICE=false` builds only for the iOS Simulator.
* `FORMAT=dynamic` builds only a dynamic framework. `FORMAT=static` builds only a static framework, for compatibility with iOS 7.x.
* `SYMBOLS=NO` strips the build output of any debug symbols, yielding much smaller binaries.
-### Access Tokens
+An example command that creates a dynamic framework suitable for eventual App Store distribution:
+
+```bash
+make iframework BUILDTYPE=Release SYMBOLS=NO
+```
+
+The products of these build commands can be found in the `build/ios/pkg` folder at the base of the repository.
+
+## Contributing
+
+### Adding a source code file
+
+To add an Objective-C header or implementation file to the iOS SDK:
+
+1. Add the file to the Headers or Compile Sources build phase, as appropriate, of both the “dynamic” and “static” targets. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
+1. _(Optional.)_ If it’s a public header, change its visibility from Project to Public and import it in [the iOS SDK’s umbrella header](./src/Mapbox.h).
+1. _(Optional.)_ If the file would also be used by the OS X SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion OS X document](../osx/DEVELOPING.md#adding-a-source-code-file) for further instructions.
+
+### Adding a resource
+
+To add a resource (such as an image, SSL certificate, property list, or strings table) to the iOS SDK:
+
+1. Add the header to the Copy Bundle Resources build phase of both the “dynamic” and “bundle” targets. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
+1. _(Optional.)_ If the resource would also be used by the OS X SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion OS X document](../osx/DEVELOPING.md#adding-a-resource) for further instructions.
+
+### Adding user-facing text
+
+To add or update text that the user may see in the iOS SDK:
-_The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)._
+1. Make sure the implementation file imports [NSBundle+MGLAdditions.h](../darwin/src/NSBundle+MGLAdditions.h).
+1. Use the `NSLocalizedStringWithDefaultValue()` macro:
+ * `key` is a unique identifier that won’t change if the user-facing text ever needs to change.
+ * `tbl` is `Foundation` in code shared between the iOS and OS X SDKs, or `nil` otherwise.
+ * `bundle` is `nil`; the redefined macro looks for the SDK bundle at runtime and ignores this argument.
+ * `val` is the English string.
+1. _(Optional.)_ When dealing with a number followed by a pluralized word, do not split the string. Instead, use a format string and make `val` ambiguous, like `%d file(s)`. Then pluralize for English in the appropriate [.stringsdict file](https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html). See [platform/darwin/resources/en.lproj/Foundation.stringsdict](../darwin/resources/en.lproj/Foundation.stringsdict) for an example. Localizers should do likewise for their languages.
+1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the OS X SDK’s strings tables.
-Set up the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`.
+## Testing
-![edit scheme](https://cloud.githubusercontent.com/assets/98601/5460702/c4610262-8519-11e4-873a-8597821da468.png)
+`make test-ios` builds and runs unit tests of cross-platform code as well as the SDK.
-![setting access token in Xcode scheme](https://cloud.githubusercontent.com/assets/162976/5349358/0a086f00-7f8c-11e4-8433-bdbaccda2b58.png)
+Before you can run unit tests of the cross-platform code on the command line, install ios-sim version 3.2.0 (not any other version):
-### Test
+```bash
+brew tap mapbox/homebrew-ios-sim-3
+brew install mapbox/homebrew-ios-sim-3/ios-sim
+```
-Run
+To instead run the cross-platform tests in Xcode instead of on the command line:
- make itest
+1. Run `make iproj` to set up the workspace.
+1. Change the scheme to “test (platform project)” and press Command-R to run core unit tests.
+1. Change the scheme to “CI” and press Command-U to run SDK integration tests.
-To run the included integration tests on the command line.
+## Access tokens
-If you want to run the tests in Xcode instead, first `make ipackage` to create a local static library version, then open `platform/ios/test/ios-tests.xcodeproj`, and lastly `Command + U` on the `Mapbox GL Tests` application target.
+The included applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). You will be prompted for this access token the first time you launch the demo or benchmarking application. In the iOS Simulator, you can paste your access token by pressing Command-V, holding down the left mouse button in the text field, and pressing the Paste button that appears.
-### Usage
+## Using iosapp
- Pan to move
- Pinch to zoom
diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md
index 416bbc6381..bdda38946d 100644
--- a/platform/ios/INSTALL.md
+++ b/platform/ios/INSTALL.md
@@ -1,16 +1,16 @@
# Integrating the Mapbox iOS SDK into your application
-This document explains how to build a development version of Mapbox iOS SDK for the purpose of incorporating it into your own Cocoa Touch application.
+This document explains how to build a development version of Mapbox iOS SDK for use in your own Cocoa Touch application. To use a production-ready version of the SDK, see the [Mapbox iOS SDK homepage](https://github.com/mapbox/ios-sdk).
### Requirements
-The Mapbox iOS SDK and iosapp demo application build against the iOS 7.0 SDK. They are intended to run on iOS 7.0 and above on the following devices and their simulators:
+The Mapbox iOS SDK application builds against the iOS 7.0 SDK. It is intended to run on iOS 7.0 and above on the following devices and their simulators:
* iPhone 4S and above (5, 5c, 5s, 6, 6 Plus)
* iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2)
* iPod touch 5th generation and above
-### Build
+### Building the SDK
1. [Install core dependencies](../../INSTALL.md).
@@ -26,7 +26,7 @@ The Mapbox iOS SDK and iosapp demo application build against the iOS 7.0 SDK. Th
- a `documentation` folder with HTML API documentation
- an example `Settings.bundle` containing an optional Mapbox Telemetry opt-out setting
-### Install
+### Installation
There are a few ways to install the Mapbox iOS SDK:
@@ -36,7 +36,7 @@ Currently, until [#1437](https://github.com/mapbox/mapbox-gl-native/issues/1437)
1. Zip up the build product.
- ```
+ ```bash
cd build/ios/pkg/
ZIP=mapbox-ios-sdk.zip
rm -f ../${ZIP}
@@ -64,14 +64,13 @@ Currently, until [#1437](https://github.com/mapbox/mapbox-gl-native/issues/1437)
1. Run `pod update` to grab the newly-built library.
-
-
-##### Testing Pre-Releases with CocoaPods
+##### Testing pre-releases with CocoaPods
To test pre-releases and/or betas, you can reference the pre-release like so in your Podfile:
- pod 'Mapbox-iOS-SDK', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/<insert branch or tag>/ios/Mapbox-iOS-SDK.podspec'
-
+```rb
+pod 'Mapbox-iOS-SDK', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/<insert branch or tag>/ios/Mapbox-iOS-SDK.podspec'
+```
#### Dynamic framework
@@ -79,7 +78,7 @@ This is the recommended workflow for manually integrating the SDK into an applic
1. Build from source manually per above.
-1. Open the project editor and select your application target. Drag `build/ios/pkg/dynamic/Mapbox.framework` into the “Embedded Binaries” section of the General tab. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
+1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework from the `build/ios/pkg/dynamic/` directory into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
1. In the Build Phases tab, click the + button at the top and select “New Run Script Phase”. Enter the following code into the script text field:
@@ -95,38 +94,42 @@ If your application targets iOS 7.x, you’ll need to install the static framewo
1. Build from source manually per above.
-1. Drag the Mapbox.bundle and Mapbox.framework files in `build/ios/pkg/static` into the Project navigator, checking "Copy items if needed". It should happen automatically, but ensure that:
-
- - `Mapbox.framework` is listed in your `Link Binary With Libraries` build phase.
- - Your *Framework Search Paths* (`FRAMEWORK_SEARCH_PATHS`) build setting includes the directory that contains `Mapbox.framework`. For most projects, the default value of `$(inherited) $(PROJECT_DIR)` should be sufficient.
- - `Mapbox.bundle` is in your target's *Copy Bundle Resources* build phase.
+1. Drag the Mapbox.bundle and Mapbox.framework from the `build/ios/pkg/static/` directory into the Project navigator. In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish. Open the project editor and select your application target to verify that the following changes occurred automatically:
-1. **Optional** As [noted in the documentation](https://www.mapbox.com/ios-sdk/#telemetry_opt_out) you may use the provided Settings.bundle to provide a Telemetry opt out for users. To use the provided file, drag the `Settings.bundle` file in `build/ios/pkg` into the Project navigator, checking "Copy items if needed". It should happen automatically, but ensure that:
+ - In the General tab, Mapbox.framework is listed in the “Linked Frameworks and Libraries” section.
+ - In the Build Settings tab, the “Framework Search Paths” (`FRAMEWORK_SEARCH_PATHS`) build setting includes the directory that contains Mapbox.framework. For most projects, the default value of `$(inherited) $(PROJECT_DIR)` should be sufficient.
+ - In the Build Phases tab, Mapbox.bundle is listed in the “Copy Bundle Resources” build phase.
- - `Settings.bundle` is in your target's *Copy Bundle Resources* build phase.
+1. Back in the General tab, add the following Cocoa Touch frameworks and libraries to the “Linked Frameworks and Libraries” section:
-1. Add the following Cocoa Touch frameworks and libraries to the “Linked Frameworks and Libraries” section:
+ - GLKit.framework
+ - ImageIO.framework
+ - MobileCoreServices.framework
+ - QuartzCore.framework
+ - SystemConfiguration.framework
+ - libc++.tbd
+ - libsqlite3.tbd
+ - libz.tbd
- - `GLKit.framework`
- - `ImageIO.framework`
- - `MobileCoreServices.framework`
- - `QuartzCore.framework`
- - `SystemConfiguration.framework`
- - `libc++.tbd`
- - `libsqlite3.tbd`
- - `libz.tbd`
+### Configuration
-1. In the Build Settings tab, add `-ObjC` to the “Other Linker Flags” (`OTHER_LDFLAGS`) build setting.
+1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom iOS Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
-### Use
+1. _(Optional)_ Mapbox Telemetry is a [powerful location analytics platform](https://www.mapbox.com/telemetry/) included in this SDK. By default, anonymized location and usage data is sent to Mapbox whenever the host application causes it to be gathered. This SDK provides users with a way to individually opt out of Mapbox Telemetry. You can also add this opt-out setting to your application’s Settings screen using a Settings bundle. An example Settings.bundle is available in the `build/ios/pkg/` directory; drag it into the Project navigator, checking “Copy items if needed” when prompted. In the project editor, verify that the following change occurred automatically:
-1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target. In the Info tab, set `MGLMapboxAccessToken` to your access token. You can obtain one from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+ - In the General tab, Settings.bundle is listed in the “Copy Bundle Resources” build phase.
-1. In a XIB or storyboard, add a View and set its custom class to `MGLMapView`. If you need to manipulate the map view programmatically, import the `Mapbox` module (Swift) or `Mapbox.h` umbrella header (Objective-C), then connect the map view to a new outlet in your view controller class. The resulting outlet declaration should look something like:
+### Usage
+
+In a storyboard or XIB, add a view to your view controller. (Drag View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically:
+
+1. Switch to the Assistant Editor.
+1. Import the `Mapbox` module.
+1. Connect the map view to a new outlet in your view controller class. (Control-drag from the map view in Interface Builder to a valid location in your view controller implementation.) The resulting outlet declaration should look something like this:
```objc
// ViewController.m
-#import <Mapbox/Mapbox.h>
+@import Mapbox;
@interface ViewController : UIViewController
@@ -143,7 +146,3 @@ class ViewController: UIViewController {
@IBOutlet var mapView: MGLMapView!
}
```
-
-### Troubleshooting
-
-On OS X, you can also try clearing the Xcode cache with `make clear_xcode_cache`. \ No newline at end of file
diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
index 5b3ef83113..3642d8b7de 100644
--- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'Mapbox-iOS-SDK'
- m.version = '3.2.3-symbols'
+ m.version = '3.3.0-beta.1-symbols'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.'
@@ -10,7 +10,7 @@ Pod::Spec.new do |m|
m.author = { 'Mapbox' => 'mobile@mapbox.com' }
m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png'
m.social_media_url = 'https://twitter.com/mapbox'
- m.documentation_url = 'https://www.mapbox.com/ios-sdk/'
+ m.documentation_url = 'https://www.mapbox.com/ios-sdk/api/'
m.source = {
:http => "https://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-ios-sdk-#{m.version.to_s}-dynamic.zip",
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index a93e002a55..da9213dba2 100644
--- a/platform/ios/Mapbox-iOS-SDK.podspec
+++ b/platform/ios/Mapbox-iOS-SDK.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'Mapbox-iOS-SDK'
- m.version = '3.2.3'
+ m.version = '3.3.0-beta.1'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.'
@@ -10,7 +10,7 @@ Pod::Spec.new do |m|
m.author = { 'Mapbox' => 'mobile@mapbox.com' }
m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png'
m.social_media_url = 'https://twitter.com/mapbox'
- m.documentation_url = 'https://www.mapbox.com/ios-sdk/'
+ m.documentation_url = 'https://www.mapbox.com/ios-sdk/api/'
m.source = {
:http => "https://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-ios-sdk-#{m.version.to_s}-dynamic.zip",
diff --git a/platform/ios/Mapbox.playground/Contents.swift b/platform/ios/Mapbox.playground/Contents.swift
new file mode 100644
index 0000000000..ed48116da9
--- /dev/null
+++ b/platform/ios/Mapbox.playground/Contents.swift
@@ -0,0 +1,158 @@
+import UIKit
+import XCPlayground
+import Mapbox
+
+let width: CGFloat = 700
+let height: CGFloat = 800
+
+class Responder: NSObject {
+ var mapView: MGLMapView?
+ func togglePitch(sender: UISwitch) {
+ let camera = mapView!.camera
+ camera.pitch = sender.on ? 60 : 0
+ mapView!.setCamera(camera, animated: false)
+ }
+}
+
+//: A control panel
+let panelWidth: CGFloat = 200
+let panel = UIView(frame: CGRect(x: width - panelWidth, y: 0, width: 200, height: 100))
+panel.alpha = 0.8
+panel.backgroundColor = UIColor.whiteColor()
+
+// Delete markers
+let deleteSwitchLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
+deleteSwitchLabel.adjustsFontSizeToFitWidth = true
+deleteSwitchLabel.text = "Delete Markers"
+let deleteMarkerSwitchView = UISwitch(frame: CGRect(x: panelWidth - panelWidth / 2.0, y:0, width: 100, height: 50))
+panel.addSubview(deleteSwitchLabel)
+panel.addSubview(deleteMarkerSwitchView)
+
+// Hide markers
+let hideSwitchLabel = UILabel(frame: CGRect(x: 0, y: 30, width: 100, height: 30))
+hideSwitchLabel.adjustsFontSizeToFitWidth = true
+hideSwitchLabel.text = "Hide Markers"
+let hideMarkerSwitchView = UISwitch(frame: CGRect(x: panelWidth - panelWidth / 2.0, y: 30, width: 100, height: 50))
+panel.addSubview(hideSwitchLabel)
+panel.addSubview(hideMarkerSwitchView)
+
+// Pitch map
+let pitchLabel = UILabel(frame: CGRect(x: 0, y: 60, width: 100, height: 30))
+pitchLabel.text = "Pitch"
+let pitchSwitch = UISwitch(frame: CGRect(x: panelWidth-panelWidth / 2.0, y: 60, width: 100, height: 50))
+let responder = Responder()
+pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(_:)), forControlEvents: .ValueChanged)
+panel.addSubview(pitchLabel)
+panel.addSubview(pitchSwitch)
+
+//: # Mapbox Maps
+
+/*:
+ Put your access token into a plain text file called `token`. Then select the “token” placeholder below, go to Editor ‣ Insert File Literal, and select the `token` file.
+ */
+var accessToken = try String(contentsOfURL: <#token#>)
+MGLAccountManager.setAccessToken(accessToken)
+
+class PlaygroundAnnotationView: MGLAnnotationView {
+
+ override func prepareForReuse() {
+ hidden = hideMarkerSwitchView.on
+ }
+
+}
+
+//: Define a map delegate
+
+class MapDelegate: NSObject, MGLMapViewDelegate {
+
+ var annotationViewByAnnotation = [MGLPointAnnotation: PlaygroundAnnotationView]()
+
+ func mapView(mapView: MGLMapView, viewForAnnotation annotation: MGLAnnotation) -> MGLAnnotationView? {
+
+ var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("annotation") as? PlaygroundAnnotationView
+
+ if (annotationView == nil) {
+ let av = PlaygroundAnnotationView(reuseIdentifier: "annotation")
+ av.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
+ av.centerOffset = CGVector(dx: -15, dy: -15)
+ av.flat = true
+ let centerView = UIView(frame: CGRectInset(av.bounds, 3, 3))
+ centerView.backgroundColor = UIColor.whiteColor()
+ av.addSubview(centerView)
+ av.backgroundColor = UIColor.purpleColor()
+ annotationView = av
+ } else {
+ annotationView!.subviews.first?.backgroundColor = UIColor.greenColor()
+ }
+
+ annotationViewByAnnotation[annotation as! MGLPointAnnotation] = annotationView
+
+ return annotationView
+ }
+
+ func mapView(mapView: MGLMapView, didSelectAnnotation annotation: MGLAnnotation) {
+ let pointAnnotation = annotation as! MGLPointAnnotation
+ let annotationView: PlaygroundAnnotationView = annotationViewByAnnotation[pointAnnotation]!
+
+ for view in annotationViewByAnnotation.values {
+ view.layer.zPosition = -1
+ }
+
+ annotationView.layer.zPosition = 1
+
+ UIView.animateWithDuration(1.25, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.6, options: .CurveEaseOut, animations: {
+ annotationView.transform = CGAffineTransformMakeScale(1.8, 1.8)
+ }) { _ in
+ annotationView.transform = CGAffineTransformMakeScale(1, 1)
+
+ if deleteMarkerSwitchView.on {
+ mapView.removeAnnotation(pointAnnotation)
+ return
+ }
+
+ if hideMarkerSwitchView.on {
+ annotationView.hidden = true
+ }
+ }
+ }
+
+ func handleTap(press: UILongPressGestureRecognizer) {
+ let mapView: MGLMapView = press.view as! MGLMapView
+
+ if (press.state == .Recognized) {
+ let coordiante: CLLocationCoordinate2D = mapView.convertPoint(press.locationInView(mapView), toCoordinateFromView: mapView)
+ let annotation = MGLPointAnnotation()
+ annotation.title = "Dropped Marker"
+ annotation.coordinate = coordiante
+ mapView.addAnnotation(annotation)
+ mapView.showAnnotations([annotation], animated: true)
+ }
+ }
+
+}
+
+//: Create a map and its delegate
+
+let lat: CLLocationDegrees = 37.174057
+let lng: CLLocationDegrees = -104.490984
+let centerCoordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
+
+let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: width, height: height))
+mapView.frame = CGRect(x: 0, y: 0, width: width, height: height)
+
+XCPlaygroundPage.currentPage.liveView = mapView
+
+let mapDelegate = MapDelegate()
+mapView.delegate = mapDelegate
+responder.mapView = mapView
+
+let tapGesture = UILongPressGestureRecognizer(target: mapDelegate, action: #selector(mapDelegate.handleTap))
+mapView.addGestureRecognizer(tapGesture)
+
+//: Zoom in to a location
+
+mapView.setCenterCoordinate(centerCoordinate, zoomLevel: 12, animated: false)
+
+//: Add control panel
+
+mapView.addSubview(panel)
diff --git a/platform/ios/Mapbox.playground/contents.xcplayground b/platform/ios/Mapbox.playground/contents.xcplayground
new file mode 100644
index 0000000000..35968656f5
--- /dev/null
+++ b/platform/ios/Mapbox.playground/contents.xcplayground
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<playground version='5.0' target-platform='ios' display-mode='raw'>
+ <timeline fileName='timeline.xctimeline'/>
+</playground> \ No newline at end of file
diff --git a/platform/ios/Mapbox.playground/timeline.xctimeline b/platform/ios/Mapbox.playground/timeline.xctimeline
new file mode 100644
index 0000000000..bf468afeca
--- /dev/null
+++ b/platform/ios/Mapbox.playground/timeline.xctimeline
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Timeline
+ version = "3.0">
+ <TimelineItems>
+ </TimelineItems>
+</Timeline>
diff --git a/platform/ios/WorkspaceSettings.xcsettings b/platform/ios/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000000..a2d959210c
--- /dev/null
+++ b/platform/ios/WorkspaceSettings.xcsettings
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BuildLocationStyle</key>
+ <string>UseAppPreferences</string>
+ <key>CustomBuildLocationType</key>
+ <string>RelativeToDerivedData</string>
+ <key>DerivedDataCustomLocation</key>
+ <string>../../build</string>
+ <key>DerivedDataLocationStyle</key>
+ <string>WorkspaceRelativePath</string>
+ <key>IssueFilterStyle</key>
+ <string>ShowActiveSchemeOnly</string>
+ <key>LiveSourceIssuesEnabled</key>
+ <true/>
+</dict>
+</plist>
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..c1b88669e7
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,86 @@
+{
+ "images" : [
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-Spotlight-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-29@2x-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-40@2x-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-83.5@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png
new file mode 100644
index 0000000000..1e609d3281
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png
new file mode 100644
index 0000000000..1e609d3281
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png
new file mode 100644
index 0000000000..c7d5aea4eb
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-40.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40.png
index eca13393e6..eca13393e6 100644
--- a/platform/ios/app/img/Icon-40.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png
new file mode 100644
index 0000000000..277535538a
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-40@2x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
index 070d037539..070d037539 100644
--- a/platform/ios/app/img/Icon-40@2x.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-60@2x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
index b7f25955f5..b7f25955f5 100644
--- a/platform/ios/app/img/Icon-60@2x.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-60@3x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
index b00d479839..b00d479839 100644
--- a/platform/ios/app/img/Icon-60@3x.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-76.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76.png
index 895b4a1761..895b4a1761 100644
--- a/platform/ios/app/img/Icon-76.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-76@2x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
index 7bc5208976..7bc5208976 100644
--- a/platform/ios/app/img/Icon-76@2x.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
new file mode 100644
index 0000000000..0082319b1e
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-Small.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
index aecbbc8a1d..aecbbc8a1d 100644
--- a/platform/ios/app/img/Icon-Small.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Binary files differ
diff --git a/platform/ios/app/img/Icon-Spotlight-40@3x.png b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png
index a3789dcb78..a3789dcb78 100644
--- a/platform/ios/app/img/Icon-Spotlight-40@3x.png
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/Contents.json b/platform/ios/app/Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/Contents.json b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/Contents.json
new file mode 100644
index 0000000000..957660ab78
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingHeadingMask.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingHeadingMask@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingHeadingMask@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/img/TrackingHeadingMask.png b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask.png
index 9ec0567a3f..9ec0567a3f 100644
--- a/platform/ios/app/img/TrackingHeadingMask.png
+++ b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingHeadingMask@2x.png b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@2x.png
index 0df8ccc229..0df8ccc229 100644
--- a/platform/ios/app/img/TrackingHeadingMask@2x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@2x.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingHeadingMask@3x.png b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@3x.png
index e4b93e42e7..e4b93e42e7 100644
--- a/platform/ios/app/img/TrackingHeadingMask@3x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingHeadingMask.imageset/TrackingHeadingMask@3x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/Contents.json b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/Contents.json
new file mode 100644
index 0000000000..08cd551fc7
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationMask.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationMask@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationMask@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/img/TrackingLocationMask.png b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask.png
index bb7348c482..bb7348c482 100644
--- a/platform/ios/app/img/TrackingLocationMask.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingLocationMask@2x.png b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@2x.png
index 35c5a293ec..35c5a293ec 100644
--- a/platform/ios/app/img/TrackingLocationMask@2x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@2x.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingLocationMask@3x.png b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@3x.png
index af523975a5..af523975a5 100644
--- a/platform/ios/app/img/TrackingLocationMask@3x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationMask.imageset/TrackingLocationMask@3x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/Contents.json b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/Contents.json
new file mode 100644
index 0000000000..3ccbab44aa
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/Contents.json
@@ -0,0 +1,26 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationOffMask.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationOffMask@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "TrackingLocationOffMask@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/img/TrackingLocationOffMask.png b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask.png
index 87b26c5710..87b26c5710 100644
--- a/platform/ios/app/img/TrackingLocationOffMask.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingLocationOffMask@2x.png b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@2x.png
index 232d534b41..232d534b41 100644
--- a/platform/ios/app/img/TrackingLocationOffMask@2x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@2x.png
Binary files differ
diff --git a/platform/ios/app/img/TrackingLocationOffMask@3x.png b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@3x.png
index b609fbe84e..b609fbe84e 100644
--- a/platform/ios/app/img/TrackingLocationOffMask@3x.png
+++ b/platform/ios/app/Assets.xcassets/TrackingLocationOffMask.imageset/TrackingLocationOffMask@3x.png
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
new file mode 100644
index 0000000000..1eeddba9b9
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
@@ -0,0 +1,25 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "settings.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "settings@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/app/img/settings.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
index 5d7643eef5..5d7643eef5 100644
--- a/platform/ios/app/img/settings.png
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
Binary files differ
diff --git a/platform/ios/app/img/settings@2x.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
index 2bb9f0ebad..2bb9f0ebad 100644
--- a/platform/ios/app/img/settings@2x.png
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png b/platform/ios/app/Default-568h@2x.png
index 0891b7aabf..0891b7aabf 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png
+++ b/platform/ios/app/Default-568h@2x.png
Binary files differ
diff --git a/platform/ios/app/Info.plist b/platform/ios/app/Info.plist
new file mode 100644
index 0000000000..3602ab7964
--- /dev/null
+++ b/platform/ios/app/Info.plist
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mapbox.MapboxGL</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>0.0.2</string>
+ <key>CFBundleSignature</key>
+ <string>MBGL</string>
+ <key>CFBundleVersion</key>
+ <string>7877</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>© 2014–2016 Mapbox</string>
+ <key>NSLocationAlwaysUsageDescription</key>
+ <string>The map will ALWAYS display the user’s location.</string>
+ <key>NSLocationWhenInUseUsageDescription</key>
+ <string>The map will display the user’s location.</string>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/platform/ios/app/LaunchScreen.storyboard b/platform/ios/app/LaunchScreen.storyboard
new file mode 100644
index 0000000000..323bd43177
--- /dev/null
+++ b/platform/ios/app/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/platform/ios/app/MBXAnnotationView.h b/platform/ios/app/MBXAnnotationView.h
new file mode 100644
index 0000000000..78dfe17699
--- /dev/null
+++ b/platform/ios/app/MBXAnnotationView.h
@@ -0,0 +1,7 @@
+#import <Mapbox/Mapbox.h>
+
+@interface MBXAnnotationView : MGLAnnotationView
+
+@property (nonatomic) UIColor *centerColor;
+
+@end
diff --git a/platform/ios/app/MBXAnnotationView.m b/platform/ios/app/MBXAnnotationView.m
new file mode 100644
index 0000000000..890881a316
--- /dev/null
+++ b/platform/ios/app/MBXAnnotationView.m
@@ -0,0 +1,28 @@
+#import "MBXAnnotationView.h"
+
+@interface MBXAnnotationView ()
+
+@property (nonatomic) UIView *centerView;
+
+@end
+
+@implementation MBXAnnotationView
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ if (!self.centerView) {
+ self.backgroundColor = [UIColor blueColor];
+ self.centerView = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 1.0, 1.0)];
+ self.centerView.backgroundColor = self.centerColor;
+ [self addSubview:self.centerView];
+ }
+}
+
+- (void)setCenterColor:(UIColor *)centerColor {
+ if (![_centerColor isEqual:centerColor]) {
+ _centerColor = centerColor;
+ self.centerView.backgroundColor = centerColor;
+ }
+}
+
+@end
diff --git a/platform/ios/app/MBXAppDelegate.h b/platform/ios/app/MBXAppDelegate.h
index da081fdcd5..8145d3b8ca 100644
--- a/platform/ios/app/MBXAppDelegate.h
+++ b/platform/ios/app/MBXAppDelegate.h
@@ -1,5 +1,7 @@
#import <UIKit/UIKit.h>
+extern NSString * const MBXMapboxAccessTokenDefaultsKey;
+
@interface MBXAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
diff --git a/platform/ios/app/MBXAppDelegate.m b/platform/ios/app/MBXAppDelegate.m
index 9ceee7cad0..c2834bfa7f 100644
--- a/platform/ios/app/MBXAppDelegate.m
+++ b/platform/ios/app/MBXAppDelegate.m
@@ -2,6 +2,8 @@
#import "MBXViewController.h"
#import <Mapbox/Mapbox.h>
+NSString * const MBXMapboxAccessTokenDefaultsKey = @"MBXMapboxAccessToken";
+
@implementation MBXAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
@@ -12,14 +14,12 @@
if (accessToken) {
// Store to preferences so that we can launch the app later on without having to specify
// token.
- [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:@"access_token"];
+ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:MBXMapboxAccessTokenDefaultsKey];
} else {
// Try to retrieve from preferences, maybe we've stored them there previously and can reuse
// the token.
- accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
+ accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MBXMapboxAccessTokenDefaultsKey];
}
- if ( ! accessToken) NSLog(@"No access token set. Mapbox vector tiles won't work.");
-
[MGLAccountManager setAccessToken:accessToken];
}
diff --git a/platform/ios/app/MBXCustomCalloutView.m b/platform/ios/app/MBXCustomCalloutView.m
index 8f9bd8ed40..9edc00f6e9 100644
--- a/platform/ios/app/MBXCustomCalloutView.m
+++ b/platform/ios/app/MBXCustomCalloutView.m
@@ -40,6 +40,11 @@ static CGFloat const tipWidth = 10.0;
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated
{
+ if ([self.delegate respondsToSelector:@selector(calloutViewWillAppear:)])
+ {
+ [self.delegate performSelector:@selector(calloutViewWillAppear:) withObject:self];
+ }
+
[view addSubview:self];
// prepare title label
if ([self.representedObject respondsToSelector:@selector(title)])
@@ -54,6 +59,11 @@ static CGFloat const tipWidth = 10.0;
CGFloat frameOriginY = rect.origin.y - frameHeight;
self.frame = CGRectMake(frameOriginX, frameOriginY,
frameWidth, frameHeight);
+
+ if ([self.delegate respondsToSelector:@selector(calloutViewDidAppear:)])
+ {
+ [self.delegate performSelector:@selector(calloutViewDidAppear:) withObject:self];
+ }
}
- (void)dismissCalloutAnimated:(BOOL)animated
diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m
index 8aad4fbbf8..008d843b1e 100644
--- a/platform/ios/app/MBXOfflinePacksTableViewController.m
+++ b/platform/ios/app/MBXOfflinePacksTableViewController.m
@@ -24,6 +24,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac
- (void)applyToMapView:(MGLMapView *)mapView {
mapView.styleURL = self.styleURL;
[mapView setVisibleCoordinateBounds:self.bounds];
+ mapView.zoomLevel = MIN(self.maximumZoomLevel, MAX(self.minimumZoomLevel, mapView.zoomLevel));
}
@end
@@ -172,7 +173,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac
numberStyle:NSNumberFormatterDecimalStyle];
}
if (progress.maximumResourcesExpected > progress.countOfResourcesExpected) {
- expectedString = [@"at least " stringByAppendingString:expectedString];
+ expectedString = [NSString stringWithFormat:@"at least %@", expectedString];
}
statusString = [NSString stringWithFormat:@"Downloading %@ of %@ resources (%@ so far)…",
completedString, expectedString, byteCountString];
@@ -246,7 +247,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac
NSError *error = notification.userInfo[MGLOfflinePackErrorUserInfoKey];
NSAssert([error isKindOfClass:[NSError class]], @"MGLOfflineStorage notification has a non-error error.");
- NSString *message = [NSString stringWithFormat:@"iosapp encountered an error while downloading the offline pack “%@”: %@", pack.name, error.localizedFailureReason];
+ NSString *message = [NSString stringWithFormat:@"Mapbox GL encountered an error while downloading the offline pack “%@”: %@", pack.name, error.localizedFailureReason];
if (error.code == MGLErrorCodeConnectionFailed) {
NSLog(@"%@", message);
} else {
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
new file mode 100644
index 0000000000..8317aee1d9
--- /dev/null
+++ b/platform/ios/app/MBXViewController.m
@@ -0,0 +1,696 @@
+#import "MBXViewController.h"
+
+#import "MBXAppDelegate.h"
+#import "MBXCustomCalloutView.h"
+#import "MBXOfflinePacksTableViewController.h"
+#import "MBXAnnotationView.h"
+
+#import <Mapbox/Mapbox.h>
+
+#import <objc/runtime.h>
+
+static const CLLocationCoordinate2D WorldTourDestinations[] = {
+ { .latitude = 38.9131982, .longitude = -77.0325453144239 },
+ { .latitude = 37.7757368, .longitude = -122.4135302 },
+ { .latitude = 12.9810816, .longitude = 77.6368034 },
+ { .latitude = -13.15589555, .longitude = -74.2178961777998 },
+};
+
+static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXViewControllerAnnotationViewReuseIdentifer";
+
+@interface MBXDroppedPinAnnotation : MGLPointAnnotation
+@end
+
+@implementation MBXDroppedPinAnnotation
+@end
+
+@interface MBXCustomCalloutAnnotation : MGLPointAnnotation
+@end
+
+@implementation MBXCustomCalloutAnnotation
+@end
+
+@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
+
+@property (nonatomic) IBOutlet MGLMapView *mapView;
+@property (nonatomic) NSInteger styleIndex;
+@property (nonatomic) BOOL debugLoggingEnabled;
+
+@end
+
+@implementation MBXViewController
+{
+ BOOL _isTouringWorld;
+}
+
+#pragma mark - Setup
+
++ (void)initialize
+{
+ if (self == [MBXViewController class])
+ {
+ [[NSUserDefaults standardUserDefaults] registerDefaults:@{
+ @"MBXUserTrackingMode": @(MGLUserTrackingModeNone),
+ @"MBXShowsUserLocation": @NO,
+ @"MBXDebug": @NO,
+ }];
+ }
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restoreState:) name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationWillTerminateNotification object:nil];
+
+ [self restoreState:nil];
+
+ self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
+}
+
+- (void)viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ if ([MGLAccountManager accessToken].length)
+ {
+ self.styleIndex = -1;
+ [self cycleStyles:self];
+ }
+ else
+ {
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Access Token" message:@"Enter your Mapbox access token to load Mapbox-hosted tiles and styles:" preferredStyle:UIAlertControllerStyleAlert];
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField)
+ {
+ textField.keyboardType = UIKeyboardTypeURL;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ }];
+
+ [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
+ UIAlertAction *OKAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
+ {
+ UITextField *textField = alertController.textFields.firstObject;
+ NSString *accessToken = textField.text;
+ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:MBXMapboxAccessTokenDefaultsKey];
+ [MGLAccountManager setAccessToken:accessToken];
+
+ self.styleIndex = -1;
+ [self cycleStyles:self];
+ [self.mapView reloadStyle:self];
+ }];
+ [alertController addAction:OKAction];
+
+ if ([alertController respondsToSelector:@selector(setPreferredAction:)])
+ {
+ alertController.preferredAction = OKAction;
+ }
+ [self presentViewController:alertController animated:YES completion:nil];
+ }
+}
+
+- (void)saveState:(__unused NSNotification *)notification
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSData *archivedCamera = [NSKeyedArchiver archivedDataWithRootObject:self.mapView.camera];
+ [defaults setObject:archivedCamera forKey:@"MBXCamera"];
+ [defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"];
+ [defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"];
+ [defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"];
+ [defaults synchronize];
+}
+
+- (void)restoreState:(__unused NSNotification *)notification
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSData *archivedCamera = [defaults objectForKey:@"MBXCamera"];
+ MGLMapCamera *camera = archivedCamera ? [NSKeyedUnarchiver unarchiveObjectWithData:archivedCamera] : nil;
+ if (camera)
+ {
+ self.mapView.camera = camera;
+ }
+ NSInteger uncheckedTrackingMode = [defaults integerForKey:@"MBXUserTrackingMode"];
+ if (uncheckedTrackingMode >= 0 &&
+ (NSUInteger)uncheckedTrackingMode >= MGLUserTrackingModeNone &&
+ (NSUInteger)uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse)
+ {
+ self.mapView.userTrackingMode = (MGLUserTrackingMode)uncheckedTrackingMode;
+ }
+ self.mapView.showsUserLocation = [defaults boolForKey:@"MBXShowsUserLocation"];
+ NSInteger uncheckedDebugMask = [defaults integerForKey:@"MBXDebugMask"];
+ if (uncheckedDebugMask >= 0)
+ {
+ self.mapView.debugMask = (MGLMapDebugMaskOptions)uncheckedDebugMask;
+ }
+}
+
+- (UIInterfaceOrientationMask)supportedInterfaceOrientations
+{
+ return UIInterfaceOrientationMaskAll;
+}
+
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(__unused id)sender {
+ if ([segue.identifier isEqualToString:@"ShowOfflinePacks"]) {
+ MBXOfflinePacksTableViewController *controller = [segue destinationViewController];
+ controller.mapView = self.mapView;
+ }
+}
+
+#pragma mark - Actions
+
+- (IBAction)showSettings:(__unused id)sender
+{
+ MGLMapDebugMaskOptions debugMask = self.mapView.debugMask;
+ UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Map Settings"
+ delegate:self
+ cancelButtonTitle:@"Cancel"
+ destructiveButtonTitle:nil
+ otherButtonTitles:
+ @"Reset Position",
+ ((debugMask & MGLMapDebugTileBoundariesMask)
+ ? @"Hide Tile Boundaries"
+ : @"Show Tile Boundaries"),
+ ((debugMask & MGLMapDebugTileInfoMask)
+ ? @"Hide Tile Info"
+ : @"Show Tile Info"),
+ ((debugMask & MGLMapDebugTimestampsMask)
+ ? @"Hide Tile Timestamps"
+ : @"Show Tile Timestamps"),
+ ((debugMask & MGLMapDebugCollisionBoxesMask)
+ ? @"Hide Collision Boxes"
+ : @"Show Collision Boxes"),
+ ((debugMask & MGLMapDebugWireframesMask)
+ ? @"Hide Wireframes"
+ : @"Show Wireframes"),
+ @"Add 100 Points",
+ @"Add 1,000 Points",
+ @"Add 10,000 Points",
+ @"Add Test Shapes",
+ @"Start World Tour",
+ @"Add Custom Callout Point",
+ @"Remove Annotations",
+ nil];
+
+ if (self.debugLoggingEnabled)
+ {
+ [sheet addButtonWithTitle:@"Print Telemetry Logfile"];
+ [sheet addButtonWithTitle:@"Delete Telemetry Logfile"];
+ }
+
+ [sheet showFromBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES];
+}
+
+- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
+{
+ if (buttonIndex == actionSheet.firstOtherButtonIndex)
+ {
+ [self.mapView resetPosition];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1)
+ {
+ self.mapView.debugMask ^= MGLMapDebugTileBoundariesMask;
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2)
+ {
+ self.mapView.debugMask ^= MGLMapDebugTileInfoMask;
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 3)
+ {
+ self.mapView.debugMask ^= MGLMapDebugTimestampsMask;
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4)
+ {
+ self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask;
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 5)
+ {
+ self.mapView.debugMask ^= MGLMapDebugWireframesMask;
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 6)
+ {
+ [self parseFeaturesAddingCount:100];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7)
+ {
+ [self parseFeaturesAddingCount:1000];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8)
+ {
+ [self parseFeaturesAddingCount:10000];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 9)
+ {
+ // PNW triangle
+ //
+ CLLocationCoordinate2D triangleCoordinates[3] =
+ {
+ CLLocationCoordinate2DMake(44, -122),
+ CLLocationCoordinate2DMake(46, -122),
+ CLLocationCoordinate2DMake(46, -121)
+ };
+
+ MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3];
+
+ [self.mapView addAnnotation:triangle];
+
+ // Orcas Island hike
+ //
+ NSDictionary *hike = [NSJSONSerialization JSONObjectWithData:
+ [NSData dataWithContentsOfFile:
+ [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]]
+ options:0
+ error:nil];
+
+ NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"];
+
+ CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++)
+ {
+ polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]);
+ }
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates
+ count:[hikeCoordinatePairs count]];
+
+ [self.mapView addAnnotation:polyline];
+
+ free(polylineCoordinates);
+
+ // PA/NJ/DE polys
+ //
+ NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData:
+ [NSData dataWithContentsOfFile:
+ [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]]
+ options:0
+ error:nil];
+
+ for (NSDictionary *feature in threestates[@"features"])
+ {
+ NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"];
+
+ while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0];
+
+ CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++)
+ {
+ polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]);
+ }
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
+
+ [self.mapView addAnnotation:polygon];
+
+ free(polygonCoordinates);
+ }
+
+ CLLocationCoordinate2D innerCoordinates[] = {
+ CLLocationCoordinate2DMake(-5, -5),
+ CLLocationCoordinate2DMake(-5, 5),
+ CLLocationCoordinate2DMake(5, 5),
+ CLLocationCoordinate2DMake(5, -5),
+ };
+ MGLPolygon *innerPolygon = [MGLPolygon polygonWithCoordinates:innerCoordinates count:sizeof(innerCoordinates) / sizeof(innerCoordinates[0])];
+ CLLocationCoordinate2D outerCoordinates[] = {
+ CLLocationCoordinate2DMake(-10, -20),
+ CLLocationCoordinate2DMake(-10, 10),
+ CLLocationCoordinate2DMake(10, 10),
+ CLLocationCoordinate2DMake(10, -10),
+ };
+ MGLPolygon *outerPolygon = [MGLPolygon polygonWithCoordinates:outerCoordinates count:sizeof(outerCoordinates) / sizeof(outerCoordinates[0]) interiorPolygons:@[innerPolygon]];
+ [self.mapView addAnnotation:outerPolygon];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 10)
+ {
+ [self startWorldTour:actionSheet];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 11)
+ {
+ [self presentAnnotationWithCustomCallout];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 12)
+ {
+ [self.mapView removeAnnotations:self.mapView.annotations];
+ }
+ else if (buttonIndex == actionSheet.numberOfButtons - 2 && self.debugLoggingEnabled)
+ {
+ NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogfilePath] encoding:NSUTF8StringEncoding error:nil];
+ NSLog(@"%@", fileContents);
+ }
+ else if (buttonIndex == actionSheet.numberOfButtons - 1 && self.debugLoggingEnabled)
+ {
+ NSString *filePath = [self telemetryDebugLogfilePath];
+ if ([[NSFileManager defaultManager] isDeletableFileAtPath:filePath]) {
+ NSError *error;
+ BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
+ if (success) {
+ NSLog(@"Deleted telemetry log.");
+ } else {
+ NSLog(@"Error deleting telemetry log: %@", error.localizedDescription);
+ }
+ }
+ }
+}
+
+- (void)parseFeaturesAddingCount:(NSUInteger)featuresCount
+{
+ [self.mapView removeAnnotations:self.mapView.annotations];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
+ {
+ NSData *featuresData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"points" ofType:@"geojson"]];
+
+ id features = [NSJSONSerialization JSONObjectWithData:featuresData
+ options:0
+ error:nil];
+
+ if ([features isKindOfClass:[NSDictionary class]])
+ {
+ NSMutableArray *annotations = [NSMutableArray array];
+
+ for (NSDictionary *feature in features[@"features"])
+ {
+ CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([feature[@"geometry"][@"coordinates"][1] doubleValue],
+ [feature[@"geometry"][@"coordinates"][0] doubleValue]);
+ NSString *title = feature[@"properties"][@"NAME"];
+
+ MGLPointAnnotation *annotation = [MGLPointAnnotation new];
+ annotation.coordinate = coordinate;
+ annotation.title = title;
+
+ [annotations addObject:annotation];
+
+ if (annotations.count == featuresCount) break;
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^
+ {
+ [self.mapView addAnnotations:annotations];
+ [self.mapView showAnnotations:annotations animated:YES];
+ });
+ }
+ });
+}
+
+- (void)presentAnnotationWithCustomCallout
+{
+ [self.mapView removeAnnotations:self.mapView.annotations];
+
+ MBXCustomCalloutAnnotation *annotation = [[MBXCustomCalloutAnnotation alloc] init];
+ annotation.coordinate = CLLocationCoordinate2DMake(48.8533940, 2.3775439);
+ annotation.title = @"Custom Callout";
+
+ [self.mapView addAnnotation:annotation];
+ [self.mapView showAnnotations:@[annotation] animated:YES];
+}
+
+- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)longPress
+{
+ if (longPress.state == UIGestureRecognizerStateBegan)
+ {
+ CGPoint point = [longPress locationInView:longPress.view];
+ NSArray *features = [self.mapView visibleFeaturesAtPoint:point];
+ NSString *title;
+ for (id <MGLFeature> feature in features) {
+ if (!title) {
+ title = [feature attributeForKey:@"name_en"] ?: [feature attributeForKey:@"name"];
+ }
+ }
+
+ MBXDroppedPinAnnotation *pin = [[MBXDroppedPinAnnotation alloc] init];
+ pin.coordinate = [self.mapView convertPoint:point
+ toCoordinateFromView:self.mapView];
+ pin.title = title ?: @"Dropped Pin";
+ pin.subtitle = [[[MGLCoordinateFormatter alloc] init] stringFromCoordinate:pin.coordinate];
+ // Calling `addAnnotation:` on mapView is not required since `selectAnnotation:animated` has the side effect of adding the annotation if required
+ [self.mapView selectAnnotation:pin animated:YES];
+ }
+}
+
+- (IBAction)cycleStyles:(__unused id)sender
+{
+ static NSArray *styleNames;
+ static NSArray *styleURLs;
+
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ styleNames = @[
+ @"Streets",
+ @"Outdoors",
+ @"Light",
+ @"Dark",
+ @"Satellite",
+ @"Satellite Streets",
+ ];
+ styleURLs = @[
+ [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion],
+ [MGLStyle outdoorsStyleURLWithVersion:MGLStyleDefaultVersion],
+ [MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion],
+ [MGLStyle darkStyleURLWithVersion:MGLStyleDefaultVersion],
+ [MGLStyle satelliteStyleURLWithVersion:MGLStyleDefaultVersion],
+ [MGLStyle satelliteStreetsStyleURLWithVersion:MGLStyleDefaultVersion],
+ ];
+ NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match.");
+
+ // Make sure defaultStyleURLs is up-to-date.
+ unsigned numMethods = 0;
+ Method *methods = class_copyMethodList(object_getClass([MGLStyle class]), &numMethods);
+ unsigned numStyleURLMethods = 0;
+ for (NSUInteger i = 0; i < numMethods; i++) {
+ Method method = methods[i];
+ if (method_getNumberOfArguments(method) == 3 /* _cmd, self, version */) {
+ SEL selector = method_getName(method);
+ NSString *name = @(sel_getName(selector));
+ if ([name hasSuffix:@"StyleURLWithVersion:"]) {
+ numStyleURLMethods += 1;
+ }
+ }
+ }
+ NSAssert(numStyleURLMethods == styleNames.count,
+ @"MGLStyle provides %u default styles but iosapp only knows about %lu of them.",
+ numStyleURLMethods, (unsigned long)styleNames.count);
+ });
+
+ self.styleIndex = (self.styleIndex + 1) % styleNames.count;
+
+ self.mapView.styleURL = styleURLs[self.styleIndex];
+
+ UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
+ [titleButton setTitle:styleNames[self.styleIndex] forState:UIControlStateNormal];
+}
+
+- (IBAction)locateUser:(id)sender
+{
+ MGLUserTrackingMode nextMode;
+ NSString *nextAccessibilityValue;
+ switch (self.mapView.userTrackingMode) {
+ case MGLUserTrackingModeNone:
+ nextMode = MGLUserTrackingModeFollow;
+ nextAccessibilityValue = @"Follow location";
+ break;
+ case MGLUserTrackingModeFollow:
+ nextMode = MGLUserTrackingModeFollowWithHeading;
+ nextAccessibilityValue = @"Follow location and heading";
+ break;
+ case MGLUserTrackingModeFollowWithHeading:
+ nextMode = MGLUserTrackingModeFollowWithCourse;
+ nextAccessibilityValue = @"Follow course";
+ break;
+ case MGLUserTrackingModeFollowWithCourse:
+ nextMode = MGLUserTrackingModeNone;
+ nextAccessibilityValue = @"Off";
+ break;
+ }
+ self.mapView.userTrackingMode = nextMode;
+ [sender setAccessibilityValue:nextAccessibilityValue];
+}
+
+- (IBAction)startWorldTour:(__unused id)sender
+{
+ _isTouringWorld = YES;
+
+ [self.mapView removeAnnotations:self.mapView.annotations];
+ NSUInteger numberOfAnnotations = sizeof(WorldTourDestinations) / sizeof(WorldTourDestinations[0]);
+ NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:numberOfAnnotations];
+ for (NSUInteger i = 0; i < numberOfAnnotations; i++)
+ {
+ MBXDroppedPinAnnotation *annotation = [[MBXDroppedPinAnnotation alloc] init];
+ annotation.coordinate = WorldTourDestinations[i];
+ [annotations addObject:annotation];
+ }
+ [self.mapView addAnnotations:annotations];
+ [self continueWorldTourWithRemainingAnnotations:annotations];
+}
+
+- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations
+{
+ MGLPointAnnotation *nextAnnotation = annotations.firstObject;
+ if (!nextAnnotation || !_isTouringWorld)
+ {
+ _isTouringWorld = NO;
+ return;
+ }
+
+ [annotations removeObjectAtIndex:0];
+ MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:nextAnnotation.coordinate
+ fromDistance:10
+ pitch:arc4random_uniform(60)
+ heading:arc4random_uniform(360)];
+ __weak MBXViewController *weakSelf = self;
+ [self.mapView flyToCamera:camera completionHandler:^{
+ MBXViewController *strongSelf = weakSelf;
+ [strongSelf performSelector:@selector(continueWorldTourWithRemainingAnnotations:)
+ withObject:annotations
+ afterDelay:2];
+ }];
+}
+
+- (NSString *)telemetryDebugLogfilePath
+{
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
+ [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
+ NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", [dateFormatter stringFromDate:[NSDate date]]]];
+
+ return filePath;
+}
+
+- (IBAction)unwindToMapViewController:(__unused UIStoryboardSegue *)sender {
+}
+
+#pragma mark - Destruction
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [self saveState:nil];
+}
+
+#pragma mark - MGLMapViewDelegate
+
+- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
+{
+ // Use GL backed pins for dropped pin annotations
+ if ([annotation isKindOfClass:[MBXDroppedPinAnnotation class]])
+ {
+ return nil;
+ }
+
+ MBXAnnotationView *annotationView = (MBXAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:MBXViewControllerAnnotationViewReuseIdentifer];
+ if (!annotationView)
+ {
+ annotationView = [[MBXAnnotationView alloc] initWithReuseIdentifier:MBXViewControllerAnnotationViewReuseIdentifer];
+ annotationView.frame = CGRectMake(0, 0, 10, 10);
+ annotationView.centerColor = [UIColor whiteColor];
+
+ // uncomment to flatten the annotation view against the map when the map is tilted
+ // this currently causes severe performance issues when more than 2k annotations are visible
+ // annotationView.flat = YES;
+
+ // uncomment to force annotation view to maintain a constant size when the map is tilted
+ // by default, annotation views will shrink and grow as the move towards and away from the
+ // horizon. Relatedly, annotations backed by GL sprites ONLY scale with viewing distance currently.
+ // annotationView.scalesWithViewingDistance = NO;
+
+ } else {
+ // orange indicates that the annotation view was reused
+ annotationView.centerColor = [UIColor orangeColor];
+ }
+ return annotationView;
+}
+
+- (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unused id <MGLAnnotation>)annotation
+{
+ return YES;
+}
+
+- (CGFloat)mapView:(__unused MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation
+{
+ return ([annotation isKindOfClass:[MGLPolygon class]] ? 0.5 : 1.0);
+}
+
+- (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation
+{
+ return ([annotation isKindOfClass:[MGLPolyline class]] ? [UIColor purpleColor] : [UIColor blackColor]);
+}
+
+- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation
+{
+ return (annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor]);
+}
+
+- (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated
+{
+ UIImage *newButtonImage;
+ NSString *newButtonTitle;
+
+ switch (mode) {
+ case MGLUserTrackingModeNone:
+ newButtonImage = [UIImage imageNamed:@"TrackingLocationOffMask.png"];
+ break;
+
+ case MGLUserTrackingModeFollow:
+ newButtonImage = [UIImage imageNamed:@"TrackingLocationMask.png"];
+ break;
+
+ case MGLUserTrackingModeFollowWithHeading:
+ newButtonImage = [UIImage imageNamed:@"TrackingHeadingMask.png"];
+ break;
+ case MGLUserTrackingModeFollowWithCourse:
+ newButtonImage = nil;
+ newButtonTitle = @"Course";
+ break;
+ }
+
+ self.navigationItem.rightBarButtonItem.title = newButtonTitle;
+ [UIView animateWithDuration:0.25 animations:^{
+ self.navigationItem.rightBarButtonItem.image = newButtonImage;
+ }];
+}
+
+- (UIView<MGLCalloutView> *)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation
+{
+ if ([annotation respondsToSelector:@selector(title)]
+ && [annotation isKindOfClass:[MBXCustomCalloutAnnotation class]])
+ {
+ MBXCustomCalloutView *calloutView = [[MBXCustomCalloutView alloc] init];
+ calloutView.representedObject = annotation;
+ return calloutView;
+ }
+ return nil;
+}
+
+- (UIView *)mapView:(__unused MGLMapView *)mapView leftCalloutAccessoryViewForAnnotation:(__unused id<MGLAnnotation>)annotation
+{
+ UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
+ button.frame = CGRectZero;
+ [button setTitle:@"Left" forState:UIControlStateNormal];
+ [button sizeToFit];
+ return button;
+}
+
+- (UIView *)mapView:(__unused MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(__unused id<MGLAnnotation>)annotation
+{
+ UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
+ button.frame = CGRectZero;
+ [button setTitle:@"Right" forState:UIControlStateNormal];
+ [button sizeToFit];
+ return button;
+}
+
+- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotation>)annotation
+{
+ if ( ! [annotation isKindOfClass:[MGLPointAnnotation class]])
+ {
+ return;
+ }
+
+ MGLPointAnnotation *point = annotation;
+ point.coordinate = [self.mapView convertPoint:self.mapView.center toCoordinateFromView:self.mapView];
+}
+
+@end
diff --git a/platform/ios/app/MBXViewController.mm b/platform/ios/app/MBXViewController.mm
deleted file mode 100644
index c8a1713104..0000000000
--- a/platform/ios/app/MBXViewController.mm
+++ /dev/null
@@ -1,641 +0,0 @@
-#import "MBXViewController.h"
-#import "MBXCustomCalloutView.h"
-#import "MBXOfflinePacksTableViewController.h"
-
-#import <Mapbox/Mapbox.h>
-#import "../../../include/mbgl/util/default_styles.hpp"
-
-#import <CoreLocation/CoreLocation.h>
-#import <OpenGLES/ES2/gl.h>
-
-static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000];
-static NSString * const kCustomCalloutTitle = @"Custom Callout";
-
-static const CLLocationCoordinate2D WorldTourDestinations[] = {
- { 38.9131982, -77.0325453144239 },
- { 37.7757368, -122.4135302 },
- { 12.9810816, 77.6368034 },
- { -13.15589555, -74.2178961777998 },
-};
-
-@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
-
-@property (nonatomic) IBOutlet MGLMapView *mapView;
-@property (nonatomic) NSUInteger styleIndex;
-
-@end
-
-@implementation MBXViewController
-{
- BOOL _isTouringWorld;
- BOOL _isShowingCustomStyleLayer;
-}
-
-#pragma mark - Setup
-
-+ (void)initialize
-{
- if (self == [MBXViewController class])
- {
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{
- @"MBXUserTrackingMode": @(MGLUserTrackingModeNone),
- @"MBXShowsUserLocation": @NO,
- @"MBXDebug": @NO,
- }];
- }
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restoreState:) name:UIApplicationWillEnterForegroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationWillTerminateNotification object:nil];
-
- self.styleIndex = 0;
-
- UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
- [titleButton setTitle:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].name) forState:UIControlStateNormal];
-
- [self restoreState:nil];
-}
-
-- (void)saveState:(__unused NSNotification *)notification
-{
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- NSData *archivedCamera = [NSKeyedArchiver archivedDataWithRootObject:self.mapView.camera];
- [defaults setObject:archivedCamera forKey:@"MBXCamera"];
- [defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"];
- [defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"];
- [defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"];
- [defaults synchronize];
-}
-
-- (void)restoreState:(__unused NSNotification *)notification
-{
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- NSData *archivedCamera = [defaults objectForKey:@"MBXCamera"];
- MGLMapCamera *camera = archivedCamera ? [NSKeyedUnarchiver unarchiveObjectWithData:archivedCamera] : nil;
- if (camera)
- {
- self.mapView.camera = camera;
- }
- NSInteger uncheckedTrackingMode = [defaults integerForKey:@"MBXUserTrackingMode"];
- if (uncheckedTrackingMode >= 0 &&
- (NSUInteger)uncheckedTrackingMode >= MGLUserTrackingModeNone &&
- (NSUInteger)uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse)
- {
- self.mapView.userTrackingMode = (MGLUserTrackingMode)uncheckedTrackingMode;
- }
- self.mapView.showsUserLocation = [defaults boolForKey:@"MBXShowsUserLocation"];
- NSInteger uncheckedDebugMask = [defaults integerForKey:@"MBXDebugMask"];
- if (uncheckedDebugMask >= 0)
- {
- self.mapView.debugMask = (MGLMapDebugMaskOptions)uncheckedDebugMask;
- }
-}
-
-- (NSUInteger)supportedInterfaceOrientations
-{
- return UIInterfaceOrientationMaskAll;
-}
-
-- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(__unused id)sender {
- if ([segue.identifier isEqualToString:@"ShowOfflinePacks"]) {
- MBXOfflinePacksTableViewController *controller = [segue destinationViewController];
- controller.mapView = self.mapView;
- }
-}
-
-#pragma mark - Actions
-
-- (IBAction)showSettings:(__unused id)sender
-{
- MGLMapDebugMaskOptions debugMask = self.mapView.debugMask;
- UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Map Settings"
- delegate:self
- cancelButtonTitle:@"Cancel"
- destructiveButtonTitle:nil
- otherButtonTitles:
- @"Reset Position",
- ((debugMask & MGLMapDebugTileBoundariesMask)
- ? @"Hide Tile Boundaries"
- : @"Show Tile Boundaries"),
- ((debugMask & MGLMapDebugTileInfoMask)
- ? @"Hide Tile Info"
- : @"Show Tile Info"),
- ((debugMask & MGLMapDebugTimestampsMask)
- ? @"Hide Tile Timestamps"
- : @"Show Tile Timestamps"),
- ((debugMask & MGLMapDebugCollisionBoxesMask)
- ? @"Hide Collision Boxes"
- : @"Show Collision Boxes"),
- @"Empty Memory",
- @"Add 100 Points",
- @"Add 1,000 Points",
- @"Add 10,000 Points",
- @"Add Test Shapes",
- @"Start World Tour",
- @"Add Custom Callout Point",
- @"Remove Annotations",
- (_isShowingCustomStyleLayer
- ? @"Hide Custom Style Layer"
- : @"Show Custom Style Layer"),
- @"Print Telemetry Logfile",
- @"Delete Telemetry Logfile",
- nil];
-
- [sheet showFromBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES];
-}
-
-- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
-{
- if (buttonIndex == actionSheet.firstOtherButtonIndex)
- {
- [self.mapView resetPosition];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 1)
- {
- self.mapView.debugMask ^= MGLMapDebugTileBoundariesMask;
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 2)
- {
- self.mapView.debugMask ^= MGLMapDebugTileInfoMask;
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 3)
- {
- self.mapView.debugMask ^= MGLMapDebugTimestampsMask;
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 4)
- {
- self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask;
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 5)
- {
- [self.mapView emptyMemoryCache];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 6)
- {
- [self parseFeaturesAddingCount:100];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7)
- {
- [self parseFeaturesAddingCount:1000];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8)
- {
- [self parseFeaturesAddingCount:10000];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 9)
- {
- // PNW triangle
- //
- CLLocationCoordinate2D triangleCoordinates[3] =
- {
- CLLocationCoordinate2DMake(44, -122),
- CLLocationCoordinate2DMake(46, -122),
- CLLocationCoordinate2DMake(46, -121)
- };
-
- MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3];
-
- [self.mapView addAnnotation:triangle];
-
- // Orcas Island hike
- //
- NSDictionary *hike = [NSJSONSerialization JSONObjectWithData:
- [NSData dataWithContentsOfFile:
- [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]]
- options:0
- error:nil];
-
- NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"];
-
- CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
-
- for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++)
- {
- polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]);
- }
-
- MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates
- count:[hikeCoordinatePairs count]];
-
- [self.mapView addAnnotation:polyline];
-
- free(polylineCoordinates);
-
- // PA/NJ/DE polys
- //
- NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData:
- [NSData dataWithContentsOfFile:
- [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]]
- options:0
- error:nil];
-
- for (NSDictionary *feature in threestates[@"features"])
- {
- NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"];
-
- while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0];
-
- CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
-
- for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++)
- {
- polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]);
- }
-
- MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
-
- [self.mapView addAnnotation:polygon];
-
- free(polygonCoordinates);
- }
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 10)
- {
- [self startWorldTour:actionSheet];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 11)
- {
- [self presentAnnotationWithCustomCallout];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 12)
- {
- [self.mapView removeAnnotations:self.mapView.annotations];
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 13)
- {
- if (_isShowingCustomStyleLayer)
- {
- [self removeCustomStyleLayer];
- }
- else
- {
- [self insertCustomStyleLayer];
- }
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 14)
- {
- NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogfilePath] encoding:NSUTF8StringEncoding error:nil];
- NSLog(@"%@", fileContents);
- }
- else if (buttonIndex == actionSheet.firstOtherButtonIndex + 15)
- {
- NSString *filePath = [self telemetryDebugLogfilePath];
- if ([[NSFileManager defaultManager] isDeletableFileAtPath:filePath]) {
- NSError *error;
- BOOL success = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
- if (success) {
- NSLog(@"Deleted telemetry log.");
- } else {
- NSLog(@"Error deleting telemetry log: %@", error.localizedDescription);
- }
- }
- }
-}
-
-- (void)parseFeaturesAddingCount:(NSUInteger)featuresCount
-{
- [self.mapView removeAnnotations:self.mapView.annotations];
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
- {
- NSData *featuresData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"points" ofType:@"geojson"]];
-
- id features = [NSJSONSerialization JSONObjectWithData:featuresData
- options:0
- error:nil];
-
- if ([features isKindOfClass:[NSDictionary class]])
- {
- NSMutableArray *annotations = [NSMutableArray array];
-
- for (NSDictionary *feature in features[@"features"])
- {
- CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([feature[@"geometry"][@"coordinates"][1] doubleValue],
- [feature[@"geometry"][@"coordinates"][0] doubleValue]);
- NSString *title = feature[@"properties"][@"NAME"];
-
- MGLPointAnnotation *annotation = [MGLPointAnnotation new];
- annotation.coordinate = coordinate;
- annotation.title = title;
-
- [annotations addObject:annotation];
-
- if (annotations.count == featuresCount) break;
- }
-
- dispatch_async(dispatch_get_main_queue(), ^
- {
- [self.mapView addAnnotations:annotations];
- [self.mapView showAnnotations:annotations animated:YES];
- });
- }
- });
-}
-
-- (void)insertCustomStyleLayer
-{
- _isShowingCustomStyleLayer = YES;
-
- static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
- static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }";
-
- __block GLuint program = 0;
- __block GLuint vertexShader = 0;
- __block GLuint fragmentShader = 0;
- __block GLuint buffer = 0;
- __block GLuint a_pos = 0;
- [self.mapView insertCustomStyleLayerWithIdentifier:@"mbx-custom" preparationHandler:^{
- program = glCreateProgram();
- vertexShader = glCreateShader(GL_VERTEX_SHADER);
- fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-
- glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
- glCompileShader(vertexShader);
- glAttachShader(program, vertexShader);
- glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
- glCompileShader(fragmentShader);
- glAttachShader(program, fragmentShader);
- glLinkProgram(program);
- a_pos = glGetAttribLocation(program, "a_pos");
-
- GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 };
- glGenBuffers(1, &buffer);
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW);
- } drawingHandler:^(__unused CGSize size,
- __unused CLLocationCoordinate2D centerCoordinate,
- __unused double zoomLevel,
- __unused CLLocationDirection direction,
- __unused CGFloat pitch,
- __unused CGFloat perspectiveSkew) {
- glUseProgram(program);
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- glEnableVertexAttribArray(a_pos);
- glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL);
- glDisable(GL_STENCIL_TEST);
- glDisable(GL_DEPTH_TEST);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- } completionHandler:^{
- if (program) {
- glDeleteBuffers(1, &buffer);
- glDetachShader(program, vertexShader);
- glDetachShader(program, fragmentShader);
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- glDeleteProgram(program);
- }
- } belowStyleLayerWithIdentifier:@"housenum-label"];
-}
-
-- (void)removeCustomStyleLayer
-{
- _isShowingCustomStyleLayer = NO;
- [self.mapView removeCustomStyleLayerWithIdentifier:@"mbx-custom"];
-}
-
-- (void)presentAnnotationWithCustomCallout
-{
- [self.mapView removeAnnotations:self.mapView.annotations];
-
- MGLPointAnnotation *annotation = [MGLPointAnnotation new];
- annotation.coordinate = CLLocationCoordinate2DMake(48.8533940, 2.3775439);
- annotation.title = kCustomCalloutTitle;
-
- [self.mapView addAnnotation:annotation];
- [self.mapView showAnnotations:@[annotation] animated:YES];
-}
-
-- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)longPress
-{
- if (longPress.state == UIGestureRecognizerStateBegan)
- {
- MGLPointAnnotation *point = [MGLPointAnnotation new];
- point.coordinate = [self.mapView convertPoint:[longPress locationInView:longPress.view]
- toCoordinateFromView:self.mapView];
- point.title = @"Dropped Marker";
- point.subtitle = [NSString stringWithFormat:@"lat: %.3f, lon: %.3f", point.coordinate.latitude, point.coordinate.longitude];
- [self.mapView addAnnotation:point];
- [self.mapView selectAnnotation:point animated:YES];
- }
-}
-
-- (IBAction)cycleStyles:(__unused id)sender
-{
- UIButton *titleButton = (UIButton *)self.navigationItem.titleView;
-
- self.styleIndex = (self.styleIndex + 1) % mbgl::util::default_styles::numOrderedStyles;
-
- self.mapView.styleURL = [NSURL URLWithString:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].url)];
-
- [titleButton setTitle:@(mbgl::util::default_styles::orderedStyles[self.styleIndex].name) forState:UIControlStateNormal];
-}
-
-- (IBAction)locateUser:(__unused id)sender
-{
- MGLUserTrackingMode nextMode;
- switch (self.mapView.userTrackingMode) {
- case MGLUserTrackingModeNone:
- nextMode = MGLUserTrackingModeFollow;
- break;
- case MGLUserTrackingModeFollow:
- nextMode = MGLUserTrackingModeFollowWithHeading;
- break;
- case MGLUserTrackingModeFollowWithHeading:
- nextMode = MGLUserTrackingModeFollowWithCourse;
- break;
- case MGLUserTrackingModeFollowWithCourse:
- nextMode = MGLUserTrackingModeNone;
- break;
- }
- self.mapView.userTrackingMode = nextMode;
-}
-
-- (IBAction)startWorldTour:(__unused id)sender
-{
- _isTouringWorld = YES;
-
- [self.mapView removeAnnotations:self.mapView.annotations];
- NSUInteger numberOfAnnotations = sizeof(WorldTourDestinations) / sizeof(WorldTourDestinations[0]);
- NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:numberOfAnnotations];
- for (NSUInteger i = 0; i < numberOfAnnotations; i++)
- {
- MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
- annotation.coordinate = WorldTourDestinations[i];
- [annotations addObject:annotation];
- }
- [self.mapView addAnnotations:annotations];
- [self continueWorldTourWithRemainingAnnotations:annotations];
-}
-
-- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations
-{
- MGLPointAnnotation *nextAnnotation = annotations.firstObject;
- if (!nextAnnotation || !_isTouringWorld)
- {
- _isTouringWorld = NO;
- return;
- }
-
- [annotations removeObjectAtIndex:0];
- MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:nextAnnotation.coordinate
- fromDistance:10
- pitch:arc4random_uniform(60)
- heading:arc4random_uniform(360)];
- __weak MBXViewController *weakSelf = self;
- [self.mapView flyToCamera:camera completionHandler:^{
- MBXViewController *strongSelf = weakSelf;
- [strongSelf performSelector:@selector(continueWorldTourWithRemainingAnnotations:)
- withObject:annotations
- afterDelay:2];
- }];
-}
-
-- (NSString *)telemetryDebugLogfilePath
-{
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
- [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
- NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", [dateFormatter stringFromDate:[NSDate date]]]];
-
- return filePath;
-}
-
-- (IBAction)unwindToMapViewController:(__unused UIStoryboardSegue *)sender {
-}
-
-#pragma mark - Destruction
-
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [self saveState:nil];
-}
-
-#pragma mark - MGLMapViewDelegate
-
-- (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotation:(id <MGLAnnotation> __nonnull)annotation
-{
- if ([annotation.title isEqualToString:@"Dropped Marker"]
- || [annotation.title isEqualToString:kCustomCalloutTitle])
- {
- return nil; // use default marker
- }
-
- NSString *title = [(MGLPointAnnotation *)annotation title];
- if (!title.length) return nil;
- NSString *lastTwoCharacters = [title substringFromIndex:title.length - 2];
-
- UIColor *color;
-
- // make every tenth annotation blue
- if ([lastTwoCharacters hasSuffix:@"0"]) {
- color = [UIColor blueColor];
- } else {
- color = [UIColor redColor];
- }
-
- MGLAnnotationImage *image = [mapView dequeueReusableAnnotationImageWithIdentifier:lastTwoCharacters];
-
- if ( ! image)
- {
- CGRect rect = CGRectMake(0, 0, 20, 15);
-
- UIGraphicsBeginImageContextWithOptions(rect.size, NO, [[UIScreen mainScreen] scale]);
-
- CGContextRef ctx = UIGraphicsGetCurrentContext();
-
- CGContextSetFillColorWithColor(ctx, [[color colorWithAlphaComponent:0.75] CGColor]);
- CGContextFillRect(ctx, rect);
-
- CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
- CGContextStrokeRectWithWidth(ctx, rect, 2);
-
- NSAttributedString *drawString = [[NSAttributedString alloc] initWithString:lastTwoCharacters attributes:@{
- NSFontAttributeName: [UIFont fontWithName:@"Arial-BoldMT" size:12],
- NSForegroundColorAttributeName: [UIColor whiteColor] }];
- CGSize stringSize = drawString.size;
- CGRect stringRect = CGRectMake((rect.size.width - stringSize.width) / 2,
- (rect.size.height - stringSize.height) / 2,
- stringSize.width,
- stringSize.height);
- [drawString drawInRect:stringRect];
-
- image = [MGLAnnotationImage annotationImageWithImage:UIGraphicsGetImageFromCurrentImageContext() reuseIdentifier:lastTwoCharacters];
-
- // don't allow touches on blue annotations
- if ([color isEqual:[UIColor blueColor]]) image.enabled = NO;
-
- UIGraphicsEndImageContext();
- }
-
- return image;
-}
-
-- (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unused id <MGLAnnotation>)annotation
-{
- return YES;
-}
-
-- (CGFloat)mapView:(__unused MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation
-{
- return ([annotation isKindOfClass:[MGLPolygon class]] ? 0.5 : 1.0);
-}
-
-- (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation
-{
- return ([annotation isKindOfClass:[MGLPolyline class]] ? [UIColor purpleColor] : [UIColor blackColor]);
-}
-
-- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation
-{
- return (annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor]);
-}
-
-- (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated
-{
- UIImage *newButtonImage;
- NSString *newButtonTitle;
-
- switch (mode) {
- case MGLUserTrackingModeNone:
- newButtonImage = [UIImage imageNamed:@"TrackingLocationOffMask.png"];
- break;
-
- case MGLUserTrackingModeFollow:
- newButtonImage = [UIImage imageNamed:@"TrackingLocationMask.png"];
- break;
-
- case MGLUserTrackingModeFollowWithHeading:
- newButtonImage = [UIImage imageNamed:@"TrackingHeadingMask.png"];
- break;
- case MGLUserTrackingModeFollowWithCourse:
- newButtonImage = nil;
- newButtonTitle = @"Course";
- break;
- }
-
- self.navigationItem.rightBarButtonItem.title = newButtonTitle;
- [UIView animateWithDuration:0.25 animations:^{
- self.navigationItem.rightBarButtonItem.image = newButtonImage;
- }];
-}
-
-- (UIView<MGLCalloutView> *)mapView:(__unused MGLMapView *)mapView calloutViewForAnnotation:(id<MGLAnnotation>)annotation
-{
- if ([annotation respondsToSelector:@selector(title)]
- && [annotation.title isEqualToString:kCustomCalloutTitle])
- {
- MBXCustomCalloutView *calloutView = [[MBXCustomCalloutView alloc] init];
- calloutView.representedObject = annotation;
- return calloutView;
- }
- return nil;
-}
-
-@end
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
new file mode 100644
index 0000000000..1190070d8e
--- /dev/null
+++ b/platform/ios/app/Main.storyboard
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="PSe-Ot-7Ff">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ <capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
+ </dependencies>
+ <scenes>
+ <!--Map View Controller-->
+ <scene sceneID="p0T-1N-kQ6">
+ <objects>
+ <viewController id="WaX-pd-UZQ" userLabel="Map View Controller" customClass="MBXViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="f0q-9O-L15"/>
+ <viewControllerLayoutGuide type="bottom" id="m8o-i7-QIy"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Z9X-fc-PUC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kNe-zV-9ha" customClass="MGLMapView">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <gestureRecognizers/>
+ <connections>
+ <outlet property="delegate" destination="WaX-pd-UZQ" id="za0-3B-qR6"/>
+ <outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
+ </connections>
+ </view>
+ </subviews>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <constraints>
+ <constraint firstItem="kNe-zV-9ha" firstAttribute="leading" secondItem="Z9X-fc-PUC" secondAttribute="leading" id="53e-Tz-QxF"/>
+ <constraint firstItem="kNe-zV-9ha" firstAttribute="bottom" secondItem="m8o-i7-QIy" secondAttribute="top" id="Etp-BC-E1N"/>
+ <constraint firstAttribute="trailing" secondItem="kNe-zV-9ha" secondAttribute="trailing" id="MGr-8G-VEb"/>
+ <constraint firstItem="kNe-zV-9ha" firstAttribute="top" secondItem="Z9X-fc-PUC" secondAttribute="top" id="qMm-e9-jxH"/>
+ </constraints>
+ </view>
+ <navigationItem key="navigationItem" id="p8W-eP-el5">
+ <nil key="title"/>
+ <barButtonItem key="leftBarButtonItem" image="settings.png" id="Jw8-JP-CaZ" userLabel="Map Settings">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="Map settings"/>
+ </userDefinedRuntimeAttributes>
+ <connections>
+ <action selector="showSettings:" destination="WaX-pd-UZQ" id="X2C-Ee-Qvt"/>
+ </connections>
+ </barButtonItem>
+ <button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
+ <rect key="frame" x="180" y="7" width="240" height="30"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
+ <state key="normal" title="Streets"/>
+ <connections>
+ <action selector="cycleStyles:" destination="WaX-pd-UZQ" eventType="touchUpInside" id="PnY-mb-J6m"/>
+ </connections>
+ </button>
+ <rightBarButtonItems>
+ <barButtonItem image="TrackingLocationOffMask.png" id="CQ1-GP-M6x" userLabel="User Tracking Mode">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="User tracking mode"/>
+ </userDefinedRuntimeAttributes>
+ <connections>
+ <action selector="locateUser:" destination="WaX-pd-UZQ" id="XgF-DB-z3f"/>
+ </connections>
+ </barButtonItem>
+ <barButtonItem systemItem="organize" id="5IK-vz-jKQ" userLabel="Offline Packs">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="Offline packs"/>
+ </userDefinedRuntimeAttributes>
+ <connections>
+ <segue destination="7q0-lI-zqb" kind="show" identifier="ShowOfflinePacks" id="xjx-0t-0LD"/>
+ </connections>
+ </barButtonItem>
+ </rightBarButtonItems>
+ </navigationItem>
+ <connections>
+ <outlet property="mapView" destination="kNe-zV-9ha" id="VNR-WO-1q4"/>
+ </connections>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="AAd-8J-9UU" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ <pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="lfd-mn-7en">
+ <connections>
+ <action selector="handleLongPress:" destination="WaX-pd-UZQ" id="dkk-bc-rvl"/>
+ </connections>
+ </pongPressGestureRecognizer>
+ </objects>
+ <point key="canvasLocation" x="1366" y="350"/>
+ </scene>
+ <!--Offline Packs-->
+ <scene sceneID="xIg-PA-7r3">
+ <objects>
+ <tableViewController id="7q0-lI-zqb" customClass="MBXOfflinePacksTableViewController" sceneMemberID="viewController">
+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="eeN-6b-zqe">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <prototypes>
+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Inactive" editingAccessoryType="detailDisclosureButton" textLabel="JtH-Ce-MI5" detailTextLabel="tTJ-jv-U9v" style="IBUITableViewCellStyleSubtitle" id="fGu-Ys-Eh1">
+ <rect key="frame" x="0.0" y="92" width="600" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fGu-Ys-Eh1" id="sUf-bc-8xG">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Inactive Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JtH-Ce-MI5">
+ <rect key="frame" x="15" y="6" width="174.5" height="19.5"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" pointSize="16"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="456 resources (789 MB)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tTJ-jv-U9v">
+ <rect key="frame" x="15" y="25.5" width="128" height="13.5"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" pointSize="11"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ </tableViewCellContentView>
+ </tableViewCell>
+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Active" editingAccessoryType="detailDisclosureButton" textLabel="9ZK-gS-wJ4" detailTextLabel="0xK-p8-Mmh" style="IBUITableViewCellStyleSubtitle" id="mKB-tz-Zfl">
+ <rect key="frame" x="0.0" y="136" width="600" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mKB-tz-Zfl" id="nS3-aU-nBr">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Active Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9ZK-gS-wJ4">
+ <rect key="frame" x="15" y="6" width="163" height="19.5"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" pointSize="16"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Downloading 123 of 456 resources… (789 MB downloaded)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0xK-p8-Mmh">
+ <rect key="frame" x="15" y="25.5" width="310.5" height="13.5"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <fontDescription key="fontDescription" type="system" pointSize="11"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ </tableViewCellContentView>
+ </tableViewCell>
+ </prototypes>
+ <connections>
+ <outlet property="dataSource" destination="7q0-lI-zqb" id="oe8-d5-Rjo"/>
+ <outlet property="delegate" destination="7q0-lI-zqb" id="D5X-oy-DSc"/>
+ </connections>
+ </tableView>
+ <navigationItem key="navigationItem" title="Offline Packs" id="UcK-PK-eQA">
+ <barButtonItem key="rightBarButtonItem" systemItem="add" id="gCV-hl-Mzc">
+ <connections>
+ <action selector="addCurrentRegion:" destination="7q0-lI-zqb" id="G2O-3V-aEA"/>
+ </connections>
+ </barButtonItem>
+ </navigationItem>
+ <connections>
+ <segue destination="x2D-ga-sM5" kind="unwind" identifier="ReturnToMap" unwindAction="unwindToMapViewController:" id="6MZ-Ed-tu2"/>
+ </connections>
+ </tableViewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="Dga-Vh-IxZ" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ <exit id="x2D-ga-sM5" userLabel="Exit" sceneMemberID="exit"/>
+ </objects>
+ <point key="canvasLocation" x="2075" y="350"/>
+ </scene>
+ <!--Navigation Controller-->
+ <scene sceneID="LFg-oU-zTK">
+ <objects>
+ <navigationController automaticallyAdjustsScrollViewInsets="NO" id="PSe-Ot-7Ff" sceneMemberID="viewController">
+ <toolbarItems/>
+ <navigationBar key="navigationBar" contentMode="scaleToFill" id="ONr-CS-J5X">
+ <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </navigationBar>
+ <nil name="viewControllers"/>
+ <connections>
+ <segue destination="WaX-pd-UZQ" kind="relationship" relationship="rootViewController" id="lV0-PD-2Ag"/>
+ </connections>
+ </navigationController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="Lom-R7-kwe" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="554" y="350"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="TrackingLocationOffMask.png" width="23" height="23"/>
+ <image name="settings.png" width="28" height="28"/>
+ </resources>
+ <color key="tintColor" red="0.12156862745098039" green="0.5490196078431373" blue="0.6705882352941176" alpha="1" colorSpace="calibratedRGB"/>
+</document>
diff --git a/platform/ios/app/Settings.bundle/Root.plist b/platform/ios/app/Settings.bundle/Root.plist
deleted file mode 100644
index 889610e152..0000000000
--- a/platform/ios/app/Settings.bundle/Root.plist
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>PreferenceSpecifiers</key>
- <array>
- <dict>
- <key>Title</key>
- <string>Privacy Settings</string>
- <key>Type</key>
- <string>PSGroupSpecifier</string>
- <key>FooterText</key>
- <string>This setting allows the application to share anonymized location and usage data with Mapbox.</string>
- </dict>
- <dict>
- <key>DefaultValue</key>
- <true/>
- <key>Key</key>
- <string>MGLMapboxMetricsEnabled</string>
- <key>Title</key>
- <string>Mapbox Telemetry</string>
- <key>Type</key>
- <string>PSToggleSwitchSpecifier</string>
- <key>TrueValue</key>
- <true/>
- <key>FalseValue</key>
- <false/>
- </dict>
- </array>
- <key>StringsTable</key>
- <string>Root</string>
-</dict>
-</plist>
diff --git a/platform/ios/app/Settings.bundle/en.lproj/Root.strings b/platform/ios/app/Settings.bundle/en.lproj/Root.strings
deleted file mode 100644
index 5343a34ae5..0000000000
--- a/platform/ios/app/Settings.bundle/en.lproj/Root.strings
+++ /dev/null
@@ -1,3 +0,0 @@
-"Privacy Settings" = "Privacy Settings";
-"Mapbox Telemetry" = "Mapbox Telemetry";
-"This setting allows the application to share anonymized location and usage data with Mapbox." = "This setting allows the application to share anonymized location and usage data with Mapbox.";
diff --git a/platform/ios/app/Storyboard.storyboard b/platform/ios/app/Storyboard.storyboard
deleted file mode 100644
index d471e9dedd..0000000000
--- a/platform/ios/app/Storyboard.storyboard
+++ /dev/null
@@ -1,184 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10115" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="PSe-Ot-7Ff">
- <dependencies>
- <deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10084"/>
- <capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
- </dependencies>
- <scenes>
- <!--Map View Controller-->
- <scene sceneID="p0T-1N-kQ6">
- <objects>
- <viewController id="WaX-pd-UZQ" userLabel="Map View Controller" customClass="MBXViewController" sceneMemberID="viewController">
- <layoutGuides>
- <viewControllerLayoutGuide type="top" id="f0q-9O-L15"/>
- <viewControllerLayoutGuide type="bottom" id="m8o-i7-QIy"/>
- </layoutGuides>
- <view key="view" contentMode="scaleToFill" id="Z9X-fc-PUC">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kNe-zV-9ha" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <gestureRecognizers/>
- <connections>
- <outlet property="delegate" destination="WaX-pd-UZQ" id="za0-3B-qR6"/>
- <outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
- </connections>
- </view>
- </subviews>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <constraints>
- <constraint firstItem="kNe-zV-9ha" firstAttribute="leading" secondItem="Z9X-fc-PUC" secondAttribute="leading" id="53e-Tz-QxF"/>
- <constraint firstItem="kNe-zV-9ha" firstAttribute="bottom" secondItem="m8o-i7-QIy" secondAttribute="top" id="Etp-BC-E1N"/>
- <constraint firstAttribute="trailing" secondItem="kNe-zV-9ha" secondAttribute="trailing" id="MGr-8G-VEb"/>
- <constraint firstItem="kNe-zV-9ha" firstAttribute="top" secondItem="Z9X-fc-PUC" secondAttribute="top" id="qMm-e9-jxH"/>
- </constraints>
- </view>
- <navigationItem key="navigationItem" id="p8W-eP-el5">
- <nil key="title"/>
- <barButtonItem key="leftBarButtonItem" image="settings.png" id="Jw8-JP-CaZ">
- <connections>
- <action selector="showSettings:" destination="WaX-pd-UZQ" id="X2C-Ee-Qvt"/>
- </connections>
- </barButtonItem>
- <button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
- <rect key="frame" x="180" y="7" width="240" height="30"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
- <state key="normal" title="Streets"/>
- <connections>
- <action selector="cycleStyles:" destination="WaX-pd-UZQ" eventType="touchUpInside" id="PnY-mb-J6m"/>
- </connections>
- </button>
- <rightBarButtonItems>
- <barButtonItem image="TrackingLocationOffMask.png" id="CQ1-GP-M6x">
- <connections>
- <action selector="locateUser:" destination="WaX-pd-UZQ" id="XgF-DB-z3f"/>
- </connections>
- </barButtonItem>
- <barButtonItem systemItem="organize" id="5IK-vz-jKQ">
- <connections>
- <segue destination="7q0-lI-zqb" kind="show" identifier="ShowOfflinePacks" id="xjx-0t-0LD"/>
- </connections>
- </barButtonItem>
- </rightBarButtonItems>
- </navigationItem>
- <connections>
- <outlet property="mapView" destination="kNe-zV-9ha" id="VNR-WO-1q4"/>
- </connections>
- </viewController>
- <placeholder placeholderIdentifier="IBFirstResponder" id="AAd-8J-9UU" userLabel="First Responder" sceneMemberID="firstResponder"/>
- <pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="lfd-mn-7en">
- <connections>
- <action selector="handleLongPress:" destination="WaX-pd-UZQ" id="dkk-bc-rvl"/>
- </connections>
- </pongPressGestureRecognizer>
- </objects>
- <point key="canvasLocation" x="1366" y="350"/>
- </scene>
- <!--Offline Packs-->
- <scene sceneID="xIg-PA-7r3">
- <objects>
- <tableViewController id="7q0-lI-zqb" customClass="MBXOfflinePacksTableViewController" sceneMemberID="viewController">
- <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="eeN-6b-zqe">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <prototypes>
- <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Inactive" editingAccessoryType="detailDisclosureButton" textLabel="JtH-Ce-MI5" detailTextLabel="tTJ-jv-U9v" style="IBUITableViewCellStyleSubtitle" id="fGu-Ys-Eh1">
- <rect key="frame" x="0.0" y="92" width="600" height="44"/>
- <autoresizingMask key="autoresizingMask"/>
- <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fGu-Ys-Eh1" id="sUf-bc-8xG">
- <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Inactive Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JtH-Ce-MI5">
- <rect key="frame" x="15" y="6" width="174.5" height="19.5"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <fontDescription key="fontDescription" type="system" pointSize="16"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
- <nil key="highlightedColor"/>
- </label>
- <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="456 resources (789 MB)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tTJ-jv-U9v">
- <rect key="frame" x="15" y="25.5" width="128" height="13.5"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <fontDescription key="fontDescription" type="system" pointSize="11"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
- <nil key="highlightedColor"/>
- </label>
- </subviews>
- </tableViewCellContentView>
- </tableViewCell>
- <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Active" editingAccessoryType="detailDisclosureButton" textLabel="9ZK-gS-wJ4" detailTextLabel="0xK-p8-Mmh" style="IBUITableViewCellStyleSubtitle" id="mKB-tz-Zfl">
- <rect key="frame" x="0.0" y="136" width="600" height="44"/>
- <autoresizingMask key="autoresizingMask"/>
- <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mKB-tz-Zfl" id="nS3-aU-nBr">
- <rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Active Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9ZK-gS-wJ4">
- <rect key="frame" x="15" y="6" width="163" height="19.5"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <fontDescription key="fontDescription" type="system" pointSize="16"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
- <nil key="highlightedColor"/>
- </label>
- <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Downloading 123 of 456 resources… (789 MB downloaded)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0xK-p8-Mmh">
- <rect key="frame" x="15" y="25.5" width="310.5" height="13.5"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
- <fontDescription key="fontDescription" type="system" pointSize="11"/>
- <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
- <nil key="highlightedColor"/>
- </label>
- </subviews>
- </tableViewCellContentView>
- </tableViewCell>
- </prototypes>
- <connections>
- <outlet property="dataSource" destination="7q0-lI-zqb" id="oe8-d5-Rjo"/>
- <outlet property="delegate" destination="7q0-lI-zqb" id="D5X-oy-DSc"/>
- </connections>
- </tableView>
- <navigationItem key="navigationItem" title="Offline Packs" id="UcK-PK-eQA">
- <barButtonItem key="rightBarButtonItem" systemItem="add" id="gCV-hl-Mzc">
- <connections>
- <action selector="addCurrentRegion:" destination="7q0-lI-zqb" id="G2O-3V-aEA"/>
- </connections>
- </barButtonItem>
- </navigationItem>
- <connections>
- <segue destination="x2D-ga-sM5" kind="unwind" identifier="ReturnToMap" unwindAction="unwindToMapViewController:" id="6MZ-Ed-tu2"/>
- </connections>
- </tableViewController>
- <placeholder placeholderIdentifier="IBFirstResponder" id="Dga-Vh-IxZ" userLabel="First Responder" sceneMemberID="firstResponder"/>
- <exit id="x2D-ga-sM5" userLabel="Exit" sceneMemberID="exit"/>
- </objects>
- <point key="canvasLocation" x="2075" y="350"/>
- </scene>
- <!--Navigation Controller-->
- <scene sceneID="LFg-oU-zTK">
- <objects>
- <navigationController automaticallyAdjustsScrollViewInsets="NO" id="PSe-Ot-7Ff" sceneMemberID="viewController">
- <toolbarItems/>
- <navigationBar key="navigationBar" contentMode="scaleToFill" id="ONr-CS-J5X">
- <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
- <autoresizingMask key="autoresizingMask"/>
- </navigationBar>
- <nil name="viewControllers"/>
- <connections>
- <segue destination="WaX-pd-UZQ" kind="relationship" relationship="rootViewController" id="lV0-PD-2Ag"/>
- </connections>
- </navigationController>
- <placeholder placeholderIdentifier="IBFirstResponder" id="Lom-R7-kwe" userLabel="First Responder" sceneMemberID="firstResponder"/>
- </objects>
- <point key="canvasLocation" x="554" y="350"/>
- </scene>
- </scenes>
- <resources>
- <image name="TrackingLocationOffMask.png" width="23" height="23"/>
- <image name="settings.png" width="28" height="28"/>
- </resources>
- <color key="tintColor" red="0.12156862745098039" green="0.5490196078431373" blue="0.6705882352941176" alpha="1" colorSpace="calibratedRGB"/>
-</document>
diff --git a/platform/ios/app/app-info.plist b/platform/ios/app/app-info.plist
deleted file mode 100644
index 13452c9427..0000000000
--- a/platform/ios/app/app-info.plist
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>CFBundleDevelopmentRegion</key>
- <string>en</string>
- <key>CFBundleDisplayName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundleExecutable</key>
- <string>${EXECUTABLE_NAME}</string>
- <key>CFBundleIdentifier</key>
- <string>com.mapbox.MapboxGL</string>
- <key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
- <key>CFBundleName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundlePackageType</key>
- <string>APPL</string>
- <key>CFBundleShortVersionString</key>
- <string>0.0.2</string>
- <key>CFBundleSignature</key>
- <string>MBGL</string>
- <key>CFBundleVersion</key>
- <string>7877</string>
- <key>LSRequiresIPhoneOS</key>
- <true/>
- <key>MGLMetricsTestServerURL</key>
- <string>https://cloudfront-staging.tilestream.net</string>
- <key>NSAppTransportSecurity</key>
- <dict>
- <key>NSExceptionDomains</key>
- <dict>
- <key>cloudfront-staging.tilestream.net</key>
- <dict>
- <key>NSExceptionRequiresForwardSecrecy</key>
- <false/>
- </dict>
- </dict>
- </dict>
- <key>NSHumanReadableCopyright</key>
- <string>© 2014–2016 Mapbox</string>
- <key>NSLocationAlwaysUsageDescription</key>
- <string>The map will ALWAYS display the user's location.</string>
- <key>NSLocationWhenInUseUsageDescription</key>
- <string>The map will display the user's location.</string>
- <key>UILaunchStoryboardName</key>
- <string>Default</string>
- <key>UIMainStoryboardFile</key>
- <string>Storyboard</string>
- <key>UIRequiredDeviceCapabilities</key>
- <array>
- <string>armv7</string>
- </array>
- <key>UISupportedInterfaceOrientations</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
- <key>UISupportedInterfaceOrientations~ipad</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationPortraitUpsideDown</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
-</dict>
-</plist>
diff --git a/platform/ios/app/img/Default-568h@2x.png b/platform/ios/app/img/Default-568h@2x.png
deleted file mode 100644
index ea3706427a..0000000000
--- a/platform/ios/app/img/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Default-667h@2x.png b/platform/ios/app/img/Default-667h@2x.png
deleted file mode 100644
index 03f139de66..0000000000
--- a/platform/ios/app/img/Default-667h@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-60.png b/platform/ios/app/img/Icon-60.png
deleted file mode 100644
index ff4c6ab4b1..0000000000
--- a/platform/ios/app/img/Icon-60.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-72.png b/platform/ios/app/img/Icon-72.png
deleted file mode 100644
index 0c876f664d..0000000000
--- a/platform/ios/app/img/Icon-72.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-72@2x.png b/platform/ios/app/img/Icon-72@2x.png
deleted file mode 100644
index 6da408204a..0000000000
--- a/platform/ios/app/img/Icon-72@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-76@3x.png b/platform/ios/app/img/Icon-76@3x.png
deleted file mode 100644
index 64edff1835..0000000000
--- a/platform/ios/app/img/Icon-76@3x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Small-50.png b/platform/ios/app/img/Icon-Small-50.png
deleted file mode 100644
index 6d17da4b00..0000000000
--- a/platform/ios/app/img/Icon-Small-50.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Small-50@2x.png b/platform/ios/app/img/Icon-Small-50@2x.png
deleted file mode 100644
index ac4ec19282..0000000000
--- a/platform/ios/app/img/Icon-Small-50@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Small@2x.png b/platform/ios/app/img/Icon-Small@2x.png
deleted file mode 100644
index 7773852e7a..0000000000
--- a/platform/ios/app/img/Icon-Small@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Small@3x.png b/platform/ios/app/img/Icon-Small@3x.png
deleted file mode 100644
index e16f3fa6a9..0000000000
--- a/platform/ios/app/img/Icon-Small@3x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Spotlight-40.png b/platform/ios/app/img/Icon-Spotlight-40.png
deleted file mode 100644
index eca13393e6..0000000000
--- a/platform/ios/app/img/Icon-Spotlight-40.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon-Spotlight-40@2x.png b/platform/ios/app/img/Icon-Spotlight-40@2x.png
deleted file mode 100644
index 070d037539..0000000000
--- a/platform/ios/app/img/Icon-Spotlight-40@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon.png b/platform/ios/app/img/Icon.png
deleted file mode 100644
index 9ca8194eef..0000000000
--- a/platform/ios/app/img/Icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/Icon@2x.png b/platform/ios/app/img/Icon@2x.png
deleted file mode 100644
index 7c2e8ba037..0000000000
--- a/platform/ios/app/img/Icon@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/iTunesArtwork b/platform/ios/app/img/iTunesArtwork
deleted file mode 100644
index ac6a0c58e8..0000000000
--- a/platform/ios/app/img/iTunesArtwork
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/iTunesArtwork.png b/platform/ios/app/img/iTunesArtwork.png
deleted file mode 100644
index b10824b048..0000000000
--- a/platform/ios/app/img/iTunesArtwork.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/img/iTunesArtwork@2x b/platform/ios/app/img/iTunesArtwork@2x
deleted file mode 100644
index fae1dad8bf..0000000000
--- a/platform/ios/app/img/iTunesArtwork@2x
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/mapboxgl-app.gypi b/platform/ios/app/mapboxgl-app.gypi
deleted file mode 100644
index c154669485..0000000000
--- a/platform/ios/app/mapboxgl-app.gypi
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'iosapp',
- 'product_name': 'Mapbox GL',
- 'type': 'executable',
- 'product_extension': 'app',
- 'mac_bundle': 1,
- 'mac_bundle_resources': [
- '<!@(find ../platform/ios/app/img -type f)',
- './Storyboard.storyboard',
- './points.geojson',
- './polyline.geojson',
- './threestates.geojson',
- './Settings.bundle/',
- './app-info.plist',
- ],
-
- 'dependencies': [
- 'iossdk',
- ],
-
- 'sources': [
- 'main.m',
- 'MBXAppDelegate.h',
- 'MBXAppDelegate.m',
- 'MBXCustomCalloutView.h',
- 'MBXCustomCalloutView.m',
- 'MBXOfflinePacksTableViewController.h',
- 'MBXOfflinePacksTableViewController.m',
- 'MBXViewController.h',
- 'MBXViewController.mm',
- ],
-
- 'xcode_settings': {
- 'SDKROOT': 'iphoneos',
- 'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
- 'IPHONEOS_DEPLOYMENT_TARGET': '8.0',
- 'INFOPLIST_FILE': '../platform/ios/app/app-info.plist',
- 'TARGETED_DEVICE_FAMILY': '1,2',
- 'COMBINE_HIDPI_IMAGES': 'NO', # disable combining @2x, @3x images into .tiff files
- 'COPY_PHASE_STRIP': 'NO',
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'CLANG_ENABLE_MODULES': 'YES',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '$(inherited)',
- '@executable_path/Frameworks',
- ],
- },
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'CODE_SIGN_IDENTITY': 'iPhone Developer',
- 'COPY_PHASE_STRIP': 'NO',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'CODE_SIGN_IDENTITY': 'iPhone Distribution',
- 'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
- 'COPY_PHASE_STRIP': 'YES',
- },
- },
- },
-
- 'copies': [
- {
- 'destination': '<(PRODUCT_DIR)/$(FRAMEWORKS_FOLDER_PATH)',
- 'files': [
- '<(PRODUCT_DIR)/Mapbox.framework',
- ],
- 'xcode_code_sign': 1,
- },
- ],
- }
- ]
-}
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..73f0fa2497
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-Small@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-2.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-Small.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-3.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-40.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-4.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "iTunesArtwork.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "iTunesArtwork@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-1.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-1.png
new file mode 100644
index 0000000000..8133332fe2
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-1.png
Binary files differ
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-2.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-2.png
new file mode 100644
index 0000000000..9b7db0607a
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-2.png
Binary files differ
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-3.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-3.png
new file mode 100644
index 0000000000..8f61a8ec03
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-3.png
Binary files differ
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-4.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-4.png
new file mode 100644
index 0000000000..783467e2ab
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-4.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-40.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40.png
index 2425133189..2425133189 100644
--- a/platform/ios/benchmark/img/Icon-40.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-40@2x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
index b827b1e5a1..b827b1e5a1 100644
--- a/platform/ios/benchmark/img/Icon-40@2x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-40@3x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
index 89c7119ac6..89c7119ac6 100644
--- a/platform/ios/benchmark/img/Icon-40@3x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-76.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76.png
index 0ed2a26730..0ed2a26730 100644
--- a/platform/ios/benchmark/img/Icon-76.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-76@2x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
index 311a1658a8..311a1658a8 100644
--- a/platform/ios/benchmark/img/Icon-76@2x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-Small.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
index 630d7c04c6..630d7c04c6 100644
--- a/platform/ios/benchmark/img/Icon-Small.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-Small@2x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
index c0ee89211a..c0ee89211a 100644
--- a/platform/ios/benchmark/img/Icon-Small@2x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-Small@3x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
index 436a8b404c..436a8b404c 100644
--- a/platform/ios/benchmark/img/Icon-Small@3x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
Binary files differ
diff --git a/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon.png
new file mode 100644
index 0000000000..2c9ecb48cb
--- /dev/null
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/Icon.png
Binary files differ
diff --git a/platform/ios/benchmark/img/iTunesArtwork.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork.png
index f87c8734b5..f87c8734b5 100644
--- a/platform/ios/benchmark/img/iTunesArtwork.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork.png
Binary files differ
diff --git a/platform/ios/benchmark/img/iTunesArtwork@2x.png b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png
index 8d456a126f..8d456a126f 100644
--- a/platform/ios/benchmark/img/iTunesArtwork@2x.png
+++ b/platform/ios/benchmark/Assets.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png
Binary files differ
diff --git a/platform/ios/benchmark/Base.lproj/LaunchScreen.storyboard b/platform/ios/benchmark/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000000..323bd43177
--- /dev/null
+++ b/platform/ios/benchmark/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/platform/ios/benchmark/Info.plist b/platform/ios/benchmark/Info.plist
new file mode 100644
index 0000000000..98ae65e170
--- /dev/null
+++ b/platform/ios/benchmark/Info.plist
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.mapbox.GL.benchmark</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>0.0.2</string>
+ <key>CFBundleSignature</key>
+ <string>MBGL</string>
+ <key>CFBundleVersion</key>
+ <string>0.0.3</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSHumanReadableCopyright</key>
+ <string>© 2015–2016 Mapbox</string>
+ <key>UIApplicationExitsOnSuspend</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
+ <true/>
+</dict>
+</plist>
diff --git a/platform/ios/benchmark/MBXBenchAppDelegate.h b/platform/ios/benchmark/MBXBenchAppDelegate.h
index b8f6c2e641..e89add93fd 100644
--- a/platform/ios/benchmark/MBXBenchAppDelegate.h
+++ b/platform/ios/benchmark/MBXBenchAppDelegate.h
@@ -1,5 +1,7 @@
#import <UIKit/UIKit.h>
+extern NSString * const MBXMapboxAccessTokenDefaultsKey;
+
@interface MBXBenchAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
diff --git a/platform/ios/benchmark/MBXBenchAppDelegate.m b/platform/ios/benchmark/MBXBenchAppDelegate.m
index 5da6ccfcec..684c90d336 100644
--- a/platform/ios/benchmark/MBXBenchAppDelegate.m
+++ b/platform/ios/benchmark/MBXBenchAppDelegate.m
@@ -2,23 +2,23 @@
#import "MBXBenchViewController.h"
#import <Mapbox/Mapbox.h>
+NSString * const MBXMapboxAccessTokenDefaultsKey = @"MBXMapboxAccessToken";
+
@implementation MBXBenchAppDelegate
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
- NSString* accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
- if (accessToken) {
- // Store to preferences so that we can launch the app later on without having to specify
- // token.
- [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:@"access_token"];
- } else {
- // Try to retrieve from preferences, maybe we've stored them there previously and can reuse
- // the token.
- accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"access_token"];
- }
- if (!accessToken) {
- NSLog(@"No access token set. Mapbox vector tiles won't work.");
- } else {
+ if (![MGLAccountManager accessToken]) {
+ NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"];
+ if (accessToken) {
+ // Store to preferences so that we can launch the app later on without having to specify
+ // token.
+ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:MBXMapboxAccessTokenDefaultsKey];
+ } else {
+ // Try to retrieve from preferences, maybe we've stored them there previously and can reuse
+ // the token.
+ accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MBXMapboxAccessTokenDefaultsKey];
+ }
[MGLAccountManager setAccessToken:accessToken];
}
diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm
index a381813ffc..43e98d7ccd 100644
--- a/platform/ios/benchmark/MBXBenchViewController.mm
+++ b/platform/ios/benchmark/MBXBenchViewController.mm
@@ -1,23 +1,14 @@
#import "MBXBenchViewController.h"
+#import "MBXBenchAppDelegate.h"
+
#import <Mapbox/Mapbox.h>
+#import "MGLMapView_Internal.h"
#include "locations.hpp"
#include <chrono>
-@interface MGLMapView (MBXBenchmarkAdditions)
-
-#pragma mark - Debugging
-
-/** Triggers another render pass even when it is not necessary. */
-- (void)setNeedsGLDisplay;
-
-/** Returns whether the map view is currently loading or processing any assets required to render the map */
-- (BOOL)isFullyLoaded;
-
-@end
-
@interface MBXBenchViewController () <MGLMapViewDelegate>
@property (nonatomic) MGLMapView *mapView;
@@ -44,7 +35,9 @@
{
[super viewDidLoad];
- NSURL* url = [[NSURL alloc] initWithString:@"mapbox://styles/mapbox/streets-v8"];
+ // Use a local style and local assets if they’ve been downloaded.
+ NSURL *tileSourceURL = [[NSBundle mainBundle] URLForResource:@"mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6" withExtension:nil subdirectory:@"tiles"];
+ NSURL *url = [NSURL URLWithString:tileSourceURL ? @"asset://styles/streets-v8.json" : @"mapbox://styles/mapbox/streets-v8"];
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds styleURL:url];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.delegate = self;
@@ -53,10 +46,42 @@
self.mapView.rotateEnabled = NO;
self.mapView.userInteractionEnabled = YES;
- [self startBenchmarkIteration];
-
[self.view addSubview:self.mapView];
+}
+- (void)viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ if ([MGLAccountManager accessToken].length) {
+ [self startBenchmarkIteration];
+ } else {
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Access Token" message:@"Enter your Mapbox access token to load Mapbox-hosted tiles and styles:" preferredStyle:UIAlertControllerStyleAlert];
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
+ textField.keyboardType = UIKeyboardTypeURL;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ }];
+
+ [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
+ [self startBenchmarkIteration];
+ }]];
+ UIAlertAction *OKAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
+ UITextField *textField = alertController.textFields.firstObject;
+ NSString *accessToken = textField.text;
+ [[NSUserDefaults standardUserDefaults] setObject:accessToken forKey:MBXMapboxAccessTokenDefaultsKey];
+ [MGLAccountManager setAccessToken:accessToken];
+ [self.mapView reloadStyle:self];
+
+ [self startBenchmarkIteration];
+ }];
+ [alertController addAction:OKAction];
+
+ if ([alertController respondsToSelector:@selector(setPreferredAction:)]) {
+ alertController.preferredAction = OKAction;
+ }
+ [self presentViewController:alertController animated:YES completion:nil];
+ }
}
size_t idx = 0;
@@ -81,13 +106,17 @@ static const int benchmarkDuration = 200; // frames
// Do nothing. The benchmark is completed.
NSLog(@"Benchmark completed.");
NSLog(@"Result:");
+ double totalFPS = 0;
size_t colWidth = 0;
for (const auto& row : result) {
colWidth = std::max(row.first.size(), colWidth);
}
for (const auto& row : result) {
NSLog(@"| %-*s | %4.1f fps |", int(colWidth), row.first.c_str(), row.second);
+ totalFPS += row.second;
}
+ NSLog(@"Total FPS: %4.1f", totalFPS);
+ NSLog(@"Average FPS: %4.1f", totalFPS / result.size());
exit(0);
}
}
@@ -136,7 +165,7 @@ static const int benchmarkDuration = 200; // frames
{
// Start the benchmarking timer.
state = State::WarmingUp;
- [self.mapView emptyMemoryCache];
+ [self.mapView didReceiveMemoryWarning];
NSLog(@"- Warming up for %d frames...", warmupDuration);
[mapView setNeedsGLDisplay];
}
diff --git a/platform/ios/benchmark/app-info.plist b/platform/ios/benchmark/app-info.plist
deleted file mode 100644
index 9d0aad09e1..0000000000
--- a/platform/ios/benchmark/app-info.plist
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>CFBundleDevelopmentRegion</key>
- <string>en</string>
- <key>CFBundleDisplayName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundleExecutable</key>
- <string>${EXECUTABLE_NAME}</string>
- <key>CFBundleIdentifier</key>
- <string>com.mapbox.GL.benchmark</string>
- <key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
- <key>CFBundleName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundlePackageType</key>
- <string>APPL</string>
- <key>CFBundleShortVersionString</key>
- <string>0.0.2</string>
- <key>CFBundleSignature</key>
- <string>MBGL</string>
- <key>CFBundleVersion</key>
- <string>0.0.3</string>
- <key>LSRequiresIPhoneOS</key>
- <true/>
- <key>NSHumanReadableCopyright</key>
- <string>© 2015–2016 Mapbox</string>
- <key>UIApplicationExitsOnSuspend</key>
- <true/>
- <key>UILaunchStoryboardName</key>
- <string>Default</string>
- <key>UIRequiredDeviceCapabilities</key>
- <array>
- <string>armv7</string>
- </array>
- <key>UISupportedInterfaceOrientations</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
- <key>UISupportedInterfaceOrientations~ipad</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationPortraitUpsideDown</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
- <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
- <true/>
-</dict>
-</plist>
diff --git a/platform/ios/benchmark/benchmark-ios.gypi b/platform/ios/benchmark/benchmark-ios.gypi
deleted file mode 100644
index 5962a6b338..0000000000
--- a/platform/ios/benchmark/benchmark-ios.gypi
+++ /dev/null
@@ -1,78 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- { 'target_name': 'ios-bench',
- 'product_name': 'Bench GL',
- 'type': 'executable',
- 'product_extension': 'app',
- 'mac_bundle': 1,
- 'mac_bundle_resources': [
- '<!@(find ../platform/ios/benchmark/img -type f)',
- 'assets/glyphs',
- 'assets/sprites',
- 'assets/tiles',
- ],
-
- 'dependencies': [
- 'iossdk',
- ],
-
- 'sources': [
- './main.m',
- './MBXBenchAppDelegate.h',
- './MBXBenchAppDelegate.m',
- './MBXBenchViewController.h',
- './MBXBenchViewController.mm',
- './locations.hpp',
- './locations.cpp',
- ],
-
- 'xcode_settings': {
- 'SDKROOT': 'iphoneos',
- 'SUPPORTED_PLATFORMS': 'iphoneos',
- 'IPHONEOS_DEPLOYMENT_TARGET': '8.0',
- 'INFOPLIST_FILE': '../platform/ios/benchmark/app-info.plist',
- 'TARGETED_DEVICE_FAMILY': '1,2',
- 'COMBINE_HIDPI_IMAGES': 'NO', # don't merge @2x.png images into .tiff files
- 'COPY_PHASE_STRIP': 'NO',
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'CLANG_ENABLE_MODULES': 'YES',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '$(inherited)',
- '@executable_path/Frameworks',
- ],
- 'OTHER_LDFLAGS': [
- '-framework CoreLocation',
- ],
- },
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'CODE_SIGN_IDENTITY': 'iPhone Developer',
- 'COPY_PHASE_STRIP': 'NO',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'CODE_SIGN_IDENTITY': 'iPhone Distribution',
- 'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
- 'COPY_PHASE_STRIP': 'YES',
- },
- },
- },
-
- 'copies': [
- {
- 'destination': '<(PRODUCT_DIR)/$(FRAMEWORKS_FOLDER_PATH)',
- 'files': [
- '<(PRODUCT_DIR)/Mapbox.framework',
- ],
- 'xcode_code_sign': 1,
- },
- ],
- }
- ]
-}
diff --git a/platform/ios/benchmark/img/Icon-60.png b/platform/ios/benchmark/img/Icon-60.png
deleted file mode 100644
index 409fad814a..0000000000
--- a/platform/ios/benchmark/img/Icon-60.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-60@2x.png b/platform/ios/benchmark/img/Icon-60@2x.png
deleted file mode 100644
index 89c7119ac6..0000000000
--- a/platform/ios/benchmark/img/Icon-60@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-60@3x.png b/platform/ios/benchmark/img/Icon-60@3x.png
deleted file mode 100644
index 158bcfe27e..0000000000
--- a/platform/ios/benchmark/img/Icon-60@3x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-72.png b/platform/ios/benchmark/img/Icon-72.png
deleted file mode 100644
index eecd90b5eb..0000000000
--- a/platform/ios/benchmark/img/Icon-72.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-72@2x.png b/platform/ios/benchmark/img/Icon-72@2x.png
deleted file mode 100644
index ca28d1fa05..0000000000
--- a/platform/ios/benchmark/img/Icon-72@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-Small-50.png b/platform/ios/benchmark/img/Icon-Small-50.png
deleted file mode 100644
index a071034743..0000000000
--- a/platform/ios/benchmark/img/Icon-Small-50.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon-Small-50@2x.png b/platform/ios/benchmark/img/Icon-Small-50@2x.png
deleted file mode 100644
index 5d7c76d5e2..0000000000
--- a/platform/ios/benchmark/img/Icon-Small-50@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon.png b/platform/ios/benchmark/img/Icon.png
deleted file mode 100644
index 7af0c68a38..0000000000
--- a/platform/ios/benchmark/img/Icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/img/Icon.svg b/platform/ios/benchmark/img/Icon.svg
deleted file mode 100644
index 28df6f600d..0000000000
--- a/platform/ios/benchmark/img/Icon.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg width="100%" height="100%" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-miterlimit:1.41421;">
- <image x="0" y="0" width="15px" height="15px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAQ0lEQVQokcXP2wkAIAxD0Sidvqu5jhTxVXdIPsz/4ZLi3hLkKgsBwOjsX3zBcztKeSvllQKeSlnCQ8GhfA5cHvfk8QNc8hvDN5gIKwAAAABJRU5ErkJggg=="/>
- <path id="path7143" d="M9.48355,2.89095C8.18827,3.22425 6.67888,4.56018 5.90484,6.38831L4.93731,6.63727C4.23814,6.81719 3.97174,7.44212 3.83781,8.06502L3.65856,8.89868L5.40366,8.44963L7.68238,9.79563L8.13143,11.5407L8.77503,10.9814C9.25592,10.5634 9.67465,10.0284 9.49474,9.32928L9.24577,8.36174C10.4732,6.80143 10.9147,4.8347 10.5814,3.53941L10.3567,2.66627L9.48355,2.89095ZM8.40657,5.10044C8.73798,5.01516 9.08086,5.2177 9.16614,5.54911C9.25142,5.88052 9.04888,6.2234 8.71747,6.30868C8.38606,6.39396 8.04318,6.19142 7.9579,5.86001C7.87262,5.5286 8.07516,5.18572 8.40657,5.10044L8.40657,5.10044ZM5.41251,9.7356L5.18818,10.1154C4.86414,10.664 4.97269,11.7811 4.97269,11.7811C4.97269,11.7811 5.9677,11.3976 6.32753,10.7884L6.55187,10.4086L5.41251,9.7356Z" style="fill:white;fill-rule:nonzero;stroke-width:0.62px;stroke:white;"/>
-</svg>
diff --git a/platform/ios/benchmark/img/Icon@2x.png b/platform/ios/benchmark/img/Icon@2x.png
deleted file mode 100644
index ff5ea0c073..0000000000
--- a/platform/ios/benchmark/img/Icon@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/benchmark/locations.hpp b/platform/ios/benchmark/locations.hpp
index 96f9c6025b..1ea65d6280 100644
--- a/platform/ios/benchmark/locations.hpp
+++ b/platform/ios/benchmark/locations.hpp
@@ -1,5 +1,4 @@
-#ifndef MBGL_BENCH_LOCATIONS
-#define MBGL_BENCH_LOCATIONS
+#pragma once
#include <vector>
#include <string>
@@ -19,5 +18,3 @@ extern const std::vector<Location> locations;
}
}
-
-#endif \ No newline at end of file
diff --git a/platform/ios/bitrise.yml b/platform/ios/bitrise.yml
index 556d10a2bb..6e4771273f 100644
--- a/platform/ios/bitrise.yml
+++ b/platform/ios/bitrise.yml
@@ -22,37 +22,21 @@ workflows:
else
envman add --key SKIPCI --value false
fi
- - select-xcode-version:
- title: Select Xcode version
- run_if: '{{enveq "SKIPCI" "false"}}'
- - script:
- title: Install Homebrew dependencies
- run_if: '{{enveq "SKIPCI" "false"}}'
- inputs:
- - content: |-
- #!/bin/bash
-
- brew install pkgconfig
- brew install automake
- gem install jazzy
- script:
- title: Run make ipackage-sim
+ title: Run build
run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- content: |-
#!/bin/bash
-
- make ipackage-sim
+ set -eu -o pipefail
+ brew tap mapbox/homebrew-ios-sim-3
+ brew install mapbox/homebrew-ios-sim-3/ios-sim
+ gem install xcpretty --no-rdoc --no-ri
+ gem install jazzy --no-rdoc --no-ri
+ export BUILDTYPE=Debug
+ make ios
+ make test-ios
- is_debug: 'yes'
- - xcode-test:
- title: Run Xcode Tests
- run_if: '{{enveq "SKIPCI" "false"}}'
- inputs:
- - project_path: "./platform/ios/test/ios-tests.xcodeproj"
- - scheme: Mapbox GL Tests
- - simulator_device: iPhone 6
- opts:
- is_expand: false
- slack:
title: Post to Slack
run_if: '{{enveq "SKIPCI" "false"}}'
@@ -71,5 +55,3 @@ workflows:
failed'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
- before_run:
- after_run:
diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md
index d3c4bd5e4a..12dc5495f6 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -4,6 +4,6 @@ The Mapbox iOS SDK is an open-source framework for embedding interactive map vie
[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png)]()
-For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/CHANGELOG.md) is also available.
+For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available.
If you have any questions, please [contact our support team](https://www.mapbox.com/contact/). We welcome your [bug reports and feature requests](https://github.com/mapbox/mapbox-gl-native/issues/).
diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md
index d62e23b606..1633c7d126 100644
--- a/platform/ios/docs/pod-README.md
+++ b/platform/ios/docs/pod-README.md
@@ -2,7 +2,7 @@
The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
-For more information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/CHANGELOG.md) online.
+For more information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online.
[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png)]()
@@ -16,7 +16,7 @@ The Mapbox iOS SDK may be installed as either a dynamic framework or a static fr
This is the recommended workflow for manually integrating the SDK into an application targeting iOS 8 and above:
-1. Open the project editor and select your application target. Drag `Mapbox.framework` from the `dynamic` folder into the “Embedded Binaries” section of the General tab. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
+1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework from the `dynamic` folder into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
1. In the Build Phases tab, click the + button at the top and select “New Run Script Phase”. Enter the following code into the script text field:
@@ -33,54 +33,62 @@ bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework/strip-fra
If your application targets iOS 7.x, you’ll need to install the static framework instead:
-1. Drag the Mapbox.bundle and Mapbox.framework files in `static` into the Project navigator, checking "Copy items if needed". It should happen automatically, but ensure that:
-
- - `Mapbox.framework` is listed in your `Link Binary With Libraries` build phase.
- - Your *Framework Search Paths* (`FRAMEWORK_SEARCH_PATHS`) build setting includes the directory that contains `Mapbox.framework`. For most projects, the default value of `$(inherited) $(PROJECT_DIR)` should be sufficient.
- - `Mapbox.bundle` is in your target's *Copy Bundle Resources* build phase.
+1. Drag Mapbox.bundle and Mapbox.framework from the `static` folder into the Project navigator. In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish. Open the project editor and select your application target to verify that the following changes occurred automatically:
-1. **Optional** As [noted in the documentation](https://www.mapbox.com/ios-sdk/#telemetry_opt_out) you may use the provided Settings.bundle to provide a Telemetry opt out for users. To use the provided file, drag the `Settings.bundle` file from the release root folder into the Project navigator, checking "Copy items if needed". It should happen automatically, but ensure that:
-
- - `Settings.bundle` is in your target's *Copy Bundle Resources* build phase.
+ - In the General tab, Mapbox.framework is listed in the “Linked Frameworks and Libraries” section.
+ - In the Build Settings tab, the “Framework Search Paths” (`FRAMEWORK_SEARCH_PATHS`) build setting includes the directory that contains Mapbox.framework. For most projects, the default value of `$(inherited) $(PROJECT_DIR)` should be sufficient.
+ - In the Build Phases tab, Mapbox.bundle is listed in the “Copy Bundle Resources” build phase.
1. Add the following Cocoa Touch frameworks and libraries to the “Linked Frameworks and Libraries” section:
- - `GLKit.framework`
- - `ImageIO.framework`
- - `MobileCoreServices.framework`
- - `QuartzCore.framework`
- - `SystemConfiguration.framework`
- - `libc++.tbd`
- - `libsqlite3.tbd`
- - `libz.tbd`
-
-1. In the Build Settings tab, add `-ObjC` to the “Other Linker Flags” (`OTHER_LDFLAGS`) build setting.
+ - GLKit.framework
+ - ImageIO.framework
+ - MobileCoreServices.framework
+ - QuartzCore.framework
+ - SystemConfiguration.framework
+ - libc++.tbd
+ - libsqlite3.tbd
+ - libz.tbd
{{/STATIC}}
## Configuration
-1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target. In the Info tab, set `MGLMapboxAccessToken` to your access token. You can obtain one from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom iOS Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+
+1. In order to show the user’s current location on the map, the SDK must ask for the user’s permission to access Location Services. Go to the Info tab of the project editor. If your application supports iOS 7, set the `NSLocationUsageDescription` key to a message that explains to the user what their location is used for. If your application supports iOS 8 and above, set the `NSLocationAlwaysUsageDescription` and/or `NSLocationWhenInUseUsageDescription` key to this message instead.
-1. In order to show the user’s position on the map, you must first ask for their permission. In iOS 8 and above, this is accomplished by creating and setting the `NSLocationAlwaysUsageDescription` and/or `NSLocationWhenInUseUsageDescription` key in the `Info.plist` file.
+1. _(Optional)_ Mapbox Telemetry is a [powerful location analytics platform](https://www.mapbox.com/telemetry/) included in this SDK. By default, anonymized location and usage data is sent to Mapbox whenever the host application causes it to be gathered. This SDK provides users with a way to individually opt out of Mapbox Telemetry. You can also add this opt-out setting to your application’s Settings screen using a Settings bundle. An example Settings.bundle is provided with this SDK; drag it into the Project navigator, checking “Copy items if needed” when prompted. In the project editor, verify that the following change occurred automatically:
-1. _(Optional)_ Mapbox Telemetry is a <a href="https://www.mapbox.com/telemetry/">powerful location analytics platform</a> included in this SDK. By default, anonymized location and usage data is sent to Mapbox whenever the host application causes it to be gathered. This SDK provides users with a way to individually opt out of Mapbox Telemetry. You can also add this opt-out setting to your application’s Settings screen, using the provided `Settings.bundle`.
+ - In the General tab, Settings.bundle is listed in the “Copy Bundle Resources” build phase.
## Usage
-In a XIB or storyboard, add a Custom View and set its custom class to `MGLMapView`. If you need to manipulate the map view programmatically, import the Mapbox module in your view controller’s implementation file:
+In a storyboard or XIB, add a view to your view controller. (Drag View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically:
+
+1. Switch to the Assistant Editor.
+1. Import the `Mapbox` module.
+1. Connect the map view to a new outlet in your view controller class. (Control-drag from the map view in Interface Builder to a valid location in your view controller implementation.) The resulting outlet declaration should look something like this:
```objc
// ViewController.m
@import Mapbox;
+
+@interface ViewController : UIViewController
+
+@property (strong) IBOutlet MGLMapView *mapView;
+
+@end
```
```swift
// ViewController.swift
import Mapbox
-```
-Then switch to the Assistant Editor and create an outlet to the map view. (Control-drag from the map view in Interface Builder to a valid location in your view controller implementation.)
+class ViewController: UIViewController {
+ @IBOutlet var mapView: MGLMapView!
+}
+```
Full API documentation is included in this package, within the `documentation` folder. For more details, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/).
diff --git a/platform/ios/framework/Info-static.plist b/platform/ios/framework/Info-static.plist
new file mode 100644
index 0000000000..b42a23322d
--- /dev/null
+++ b/platform/ios/framework/Info-static.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(CURRENT_SHORT_VERSION)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
+ <key>MGLCommitHash</key>
+ <string>$(CURRENT_COMMIT_HASH)</string>
+ <key>MGLSemanticVersionString</key>
+ <string>$(CURRENT_SEMANTIC_VERSION)</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/platform/ios/framework/Info.plist b/platform/ios/framework/Info.plist
index 3bf250da27..d8962c53ab 100644
--- a/platform/ios/framework/Info.plist
+++ b/platform/ios/framework/Info.plist
@@ -5,20 +5,26 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
- <string>${EXECUTABLE_NAME}</string>
+ <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
- <string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
- <string>${PRODUCT_NAME}</string>
+ <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
- <string>0.0.0</string>
+ <string>$(CURRENT_SHORT_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>0</string>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
+ <key>MGLCommitHash</key>
+ <string>$(CURRENT_COMMIT_HASH)</string>
+ <key>MGLSemanticVersionString</key>
+ <string>$(CURRENT_SEMANTIC_VERSION)</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
</dict>
</plist>
diff --git a/platform/ios/framework/Mapbox.h b/platform/ios/framework/Mapbox.h
deleted file mode 100644
index 7b233ecac5..0000000000
--- a/platform/ios/framework/Mapbox.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#import <Foundation/Foundation.h>
-
-/// Project version number for Mapbox.
-FOUNDATION_EXPORT double MapboxVersionNumber;
-
-/// Project version string for Mapbox.
-FOUNDATION_EXPORT const unsigned char MapboxVersionString[];
-
-#import "MGLAccountManager.h"
-#import "MGLAnnotation.h"
-#import "MGLAnnotationImage.h"
-#import "MGLCalloutView.h"
-#import "MGLMapCamera.h"
-#import "MGLGeometry.h"
-#import "MGLMapView.h"
-#import "MGLMapView+IBAdditions.h"
-#import "MGLMapView+MGLCustomStyleLayerAdditions.h"
-#import "MGLMultiPoint.h"
-#import "MGLOfflinePack.h"
-#import "MGLOfflineRegion.h"
-#import "MGLOfflineStorage.h"
-#import "MGLOverlay.h"
-#import "MGLPointAnnotation.h"
-#import "MGLPolygon.h"
-#import "MGLPolyline.h"
-#import "MGLShape.h"
-#import "MGLStyle.h"
-#import "MGLTilePyramidOfflineRegion.h"
-#import "MGLTypes.h"
-#import "MGLUserLocation.h"
diff --git a/platform/ios/framework/Mapbox.m b/platform/ios/framework/Mapbox.m
deleted file mode 100644
index e745602df0..0000000000
--- a/platform/ios/framework/Mapbox.m
+++ /dev/null
@@ -1,32 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-#import "../src/NSBundle+MGLAdditions.h"
-#import "../src/NSProcessInfo+MGLAdditions.h"
-#import "../../darwin/src/NSString+MGLAdditions.h"
-
-__attribute__((constructor))
-static void InitializeMapbox() {
- static int initialized = 0;
- if (initialized) {
- return;
- }
-
- mgl_linkBundleCategory();
- mgl_linkStringCategory();
- mgl_linkProcessInfoCategory();
-
- [MGLAccountManager class];
- [MGLAnnotationImage class];
- [MGLMapCamera class];
- [MGLMapView class];
- [MGLMultiPoint class];
- [MGLOfflinePack class];
- [MGLOfflineStorage class];
- [MGLPointAnnotation class];
- [MGLPolygon class];
- [MGLPolyline class];
- [MGLShape class];
- [MGLStyle class];
- [MGLTilePyramidOfflineRegion class];
- [MGLUserLocation class];
-}
diff --git a/platform/ios/framework/Settings.bundle/Base.lproj/Root.strings b/platform/ios/framework/Settings.bundle/Base.lproj/Root.strings
new file mode 100644
index 0000000000..1945fe1141
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/Base.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Privacy Settings";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetry";
+"TELEMETRY_GROUP_FOOTER" = "This setting allows the application to share anonymized location and usage data with Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/Root.plist b/platform/ios/framework/Settings.bundle/Root.plist
index 889610e152..fba2065a8f 100644
--- a/platform/ios/framework/Settings.bundle/Root.plist
+++ b/platform/ios/framework/Settings.bundle/Root.plist
@@ -6,11 +6,11 @@
<array>
<dict>
<key>Title</key>
- <string>Privacy Settings</string>
+ <string>TELEMETRY_GROUP_TITLE</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>FooterText</key>
- <string>This setting allows the application to share anonymized location and usage data with Mapbox.</string>
+ <string>TELEMETRY_GROUP_FOOTER</string>
</dict>
<dict>
<key>DefaultValue</key>
@@ -18,7 +18,7 @@
<key>Key</key>
<string>MGLMapboxMetricsEnabled</string>
<key>Title</key>
- <string>Mapbox Telemetry</string>
+ <string>TELEMETRY_SWITCH_TITLE</string>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>TrueValue</key>
diff --git a/platform/ios/framework/Settings.bundle/en.lproj/Root.strings b/platform/ios/framework/Settings.bundle/en.lproj/Root.strings
deleted file mode 100644
index e7658d692d..0000000000
--- a/platform/ios/framework/Settings.bundle/en.lproj/Root.strings
+++ /dev/null
Binary files differ
diff --git a/platform/ios/framework/framework-ios.gypi b/platform/ios/framework/framework-ios.gypi
deleted file mode 100644
index 3d733d963a..0000000000
--- a/platform/ios/framework/framework-ios.gypi
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'iossdk',
- 'product_name': 'Mapbox',
- 'type': 'shared_library',
- 'mac_bundle': 1,
-
- 'dependencies': [
- 'mbgl.gyp:core',
- 'mbgl.gyp:platform-<(platform_lib)',
- 'mbgl.gyp:http-<(http_lib)',
- 'mbgl.gyp:asset-<(asset_lib)',
- ],
-
- 'xcode_settings': {
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'COMBINE_HIDPI_IMAGES': 'NO', # disable combining @2x, @3x images into .tiff files
- 'CURRENT_PROJECT_VERSION': '0',
- 'DEFINES_MODULE': 'YES',
- 'DYLIB_INSTALL_NAME_BASE': '@rpath',
- 'INFOPLIST_FILE': '../platform/ios/framework/Info.plist',
- 'IPHONEOS_DEPLOYMENT_TARGET': '8.0',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '$(inherited)',
- '@executable_path/Frameworks',
- '@loader_path/Frameworks',
- ],
- 'PRODUCT_BUNDLE_IDENTIFIER': 'com.mapbox.sdk.ios',
- 'OTHER_LDFLAGS': [ '-stdlib=libc++', '-lstdc++' ],
- 'SDKROOT': 'iphoneos',
- 'SKIP_INSTALL': 'YES',
- 'SUPPORTED_PLATFORMS': [
- 'iphonesimulator',
- 'iphoneos',
- ],
- 'VERSIONING_SYSTEM': 'apple-generic',
- },
-
- 'mac_framework_headers': [
- 'Mapbox.h',
- '<!@(find ../platform/{darwin,ios}/include -type f \! -name \'.*\' \! -name Mapbox.h)',
- ],
-
- 'sources': [
- 'Mapbox.m',
- ],
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'CODE_SIGN_IDENTITY': 'iPhone Developer',
- 'DEAD_CODE_STRIPPING': 'YES',
- 'GCC_OPTIMIZATION_LEVEL': '0',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'ARCHS': [ "armv7", "armv7s", "arm64", "i386", "x86_64" ],
- 'CODE_SIGN_IDENTITY': 'iPhone Distribution',
- 'DEAD_CODE_STRIPPING': 'YES',
- 'GCC_OPTIMIZATION_LEVEL': 's',
- },
- },
- },
- },
- ]
-}
diff --git a/platform/ios/include/MGLAnnotationImage.h b/platform/ios/include/MGLAnnotationImage.h
deleted file mode 100644
index f9d9e70566..0000000000
--- a/platform/ios/include/MGLAnnotationImage.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#import <UIKit/UIKit.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The MGLAnnotationImage class is responsible for presenting point-based annotations visually on a map view. Annotation image objects wrap `UIImage` objects and may be recycled later and put into a reuse queue that is maintained by the map view. */
-@interface MGLAnnotationImage : NSObject
-
-#pragma mark Initializing and Preparing the Image Object
-
-/**
- Initializes and returns a new annotation image object.
-
- @param image The image to be displayed for the annotation.
- @param reuseIdentifier The string that identifies that this annotation image is reusable.
- @return The initialized annotation image object or `nil` if there was a problem initializing the object.
- */
-+ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier;
-
-#pragma mark Getting and Setting Attributes
-
-/** The image to be displayed for the annotation. */
-@property (nonatomic, strong) UIImage *image;
-
-/**
- The string that identifies that this annotation image is reusable. (read-only)
-
- You specify the reuse identifier when you create the image object. You use this type later to retrieve an annotation image object that was created previously but which is currently unused because its annotation is not on screen.
-
- If you define distinctly different types of annotations (with distinctly different annotation images to go with them), you can differentiate between the annotation types by specifying different reuse identifiers for each one.
- */
-@property (nonatomic, readonly) NSString *reuseIdentifier;
-
-/**
- A Boolean value indicating whether the annotation is enabled.
-
- The default value of this property is `YES`. If the value of this property is `NO`, the annotation image ignores touch events and cannot be selected.
- */
-@property (nonatomic, getter=isEnabled) BOOL enabled;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/ios/include/MGLCalloutView.h b/platform/ios/include/MGLCalloutView.h
deleted file mode 100644
index 8e72ee9d68..0000000000
--- a/platform/ios/include/MGLCalloutView.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol MGLCalloutViewDelegate;
-@protocol MGLAnnotation;
-
-/**
- A protocol for a `UIView` subclass that displays information about a selected annotation near that annotation.
- */
-@protocol MGLCalloutView <NSObject>
-
-/**
- An object conforming to the `MGLAnnotation` protocol whose details this callout view displays.
- */
-@property (nonatomic, strong) id <MGLAnnotation> representedObject;
-
-/**
- A view that the user may tap to perform an action. This view is conventionally positioned on the left side of the callout view.
- */
-@property (nonatomic, strong) UIView *leftAccessoryView;
-
-/**
- A view that the user may tap to perform an action. This view is conventionally positioned on the right side of the callout view.
- */
-@property (nonatomic, strong) UIView *rightAccessoryView;
-
-/**
- An object conforming to the `MGLCalloutViewDelegate` method that receives messages related to the callout view’s interactive subviews.
- */
-@property (nonatomic, weak) id<MGLCalloutViewDelegate> delegate;
-
-/**
- Presents a callout view by adding it to `inView` and pointing at the given rect of `inView`’s bounds. Constrains the callout to the bounds of the given view.
- */
-- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
-
-/**
- Dismisses the callout view.
- */
-- (void)dismissCalloutAnimated:(BOOL)animated;
-
-@end
-
-/**
- The MGLCalloutViewDelegate protocol defines a set of optional methods that you can use to receive messages from an object that conforms to the MGLCalloutView protocol. The callout view uses these methods to inform the delegate that the user has interacted with the the callout view.
- */
-@protocol MGLCalloutViewDelegate <NSObject>
-
-@optional
-/**
- Returns a Boolean value indicating whether the entire callout view “highlights” when tapped. The default value is `YES`, which means the callout view highlights when tapped.
-
- The return value of this method is ignored unless the delegate also responds to the `-calloutViewTapped` method.
- */
-- (BOOL)calloutViewShouldHighlight:(UIView<MGLCalloutView> *)calloutView;
-
-/**
- Tells the delegate that the callout view has been tapped.
- */
-- (void)calloutViewTapped:(UIView<MGLCalloutView> *)calloutView;
-
-@end
-
-NS_ASSUME_NONNULL_END \ No newline at end of file
diff --git a/platform/ios/include/MGLMapView.h b/platform/ios/include/MGLMapView.h
deleted file mode 100644
index bfc1d3c679..0000000000
--- a/platform/ios/include/MGLMapView.h
+++ /dev/null
@@ -1,1260 +0,0 @@
-#import "MGLGeometry.h"
-#import "MGLMapCamera.h"
-
-#import <UIKit/UIKit.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@class MGLAnnotationImage;
-@class MGLUserLocation;
-@class MGLPolyline;
-@class MGLPolygon;
-@class MGLShape;
-
-@protocol MGLMapViewDelegate;
-@protocol MGLAnnotation;
-@protocol MGLOverlay;
-@protocol MGLCalloutView;
-
-/** The vertical alignment of an annotation within a map view. */
-typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
- /** Aligns the annotation vertically in the center of the map view. */
- MGLAnnotationVerticalAlignmentCenter = 0,
- /** Aligns the annotation vertically at the top of the map view. */
- MGLAnnotationVerticalAlignmentTop,
- /** Aligns the annotation vertically at the bottom of the map view. */
- MGLAnnotationVerticalAlignmentBottom,
-};
-
-/** Options for enabling debugging features in an MGLMapView instance. */
-typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
- /** Edges of tile boundaries are shown as thick, red lines to help diagnose
- tile clipping issues. */
- MGLMapDebugTileBoundariesMask = 1 << 1,
- /** Each tile shows its tile coordinate (x/y/z) in the upper-left corner. */
- MGLMapDebugTileInfoMask = 1 << 2,
- /** Each tile shows a timestamp indicating when it was loaded. */
- MGLMapDebugTimestampsMask = 1 << 3,
- /** Edges of glyphs and symbols are shown as faint, green lines to help
- diagnose collision and label placement issues. */
- MGLMapDebugCollisionBoxesMask = 1 << 4,
-};
-
-/**
- An interactive, customizable map view with an interface similar to the one
- provided by Apple's MapKit.
-
- Using `MGLMapView`, you can embed the map inside a view, allow users to
- manipulate it with standard gestures, animate the map between different
- viewpoints, and present information in the form of annotations and overlays.
-
- The map view loads scalable vector tiles that conform to the
- <a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
- It styles them with a style that conforms to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
- Such styles can be designed in
- <a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
- mapbox.com.
-
- A collection of Mapbox-hosted styles is available through the `MGLStyle`
- class. These basic styles use
- <a href="https://www.mapbox.com/developers/vector-tiles/mapbox-streets">Mapbox Streets</a>
- or <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a> data
- sources, but you can specify a custom style that makes use of your own data.
-
- Mapbox-hosted vector tiles and styles require an API access token, which you
- can obtain from the
- <a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>.
- Access tokens associate requests to Mapbox's vector tile and style APIs with
- your Mapbox account. They also deter other developers from using your styles
- without your permission.
-
- @note You are responsible for getting permission to use the map data and for
- ensuring that your use adheres to the relevant terms of use.
- */
-IB_DESIGNABLE
-@interface MGLMapView : UIView
-
-#pragma mark Creating Instances
-
-/**
- Initializes and returns a newly allocated map view with the specified frame
- and the default style.
-
- @param frame The frame for the view, measured in points.
- @return An initialized map view.
- */
-- (instancetype)initWithFrame:(CGRect)frame;
-
-/**
- Initializes and returns a newly allocated map view with the specified frame
- and style URL.
-
- @param frame The frame for the view, measured in points.
- @param styleURL URL of the map style to display. The URL may be a full HTTP
- or HTTPS URL, a Mapbox URL indicating the style's map ID
- (`mapbox://styles/{user}/{style}`), or a path to a local file relative
- to the application's resource path. Specify `nil` for the default style.
- @return An initialized map view.
- */
-- (instancetype)initWithFrame:(CGRect)frame styleURL:(nullable NSURL *)styleURL;
-
-#pragma mark Accessing the Delegate
-
-/**
- The receiver's delegate.
-
- A map view sends messages to its delegate to notify it of changes to its
- contents or the viewpoint. The delegate also provides information about
- annotations displayed on the map, such as the styles to apply to individual
- annotations.
- */
-@property(nonatomic, weak, nullable) IBOutlet id<MGLMapViewDelegate> delegate;
-
-#pragma mark Configuring the Map's Appearance
-
-/**
- URLs of the styles bundled with the library.
-
- @deprecated Call the relevant class method of `MGLStyle` for the URL of a
- particular default style.
- */
-@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((deprecated("Call the relevant class method of MGLStyle for the URL of a particular default style.")));
-
-/**
- URL of the style currently displayed in the receiver.
-
- The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style's
- map ID (`mapbox://styles/{user}/{style}`), or a path to a local file
- relative to the application's resource path.
-
- If you set this property to `nil`, the receiver will use the default style
- and this property will automatically be set to that style's URL.
- */
-@property (nonatomic, null_resettable) NSURL *styleURL;
-
-/**
- A control indicating the map's direction and allowing the user to manipulate
- the direction, positioned in the upper-right corner.
- */
-@property (nonatomic, readonly) UIImageView *compassView;
-
-/**
- The Mapbox logo, positioned in the lower-left corner.
-
- @note The Mapbox terms of service, which governs the use of Mapbox-hosted
- vector tiles and styles,
- <a href="https://www.mapbox.com/help/mapbox-logo/">requires</a> most Mapbox
- customers to display the Mapbox logo. If this applies to you, do not
- hide this view or change its contents.
- */
-@property (nonatomic, readonly) UIImageView *logoView;
-
-/**
- A view showing legally required copyright notices and telemetry settings,
- positioned at the bottom-right of the map view.
-
- @note The Mapbox terms of service, which governs the use of Mapbox-hosted
- vector tiles and styles,
- <a href="https://www.mapbox.com/help/attribution/">requires</a> these
- copyright notices to accompany any map that features Mapbox-designed styles,
- OpenStreetMap data, or other Mapbox data such as satellite or terrain
- data. If that applies to this map view, do not hide this view or remove
- any notices from it.
-
- @note You are additionally
- <a href="https://www.mapbox.com/help/telemetry-opt-out-for-users/">required</a>
- to provide users with the option to disable anonymous usage and location
- sharing (telemetry). If this view is hidden, you must implement this
- setting elsewhere in your app or via `Settings.bundle`. See our
- <a href="https://www.mapbox.com/ios-sdk/#telemetry_opt_out">website</a> for
- implementation help.
- */
-@property (nonatomic, readonly) UIButton *attributionButton;
-
-/**
- Currently active style classes, represented as an array of string identifiers.
- */
-@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses;
-
-/**
- Returns a Boolean value indicating whether the style class with the given
- identifier is currently active.
-
- @param styleClass The style class to query for.
- @return Whether the style class is currently active.
- */
-- (BOOL)hasStyleClass:(NSString *)styleClass;
-
-/**
- Activates the style class with the given identifier.
-
- @param styleClass The style class to activate.
- */
-- (void)addStyleClass:(NSString *)styleClass;
-
-/**
- Deactivates the style class with the given identifier.
-
- @param styleClass The style class to deactivate.
- */
-- (void)removeStyleClass:(NSString *)styleClass;
-
-#pragma mark Displaying the User's Location
-
-/**
- A Boolean value indicating whether the map may display the user location.
-
- Setting this property to `YES` causes the map view to use the Core Location
- framework to find the current location. As long as this property is `YES`, the
- map view continues to track the user's location and update it periodically.
-
- This property does not indicate whether the user's position is actually visible
- on the map, only whether the map view is allowed to display it. To determine
- whether the user's position is visible, use the `userLocationVisible` property.
- The default value of this property is `NO`.
-
- On iOS 8 and above, your app must specify a value for
- `NSLocationWhenInUseUsageDescription` or `NSLocationAlwaysUsageDescription` in
- its `Info.plist` to satisfy the requirements of the underlying Core Location
- framework when enabling this property.
- */
-@property (nonatomic, assign) BOOL showsUserLocation;
-
-/**
- A Boolean value indicating whether the device's current location is visible in
- the map view.
-
- Use `showsUserLocation` to control the visibility of the on-screen user
- location annotation.
- */
-@property (nonatomic, assign, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
-
-/**
- Returns the annotation object indicating the user's current location.
- */
-@property (nonatomic, readonly, nullable) MGLUserLocation *userLocation;
-
-/**
- The mode used to track the user location. The default value is
- `MGLUserTrackingModeNone`.
-
- Changing the value of this property updates the map view with an animated
- transition. If you don’t want to animate the change, use the
- `-setUserTrackingMode:animated:` method instead.
- */
-@property (nonatomic, assign) MGLUserTrackingMode userTrackingMode;
-
-/**
- Sets the mode used to track the user location, with an optional transition.
-
- @param mode The mode used to track the user location.
- @param animated If `YES`, there is an animated transition from the current
- viewport to a viewport that results from the change to `mode`. If `NO`, the
- map view instantaneously changes to the new viewport. This parameter only
- affects the initial transition; subsequent changes to the user location or
- heading are always animated.
- */
-- (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
-
-/**
- The vertical alignment of the user location annotation within the receiver. The
- default value is `MGLAnnotationVerticalAlignmentCenter`.
-
- Changing the value of this property updates the map view with an animated
- transition. If you don’t want to animate the change, use the
- `-setUserLocationVerticalAlignment:animated:` method instead.
- */
-@property (nonatomic, assign) MGLAnnotationVerticalAlignment userLocationVerticalAlignment;
-
-/**
- Sets the vertical alignment of the user location annotation within the
- receiver, with an optional transition.
-
- @param alignment The vertical alignment of the user location annotation.
- @param animated If `YES`, the user location annotation animates to its new
- position within the map view. If `NO`, the user location annotation
- instantaneously moves to its new position.
- */
-- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated;
-
-/**
- Whether the map view should display a heading calibration alert when necessary.
- The default value is `YES`.
- */
-@property (nonatomic, assign) BOOL displayHeadingCalibration;
-
-/**
- The geographic coordinate that is the subject of observation as the user
- location is being tracked.
-
- By default, this property is set to an invalid coordinate, indicating that
- there is no target. In course tracking mode, the target forms one of two foci
- in the viewport, the other being the user location annotation. Typically, this
- property is set to a destination or waypoint in a real-time navigation scene.
- As the user annotation moves toward the target, the map automatically zooms in
- to fit both foci optimally within the viewport.
-
- This property has no effect if the `userTrackingMode` property is set to a
- value other than `MGLUserTrackingModeFollowWithCourse`.
-
- Changing the value of this property updates the map view with an animated
- transition. If you don’t want to animate the change, use the
- `-setTargetCoordinate:animated:` method instead.
- */
-@property (nonatomic, assign) CLLocationCoordinate2D targetCoordinate;
-
-/**
- Sets the geographic coordinate that is the subject of observation as the user
- location is being tracked, with an optional transition animation.
-
- By default, the target coordinate is set to an invalid coordinate, indicating
- that there is no target. In course tracking mode, the target forms one of two
- foci in the viewport, the other being the user location annotation. Typically,
- the target is set to a destination or waypoint in a real-time navigation scene.
- As the user annotation moves toward the target, the map automatically zooms in
- to fit both foci optimally within the viewport.
-
- This method has no effect if the `userTrackingMode` property is set to a value
- other than `MGLUserTrackingModeFollowWithCourse`.
-
- @param targetCoordinate The target coordinate to fit within the viewport.
- @param animated If `YES`, the map animates to fit the target within the map
- view. If `NO`, the map fits the target instantaneously.
- */
-- (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated;
-
-#pragma mark Configuring How the User Interacts with the Map
-
-/**
- A Boolean value that determines whether the user may zoom the map in and
- out, changing the zoom level.
-
- When this property is set to `YES`, the default, the user may zoom the map
- in and out by pinching two fingers or by double tapping, holding, and moving
- the finger up and down.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the map zoom
- programmatically.
- */
-@property(nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
-
-/**
- A Boolean value that determines whether the user may scroll around the map,
- changing the center coordinate.
-
- When this property is set to `YES`, the default, the user may scroll the map
- by dragging or swiping with one finger.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the map location
- programmatically.
- */
-@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
-
-/**
- A Boolean value that determines whether the user may rotate the map,
- changing the direction.
-
- When this property is set to `YES`, the default, the user may rotate the map
- by moving two fingers in a circular motion.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still rotate the map
- programmatically.
- */
-@property(nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
-
-/**
- A Boolean value that determines whether the user may change the pitch (tilt) of
- the map.
-
- When this property is set to `YES`, the default, the user may tilt the map by
- vertically dragging two fingers.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the pitch of the map
- programmatically.
-
- The default value of this property is `YES`.
- */
-@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
-
-#pragma mark Manipulating the Viewpoint
-
-/**
- The geographic coordinate at the center of the map view.
-
- Changing the value of this property centers the map on the new coordinate
- without changing the current zoom level.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setCenterCoordinate:animated:` method
- instead.
- */
-@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
-
-/**
- Changes the center coordinate of the map and optionally animates the change.
-
- Changing the center coordinate centers the map on the new coordinate without
- changing the current zoom level.
-
- @param coordinate The new center coordinate for the map.
- @param animated Specify `YES` if you want the map view to scroll to the new
- location or `NO` if you want the map to display the new location
- immediately.
- */
-- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
-
-/**
- Changes the center coordinate and zoom level of the map and optionally animates
- the change.
-
- @param centerCoordinate The new center coordinate for the map.
- @param zoomLevel The new zoom level for the map.
- @param animated Specify `YES` if you want the map view to animate scrolling and
- zooming to the new location or `NO` if you want the map to display the new
- location immediately.
- */
-- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated;
-
-/**
- Changes the center coordinate, zoom level, and direction of the map and
- optionally animates the change.
-
- @param centerCoordinate The new center coordinate for the map.
- @param zoomLevel The new zoom level for the map.
- @param direction The new direction for the map, measured in degrees relative to
- true north.
- @param animated Specify `YES` if you want the map view to animate scrolling,
- zooming, and rotating to the new location or `NO` if you want the map to
- display the new location immediately.
- */
-- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated;
-
-/**
- Changes the center coordinate, zoom level, and direction of the map, calling a
- completion handler at the end of an optional animation.
-
- @param centerCoordinate The new center coordinate for the map.
- @param zoomLevel The new zoom level for the map.
- @param direction The new direction for the map, measured in degrees relative to
- true north.
- @param animated Specify `YES` if you want the map view to animate scrolling,
- zooming, and rotating to the new location or `NO` if you want the map to
- display the new location immediately.
- @param completion The block executed after the animation finishes.
- */
-- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion;
-
-/** The zoom level of the receiver.
-
- In addition to affecting the visual size and detail of features on the map,
- the zoom level affects the size of the vector tiles that are loaded. At zoom
- level 0, each tile covers the entire world map; at zoom level 1, it covers ¼
- of the world; at zoom level 2, <sup>1</sup>⁄<sub>16</sub> of the world, and
- so on.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setZoomLevel:animated:` method instead.
- */
-@property (nonatomic) double zoomLevel;
-
-/**
- Changes the zoom level of the map and optionally animates the change.
-
- Changing the zoom level scales the map without changing the current center
- coordinate.
-
- @param zoomLevel The new zoom level for the map.
- @param animated Specify `YES` if you want the map view to animate the change
- to the new zoom level or `NO` if you want the map to display the new
- zoom level immediately.
- */
-- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated;
-
-/**
- * The minimum zoom level at which the map can be shown.
- *
- * Depending on the map view’s aspect ratio, the map view may be prevented
- * from reaching the minimum zoom level, in order to keep the map from
- * repeating within the current viewport.
- *
- * If the value of this property is greater than that of the
- * maximumZoomLevel property, the behavior is undefined.
- *
- * The default minimumZoomLevel is 0.
- */
-@property (nonatomic) double minimumZoomLevel;
-
-/**
- * The maximum zoom level the map can be shown at.
- *
- * If the value of this property is smaller than that of the
- * minimumZoomLevel property, the behavior is undefined.
- *
- * The default maximumZoomLevel is 20.
- */
-@property (nonatomic) double maximumZoomLevel;
-
-/**
- The heading of the map, measured in degrees clockwise from true north.
-
- The value `0` means that the top edge of the map view corresponds to true
- north. The value `90` means the top of the map is pointing due east. The
- value `180` means the top of the map points due south, and so on.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setDirection:animated:` method instead.
- */
-@property (nonatomic) CLLocationDirection direction;
-
-/**
- Changes the heading of the map and optionally animates the change.
-
- @param direction The heading of the map, measured in degrees clockwise from
- true north.
- @param animated Specify `YES` if you want the map view to animate the change
- to the new heading or `NO` if you want the map to display the new
- heading immediately.
-
- Changing the heading rotates the map without changing the current center
- coordinate or zoom level.
- */
-- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated;
-
-/**
- Resets the map rotation to a northern heading — a `direction` of `0` degrees.
- */
-- (IBAction)resetNorth;
-
-/**
- The coordinate bounds visible in the receiver's viewport.
-
- Changing the value of this property updates the receiver immediately. If you
- want to animate the change, call `-setVisibleCoordinateBounds:animated:`
- instead.
- */
-@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
-
-/**
- Changes the receiver’s viewport to fit the given coordinate bounds,
- optionally animating the change.
-
- @param bounds The bounds that the viewport will show in its entirety.
- @param animated Specify `YES` to animate the change by smoothly scrolling
- and zooming or `NO` to immediately display the given bounds.
- */
-- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
-
-/**
- Changes the receiver's viewport to fit the given coordinate bounds and
- optionally some additional padding on each side.
-
- @param bounds The bounds that the viewport will show in its entirety.
- @param insets The minimum padding (in screen points) that will be visible
- around the given coordinate bounds.
- @param animated Specify `YES` to animate the change by smoothly scrolling and
- zooming or `NO` to immediately display the given bounds.
- */
-- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
-
-/**
- Changes the receiver's viewport to fit all of the given coordinates and
- optionally some additional padding on each side.
-
- @param coordinates The coordinates that the viewport will show.
- @param count The number of coordinates. This number must not be greater than
- the number of elements in `coordinates`.
- @param insets The minimum padding (in screen points) that will be visible
- around the given coordinate bounds.
- @param animated Specify `YES` to animate the change by smoothly scrolling and
- zooming or `NO` to immediately display the given bounds.
- */
-- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
-
-/**
- Changes the receiver's viewport to fit all of the given coordinates and
- optionally some additional padding on each side.
-
- @param coordinates The coordinates that the viewport will show.
- @param count The number of coordinates. This number must not be greater than
- the number of elements in `coordinates`.
- @param insets The minimum padding (in screen points) that will be visible
- around the given coordinate bounds.
- @param direction The direction to rotate the map to, measured in degrees
- relative to true north.
- @param duration The duration to animate the change in seconds.
- @param function The timing function to animate the change.
- @param completion The block executed after the animation finishes.
- */
-- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
-
-/**
- Sets the visible region so that the map displays the specified annotations.
-
- Calling this method updates the value in the `visibleCoordinateBounds` property
- and potentially other properties to reflect the new map region. A small amount
- of padding is reserved around the edges of the map view. To specify a different
- amount of padding, use the `-showAnnotations:edgePadding:animated:` method.
-
- @param annotations The annotations that you want to be visible in the map.
- @param animated `YES` if you want the map region change to be animated, or `NO`
- if you want the map to display the new region immediately without animations.
- */
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated;
-
-/**
- Sets the visible region so that the map displays the specified annotations with
- the specified amount of padding on each side.
-
- Calling this method updates the value in the visibleCoordinateBounds property
- and potentially other properties to reflect the new map region.
-
- @param annotations The annotations that you want to be visible in the map.
- @param insets The minimum padding (in screen points) around the edges of the
- map view to keep clear of annotations.
- @param animated `YES` if you want the map region change to be animated, or `NO`
- if you want the map to display the new region immediately without animations.
- */
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
-
-/**
- A camera representing the current viewpoint of the map.
- */
-@property (nonatomic, copy) MGLMapCamera *camera;
-
-/**
- Moves the viewpoint to a different location with respect to the map with an
- optional transition animation.
-
- @param camera The new viewpoint.
- @param animated Specify `YES` if you want the map view to animate the change to
- the new viewpoint or `NO` if you want the map to display the new viewpoint
- immediately.
- */
-- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
-
-/**
- Moves the viewpoint to a different location with respect to the map with an
- optional transition duration and timing function.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously.
- @param function A timing function used for the animation. Set this parameter to
- `nil` for a transition that matches most system animations. If the duration
- is `0`, this parameter is ignored.
- */
-- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function;
-
-/**
- Moves the viewpoint to a different location with respect to the map with an
- optional transition duration and timing function.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously.
- @param function A timing function used for the animation. Set this parameter to
- `nil` for a transition that matches most system animations. If the duration
- is `0`, this parameter is ignored.
- @param completion The block to execute after the animation finishes.
- */
-- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
-
-/**
- Moves the viewpoint to a different location using a transition animation that
- evokes powered flight and a default duration based on the length of the flight
- path.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param completion The block to execute after the animation finishes.
- */
-- (void)flyToCamera:(MGLMapCamera *)camera completionHandler:(nullable void (^)(void))completion;
-
-/**
- Moves the viewpoint to a different location using a transition animation that
- evokes powered flight and an optional transition duration.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously. Specify a negative value to use the default duration, which
- is based on the length of the flight path.
- @param completion The block to execute after the animation finishes.
- */
-- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration completionHandler:(nullable void (^)(void))completion;
-
-/**
- Moves the viewpoint to a different location using a transition animation that
- evokes powered flight and an optional transition duration and peak altitude.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously. Specify a negative value to use the default duration, which
- is based on the length of the flight path.
- @param peakAltitude The altitude, measured in meters, at the midpoint of the
- animation. The value of this parameter is ignored if it is negative or if
- the animation transition resulting from a similar call to
- `-setCamera:animated:` would have a midpoint at a higher altitude.
- @param completion The block to execute after the animation finishes.
- */
-- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;
-
-/**
- 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 `UIEdgeInsetsZero`, 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.
-
- When the map view’s superview is an instance of `UIViewController` whose
- `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this
- property may be overridden at any time.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setContentInset:animated:` method
- instead.
- */
-@property (nonatomic, assign) UIEdgeInsets contentInset;
-
-/**
- Sets the distance from the edges of the map view’s frame to the edges of the
- map view’s logical viewport with an optional transition animation.
-
- When the value of this property is equal to `UIEdgeInsetsZero`, 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.
-
- When the map view’s superview is an instance of `UIViewController` whose
- `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this
- property may be overridden at any time.
-
- @param contentInset The new values to inset the content by.
- @param animated Specify `YES` if you want the map view to animate the change to
- the content inset or `NO` if you want the map to inset the content
- immediately.
- */
-- (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated;
-
-#pragma mark Converting Geographic Coordinates
-
-/**
- Converts a point in the given view's coordinate system to a geographic
- coordinate.
-
- @param point The point to convert.
- @param view The view in whose coordinate system the point is expressed.
- @return The geographic coordinate at the given point.
- */
-- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view;
-
-/**
- Converts a geographic coordinate to a point in the given view's coordinate
- system.
-
- @param coordinate The geographic coordinate to convert.
- @param view The view in whose coordinate system the returned point should be
- expressed. If this parameter is `nil`, the returned point is expressed
- in the window's coordinate system. If `view` is not `nil`, it must
- belong to the same window as the map view.
- @return The point (in the appropriate view or window coordinate system)
- corresponding to the given geographic coordinate.
- */
-- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable UIView *)view;
-
-/**
- Converts a rectangle in the given view’s coordinate system to a geographic
- bounding box.
-
- @param rect The rectangle to convert.
- @param view The view in whose coordinate system the rectangle is expressed.
- @return The geographic bounding box coextensive with the given rectangle.
- */
-- (MGLCoordinateBounds)convertRect:(CGRect)rect toCoordinateBoundsFromView:(nullable UIView *)view;
-
-/**
- Converts a geographic bounding box to a rectangle in the given view’s
- coordinate system.
-
- @param bounds The geographic bounding box to convert.
- @param view The view in whose coordinate system the returned rectangle should
- be expressed. If this parameter is `nil`, the returned rectangle is
- expressed in the window’s coordinate system. If `view` is not `nil`, it must
- belong to the same window as the map view.
- */
-- (CGRect)convertCoordinateBounds:(MGLCoordinateBounds)bounds toRectToView:(nullable UIView *)view;
-
-/**
- Returns the distance spanned by one point in the map view's coordinate system
- at the given latitude and current zoom level.
-
- The distance between points decreases as the latitude approaches the poles.
- This relationship parallels the relationship between longitudinal coordinates
- at different latitudes.
-
- @param latitude The latitude of the geographic coordinate represented by the
- point.
- @return The distance in meters spanned by a single point.
- */
-- (CLLocationDistance)metersPerPointAtLatitude:(CLLocationDegrees)latitude;
-
-- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude __attribute__((deprecated("Use -metersPerPointAtLatitude:.")));
-
-#pragma mark Annotating the Map
-
-/**
- The complete list of annotations associated with the receiver. (read-only)
-
- The objects in this array must adopt the `MGLAnnotation` protocol. If no
- annotations are associated with the map view, the value of this property is
- `nil`.
- */
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
-
-/**
- Adds an annotation to the map view.
-
- @param annotation The annotation object to add to the receiver. This object
- must conform to the `MGLAnnotation` protocol. The map view retains the
- annotation object. */
-- (void)addAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Adds an array of annotations to the map view.
-
- @param annotations An array of annotation objects. Each object in the array
- must conform to the `MGLAnnotation` protocol. The map view retains each
- individual annotation object.
- */
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
-
-/**
- Removes an annotation from the map view, deselecting it if it is selected.
-
- Removing an annotation object dissociates it from the map view entirely,
- preventing it from being displayed on the map. Thus you would typically call
- this method only when you want to hide or delete a given annotation.
-
- @param annotation The annotation object to remove. This object must conform
- to the `MGLAnnotation` protocol
- */
-- (void)removeAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Removes an array of annotations from the map view, deselecting any selected
- annotations in the array.
-
- Removing annotation objects dissociates them from the map view entirely,
- preventing them from being displayed on the map. Thus you would typically
- call this method only when you want to hide or delete the given annotations.
-
- @param annotations The array of annotation objects to remove. Objects in the
- array must conform to the `MGLAnnotation` protocol.
- */
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
-
-/**
- Returns a reusable annotation image object associated with its identifier.
-
- For performance reasons, you should generally reuse `MGLAnnotationImage`
- objects for identical-looking annotations in your map views. Dequeueing
- saves time and memory during performance-critical operations such as
- scrolling.
-
- @param identifier A string identifying the annotation image to be reused.
- This string is the same one you specify when initially returning the
- annotation image object using the `-mapView:imageForAnnotation:` method.
- @return An annotation image object with the given identifier, or `nil` if no
- such object exists in the reuse queue.
- */
-- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier;
-
-#pragma mark Managing Annotation Selections
-
-/**
- The currently selected annotations.
-
- Assigning a new array to this property selects only the first annotation in
- the array.
- */
-@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
-
-/**
- Selects an annotation and displays a callout view for it.
-
- If the given annotation is not visible within the current viewport, this
- method has no effect.
-
- @param annotation The annotation object to select.
- @param animated If `YES`, the callout view is animated into position.
- */
-- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated;
-
-/**
- Deselects an annotation and hides its callout view.
-
- @param annotation The annotation object to deselect.
- @param animated If `YES`, the callout view is animated offscreen.
- */
-- (void)deselectAnnotation:(nullable id <MGLAnnotation>)annotation animated:(BOOL)animated;
-
-#pragma mark Overlaying the Map
-
-/**
- Adds a single overlay object to the map.
-
- To remove an overlay from a map, use the `-removeOverlay:` method.
-
- @param overlay The overlay object to add. This object must conform to the
- `MGLOverlay` protocol. */
-- (void)addOverlay:(id <MGLOverlay>)overlay;
-
-/**
- Adds an array of overlay objects to the map.
-
- To remove multiple overlays from a map, use the `-removeOverlays:` method.
-
- @param overlays An array of objects, each of which must conform to the
- `MGLOverlay` protocol.
- */
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
-
-/**
- Removes a single overlay object from the map.
-
- If the specified overlay is not currently associated with the map view, this
- method does nothing.
-
- @param overlay The overlay object to remove.
- */
-- (void)removeOverlay:(id <MGLOverlay>)overlay;
-
-/**
- Removes one or more overlay objects from the map.
-
- If a given overlay object is not associated with the map view, it is ignored.
-
- @param overlays An array of objects, each of which conforms to the `MGLOverlay`
- protocol.
- */
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
-
-#pragma mark Debugging the Map
-
-/**
- The options that determine which debugging aids are shown on the map.
-
- These options are all disabled by default and should remain disabled in
- released software for performance and aesthetic reasons.
- */
-@property (nonatomic) MGLMapDebugMaskOptions debugMask;
-
-@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((deprecated("Use -debugMask and -setDebugMask:.")));
-
-- (void)toggleDebug __attribute__((deprecated("Use -setDebugMask:.")));
-
-/**
- Empties the in-memory tile cache.
- */
-- (void)emptyMemoryCache;
-
-/**
- Resets the map to the minimum zoom level, a center coordinate of (0, 0), and
- a northern heading.
- */
-- (void)resetPosition;
-
-@end
-
-#pragma mark - MGLMapViewDelegate
-
-/** The MGLMapViewDelegate protocol defines a set of optional methods that you can use to receive map-related update messages. Because many map operations require the `MGLMapView` class to load data asynchronously, the map view calls these methods to notify your application when specific operations complete. The map view also uses these methods to request annotation marker symbology and to manage interactions with those markers. */
-@protocol MGLMapViewDelegate <NSObject>
-
-@optional
-
-#pragma mark Responding to Map Position Changes
-
-/**
- Tells the delegate that the region displayed by the map view is about to change.
-
- This method is called whenever the currently displayed map region will start changing.
-
- @param mapView The map view whose visible region will change.
- @param animated Whether the change will cause an animated effect on the map.
- */
-- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
-
-/**
- Tells the delegate that the region displayed by the map view is changing.
-
- This method is called whenever the currently displayed map region changes. During movement, this method may be called many times to report updates to the map position. Therefore, your implementation of this method should be as lightweight as possible to avoid affecting performance.
-
- @param mapView The map view whose visible region is changing.
- */
-- (void)mapViewRegionIsChanging:(MGLMapView *)mapView;
-
-/**
- Tells the delegate that the region displayed by the map view just changed.
-
- This method is called whenever the currently displayed map region has finished changing.
-
- @param mapView The map view whose visible region changed.
- @param animated Whether the change caused an animated effect on the map.
- */
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
-
-#pragma mark Loading the Map
-
-/**
- Tells the delegate that the map view will begin to load.
-
- This method is called whenever the map view starts loading, including when a new style has been set and the map must reload.
-
- @param mapView The map view that is starting to load.
- */
-- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView;
-
-/**
- Tells the delegate that the map view has finished loading.
-
- This method is called whenever the map view finishes loading, either after the initial load or after a style change has forced a reload.
-
- @param mapView The map view that has finished loading.
- */
-- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView;
-
-// TODO
-- (void)mapViewDidFailLoadingMap:(MGLMapView *)mapView withError:(NSError *)error;
-
-// TODO
-- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
-
-// TODO
-- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-
-// TODO
-- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
-
-// TODO
-- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-
-#pragma mark Tracking User Location
-
-/**
- Tells the delegate that the map view will begin tracking the user's location.
-
- This method is called when the value of the `showsUserLocation` property changes to `YES`.
-
- @param mapView The map view that is tracking the user's location.
- */
-- (void)mapViewWillStartLocatingUser:(MGLMapView *)mapView;
-
-/**
- Tells the delegate that the map view has stopped tracking the user's location.
-
- This method is called when the value of the `showsUserLocation` property changes to `NO`.
-
- @param mapView The map view that is tracking the user's location.
- */
-- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView;
-
-/**
- Tells the delegate that the location of the user was updated.
-
- While the `showsUserLocation` property is set to `YES`, this method is called whenever a new location update is received by the map view. This method is also called if the map view's user tracking mode is set to `MGLUserTrackingModeFollowWithHeading` and the heading changes, or if it is set to `MGLUserTrackingModeFollowWithCourse` and the course changes.
-
- This method is not called if the application is currently running in the background. If you want to receive location updates while running in the background, you must use the Core Location framework.
-
- @param mapView The map view that is tracking the user's location.
- @param userLocation The location object representing the user's latest location. This property may be `nil`.
- */
-- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(nullable MGLUserLocation *)userLocation;
-
-/**
- Tells the delegate that an attempt to locate the user's position failed.
-
- @param mapView The map view that is tracking the user's location.
- @param error An error object containing the reason why location tracking failed.
- */
-- (void)mapView:(MGLMapView *)mapView didFailToLocateUserWithError:(NSError *)error;
-
-/**
- Tells the delegate that the map view's user tracking mode has changed.
-
- This method is called after the map view asynchronously changes to reflect the new user tracking mode, for example by beginning to zoom or rotate.
-
- @param mapView The map view that changed its tracking mode.
- @param mode The new tracking mode.
- @param animated Whether the change caused an animated effect on the map.
- */
-- (void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
-
-#pragma mark Managing the Display of Annotations
-
-/**
- Returns an image object to use for the marker for the specified point annotation object.
-
- @param mapView The map view that requested the annotation image.
- @param annotation The object representing the annotation that is about to be displayed.
- @return The image object to display for the specified annotation or `nil` if you want to display the default marker image.
- */
-- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Returns the alpha value to use when rendering a shape annotation. Defaults to `1.0`.
-
- @param mapView The map view rendering the shape annotation.
- @param annotation The annotation being rendered.
- @return An alpha value between `0` and `1.0`.
- */
-- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
-
-/**
- Returns the stroke color to use when rendering a shape annotation. Defaults to the map view’s tint color.
-
- @param mapView The map view rendering the shape annotation.
- @param annotation The annotation being rendered.
- @return A color to use for the shape outline.
- */
-- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
-
-/**
- Returns the fill color to use when rendering a polygon annotation. Defaults to the map view’s tint color.
-
- @param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return A color to use for the polygon interior.
- */
-- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
-
-/**
- Returns the line width to use when rendering a polyline annotation. Defaults to `3.0`.
-
- @param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return A line width for the polyline.
- */
-- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation;
-
-/**
- Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble.
-
- If the value returned is `YES`, a standard callout bubble is shown when the user taps a selected annotation. The callout uses the title and subtitle text from the associated annotation object. If there is no title text, though, the annotation will not show a callout. The callout also displays any custom callout views returned by the delegate for the left and right callout accessory views.
-
- If the value returned is `NO`, the value of the title and subtitle strings are ignored.
-
- @param mapView The map view that requested the annotation callout ability.
- @param annotation The object representing the annotation.
- @return A Boolean indicating whether the annotation should show a callout.
- */
-- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
-
-/**
- Returns a callout view to display for the specified annotation.
-
- If this method is present in the delegate, it must return a new instance of a view dedicated to display the callout bubble. It will be configured by the map view. If this method is not present, or if it returns `nil`, a standard, two-line, bubble-like callout view is displayed by default.
-
- @param mapView The map view that requested the callout view.
- @param annotation The object representing the annotation.
- @return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the default callout view.
- */
-- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Returns the view to display on the left side of the standard callout bubble.
-
- The default value is treated as if `nil`. The left callout view is typically used to display information about the annotation or to link to custom information provided by your application.
-
- If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
-
- @param mapView The map view presenting the annotation callout.
- @param annotation The object representing the annotation with the callout.
- @return The accessory view to display.
- */
-- (nullable UIView *)mapView:(MGLMapView *)mapView leftCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Returns the view to display on the right side of the standard callout bubble.
-
- The default value is treated is if `nil`. The right callout view is typically used to link to more detailed information about the annotation. A common view to specify for this property is `UIButton` object whose type is set to `UIButtonTypeDetailDisclosure`.
-
- If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
-
- @param mapView The map view presenting the annotation callout.
- @param annotation The object representing the annotation with the callout.
- @return The accessory view to display.
- */
-- (nullable UIView *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
-
-#pragma mark Managing Annotations
-
-/**
- Tells the delegate that the user tapped one of the annotation's accessory buttons.
-
- Accessory views contain custom content and are positioned on either side of the annotation title text. If a view you specify is a descendant of the `UIControl` class, the map view calls this method as a convenience whenever the user taps your view. You can use this method to respond to taps and perform any actions associated with that control. For example, if your control displayed additional information about the annotation, you could use this method to present a modal panel with that information.
-
- If your custom accessory views are not descendants of the `UIControl` class, the map view does not call this method.
-
- @param mapView The map view containing the specified annotation.
- @param annotation The annotation whose button was tapped.
- @param control The control that was tapped.
- */
-- (void)mapView:(MGLMapView *)mapView annotation:(id <MGLAnnotation>)annotation calloutAccessoryControlTapped:(UIControl *)control;
-
-/**
- Tells the delegate that the user tapped on an annotation's callout view.
-
- @param mapView The map view containing the specified annotation.
- @param annotation The annotation whose callout was tapped.
- */
-- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotation>)annotation;
-
-#pragma mark Selecting Annotations
-
-/**
- Tells the delegate that one of its annotations was selected.
-
- You can use this method to track changes in the selection state of annotations.
-
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was selected.
- */
-- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Tells the delegate that one of its annotations was deselected.
-
- You can use this method to track changes in the selection state of annotations.
-
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was deselected.
- */
-- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/ios/include/Mapbox.h b/platform/ios/include/Mapbox.h
deleted file mode 100644
index fb25a0104e..0000000000
--- a/platform/ios/include/Mapbox.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#import "MGLAccountManager.h"
-#import "MGLAnnotation.h"
-#import "MGLAnnotationImage.h"
-#import "MGLCalloutView.h"
-#import "MGLGeometry.h"
-#import "MGLMapCamera.h"
-#import "MGLMapView.h"
-#import "MGLMapView+IBAdditions.h"
-#import "MGLMapView+MGLCustomStyleLayerAdditions.h"
-#import "MGLMultiPoint.h"
-#import "MGLOfflinePack.h"
-#import "MGLOfflineRegion.h"
-#import "MGLOfflineStorage.h"
-#import "MGLOverlay.h"
-#import "MGLPointAnnotation.h"
-#import "MGLPolygon.h"
-#import "MGLPolyline.h"
-#import "MGLShape.h"
-#import "MGLStyle.h"
-#import "MGLTilePyramidOfflineRegion.h"
-#import "MGLTypes.h"
-#import "MGLUserLocation.h"
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..cb1bd70199
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -0,0 +1,1972 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
+ 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
+ 4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; };
+ 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; };
+ 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
+ 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
+ 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; };
+ DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */; };
+ DA17BE301CC4BAC300402C41 /* MGLMapView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */; };
+ DA17BE311CC4BDAA00402C41 /* MGLMapView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */; };
+ DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9671CB6C6B7006E619F /* MBXCustomCalloutView.m */; };
+ DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9691CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m */; };
+ DA1DC9701CB6C6CE006E619F /* points.geojson in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC96C1CB6C6CE006E619F /* points.geojson */; };
+ DA1DC9711CB6C6CE006E619F /* polyline.geojson in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC96D1CB6C6CE006E619F /* polyline.geojson */; };
+ DA1DC9731CB6C6CE006E619F /* threestates.geojson in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC96F1CB6C6CE006E619F /* threestates.geojson */; };
+ DA1DC9971CB6E046006E619F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9961CB6E046006E619F /* main.m */; };
+ DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */; };
+ DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC99A1CB6E064006E619F /* MBXViewController.m */; };
+ DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */; };
+ DA1DC99F1CB6E088006E619F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99E1CB6E088006E619F /* Assets.xcassets */; };
+ DA25D5C01CCD9F8400607828 /* Root.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5BF1CCD9F8400607828 /* Root.plist */; };
+ DA25D5C61CCDA06800607828 /* Root.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5C41CCDA06800607828 /* Root.strings */; };
+ DA25D5CD1CCDA11500607828 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DA25D5B91CCD9EDE00607828 /* Settings.bundle */; };
+ DA27C24E1CBB3811000B0ECD /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA27C24D1CBB3811000B0ECD /* GLKit.framework */; };
+ DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */; };
+ DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
+ DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */; };
+ DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */; };
+ DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */; };
+ DA2E88641CC0382C00F24E7B /* MGLOfflineStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2E885F1CC0382C00F24E7B /* MGLOfflineStorageTests.m */; };
+ DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */; };
+ DA35A29E1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */; };
+ DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */; };
+ DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */; };
+ DA35A2B11CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2AF1CCA141D00E826B2 /* MGLCompassDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2AF1CCA141D00E826B2 /* MGLCompassDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2B31CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2B01CCA141D00E826B2 /* MGLCompassDirectionFormatter.m */; };
+ DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2B01CCA141D00E826B2 /* MGLCompassDirectionFormatter.m */; };
+ DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2B71CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m */; };
+ DA35A2B91CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2B71CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m */; };
+ DA35A2BB1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2BA1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2BA1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */; };
+ DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */; };
+ DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
+ DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
+ DA737EE11D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA737EE21D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA821D061CCC6D59007508D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */; };
+ DA821D071CCC6D59007508D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DA821D051CCC6D59007508D4 /* Main.storyboard */; };
+ DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
+ DA8847DA1CBAF91600AB86E3 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F01CBAFA5100AB86E3 /* MGLAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E01CBAFA5100AB86E3 /* MGLAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F11CBAFA5100AB86E3 /* MGLGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E11CBAFA5100AB86E3 /* MGLGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F21CBAFA5100AB86E3 /* MGLMapCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E31CBAFA5100AB86E3 /* MGLMultiPoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E41CBAFA5100AB86E3 /* MGLOfflinePack.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E51CBAFA5100AB86E3 /* MGLOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F61CBAFA5100AB86E3 /* MGLOfflineStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E61CBAFA5100AB86E3 /* MGLOfflineStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F71CBAFA5100AB86E3 /* MGLOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E71CBAFA5100AB86E3 /* MGLOverlay.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E81CBAFA5100AB86E3 /* MGLPointAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847F91CBAFA5100AB86E3 /* MGLPolygon.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E91CBAFA5100AB86E3 /* MGLPolygon.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847FA1CBAFA5100AB86E3 /* MGLPolyline.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EA1CBAFA5100AB86E3 /* MGLPolyline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EB1CBAFA5100AB86E3 /* MGLShape.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847ED1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8847FE1CBAFA5100AB86E3 /* MGLTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EE1CBAFA5100AB86E3 /* MGLTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */; };
+ DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848011CBAFA6200AB86E3 /* MGLGeometry_Private.h */; };
+ DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848021CBAFA6200AB86E3 /* MGLGeometry.mm */; };
+ DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */; };
+ DA88481E1CBAFA6200AB86E3 /* MGLMultiPoint_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848041CBAFA6200AB86E3 /* MGLMultiPoint_Private.h */; };
+ DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848051CBAFA6200AB86E3 /* MGLMultiPoint.mm */; };
+ DA8848201CBAFA6200AB86E3 /* MGLOfflinePack_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848061CBAFA6200AB86E3 /* MGLOfflinePack_Private.h */; };
+ DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848071CBAFA6200AB86E3 /* MGLOfflinePack.mm */; };
+ DA8848221CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848081CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h */; };
+ DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848091CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h */; };
+ DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480A1CBAFA6200AB86E3 /* MGLOfflineStorage.mm */; };
+ DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88480B1CBAFA6200AB86E3 /* MGLPointAnnotation.m */; };
+ DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480C1CBAFA6200AB86E3 /* MGLPolygon.mm */; };
+ DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */; };
+ DA8848281CBAFA6200AB86E3 /* MGLShape.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88480E1CBAFA6200AB86E3 /* MGLShape.m */; };
+ DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */; };
+ DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */; };
+ DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848111CBAFA6200AB86E3 /* MGLTypes.m */; };
+ DA88482C1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */; };
+ DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */; };
+ DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */; };
+ DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */; };
+ DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */; };
+ DA8848311CBAFA6200AB86E3 /* NSString+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */; };
+ DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */; };
+ DA88483A1CBAFB8500AB86E3 /* MGLAnnotationImage.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848351CBAFB8500AB86E3 /* MGLCalloutView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88483C1CBAFB8500AB86E3 /* MGLMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848361CBAFB8500AB86E3 /* MGLMapView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88483D1CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88483E1CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848381CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88484F1CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */; };
+ DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; };
+ DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */; };
+ DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; };
+ DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */; };
+ DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
+ DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */; };
+ DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; };
+ DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; };
+ DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; };
+ DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; };
+ DA88485A1CBAFB9800AB86E3 /* MGLUserLocation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88484B1CBAFB9800AB86E3 /* MGLUserLocation_Private.h */; };
+ DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */; };
+ DA88485C1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88484D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h */; };
+ DA88485D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484E1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m */; };
+ DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA88486D1CBAFCC100AB86E3 /* Compass.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848631CBAFCC100AB86E3 /* Compass.png */; };
+ DA88486E1CBAFCC100AB86E3 /* Compass@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848641CBAFCC100AB86E3 /* Compass@2x.png */; };
+ DA88486F1CBAFCC100AB86E3 /* Compass@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848651CBAFCC100AB86E3 /* Compass@3x.png */; };
+ DA8848701CBAFCC100AB86E3 /* default_marker.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848661CBAFCC100AB86E3 /* default_marker.png */; };
+ DA8848711CBAFCC100AB86E3 /* default_marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */; };
+ DA8848721CBAFCC100AB86E3 /* default_marker@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */; };
+ DA8848731CBAFCC100AB86E3 /* mapbox.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848691CBAFCC100AB86E3 /* mapbox.png */; };
+ DA8848741CBAFCC100AB86E3 /* mapbox@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */; };
+ DA8848751CBAFCC100AB86E3 /* mapbox@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */; };
+ DA88487A1CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848771CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der */; };
+ DA88487B1CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848781CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der */; };
+ DA88487C1CBAFD5C00AB86E3 /* star_tilestream_net.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848791CBAFD5C00AB86E3 /* star_tilestream_net.der */; };
+ DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848801CBB033F00AB86E3 /* FABAttributes.h */; };
+ DA8848851CBB033F00AB86E3 /* FABKitProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */; };
+ DA8848861CBB033F00AB86E3 /* Fabric+FABKits.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */; };
+ DA8848871CBB033F00AB86E3 /* Fabric.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848831CBB033F00AB86E3 /* Fabric.h */; };
+ DA88488B1CBB037E00AB86E3 /* SMCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848891CBB037E00AB86E3 /* SMCalloutView.h */; };
+ DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; };
+ DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88488D1CBB047F00AB86E3 /* reachability.h */; };
+ DA8848901CBB048E00AB86E3 /* reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488F1CBB048E00AB86E3 /* reachability.m */; };
+ DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA89339F1CCC951200E68420 /* Localizable.strings */; };
+ DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BA1CCD2CA100E68420 /* Foundation.strings */; };
+ DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */; };
+ DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BA1CCD2CA100E68420 /* Foundation.strings */; };
+ DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */; };
+ DA8933DD1CCD31D400E68420 /* api_mapbox_com-digicert.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848771CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der */; };
+ DA8933DE1CCD31D400E68420 /* api_mapbox_com-geotrust.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848781CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der */; };
+ DA8933DF1CCD31D400E68420 /* star_tilestream_net.der in Resources */ = {isa = PBXBuildFile; fileRef = DA8848791CBAFD5C00AB86E3 /* star_tilestream_net.der */; };
+ DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA89339F1CCC951200E68420 /* Localizable.strings */; };
+ DA8933E11CCD31DF00E68420 /* Compass.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848631CBAFCC100AB86E3 /* Compass.png */; };
+ DA8933E21CCD31DF00E68420 /* Compass@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848641CBAFCC100AB86E3 /* Compass@2x.png */; };
+ DA8933E31CCD31DF00E68420 /* Compass@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848651CBAFCC100AB86E3 /* Compass@3x.png */; };
+ DA8933E41CCD31DF00E68420 /* default_marker.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848661CBAFCC100AB86E3 /* default_marker.png */; };
+ DA8933E51CCD31DF00E68420 /* default_marker@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */; };
+ DA8933E61CCD31DF00E68420 /* default_marker@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */; };
+ DA8933E71CCD31DF00E68420 /* mapbox.png in Resources */ = {isa = PBXBuildFile; fileRef = DA8848691CBAFCC100AB86E3 /* mapbox.png */; };
+ DA8933E81CCD31DF00E68420 /* mapbox@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */; };
+ DA8933E91CCD31DF00E68420 /* mapbox@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */; };
+ DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */ = {isa = PBXBuildFile; fileRef = DA8933EF1CCD387900E68420 /* strip-frameworks.sh */; };
+ DA8963371CC549A100684375 /* glyphs in Resources */ = {isa = PBXBuildFile; fileRef = DA8963331CC549A100684375 /* glyphs */; };
+ DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; };
+ DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; };
+ DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; };
+ DAA4E4051CBB5C9E00178DFB /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA4E4041CBB5C9E00178DFB /* ImageIO.framework */; };
+ DAA4E4071CBB5CBF00178DFB /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA4E4061CBB5CBF00178DFB /* MobileCoreServices.framework */; };
+ DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
+ DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ DAA4E41C1CBB730400178DFB /* MGLAccountManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */; };
+ DAA4E41D1CBB730400178DFB /* MGLGeometry.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848021CBAFA6200AB86E3 /* MGLGeometry.mm */; };
+ DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */; };
+ DAA4E41F1CBB730400178DFB /* MGLMultiPoint.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848051CBAFA6200AB86E3 /* MGLMultiPoint.mm */; };
+ DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848071CBAFA6200AB86E3 /* MGLOfflinePack.mm */; };
+ DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480A1CBAFA6200AB86E3 /* MGLOfflineStorage.mm */; };
+ DAA4E4221CBB730400178DFB /* MGLPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88480B1CBAFA6200AB86E3 /* MGLPointAnnotation.m */; };
+ DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480C1CBAFA6200AB86E3 /* MGLPolygon.mm */; };
+ DAA4E4241CBB730400178DFB /* MGLPolyline.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */; };
+ DAA4E4251CBB730400178DFB /* MGLShape.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88480E1CBAFA6200AB86E3 /* MGLShape.m */; };
+ DAA4E4261CBB730400178DFB /* MGLStyle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */; };
+ DAA4E4271CBB730400178DFB /* MGLTilePyramidOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */; };
+ DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848111CBAFA6200AB86E3 /* MGLTypes.m */; };
+ DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */; };
+ DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */; };
+ DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */; };
+ DAA4E42C1CBB730400178DFB /* reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488F1CBB048E00AB86E3 /* reachability.m */; };
+ DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; };
+ DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; };
+ DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
+ DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; };
+ DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; };
+ DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; };
+ DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */; };
+ DAA4E4341CBB730400178DFB /* MGLUserLocationAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484E1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m */; };
+ DAA4E4351CBB730400178DFB /* SMCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */; };
+ DAABF73D1CBC59BB005B1825 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAABF73B1CBC59BB005B1825 /* libmbgl-core.a */; };
+ DAABF73E1CBC59BB005B1825 /* libmbgl-platform-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAABF73C1CBC59BB005B1825 /* libmbgl-platform-ios.a */; };
+ DABCABAC1CB80692000A7C39 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DABCABAB1CB80692000A7C39 /* main.m */; };
+ DABCABAF1CB80692000A7C39 /* MBXBenchAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABCABAE1CB80692000A7C39 /* MBXBenchAppDelegate.m */; };
+ DABCABB21CB80692000A7C39 /* MBXBenchViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = DABCABB11CB80692000A7C39 /* MBXBenchViewController.mm */; };
+ DABCABB71CB80692000A7C39 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DABCABB61CB80692000A7C39 /* Assets.xcassets */; };
+ DABCABBA1CB80692000A7C39 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DABCABB81CB80692000A7C39 /* LaunchScreen.storyboard */; };
+ DABCABC21CB8071D000A7C39 /* locations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DABCABBF1CB80717000A7C39 /* locations.cpp */; };
+ DABFB85D1CBE99E500D62B32 /* MGLAccountManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB85E1CBE99E500D62B32 /* MGLAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E01CBAFA5100AB86E3 /* MGLAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB85F1CBE99E500D62B32 /* MGLGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E11CBAFA5100AB86E3 /* MGLGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E31CBAFA5100AB86E3 /* MGLMultiPoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8621CBE99E500D62B32 /* MGLOfflinePack.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E41CBAFA5100AB86E3 /* MGLOfflinePack.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E51CBAFA5100AB86E3 /* MGLOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E61CBAFA5100AB86E3 /* MGLOfflineStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8651CBE99E500D62B32 /* MGLOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E71CBAFA5100AB86E3 /* MGLOverlay.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8661CBE99E500D62B32 /* MGLPointAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E81CBAFA5100AB86E3 /* MGLPointAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847E91CBAFA5100AB86E3 /* MGLPolygon.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8681CBE99E500D62B32 /* MGLPolyline.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EA1CBAFA5100AB86E3 /* MGLPolyline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8691CBE99E500D62B32 /* MGLShape.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EB1CBAFA5100AB86E3 /* MGLShape.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86B1CBE99E500D62B32 /* MGLTilePyramidOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847ED1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86C1CBE99E500D62B32 /* MGLTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8847EE1CBAFA5100AB86E3 /* MGLTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848351CBAFB8500AB86E3 /* MGLCalloutView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB86F1CBE9A0F00D62B32 /* MGLMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848361CBAFB8500AB86E3 /* MGLMapView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8711CBE9A0F00D62B32 /* MGLMapView+MGLCustomStyleLayerAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848381CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; };
+ DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */; };
+ DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165691CF41981001FF4B9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165691CF41981001FF4B9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */; };
+ DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */; };
+ DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */; };
+ DAD165781CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.m */; };
+ DAD1657B1CF4CDFF001FF4B9 /* MGLShapeCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ DA25D5C71CCDA0C100607828 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA25D5B81CCD9EDE00607828;
+ remoteInfo = settings;
+ };
+ DA25D5C91CCDA0CC00607828 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8933D41CCD306400E68420;
+ remoteInfo = bundle;
+ };
+ DA25D5CB1CCDA0CC00607828 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA25D5B81CCD9EDE00607828;
+ remoteInfo = settings;
+ };
+ DA2E88571CC036F400F24E7B /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
+ remoteInfo = dynamic;
+ };
+ DA8847D71CBAF91600AB86E3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
+ remoteInfo = framework;
+ };
+ DAA4E40A1CBB6C9500178DFB /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
+ remoteInfo = framework;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ DA4A269A1CB6F5D3000B7809 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ DA8847DA1CBAF91600AB86E3 /* Mapbox.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAA4E40C1CBB6C9600178DFB /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAA4E4111CBB71D400178DFB /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "include/$(PRODUCT_NAME)";
+ dstSubfolderSpec = 16;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView_Private.h; sourceTree = "<group>"; };
+ 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = "<group>"; };
+ 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
+ 402E9DE01CD2C76200FD4519 /* Mapbox.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Mapbox.playground; sourceTree = "<group>"; };
+ 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationContainerView.h; sourceTree = "<group>"; };
+ 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; };
+ 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
+ 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; };
+ DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
+ DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView_Internal.h; sourceTree = "<group>"; };
+ DA1DC94A1CB6C1C2006E619F /* Mapbox GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA1DC9501CB6C1C2006E619F /* MBXAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXAppDelegate.h; sourceTree = "<group>"; };
+ DA1DC9531CB6C1C2006E619F /* MBXViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXViewController.h; sourceTree = "<group>"; };
+ DA1DC95E1CB6C1C2006E619F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DA1DC9661CB6C6B7006E619F /* MBXCustomCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXCustomCalloutView.h; sourceTree = "<group>"; };
+ DA1DC9671CB6C6B7006E619F /* MBXCustomCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXCustomCalloutView.m; sourceTree = "<group>"; };
+ DA1DC9681CB6C6B7006E619F /* MBXOfflinePacksTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXOfflinePacksTableViewController.h; sourceTree = "<group>"; };
+ DA1DC9691CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXOfflinePacksTableViewController.m; sourceTree = "<group>"; };
+ DA1DC96C1CB6C6CE006E619F /* points.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = points.geojson; sourceTree = "<group>"; };
+ DA1DC96D1CB6C6CE006E619F /* polyline.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = polyline.geojson; sourceTree = "<group>"; };
+ DA1DC96F1CB6C6CE006E619F /* threestates.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = threestates.geojson; sourceTree = "<group>"; };
+ DA1DC9961CB6E046006E619F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAppDelegate.m; sourceTree = "<group>"; };
+ DA1DC99A1CB6E064006E619F /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = "<group>"; };
+ DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
+ DA1DC99E1CB6E088006E619F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ DA25D5B91CCD9EDE00607828 /* Settings.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Settings.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA25D5BF1CCD9F8400607828 /* Root.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Root.plist; sourceTree = "<group>"; };
+ DA25D5C51CCDA06800607828 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Root.strings; sourceTree = "<group>"; };
+ DA27C24D1CBB3811000B0ECD /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
+ DA2E88511CC036F400F24E7B /* test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = test.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA2E88551CC036F400F24E7B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLGeometryTests.mm; path = ../../darwin/test/MGLGeometryTests.mm; sourceTree = "<group>"; };
+ DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflinePackTests.m; path = ../../darwin/test/MGLOfflinePackTests.m; sourceTree = "<group>"; };
+ DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = "<group>"; };
+ DA2E885F1CC0382C00F24E7B /* MGLOfflineStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineStorageTests.m; path = ../../darwin/test/MGLOfflineStorageTests.m; sourceTree = "<group>"; };
+ DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = "<group>"; };
+ DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; };
+ DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; };
+ DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2AF1CCA141D00E826B2 /* MGLCompassDirectionFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompassDirectionFormatter.h; sourceTree = "<group>"; };
+ DA35A2B01CCA141D00E826B2 /* MGLCompassDirectionFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompassDirectionFormatter.m; sourceTree = "<group>"; };
+ DA35A2B71CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLClockDirectionFormatter.m; sourceTree = "<group>"; };
+ DA35A2BA1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLClockDirectionFormatter.h; sourceTree = "<group>"; };
+ DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLClockDirectionFormatterTests.m; path = ../../darwin/test/MGLClockDirectionFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCompassDirectionFormatterTests.m; path = ../../darwin/test/MGLCompassDirectionFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA35A2D11CCAB25200E826B2 /* jazzy.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = jazzy.yml; sourceTree = "<group>"; };
+ DA4A26961CB6E795000B7809 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapViewDelegate.h; sourceTree = "<group>"; };
+ DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
+ DA821D051CCC6D59007508D4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
+ DA8847D21CBAF91600AB86E3 /* Mapbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA8847D61CBAF91600AB86E3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAccountManager.h; sourceTree = "<group>"; };
+ DA8847E01CBAFA5100AB86E3 /* MGLAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotation.h; sourceTree = "<group>"; };
+ DA8847E11CBAFA5100AB86E3 /* MGLGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLGeometry.h; sourceTree = "<group>"; };
+ DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapCamera.h; sourceTree = "<group>"; };
+ DA8847E31CBAFA5100AB86E3 /* MGLMultiPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMultiPoint.h; sourceTree = "<group>"; };
+ DA8847E41CBAFA5100AB86E3 /* MGLOfflinePack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflinePack.h; sourceTree = "<group>"; };
+ DA8847E51CBAFA5100AB86E3 /* MGLOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineRegion.h; sourceTree = "<group>"; };
+ DA8847E61CBAFA5100AB86E3 /* MGLOfflineStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineStorage.h; sourceTree = "<group>"; };
+ DA8847E71CBAFA5100AB86E3 /* MGLOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOverlay.h; sourceTree = "<group>"; };
+ DA8847E81CBAFA5100AB86E3 /* MGLPointAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointAnnotation.h; sourceTree = "<group>"; };
+ DA8847E91CBAFA5100AB86E3 /* MGLPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon.h; sourceTree = "<group>"; };
+ DA8847EA1CBAFA5100AB86E3 /* MGLPolyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline.h; sourceTree = "<group>"; };
+ DA8847EB1CBAFA5100AB86E3 /* MGLShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShape.h; sourceTree = "<group>"; };
+ DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyle.h; sourceTree = "<group>"; };
+ DA8847ED1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTilePyramidOfflineRegion.h; sourceTree = "<group>"; };
+ DA8847EE1CBAFA5100AB86E3 /* MGLTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTypes.h; sourceTree = "<group>"; };
+ DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAccountManager_Private.h; sourceTree = "<group>"; };
+ DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAccountManager.m; sourceTree = "<group>"; };
+ DA8848011CBAFA6200AB86E3 /* MGLGeometry_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLGeometry_Private.h; sourceTree = "<group>"; };
+ DA8848021CBAFA6200AB86E3 /* MGLGeometry.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLGeometry.mm; sourceTree = "<group>"; };
+ DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapCamera.mm; sourceTree = "<group>"; };
+ DA8848041CBAFA6200AB86E3 /* MGLMultiPoint_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMultiPoint_Private.h; sourceTree = "<group>"; };
+ DA8848051CBAFA6200AB86E3 /* MGLMultiPoint.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMultiPoint.mm; sourceTree = "<group>"; };
+ DA8848061CBAFA6200AB86E3 /* MGLOfflinePack_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflinePack_Private.h; sourceTree = "<group>"; };
+ DA8848071CBAFA6200AB86E3 /* MGLOfflinePack.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOfflinePack.mm; sourceTree = "<group>"; };
+ DA8848081CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineRegion_Private.h; sourceTree = "<group>"; };
+ DA8848091CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineStorage_Private.h; sourceTree = "<group>"; };
+ DA88480A1CBAFA6200AB86E3 /* MGLOfflineStorage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOfflineStorage.mm; sourceTree = "<group>"; };
+ DA88480B1CBAFA6200AB86E3 /* MGLPointAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLPointAnnotation.m; sourceTree = "<group>"; };
+ DA88480C1CBAFA6200AB86E3 /* MGLPolygon.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPolygon.mm; sourceTree = "<group>"; };
+ DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPolyline.mm; sourceTree = "<group>"; };
+ DA88480E1CBAFA6200AB86E3 /* MGLShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLShape.m; sourceTree = "<group>"; };
+ DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyle.mm; sourceTree = "<group>"; };
+ DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTilePyramidOfflineRegion.mm; sourceTree = "<group>"; };
+ DA8848111CBAFA6200AB86E3 /* MGLTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTypes.m; sourceTree = "<group>"; };
+ DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSException+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSProcessInfo+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSProcessInfo+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage.h; sourceTree = "<group>"; };
+ DA8848351CBAFB8500AB86E3 /* MGLCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCalloutView.h; sourceTree = "<group>"; };
+ DA8848361CBAFB8500AB86E3 /* MGLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView.h; sourceTree = "<group>"; };
+ DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+IBAdditions.h"; sourceTree = "<group>"; };
+ DA8848381CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+MGLCustomStyleLayerAdditions.h"; sourceTree = "<group>"; };
+ DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocation.h; sourceTree = "<group>"; };
+ DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; };
+ DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationImage.m; sourceTree = "<group>"; };
+ DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAPIClient.h; sourceTree = "<group>"; };
+ DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAPIClient.m; sourceTree = "<group>"; };
+ DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompactCalloutView.h; sourceTree = "<group>"; };
+ DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompactCalloutView.m; sourceTree = "<group>"; };
+ DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLocationManager.h; sourceTree = "<group>"; };
+ DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLLocationManager.m; sourceTree = "<group>"; };
+ DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapboxEvents.h; sourceTree = "<group>"; };
+ DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapboxEvents.m; sourceTree = "<group>"; };
+ DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapView.mm; sourceTree = "<group>"; };
+ DA88484B1CBAFB9800AB86E3 /* MGLUserLocation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocation_Private.h; sourceTree = "<group>"; };
+ DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocation.m; sourceTree = "<group>"; };
+ DA88484D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationAnnotationView.h; sourceTree = "<group>"; };
+ DA88484E1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationAnnotationView.m; sourceTree = "<group>"; };
+ DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mapbox.h; path = src/Mapbox.h; sourceTree = SOURCE_ROOT; };
+ DA8848631CBAFCC100AB86E3 /* Compass.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Compass.png; sourceTree = "<group>"; };
+ DA8848641CBAFCC100AB86E3 /* Compass@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@2x.png"; sourceTree = "<group>"; };
+ DA8848651CBAFCC100AB86E3 /* Compass@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Compass@3x.png"; sourceTree = "<group>"; };
+ DA8848661CBAFCC100AB86E3 /* default_marker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = default_marker.png; sourceTree = "<group>"; };
+ DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@2x.png"; sourceTree = "<group>"; };
+ DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default_marker@3x.png"; sourceTree = "<group>"; };
+ DA8848691CBAFCC100AB86E3 /* mapbox.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mapbox.png; sourceTree = "<group>"; };
+ DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@2x.png"; sourceTree = "<group>"; };
+ DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mapbox@3x.png"; sourceTree = "<group>"; };
+ DA8848771CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = "api_mapbox_com-digicert.der"; path = "../../default/resources/api_mapbox_com-digicert.der"; sourceTree = "<group>"; };
+ DA8848781CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = "api_mapbox_com-geotrust.der"; path = "../../default/resources/api_mapbox_com-geotrust.der"; sourceTree = "<group>"; };
+ DA8848791CBAFD5C00AB86E3 /* star_tilestream_net.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = star_tilestream_net.der; path = ../../default/resources/star_tilestream_net.der; sourceTree = "<group>"; };
+ DA8848801CBB033F00AB86E3 /* FABAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABAttributes.h; sourceTree = "<group>"; };
+ DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = "<group>"; };
+ DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = "<group>"; };
+ DA8848831CBB033F00AB86E3 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = "<group>"; };
+ DA8848891CBB037E00AB86E3 /* SMCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMCalloutView.h; sourceTree = "<group>"; };
+ DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMCalloutView.m; sourceTree = "<group>"; };
+ DA88488D1CBB047F00AB86E3 /* reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reachability.h; path = ../../include/mbgl/platform/darwin/reachability.h; sourceTree = "<group>"; };
+ DA88488F1CBB048E00AB86E3 /* reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = reachability.m; path = src/reachability.m; sourceTree = "<group>"; };
+ DA8933A01CCC951200E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA8933BB1CCD2CA100E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA8933BE1CCD2CAD00E68420 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DA8933D51CCD306400E68420 /* Mapbox.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Mapbox.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA8933EF1CCD387900E68420 /* strip-frameworks.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "strip-frameworks.sh"; path = "framework/strip-frameworks.sh"; sourceTree = SOURCE_ROOT; };
+ DA8933F11CCD3FD700E68420 /* Info-static.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-static.plist"; sourceTree = "<group>"; };
+ DA8963331CC549A100684375 /* glyphs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = glyphs; sourceTree = "<group>"; };
+ DA8963341CC549A100684375 /* sprites */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sprites; sourceTree = "<group>"; };
+ DA8963351CC549A100684375 /* styles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = styles; sourceTree = "<group>"; };
+ DA8963361CC549A100684375 /* tiles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = tiles; sourceTree = "<group>"; };
+ DAA4E4021CBB5C2F00178DFB /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ DAA4E4041CBB5C9E00178DFB /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
+ DAA4E4061CBB5CBF00178DFB /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
+ DAA4E4131CBB71D400178DFB /* libMapbox.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMapbox.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAABF73B1CBC59BB005B1825 /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libmbgl-core.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAABF73C1CBC59BB005B1825 /* libmbgl-platform-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libmbgl-platform-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DABCABA81CB80692000A7C39 /* Bench GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Bench GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DABCABAB1CB80692000A7C39 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ DABCABAD1CB80692000A7C39 /* MBXBenchAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXBenchAppDelegate.h; sourceTree = "<group>"; };
+ DABCABAE1CB80692000A7C39 /* MBXBenchAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBXBenchAppDelegate.m; sourceTree = "<group>"; };
+ DABCABB01CB80692000A7C39 /* MBXBenchViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBXBenchViewController.h; sourceTree = "<group>"; };
+ DABCABB11CB80692000A7C39 /* MBXBenchViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MBXBenchViewController.mm; sourceTree = "<group>"; };
+ DABCABB61CB80692000A7C39 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ DABCABB91CB80692000A7C39 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ DABCABBB1CB80692000A7C39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DABCABBF1CB80717000A7C39 /* locations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = locations.cpp; sourceTree = "<group>"; };
+ DABCABC01CB80717000A7C39 /* locations.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locations.hpp; sourceTree = "<group>"; };
+ DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = mbgl.xcconfig; path = ../../build/ios/mbgl.xcconfig; sourceTree = "<group>"; };
+ DAC49C621CD07D74009E1AA3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DAD165691CF41981001FF4B9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
+ DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
+ DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
+ DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = "<group>"; };
+ DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLShapeCollection.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ DA1DC9471CB6C1C2006E619F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA2E884E1CC036F400F24E7B /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA8847CE1CBAF91600AB86E3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAABF73D1CBC59BB005B1825 /* libmbgl-core.a in Frameworks */,
+ DAABF73E1CBC59BB005B1825 /* libmbgl-platform-ios.a in Frameworks */,
+ DA27C24E1CBB3811000B0ECD /* GLKit.framework in Frameworks */,
+ DAA4E4051CBB5C9E00178DFB /* ImageIO.framework in Frameworks */,
+ DAA4E4071CBB5CBF00178DFB /* MobileCoreServices.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAA4E4101CBB71D400178DFB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DABCABA51CB80692000A7C39 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 402E9DE21CD3A56500FD4519 /* Playground */ = {
+ isa = PBXGroup;
+ children = (
+ 402E9DE01CD2C76200FD4519 /* Mapbox.playground */,
+ );
+ name = Playground;
+ sourceTree = "<group>";
+ };
+ DA1DC9411CB6C1C2006E619F = {
+ isa = PBXGroup;
+ children = (
+ 402E9DE21CD3A56500FD4519 /* Playground */,
+ DA1DC94C1CB6C1C2006E619F /* Demo App */,
+ DABCABA91CB80692000A7C39 /* Benchmarking App */,
+ DA8847D31CBAF91600AB86E3 /* SDK */,
+ DA2E88521CC036F400F24E7B /* SDK Tests */,
+ DA1DC9921CB6DF24006E619F /* Frameworks */,
+ DAC07C951CBB2CAD000CB309 /* Configuration */,
+ DA1DC94B1CB6C1C2006E619F /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ DA1DC94B1CB6C1C2006E619F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DA1DC94A1CB6C1C2006E619F /* Mapbox GL.app */,
+ DABCABA81CB80692000A7C39 /* Bench GL.app */,
+ DA8847D21CBAF91600AB86E3 /* Mapbox.framework */,
+ DAA4E4131CBB71D400178DFB /* libMapbox.a */,
+ DA2E88511CC036F400F24E7B /* test.xctest */,
+ DA8933D51CCD306400E68420 /* Mapbox.bundle */,
+ DA25D5B91CCD9EDE00607828 /* Settings.bundle */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ DA1DC94C1CB6C1C2006E619F /* Demo App */ = {
+ isa = PBXGroup;
+ children = (
+ DA1DC9501CB6C1C2006E619F /* MBXAppDelegate.h */,
+ DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */,
+ 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */,
+ 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */,
+ DA1DC9661CB6C6B7006E619F /* MBXCustomCalloutView.h */,
+ DA1DC9671CB6C6B7006E619F /* MBXCustomCalloutView.m */,
+ DA1DC9681CB6C6B7006E619F /* MBXOfflinePacksTableViewController.h */,
+ DA1DC9691CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m */,
+ DA1DC9531CB6C1C2006E619F /* MBXViewController.h */,
+ DA1DC99A1CB6E064006E619F /* MBXViewController.m */,
+ DA821D051CCC6D59007508D4 /* Main.storyboard */,
+ DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */,
+ DA1DC99E1CB6E088006E619F /* Assets.xcassets */,
+ DA1DC96C1CB6C6CE006E619F /* points.geojson */,
+ DA1DC96D1CB6C6CE006E619F /* polyline.geojson */,
+ DA1DC96F1CB6C6CE006E619F /* threestates.geojson */,
+ DA1DC95E1CB6C1C2006E619F /* Info.plist */,
+ DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */,
+ DA1DC94D1CB6C1C2006E619F /* Supporting Files */,
+ );
+ name = "Demo App";
+ path = app;
+ sourceTree = "<group>";
+ };
+ DA1DC94D1CB6C1C2006E619F /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DA1DC9961CB6E046006E619F /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ DA1DC9921CB6DF24006E619F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ DAABF73B1CBC59BB005B1825 /* libmbgl-core.a */,
+ DAABF73C1CBC59BB005B1825 /* libmbgl-platform-ios.a */,
+ DAA4E4021CBB5C2F00178DFB /* CoreGraphics.framework */,
+ DA27C24D1CBB3811000B0ECD /* GLKit.framework */,
+ DAA4E4041CBB5C9E00178DFB /* ImageIO.framework */,
+ DAA4E4061CBB5CBF00178DFB /* MobileCoreServices.framework */,
+ DA4A26961CB6E795000B7809 /* Mapbox.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ DA25D5BA1CCD9EDE00607828 /* Example Settings Bundle */ = {
+ isa = PBXGroup;
+ children = (
+ DA25D5BF1CCD9F8400607828 /* Root.plist */,
+ DA25D5C41CCDA06800607828 /* Root.strings */,
+ );
+ name = "Example Settings Bundle";
+ path = Settings.bundle;
+ sourceTree = "<group>";
+ };
+ DA2E88521CC036F400F24E7B /* SDK Tests */ = {
+ isa = PBXGroup;
+ children = (
+ DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */,
+ DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */,
+ DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */,
+ DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
+ DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
+ DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */,
+ DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */,
+ DA2E885F1CC0382C00F24E7B /* MGLOfflineStorageTests.m */,
+ DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */,
+ DA2E88551CC036F400F24E7B /* Info.plist */,
+ );
+ name = "SDK Tests";
+ path = test;
+ sourceTree = "<group>";
+ };
+ DA8847D31CBAF91600AB86E3 /* SDK */ = {
+ isa = PBXGroup;
+ children = (
+ DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */,
+ DA8847DE1CBAFA3E00AB86E3 /* Foundation */,
+ DA8933B91CCD2C6700E68420 /* Foundation Resources */,
+ DA8848331CBAFB2A00AB86E3 /* Kit */,
+ DA8848621CBAFCC100AB86E3 /* Kit Resources */,
+ DA25D5BA1CCD9EDE00607828 /* Example Settings Bundle */,
+ DA8847D61CBAF91600AB86E3 /* Info.plist */,
+ DA8933F11CCD3FD700E68420 /* Info-static.plist */,
+ );
+ name = SDK;
+ path = framework;
+ sourceTree = "<group>";
+ };
+ DA8847DE1CBAFA3E00AB86E3 /* Foundation */ = {
+ isa = PBXGroup;
+ children = (
+ DAD165831CF4CFED001FF4B9 /* Categories */,
+ DAD165801CF4CF9A001FF4B9 /* Formatters */,
+ DAD165811CF4CFC4001FF4B9 /* Geometry */,
+ DAD165821CF4CFE3001FF4B9 /* Offline Maps */,
+ DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */,
+ DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */,
+ DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */,
+ DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */,
+ DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */,
+ DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */,
+ DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */,
+ DA8847EE1CBAFA5100AB86E3 /* MGLTypes.h */,
+ DA8848111CBAFA6200AB86E3 /* MGLTypes.m */,
+ DA8848911CBB049300AB86E3 /* reachability */,
+ );
+ name = Foundation;
+ path = ../darwin/src;
+ sourceTree = SOURCE_ROOT;
+ };
+ DA8848331CBAFB2A00AB86E3 /* Kit */ = {
+ isa = PBXGroup;
+ children = (
+ DAD165841CF4D06B001FF4B9 /* Annotations */,
+ DAD165851CF4D08B001FF4B9 /* Telemetry */,
+ DA8848361CBAFB8500AB86E3 /* MGLMapView.h */,
+ DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */,
+ DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */,
+ DA8848381CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h */,
+ DA737EE01D056A4E005BDA16 /* MGLMapViewDelegate.h */,
+ DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */,
+ DA88487F1CBB033F00AB86E3 /* Fabric */,
+ DA8848881CBB036000AB86E3 /* SMCalloutView */,
+ );
+ name = Kit;
+ path = src;
+ sourceTree = SOURCE_ROOT;
+ };
+ DA8848621CBAFCC100AB86E3 /* Kit Resources */ = {
+ isa = PBXGroup;
+ children = (
+ DA89339F1CCC951200E68420 /* Localizable.strings */,
+ DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */,
+ DA8848771CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der */,
+ DA8848781CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der */,
+ DA8848791CBAFD5C00AB86E3 /* star_tilestream_net.der */,
+ DA8848631CBAFCC100AB86E3 /* Compass.png */,
+ DA8848641CBAFCC100AB86E3 /* Compass@2x.png */,
+ DA8848651CBAFCC100AB86E3 /* Compass@3x.png */,
+ DA8848661CBAFCC100AB86E3 /* default_marker.png */,
+ DA8848671CBAFCC100AB86E3 /* default_marker@2x.png */,
+ DA8848681CBAFCC100AB86E3 /* default_marker@3x.png */,
+ DA8848691CBAFCC100AB86E3 /* mapbox.png */,
+ DA88486A1CBAFCC100AB86E3 /* mapbox@2x.png */,
+ DA88486B1CBAFCC100AB86E3 /* mapbox@3x.png */,
+ DA8933EF1CCD387900E68420 /* strip-frameworks.sh */,
+ );
+ name = "Kit Resources";
+ path = resources;
+ sourceTree = SOURCE_ROOT;
+ };
+ DA88487F1CBB033F00AB86E3 /* Fabric */ = {
+ isa = PBXGroup;
+ children = (
+ DA8848831CBB033F00AB86E3 /* Fabric.h */,
+ DA8848821CBB033F00AB86E3 /* Fabric+FABKits.h */,
+ DA8848801CBB033F00AB86E3 /* FABAttributes.h */,
+ DA8848811CBB033F00AB86E3 /* FABKitProtocol.h */,
+ );
+ name = Fabric;
+ path = ../vendor/Fabric;
+ sourceTree = "<group>";
+ };
+ DA8848881CBB036000AB86E3 /* SMCalloutView */ = {
+ isa = PBXGroup;
+ children = (
+ DA8848891CBB037E00AB86E3 /* SMCalloutView.h */,
+ DA88488A1CBB037E00AB86E3 /* SMCalloutView.m */,
+ );
+ name = SMCalloutView;
+ path = ../vendor/SMCalloutView;
+ sourceTree = "<group>";
+ };
+ DA8848911CBB049300AB86E3 /* reachability */ = {
+ isa = PBXGroup;
+ children = (
+ DA88488D1CBB047F00AB86E3 /* reachability.h */,
+ DA88488F1CBB048E00AB86E3 /* reachability.m */,
+ );
+ name = reachability;
+ path = ..;
+ sourceTree = "<group>";
+ };
+ DA8933B91CCD2C6700E68420 /* Foundation Resources */ = {
+ isa = PBXGroup;
+ children = (
+ DA8933BA1CCD2CA100E68420 /* Foundation.strings */,
+ DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */,
+ );
+ name = "Foundation Resources";
+ path = ../../darwin/resources;
+ sourceTree = "<group>";
+ };
+ DA8963321CC5498400684375 /* Fixtures */ = {
+ isa = PBXGroup;
+ children = (
+ DA8963331CC549A100684375 /* glyphs */,
+ DA8963341CC549A100684375 /* sprites */,
+ DA8963351CC549A100684375 /* styles */,
+ DA8963361CC549A100684375 /* tiles */,
+ );
+ name = Fixtures;
+ path = assets;
+ sourceTree = "<group>";
+ };
+ DABCABA91CB80692000A7C39 /* Benchmarking App */ = {
+ isa = PBXGroup;
+ children = (
+ DABCABAD1CB80692000A7C39 /* MBXBenchAppDelegate.h */,
+ DABCABAE1CB80692000A7C39 /* MBXBenchAppDelegate.m */,
+ DABCABB01CB80692000A7C39 /* MBXBenchViewController.h */,
+ DABCABB11CB80692000A7C39 /* MBXBenchViewController.mm */,
+ DABCABBF1CB80717000A7C39 /* locations.cpp */,
+ DABCABC01CB80717000A7C39 /* locations.hpp */,
+ DABCABB61CB80692000A7C39 /* Assets.xcassets */,
+ DA8963321CC5498400684375 /* Fixtures */,
+ DABCABB81CB80692000A7C39 /* LaunchScreen.storyboard */,
+ DABCABBB1CB80692000A7C39 /* Info.plist */,
+ DABCABAA1CB80692000A7C39 /* Supporting Files */,
+ );
+ name = "Benchmarking App";
+ path = benchmark;
+ sourceTree = "<group>";
+ };
+ DABCABAA1CB80692000A7C39 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DABCABAB1CB80692000A7C39 /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ DAC07C951CBB2CAD000CB309 /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ DA35A2D11CCAB25200E826B2 /* jazzy.yml */,
+ DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */,
+ );
+ name = Configuration;
+ sourceTree = "<group>";
+ };
+ DAD165801CF4CF9A001FF4B9 /* Formatters */ = {
+ isa = PBXGroup;
+ children = (
+ DA35A2BA1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h */,
+ DA35A2B71CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m */,
+ DA35A2AF1CCA141D00E826B2 /* MGLCompassDirectionFormatter.h */,
+ DA35A2B01CCA141D00E826B2 /* MGLCompassDirectionFormatter.m */,
+ DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */,
+ DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */,
+ );
+ name = Formatters;
+ sourceTree = "<group>";
+ };
+ DAD165811CF4CFC4001FF4B9 /* Geometry */ = {
+ isa = PBXGroup;
+ children = (
+ DA8847E01CBAFA5100AB86E3 /* MGLAnnotation.h */,
+ DAD165691CF41981001FF4B9 /* MGLFeature.h */,
+ DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */,
+ DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */,
+ DA8847E11CBAFA5100AB86E3 /* MGLGeometry.h */,
+ DA8848011CBAFA6200AB86E3 /* MGLGeometry_Private.h */,
+ DA8848021CBAFA6200AB86E3 /* MGLGeometry.mm */,
+ DA8847E31CBAFA5100AB86E3 /* MGLMultiPoint.h */,
+ DA8848041CBAFA6200AB86E3 /* MGLMultiPoint_Private.h */,
+ DA8848051CBAFA6200AB86E3 /* MGLMultiPoint.mm */,
+ DA8847E71CBAFA5100AB86E3 /* MGLOverlay.h */,
+ DA8847E81CBAFA5100AB86E3 /* MGLPointAnnotation.h */,
+ DA88480B1CBAFA6200AB86E3 /* MGLPointAnnotation.m */,
+ DA8847E91CBAFA5100AB86E3 /* MGLPolygon.h */,
+ DA88480C1CBAFA6200AB86E3 /* MGLPolygon.mm */,
+ DA8847EA1CBAFA5100AB86E3 /* MGLPolyline.h */,
+ DA88480D1CBAFA6200AB86E3 /* MGLPolyline.mm */,
+ DA8847EB1CBAFA5100AB86E3 /* MGLShape.h */,
+ DA88480E1CBAFA6200AB86E3 /* MGLShape.m */,
+ DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */,
+ DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.m */,
+ );
+ name = Geometry;
+ sourceTree = "<group>";
+ };
+ DAD165821CF4CFE3001FF4B9 /* Offline Maps */ = {
+ isa = PBXGroup;
+ children = (
+ DA8847E41CBAFA5100AB86E3 /* MGLOfflinePack.h */,
+ DA8848061CBAFA6200AB86E3 /* MGLOfflinePack_Private.h */,
+ DA8848071CBAFA6200AB86E3 /* MGLOfflinePack.mm */,
+ DA8847E51CBAFA5100AB86E3 /* MGLOfflineRegion.h */,
+ DA8848081CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h */,
+ DA8847E61CBAFA5100AB86E3 /* MGLOfflineStorage.h */,
+ DA8848091CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h */,
+ DA88480A1CBAFA6200AB86E3 /* MGLOfflineStorage.mm */,
+ DA8847ED1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h */,
+ DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */,
+ );
+ name = "Offline Maps";
+ sourceTree = "<group>";
+ };
+ DAD165831CF4CFED001FF4B9 /* Categories */ = {
+ isa = PBXGroup;
+ children = (
+ DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */,
+ DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */,
+ DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */,
+ DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */,
+ DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */,
+ DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */,
+ DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */,
+ DA35A2C71CCAAAD200E826B2 /* NSValue+MGLAdditions.h */,
+ DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */,
+ );
+ name = Categories;
+ sourceTree = "<group>";
+ };
+ DAD165841CF4D06B001FF4B9 /* Annotations */ = {
+ isa = PBXGroup;
+ children = (
+ 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
+ 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */,
+ 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */,
+ 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */,
+ 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */,
+ DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */,
+ DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */,
+ DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */,
+ DA8848351CBAFB8500AB86E3 /* MGLCalloutView.h */,
+ DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */,
+ DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */,
+ DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */,
+ DA88484B1CBAFB9800AB86E3 /* MGLUserLocation_Private.h */,
+ DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */,
+ DA88484D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h */,
+ DA88484E1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m */,
+ );
+ name = Annotations;
+ sourceTree = "<group>";
+ };
+ DAD165851CF4D08B001FF4B9 /* Telemetry */ = {
+ isa = PBXGroup;
+ children = (
+ DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */,
+ DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */,
+ DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */,
+ DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */,
+ DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */,
+ DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */,
+ );
+ name = Telemetry;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ DA8847CF1CBAF91600AB86E3 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA88483A1CBAFB8500AB86E3 /* MGLAnnotationImage.h in Headers */,
+ DA35A2BB1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */,
+ DA8848861CBB033F00AB86E3 /* Fabric+FABKits.h in Headers */,
+ DA8848201CBAFA6200AB86E3 /* MGLOfflinePack_Private.h in Headers */,
+ DA8847FA1CBAFA5100AB86E3 /* MGLPolyline.h in Headers */,
+ 4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */,
+ DA88482C1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h in Headers */,
+ DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */,
+ DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
+ DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */,
+ DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */,
+ DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */,
+ DA88485A1CBAFB9800AB86E3 /* MGLUserLocation_Private.h in Headers */,
+ DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */,
+ DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */,
+ DA8847F01CBAFA5100AB86E3 /* MGLAnnotation.h in Headers */,
+ DA88483E1CBAFB8500AB86E3 /* MGLMapView+MGLCustomStyleLayerAdditions.h in Headers */,
+ 4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
+ DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */,
+ DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */,
+ DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
+ DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */,
+ DA8848311CBAFA6200AB86E3 /* NSString+MGLAdditions.h in Headers */,
+ DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */,
+ DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */,
+ DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */,
+ DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */,
+ DA88483D1CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h in Headers */,
+ DA17BE301CC4BAC300402C41 /* MGLMapView_Internal.h in Headers */,
+ DAD165781CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
+ DA88481E1CBAFA6200AB86E3 /* MGLMultiPoint_Private.h in Headers */,
+ DA35A29E1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
+ DA8847F71CBAFA5100AB86E3 /* MGLOverlay.h in Headers */,
+ DA35A2B11CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
+ DA88488B1CBB037E00AB86E3 /* SMCalloutView.h in Headers */,
+ DA8847FE1CBAFA5100AB86E3 /* MGLTypes.h in Headers */,
+ DA8847F11CBAFA5100AB86E3 /* MGLGeometry.h in Headers */,
+ DA8848221CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h in Headers */,
+ DA8847F91CBAFA5100AB86E3 /* MGLPolygon.h in Headers */,
+ DA8847F81CBAFA5100AB86E3 /* MGLPointAnnotation.h in Headers */,
+ DA8847F31CBAFA5100AB86E3 /* MGLMultiPoint.h in Headers */,
+ DAD1656C1CF41981001FF4B9 /* MGLFeature.h in Headers */,
+ 40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */,
+ DA88484F1CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h in Headers */,
+ DA8847F21CBAFA5100AB86E3 /* MGLMapCamera.h in Headers */,
+ DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */,
+ DA737EE11D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */,
+ DA8848851CBB033F00AB86E3 /* FABKitProtocol.h in Headers */,
+ DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */,
+ DA88485C1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h in Headers */,
+ DA8848871CBB033F00AB86E3 /* Fabric.h in Headers */,
+ DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */,
+ DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */,
+ DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */,
+ DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */,
+ DA8847F61CBAFA5100AB86E3 /* MGLOfflineStorage.h in Headers */,
+ DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */,
+ DA88483C1CBAFB8500AB86E3 /* MGLMapView.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DABFB85C1CBE99DE00D62B32 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
+ DABFB85E1CBE99E500D62B32 /* MGLAnnotation.h in Headers */,
+ DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */,
+ DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
+ DA35A29F1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
+ DABFB8711CBE9A0F00D62B32 /* MGLMapView+MGLCustomStyleLayerAdditions.h in Headers */,
+ DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */,
+ DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
+ DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */,
+ DABFB8661CBE99E500D62B32 /* MGLPointAnnotation.h in Headers */,
+ DABFB8621CBE99E500D62B32 /* MGLOfflinePack.h in Headers */,
+ DAD1656D1CF41981001FF4B9 /* MGLFeature.h in Headers */,
+ DA17BE311CC4BDAA00402C41 /* MGLMapView_Internal.h in Headers */,
+ DABFB86C1CBE99E500D62B32 /* MGLTypes.h in Headers */,
+ DABFB8691CBE99E500D62B32 /* MGLShape.h in Headers */,
+ DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */,
+ DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */,
+ DABFB8651CBE99E500D62B32 /* MGLOverlay.h in Headers */,
+ DABFB8681CBE99E500D62B32 /* MGLPolyline.h in Headers */,
+ DABFB86F1CBE9A0F00D62B32 /* MGLMapView.h in Headers */,
+ DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
+ DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
+ DABFB8731CBE9A9900D62B32 /* Mapbox.h in Headers */,
+ DABFB86B1CBE99E500D62B32 /* MGLTilePyramidOfflineRegion.h in Headers */,
+ 4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
+ DABFB85F1CBE99E500D62B32 /* MGLGeometry.h in Headers */,
+ DABFB85D1CBE99E500D62B32 /* MGLAccountManager.h in Headers */,
+ DA35A2BC1CCA9A6900E826B2 /* MGLClockDirectionFormatter.h in Headers */,
+ DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */,
+ DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */,
+ DA737EE21D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */,
+ DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ DA1DC9491CB6C1C2006E619F /* iosapp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA1DC9611CB6C1C2006E619F /* Build configuration list for PBXNativeTarget "iosapp" */;
+ buildPhases = (
+ DA1DC9461CB6C1C2006E619F /* Sources */,
+ DA1DC9471CB6C1C2006E619F /* Frameworks */,
+ DA1DC9481CB6C1C2006E619F /* Resources */,
+ DA4A269A1CB6F5D3000B7809 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DA8847D81CBAF91600AB86E3 /* PBXTargetDependency */,
+ );
+ name = iosapp;
+ productName = iosapp;
+ productReference = DA1DC94A1CB6C1C2006E619F /* Mapbox GL.app */;
+ productType = "com.apple.product-type.application";
+ };
+ DA25D5B81CCD9EDE00607828 /* settings */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA25D5BE1CCD9EDE00607828 /* Build configuration list for PBXNativeTarget "settings" */;
+ buildPhases = (
+ DA25D5B71CCD9EDE00607828 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = settings;
+ productName = settings;
+ productReference = DA25D5B91CCD9EDE00607828 /* Settings.bundle */;
+ productType = "com.apple.product-type.bundle";
+ };
+ DA2E88501CC036F400F24E7B /* test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA2E88591CC036F400F24E7B /* Build configuration list for PBXNativeTarget "test" */;
+ buildPhases = (
+ DA2E884D1CC036F400F24E7B /* Sources */,
+ DA2E884E1CC036F400F24E7B /* Frameworks */,
+ DA2E884F1CC036F400F24E7B /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DA2E88581CC036F400F24E7B /* PBXTargetDependency */,
+ );
+ name = test;
+ productName = test;
+ productReference = DA2E88511CC036F400F24E7B /* test.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ DA8847D11CBAF91600AB86E3 /* dynamic */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA8847DD1CBAF91600AB86E3 /* Build configuration list for PBXNativeTarget "dynamic" */;
+ buildPhases = (
+ DA8847CD1CBAF91600AB86E3 /* Sources */,
+ DA8847CE1CBAF91600AB86E3 /* Frameworks */,
+ DA8847CF1CBAF91600AB86E3 /* Headers */,
+ DA8847D01CBAF91600AB86E3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DA25D5C81CCDA0C100607828 /* PBXTargetDependency */,
+ );
+ name = dynamic;
+ productName = framework;
+ productReference = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ DA8933D41CCD306400E68420 /* bundle */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA8933D81CCD306400E68420 /* Build configuration list for PBXNativeTarget "bundle" */;
+ buildPhases = (
+ DA8933D31CCD306400E68420 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = bundle;
+ productName = bundle;
+ productReference = DA8933D51CCD306400E68420 /* Mapbox.bundle */;
+ productType = "com.apple.product-type.bundle";
+ };
+ DAA4E4121CBB71D400178DFB /* static */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DAA4E4191CBB71D500178DFB /* Build configuration list for PBXNativeTarget "static" */;
+ buildPhases = (
+ DAA4E40F1CBB71D400178DFB /* Sources */,
+ DAA4E4101CBB71D400178DFB /* Frameworks */,
+ DAA4E4111CBB71D400178DFB /* CopyFiles */,
+ DABFB85C1CBE99DE00D62B32 /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DA25D5CA1CCDA0CC00607828 /* PBXTargetDependency */,
+ DA25D5CC1CCDA0CC00607828 /* PBXTargetDependency */,
+ );
+ name = static;
+ productName = static;
+ productReference = DAA4E4131CBB71D400178DFB /* libMapbox.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ DABCABA71CB80692000A7C39 /* bench */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DABCABBE1CB80692000A7C39 /* Build configuration list for PBXNativeTarget "bench" */;
+ buildPhases = (
+ DABCABA41CB80692000A7C39 /* Sources */,
+ DABCABA51CB80692000A7C39 /* Frameworks */,
+ DABCABA61CB80692000A7C39 /* Resources */,
+ DAA4E40C1CBB6C9600178DFB /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DAA4E40B1CBB6C9500178DFB /* PBXTargetDependency */,
+ );
+ name = bench;
+ productName = bench;
+ productReference = DABCABA81CB80692000A7C39 /* Bench GL.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ DA1DC9421CB6C1C2006E619F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = MBX;
+ LastUpgradeCheck = 0730;
+ ORGANIZATIONNAME = Mapbox;
+ TargetAttributes = {
+ DA1DC9491CB6C1C2006E619F = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DA25D5B81CCD9EDE00607828 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DA2E88501CC036F400F24E7B = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DA8847D11CBAF91600AB86E3 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DA8933D41CCD306400E68420 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DAA4E4121CBB71D400178DFB = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DABCABA71CB80692000A7C39 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ };
+ };
+ buildConfigurationList = DA1DC9451CB6C1C2006E619F /* Build configuration list for PBXProject "ios" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = DA1DC9411CB6C1C2006E619F;
+ productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ DA1DC9491CB6C1C2006E619F /* iosapp */,
+ DABCABA71CB80692000A7C39 /* bench */,
+ DA8847D11CBAF91600AB86E3 /* dynamic */,
+ DAA4E4121CBB71D400178DFB /* static */,
+ DA8933D41CCD306400E68420 /* bundle */,
+ DA25D5B81CCD9EDE00607828 /* settings */,
+ DA2E88501CC036F400F24E7B /* test */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ DA1DC9481CB6C1C2006E619F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA25D5CD1CCDA11500607828 /* Settings.bundle in Resources */,
+ DA1DC9701CB6C6CE006E619F /* points.geojson in Resources */,
+ DA1DC9711CB6C6CE006E619F /* polyline.geojson in Resources */,
+ DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */,
+ DA821D071CCC6D59007508D4 /* Main.storyboard in Resources */,
+ DA1DC9731CB6C6CE006E619F /* threestates.geojson in Resources */,
+ DA821D061CCC6D59007508D4 /* LaunchScreen.storyboard in Resources */,
+ DA1DC99F1CB6E088006E619F /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA25D5B71CCD9EDE00607828 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA25D5C61CCDA06800607828 /* Root.strings in Resources */,
+ DA25D5C01CCD9F8400607828 /* Root.plist in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA2E884F1CC036F400F24E7B /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA8847D01CBAF91600AB86E3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA8848731CBAFCC100AB86E3 /* mapbox.png in Resources */,
+ DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */,
+ DA8848741CBAFCC100AB86E3 /* mapbox@2x.png in Resources */,
+ DA88487A1CBAFD5C00AB86E3 /* api_mapbox_com-digicert.der in Resources */,
+ DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */,
+ DA88486D1CBAFCC100AB86E3 /* Compass.png in Resources */,
+ DA8848721CBAFCC100AB86E3 /* default_marker@3x.png in Resources */,
+ DA88487C1CBAFD5C00AB86E3 /* star_tilestream_net.der in Resources */,
+ DA88486F1CBAFCC100AB86E3 /* Compass@3x.png in Resources */,
+ DA88486E1CBAFCC100AB86E3 /* Compass@2x.png in Resources */,
+ DA8848701CBAFCC100AB86E3 /* default_marker.png in Resources */,
+ DA8848711CBAFCC100AB86E3 /* default_marker@2x.png in Resources */,
+ DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */,
+ DA8848751CBAFCC100AB86E3 /* mapbox@3x.png in Resources */,
+ DA88487B1CBAFD5C00AB86E3 /* api_mapbox_com-geotrust.der in Resources */,
+ DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
+ DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA8933D31CCD306400E68420 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */,
+ DA8933E11CCD31DF00E68420 /* Compass.png in Resources */,
+ DA8933E21CCD31DF00E68420 /* Compass@2x.png in Resources */,
+ DA8933E31CCD31DF00E68420 /* Compass@3x.png in Resources */,
+ DA8933E41CCD31DF00E68420 /* default_marker.png in Resources */,
+ DA8933E51CCD31DF00E68420 /* default_marker@2x.png in Resources */,
+ DA8933E61CCD31DF00E68420 /* default_marker@3x.png in Resources */,
+ DA8933E71CCD31DF00E68420 /* mapbox.png in Resources */,
+ DA8933E81CCD31DF00E68420 /* mapbox@2x.png in Resources */,
+ DA8933E91CCD31DF00E68420 /* mapbox@3x.png in Resources */,
+ DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */,
+ DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */,
+ DA8933DD1CCD31D400E68420 /* api_mapbox_com-digicert.der in Resources */,
+ DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
+ DA8933DE1CCD31D400E68420 /* api_mapbox_com-geotrust.der in Resources */,
+ DA8933DF1CCD31D400E68420 /* star_tilestream_net.der in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DABCABA61CB80692000A7C39 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA89633A1CC549A100684375 /* tiles in Resources */,
+ DA8963391CC549A100684375 /* styles in Resources */,
+ DABCABBA1CB80692000A7C39 /* LaunchScreen.storyboard in Resources */,
+ DA8963381CC549A100684375 /* sprites in Resources */,
+ DABCABB71CB80692000A7C39 /* Assets.xcassets in Resources */,
+ DA8963371CC549A100684375 /* glyphs in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ DA1DC9461CB6C1C2006E619F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA1DC9971CB6E046006E619F /* main.m in Sources */,
+ DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */,
+ DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */,
+ DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */,
+ DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */,
+ 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA2E884D1CC036F400F24E7B /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA2E88631CC0382C00F24E7B /* MGLOfflineRegionTests.m in Sources */,
+ DA2E88651CC0382C00F24E7B /* MGLStyleTests.mm in Sources */,
+ DA2E88611CC0382C00F24E7B /* MGLGeometryTests.mm in Sources */,
+ DA2E88641CC0382C00F24E7B /* MGLOfflineStorageTests.m in Sources */,
+ DA35A2C61CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
+ DA35A2C51CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m in Sources */,
+ DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
+ DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
+ DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DA8847CD1CBAF91600AB86E3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA88485D1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.m in Sources */,
+ DAD165701CF41981001FF4B9 /* MGLFeature.mm in Sources */,
+ 40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
+ DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */,
+ DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.m in Sources */,
+ DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */,
+ DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */,
+ DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */,
+ DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
+ DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.m in Sources */,
+ DA8848901CBB048E00AB86E3 /* reachability.m in Sources */,
+ DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */,
+ DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */,
+ DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */,
+ DA8848281CBAFA6200AB86E3 /* MGLShape.m in Sources */,
+ DA35A2B31CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
+ DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
+ DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */,
+ DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */,
+ DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */,
+ DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
+ DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
+ 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
+ DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
+ DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
+ DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
+ DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */,
+ DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */,
+ DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */,
+ DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */,
+ DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */,
+ DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAA4E40F1CBB71D400178DFB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAA4E4221CBB730400178DFB /* MGLPointAnnotation.m in Sources */,
+ DAD165711CF41981001FF4B9 /* MGLFeature.mm in Sources */,
+ 40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
+ DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */,
+ DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */,
+ DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
+ DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */,
+ DAA4E4351CBB730400178DFB /* SMCalloutView.m in Sources */,
+ DA35A2B91CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
+ DAD1657B1CF4CDFF001FF4B9 /* MGLShapeCollection.m in Sources */,
+ DAA4E4251CBB730400178DFB /* MGLShape.m in Sources */,
+ DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */,
+ DAA4E4261CBB730400178DFB /* MGLStyle.mm in Sources */,
+ DAA4E41D1CBB730400178DFB /* MGLGeometry.mm in Sources */,
+ DAA4E41F1CBB730400178DFB /* MGLMultiPoint.mm in Sources */,
+ DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
+ DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
+ DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */,
+ DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */,
+ DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
+ DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
+ DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */,
+ 4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ DAA4E4341CBB730400178DFB /* MGLUserLocationAnnotationView.m in Sources */,
+ DAA4E42C1CBB730400178DFB /* reachability.m in Sources */,
+ DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
+ DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
+ DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */,
+ DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */,
+ DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
+ DAA4E4271CBB730400178DFB /* MGLTilePyramidOfflineRegion.mm in Sources */,
+ DAA4E41C1CBB730400178DFB /* MGLAccountManager.m in Sources */,
+ DAA4E4241CBB730400178DFB /* MGLPolyline.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DABCABA41CB80692000A7C39 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DABCABB21CB80692000A7C39 /* MBXBenchViewController.mm in Sources */,
+ DABCABAF1CB80692000A7C39 /* MBXBenchAppDelegate.m in Sources */,
+ DABCABC21CB8071D000A7C39 /* locations.cpp in Sources */,
+ DABCABAC1CB80692000A7C39 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ DA25D5C81CCDA0C100607828 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA25D5B81CCD9EDE00607828 /* settings */;
+ targetProxy = DA25D5C71CCDA0C100607828 /* PBXContainerItemProxy */;
+ };
+ DA25D5CA1CCDA0CC00607828 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8933D41CCD306400E68420 /* bundle */;
+ targetProxy = DA25D5C91CCDA0CC00607828 /* PBXContainerItemProxy */;
+ };
+ DA25D5CC1CCDA0CC00607828 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA25D5B81CCD9EDE00607828 /* settings */;
+ targetProxy = DA25D5CB1CCDA0CC00607828 /* PBXContainerItemProxy */;
+ };
+ DA2E88581CC036F400F24E7B /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8847D11CBAF91600AB86E3 /* dynamic */;
+ targetProxy = DA2E88571CC036F400F24E7B /* PBXContainerItemProxy */;
+ };
+ DA8847D81CBAF91600AB86E3 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8847D11CBAF91600AB86E3 /* dynamic */;
+ targetProxy = DA8847D71CBAF91600AB86E3 /* PBXContainerItemProxy */;
+ };
+ DAA4E40B1CBB6C9500178DFB /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8847D11CBAF91600AB86E3 /* dynamic */;
+ targetProxy = DAA4E40A1CBB6C9500178DFB /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ DA25D5C41CCDA06800607828 /* Root.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA25D5C51CCDA06800607828 /* Base */,
+ );
+ name = Root.strings;
+ sourceTree = "<group>";
+ };
+ DA89339F1CCC951200E68420 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933A01CCC951200E68420 /* Base */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ DA8933BA1CCD2CA100E68420 /* Foundation.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933BB1CCD2CA100E68420 /* Base */,
+ );
+ name = Foundation.strings;
+ sourceTree = "<group>";
+ };
+ DA8933BD1CCD2CAD00E68420 /* Foundation.stringsdict */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933BE1CCD2CAD00E68420 /* en */,
+ );
+ name = Foundation.stringsdict;
+ sourceTree = "<group>";
+ };
+ DABCABB81CB80692000A7C39 /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DABCABB91CB80692000A7C39 /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+ DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DAC49C621CD07D74009E1AA3 /* en */,
+ );
+ name = Localizable.stringsdict;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ DA1DC95F1CB6C1C2006E619F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_COMMIT_HASH = deadbeef;
+ CURRENT_SEMANTIC_VERSION = 1.0.0;
+ CURRENT_SHORT_VERSION = 1.0;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SYMROOT = ../../build/ios;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ DA1DC9601CB6C1C2006E619F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ CURRENT_COMMIT_HASH = deadbeef;
+ CURRENT_SEMANTIC_VERSION = 1.0.0;
+ CURRENT_SHORT_VERSION = 1.0;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SYMROOT = ../../build/ios;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ DA1DC9621CB6C1C2006E619F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = "Mapbox GL";
+ };
+ name = Debug;
+ };
+ DA1DC9631CB6C1C2006E619F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = "Mapbox GL";
+ };
+ name = Release;
+ };
+ DA25D5BC1CCD9EDE00607828 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = Settings;
+ SKIP_INSTALL = YES;
+ WRAPPER_EXTENSION = bundle;
+ };
+ name = Debug;
+ };
+ DA25D5BD1CCD9EDE00607828 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = Settings;
+ SKIP_INSTALL = YES;
+ WRAPPER_EXTENSION = bundle;
+ };
+ name = Release;
+ };
+ DA2E885A1CC036F400F24E7B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../../include;
+ INFOPLIST_FILE = test/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ DA2E885B1CC036F400F24E7B /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../../include;
+ INFOPLIST_FILE = test/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ DA8847DB1CBAF91600AB86E3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ INFOPLIST_FILE = framework/Info.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(sqlite_cflags)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "$(sqlite_ldflags)",
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ DA8847DC1CBAF91600AB86E3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ INFOPLIST_FILE = framework/Info.plist;
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(sqlite_cflags)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "$(sqlite_ldflags)",
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ DA8933D91CCD306400E68420 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "framework/Info-static.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ WRAPPER_EXTENSION = bundle;
+ };
+ name = Debug;
+ };
+ DA8933DA1CCD306400E68420 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ INFOPLIST_FILE = "framework/Info-static.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ WRAPPER_EXTENSION = bundle;
+ };
+ name = Release;
+ };
+ DAA4E41A1CBB71D500178DFB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(sqlite_cflags)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "$(sqlite_ldflags)",
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_NAME = Mapbox;
+ PUBLIC_HEADERS_FOLDER_PATH = Headers;
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ DAA4E41B1CBB71D500178DFB /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(sqlite_cflags)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "$(sqlite_ldflags)",
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_NAME = Mapbox;
+ PUBLIC_HEADERS_FOLDER_PATH = Headers;
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ DABCABBC1CB80692000A7C39 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench;
+ PRODUCT_NAME = "Bench GL";
+ };
+ name = Debug;
+ };
+ DABCABBD1CB80692000A7C39 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench;
+ PRODUCT_NAME = "Bench GL";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ DA1DC9451CB6C1C2006E619F /* Build configuration list for PBXProject "ios" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA1DC95F1CB6C1C2006E619F /* Debug */,
+ DA1DC9601CB6C1C2006E619F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA1DC9611CB6C1C2006E619F /* Build configuration list for PBXNativeTarget "iosapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA1DC9621CB6C1C2006E619F /* Debug */,
+ DA1DC9631CB6C1C2006E619F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA25D5BE1CCD9EDE00607828 /* Build configuration list for PBXNativeTarget "settings" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA25D5BC1CCD9EDE00607828 /* Debug */,
+ DA25D5BD1CCD9EDE00607828 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA2E88591CC036F400F24E7B /* Build configuration list for PBXNativeTarget "test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA2E885A1CC036F400F24E7B /* Debug */,
+ DA2E885B1CC036F400F24E7B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA8847DD1CBAF91600AB86E3 /* Build configuration list for PBXNativeTarget "dynamic" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA8847DB1CBAF91600AB86E3 /* Debug */,
+ DA8847DC1CBAF91600AB86E3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA8933D81CCD306400E68420 /* Build configuration list for PBXNativeTarget "bundle" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA8933D91CCD306400E68420 /* Debug */,
+ DA8933DA1CCD306400E68420 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DAA4E4191CBB71D500178DFB /* Build configuration list for PBXNativeTarget "static" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DAA4E41A1CBB71D500178DFB /* Debug */,
+ DAA4E41B1CBB71D500178DFB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DABCABBE1CB80692000A7C39 /* Build configuration list for PBXNativeTarget "bench" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DABCABBC1CB80692000A7C39 /* Debug */,
+ DABCABBD1CB80692000A7C39 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = DA1DC9421CB6C1C2006E619F /* Project object */;
+}
diff --git a/platform/ios/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/ios/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..92ad79b9dd
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:ios.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
new file mode 100644
index 0000000000..26982c0747
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "4E8A9455A3A23B7FD2A8FC52"
+ BuildableName = "All"
+ BlueprintName = "All"
+ ReferencedContainer = "container:../../build/ios/platform/ios/platform.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
new file mode 100644
index 0000000000..bfddf3c6c6
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DABCABA71CB80692000A7C39"
+ BuildableName = "Bench GL.app"
+ BlueprintName = "bench"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DABCABA71CB80692000A7C39"
+ BuildableName = "Bench GL.app"
+ BlueprintName = "bench"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DABCABA71CB80692000A7C39"
+ BuildableName = "Bench GL.app"
+ BlueprintName = "bench"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DABCABA71CB80692000A7C39"
+ BuildableName = "Bench GL.app"
+ BlueprintName = "bench"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme
new file mode 100644
index 0000000000..4b50564e54
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAA4E4121CBB71D400178DFB"
+ BuildableName = "libMapbox.a"
+ BlueprintName = "static"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
new file mode 100644
index 0000000000..b77beb34aa
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA2E88501CC036F400F24E7B"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA8847D11CBAF91600AB86E3"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
new file mode 100644
index 0000000000..de74d64314
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA1DC9491CB6C1C2006E619F"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "iosapp"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme
new file mode 100644
index 0000000000..8293b8e2a9
--- /dev/null
+++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAA4E4121CBB71D400178DFB"
+ BuildableName = "libMapbox.a"
+ BlueprintName = "static"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAA4E4121CBB71D400178DFB"
+ BuildableName = "libMapbox.a"
+ BlueprintName = "static"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAA4E4121CBB71D400178DFB"
+ BuildableName = "libMapbox.a"
+ BlueprintName = "static"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAA4E4121CBB71D400178DFB"
+ BuildableName = "libMapbox.a"
+ BlueprintName = "static"
+ ReferencedContainer = "container:ios.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/ios/ios.xcworkspace/contents.xcworkspacedata b/platform/ios/ios.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..929ade2fee
--- /dev/null
+++ b/platform/ios/ios.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:ios.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:uitest/ios-tests.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:uitest/KIF/KIF.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../../build/ios/platform/ios/platform.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/platform/ios/ios.xcworkspace/xcshareddata/ios.xcscmblueprint b/platform/ios/ios.xcworkspace/xcshareddata/ios.xcscmblueprint
new file mode 100644
index 0000000000..a86fb58e7b
--- /dev/null
+++ b/platform/ios/ios.xcworkspace/xcshareddata/ios.xcscmblueprint
@@ -0,0 +1,37 @@
+{
+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "7E68CB584078A487C0535CC191D3B7551EEE2095",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+ },
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+ "10265E242415D473A6A613214DB7AC3EE3D43F93" : 0,
+ "D1BB63D501E0048981F00509C5A268D4AF5FEE4C" : 0,
+ "7E68CB584078A487C0535CC191D3B7551EEE2095" : 0
+ },
+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "D6F4212C-91A7-4C87-9973-DF38C92B9C5F",
+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+ "10265E242415D473A6A613214DB7AC3EE3D43F93" : "mapbox-gl-native\/platform\/ios\/test\/KIF\/",
+ "D1BB63D501E0048981F00509C5A268D4AF5FEE4C" : "mapbox-gl-native\/platform\/ios\/vendor\/SMCalloutView\/",
+ "7E68CB584078A487C0535CC191D3B7551EEE2095" : "mapbox-gl-native\/"
+ },
+ "DVTSourceControlWorkspaceBlueprintNameKey" : "ios",
+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "platform\/ios\/ios.xcworkspace",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kif-framework\/KIF.git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "10265E242415D473A6A613214DB7AC3EE3D43F93"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/mapbox\/mapbox-gl-native.git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "7E68CB584078A487C0535CC191D3B7551EEE2095"
+ },
+ {
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/mapbox\/smcalloutview.git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D1BB63D501E0048981F00509C5A268D4AF5FEE4C"
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/ios/ios.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist b/platform/ios/ios.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000000..cb6ecad738
--- /dev/null
+++ b/platform/ios/ios.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+ type = "3"
+ version = "2.0">
+ <Breakpoints>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
+ <BreakpointContent
+ shouldBeEnabled = "No"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ scope = "1"
+ stopOnStyle = "0">
+ <Actions>
+ <BreakpointActionProxy
+ ActionExtensionID = "Xcode.BreakpointAction.Sound">
+ <ActionContent
+ soundName = "Basso">
+ </ActionContent>
+ </BreakpointActionProxy>
+ </Actions>
+ </BreakpointContent>
+ </BreakpointProxy>
+ </Breakpoints>
+</Bucket>
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
new file mode 100644
index 0000000000..67d877cda3
--- /dev/null
+++ b/platform/ios/jazzy.yml
@@ -0,0 +1,90 @@
+module: Mapbox
+author: Mapbox
+author_url: https://www.mapbox.com/
+github_url: https://github.com/mapbox/mapbox-gl-native
+dash_url: https://www.mapbox.com/ios-sdk/docsets/Mapbox.xml
+copyright: '© 2014–2016 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
+
+head: |
+ <link rel='shortcut icon' href='https://www.mapbox.com/img/favicon.ico' type='image/x-icon' />
+
+objc: Yes
+skip_undocumented: Yes
+hide_documentation_coverage: Yes
+umbrella_header: src/Mapbox.h
+framework_root: ../darwin/src
+
+custom_categories:
+ - name: Maps
+ children:
+ - MGLAccountManager
+ - MGLMapCamera
+ - MGLMapDebugMaskOptions
+ - MGLMapView
+ - MGLMapViewDelegate
+ - MGLStyle
+ - MGLStyleDefaultVersion
+ - MGLUserTrackingMode
+ - name: Annotations
+ children:
+ - MGLAnnotation
+ - MGLAnnotationImage
+ - MGLAnnotationVerticalAlignment
+ - MGLAnnotationView
+ - MGLCalloutView
+ - MGLCalloutViewDelegate
+ - MGLMultiPoint
+ - MGLMultiPolygon
+ - MGLMultiPolyline
+ - MGLPointAnnotation
+ - MGLPolygon
+ - MGLPolyline
+ - MGLOverlay
+ - MGLShape
+ - MGLShapeCollection
+ - MGLUserLocation
+ - name: Map Data
+ children:
+ - MGLFeature
+ - MGLMultiPointFeature
+ - MGLMultiPolygonFeature
+ - MGLMultiPolylineFeature
+ - MGLPointFeature
+ - MGLPolygonFeature
+ - MGLPolylineFeature
+ - MGLShapeCollectionFeature
+ - name: Offline Maps
+ children:
+ - MGLOfflineRegion
+ - MGLOfflineStorage
+ - MGLOfflinePack
+ - MGLOfflinePackAdditionCompletionHandler
+ - MGLOfflinePackErrorNotification
+ - MGLOfflinePackErrorUserInfoKey
+ - MGLOfflinePackMaximumCountUserInfoKey
+ - MGLOfflinePackMaximumMapboxTilesReachedNotification
+ - MGLOfflinePackProgress
+ - MGLOfflinePackProgressChangedNotification
+ - MGLOfflinePackProgressUserInfoKey
+ - MGLOfflinePackRemovalCompletionHandler
+ - MGLOfflinePackState
+ - MGLOfflinePackStateUserInfoKey
+ - MGLTilePyramidOfflineRegion
+ - name: Geometry
+ children:
+ - MGLClockDirectionFormatter
+ - MGLCompassDirectionFormatter
+ - MGLCoordinateBounds
+ - MGLCoordinateBoundsEqualToCoordinateBounds
+ - MGLCoordinateBoundsGetCoordinateSpan
+ - MGLCoordinateBoundsIsEmpty
+ - MGLCoordinateBoundsMake
+ - MGLCoordinateBoundsOffset
+ - MGLCoordinateFormatter
+ - MGLCoordinateSpan
+ - MGLCoordinateSpanEqualToCoordinateSpan
+ - MGLCoordinateSpanMake
+ - MGLCoordinateSpanZero
+ - MGLDegreesFromRadians
+ - MGLRadiansFromDegrees
+ - MGLStringFromCoordinateBounds
diff --git a/platform/ios/platform.gyp b/platform/ios/platform.gyp
new file mode 100644
index 0000000000..e4abff27e3
--- /dev/null
+++ b/platform/ios/platform.gyp
@@ -0,0 +1,141 @@
+{
+ 'variables': {
+ 'loop_lib': 'darwin',
+ 'headless_lib': 'eagl',
+ 'coverage': 0,
+ },
+ 'includes': [
+ '../../build/ios/config.gypi',
+ '../../mbgl.gypi',
+ '../../test/test.gypi',
+ ],
+ 'target_defaults': {
+ 'target_conditions': [
+ ['_type == "static_library"', {
+ 'xcode_settings': {
+ 'SDKROOT': 'iphoneos',
+ 'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '7.0',
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
+ 'CODE_SIGN_IDENTITY': 'iPhone Developer',
+ },
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'test',
+ 'type': 'executable',
+ 'product_name': 'ios-test',
+ 'product_extension': 'app',
+ 'mac_bundle': 1,
+
+ 'dependencies': [
+ 'test-lib',
+ 'platform-lib',
+ ],
+
+ 'sources': [
+ '../../test/src/main.mm',
+# '../../src/mbgl/util/premultiply.cpp',
+ ],
+
+ 'xcode_settings': {
+ 'SDKROOT': 'iphoneos',
+ 'SUPPORTED_PLATFORMS': 'iphonesimulator iphoneos',
+ 'INFOPLIST_FILE': '../../test/src/app-info.plist',
+ 'IPHONEOS_DEPLOYMENT_TARGET': '8.0',
+ 'TARGETED_DEVICE_FAMILY': '1,2',
+ 'COPY_PHASE_STRIP': 'NO',
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ 'CLANG_ENABLE_MODULES': 'YES',
+ 'CODE_SIGN_IDENTITY': 'iPhone Developer',
+ 'PRODUCT_BUNDLE_IDENTIFIER': 'com.mapbox.MapboxGLUnitTest',
+ },
+
+ 'copies': [{
+ 'destination': '<(PRODUCT_DIR)/$(WRAPPER_NAME)/test',
+ 'files': [ '../../test/fixtures' ],
+ }],
+
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+ '$(SDKROOT)/System/Library/Frameworks/GLKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+ '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/OpenGLES.framework',
+ ],
+ },
+ },
+ {
+ 'target_name': 'platform-lib',
+ 'product_name': 'mbgl-platform-ios',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'core',
+ ],
+
+ 'include_dirs': [
+ 'include',
+ '../darwin/include',
+ '../default',
+ '../../include',
+ '../../src', # TODO: eliminate
+ '<(SHARED_INTERMEDIATE_DIR)/include',
+ ],
+
+ 'sources': [
+ '../default/asset_file_source.cpp',
+ '../default/default_file_source.cpp',
+ '../default/online_file_source.cpp',
+ '../default/mbgl/storage/offline.hpp',
+ '../default/mbgl/storage/offline.cpp',
+ '../default/mbgl/storage/offline_database.hpp',
+ '../default/mbgl/storage/offline_database.cpp',
+ '../default/mbgl/storage/offline_download.hpp',
+ '../default/mbgl/storage/offline_download.cpp',
+ '../default/sqlite3.hpp',
+ '../default/sqlite3.cpp',
+ '../darwin/src/http_file_source.mm',
+ '../darwin/src/log_nslog.mm',
+ '../darwin/src/string_nsstring.mm',
+ '../darwin/src/image.mm',
+ '../darwin/src/nsthread.mm',
+ '../darwin/src/reachability.m',
+ ],
+
+ 'variables': {
+ 'cflags_cc': [
+ '<@(sqlite_cflags)',
+ '<@(zlib_cflags)',
+ '<@(rapidjson_cflags)',
+ ],
+ 'ldflags': [
+ '<@(sqlite_ldflags)',
+ '<@(zlib_ldflags)',
+ ],
+ 'libraries': [
+ '<@(sqlite_static_libs)',
+ '<@(zlib_static_libs)',
+ ],
+ },
+
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ 'CLANG_ENABLE_MODULES': 'YES',
+ },
+
+ 'link_settings': {
+ 'libraries': [ '<@(libraries)' ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ },
+ },
+ },
+ ],
+}
diff --git a/platform/ios/resources/Base.lproj/Localizable.strings b/platform/ios/resources/Base.lproj/Localizable.strings
new file mode 100644
index 0000000000..c4569fe239
--- /dev/null
+++ b/platform/ios/resources/Base.lproj/Localizable.strings
@@ -0,0 +1,84 @@
+/* Accessibility hint */
+"ANNOTATION_A11Y_HINT" = "Shows more info";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_REASON" = "The status code was %ld";
+
+/* No comment provided by engineer. */
+"CANCEL" = "Cancel";
+
+/* Accessibility hint */
+"COMPASS_A11Y_HINT" = "Rotates the map to face due north";
+
+/* Accessibility label */
+"COMPASS_A11Y_LABEL" = "Compass";
+
+/* Compass abbreviation for north */
+"COMPASS_NORTH" = "N";
+
+/* Copyright notice in attribution sheet */
+"COPY_MAPBOX" = "© Mapbox";
+
+/* Copyright notice in attribution sheet */
+"COPY_OSM" = "© OpenStreetMap";
+
+/* Instructions in Interface Builder designable; {key}, {plist file name} */
+"DESIGNABLE" = "To display a Mapbox-hosted map here, set %1$@ to your access token in %2$@\n\nFor detailed instructions, see:";
+
+/* Setup documentation URL display string; keep as short as possible */
+"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
+
+/* Accessibility hint */
+"INFO_A11Y_HINT" = "Shows credits, a feedback form, and more";
+
+/* Accessibility label */
+"INFO_A11Y_LABEL" = "About this map";
+
+/* Accessibility label */
+"LOGO_A11Y_LABEL" = "Mapbox";
+
+/* Accessibility label */
+"MAP_A11Y_LABEL" = "Map";
+
+/* Map accessibility value */
+"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
+
+/* Action in attribution sheet */
+"MAP_FEEDBACK" = "Improve This Map";
+
+/* Action sheet title */
+"SDK_NAME" = "Mapbox iOS SDK";
+
+/* Telemetry prompt message */
+"TELEMETRY_DISABLED_MSG" = "You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_OFF" = "Don’t Participate";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_ON" = "Participate";
+
+/* Telemetry prompt message */
+"TELEMETRY_ENABLED_MSG" = "You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_OFF" = "Stop Participating";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_ON" = "Keep Participating";
+
+/* Telemetry prompt button */
+"TELEMETRY_MORE" = "Tell Me More";
+
+/* Action in attribution sheet */
+"TELEMETRY_NAME" = "Mapbox Telemetry";
+
+/* Telemetry prompt title */
+"TELEMETRY_TITLE" = "Make Mapbox Maps Better";
+
+/* Default user location annotation title */
+"USER_DOT_TITLE" = "You Are Here";
+
diff --git a/platform/ios/resources/Compass.png b/platform/ios/resources/Compass.png
index e6b0b52c58..08bed0591b 100644
--- a/platform/ios/resources/Compass.png
+++ b/platform/ios/resources/Compass.png
Binary files differ
diff --git a/platform/ios/resources/Compass@2x.png b/platform/ios/resources/Compass@2x.png
index 2bd8a286da..8473a2d1ec 100644
--- a/platform/ios/resources/Compass@2x.png
+++ b/platform/ios/resources/Compass@2x.png
Binary files differ
diff --git a/platform/ios/resources/Compass@3x.png b/platform/ios/resources/Compass@3x.png
index 9308a46a51..9cf66ca483 100644
--- a/platform/ios/resources/Compass@3x.png
+++ b/platform/ios/resources/Compass@3x.png
Binary files differ
diff --git a/platform/ios/resources/en.lproj/Localizable.stringsdict b/platform/ios/resources/en.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..ee4de02116
--- /dev/null
+++ b/platform/ios/resources/en.lproj/Localizable.stringsdict
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>MAP_A11Y_VALUE</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>Zoom %dx
+%#@count@ visible</string>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%d annotation</string>
+ <key>other</key>
+ <string>%d annotations</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/scripts/after_failure.sh b/platform/ios/scripts/after_failure.sh
deleted file mode 100755
index 88a6427f75..0000000000
--- a/platform/ios/scripts/after_failure.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-set -u
-
-ls $KIF_SCREENSHOTS/
-
-REPO_NAME=$(basename $TRAVIS_REPO_SLUG)
-
-aws s3 cp $KIF_SCREENSHOTS/ s3://mapbox/$REPO_NAME/ios/tests/$TRAVIS_JOB_NUMBER/ --acl public-read --recursive > /dev/null
-
-echo http://mapbox.s3.amazonaws.com/$REPO_NAME/ios/tests/$TRAVIS_JOB_NUMBER/index.html
diff --git a/platform/ios/scripts/configure.sh b/platform/ios/scripts/configure.sh
index ee6d60de2f..408180881c 100644
--- a/platform/ios/scripts/configure.sh
+++ b/platform/ios/scripts/configure.sh
@@ -1,9 +1,14 @@
#!/usr/bin/env bash
-BOOST_VERSION=1.59.0
+UNIQUE_RESOURCE_VERSION=dev
+PROTOZERO_VERSION=1.3.0
+BOOST_VERSION=1.60.0
SQLITE_VERSION=system
-LIBUV_VERSION=1.7.5
ZLIB_VERSION=system
-GEOJSONVT_VERSION=3.1.0
-VARIANT_VERSION=1.0
+GEOMETRY_VERSION=0.5.0
+GEOJSONVT_VERSION=4.1.2
+VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
+GTEST_VERSION=1.7.0
+PIXELMATCH_VERSION=0.9.0
+EARCUT_VERSION=0.11
diff --git a/platform/ios/scripts/defaults.mk b/platform/ios/scripts/defaults.mk
deleted file mode 100644
index c2a3e95eeb..0000000000
--- a/platform/ios/scripts/defaults.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-HEADLESS = none
-PLATFORM ?= ios
-ASSET ?= fs
-HTTP ?= nsurl
-
-HOST_VERSION = all
-
-PROVISIONING_PROFILE ?= 19324a54-7455-4f0b-8e1c-e6957c718ebc
-
-export MASON_PLATFORM=$(HOST)
-export MASON_PLATFORM_VERSION=$(HOST_VERSION)
diff --git a/platform/ios/scripts/deploy-packages.sh b/platform/ios/scripts/deploy-packages.sh
index 64cb472f6b..42bce89cf3 100755
--- a/platform/ios/scripts/deploy-packages.sh
+++ b/platform/ios/scripts/deploy-packages.sh
@@ -56,6 +56,7 @@ export TRAVIS_REPO_SLUG=mapbox-gl-native
export PUBLISH_VERSION=$1
export GITHUB_USER=mapbox
export GITHUB_REPO=mapbox-gl-native
+export BUILDTYPE=Release
BINARY_DIRECTORY=$2
PUBLISH_PRE_FLAG=''
diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh
index 4890aca15c..3059550ba0 100755
--- a/platform/ios/scripts/document.sh
+++ b/platform/ios/scripts/document.sh
@@ -33,6 +33,7 @@ rm -rf ${OUTPUT}
mkdir -p ${OUTPUT}
jazzy \
+ --config platform/ios/jazzy.yml \
--sdk iphonesimulator \
--swift-version $SWIFT_VERSION \
--github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \
diff --git a/platform/ios/scripts/install.sh b/platform/ios/scripts/install.sh
deleted file mode 100755
index 437193b1f2..0000000000
--- a/platform/ios/scripts/install.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-git fetch --tags
-
-mapbox_time "checkout_mason" \
-git submodule update --init .mason
-
-mapbox_time "install_recent_git" \
-brew install git
-
-mapbox_time "install_awscli" \
-brew install awscli
-
-mapbox_time "install_jazzy" \
-gem install jazzy
-
-mkdir -p ${KIF_SCREENSHOTS}
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index 137775ef71..f0ca683389 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -6,11 +6,11 @@ set -u
NAME=Mapbox
OUTPUT=build/ios/pkg
-LIBUV_VERSION=1.7.5
+DERIVED_DATA=build/ios
+PRODUCTS=${DERIVED_DATA}/Build/Products
-BUILDTYPE=${BUILDTYPE:-Release}
+BUILDTYPE=${BUILDTYPE:-Debug}
BUILD_FOR_DEVICE=${BUILD_DEVICE:-true}
-ENABLE_BITCODE=${BITCODE:-YES}
GCC_GENERATE_DEBUGGING_SYMBOLS=${SYMBOLS:-YES}
BUILD_DYNAMIC=true
@@ -21,25 +21,19 @@ elif [[ ${FORMAT} == "dynamic" ]]; then
BUILD_STATIC=false
fi
-BUNDLE_RESOURCES=${BUNDLE_RESOURCES:-}
-BUNDLE_PATH=
-if [[ ${BUNDLE_RESOURCES} ]]; then
- BUNDLE_PATH="/${NAME}.bundle"
-fi
-
-PLACE_RESOURCE_BUNDLES_OUTSIDE_FRAMEWORK=${PLACE_RESOURCE_BUNDLES_OUTSIDE_FRAMEWORK:-}
-STATIC_BUNDLE_PATH=
-if [[ ${PLACE_RESOURCE_BUNDLES_OUTSIDE_FRAMEWORK} ]]; then
- STATIC_BUNDLE_PATH="${OUTPUT}/static${BUNDLE_PATH}"
+SELF_CONTAINED=${SELF_CONTAINED:-}
+STATIC_BUNDLE_DIR=
+if [[ ${SELF_CONTAINED} ]]; then
+ STATIC_BUNDLE_DIR="${OUTPUT}/static/${NAME}.framework"
else
- STATIC_BUNDLE_PATH="${OUTPUT}/static/${NAME}.framework${BUNDLE_PATH}"
+ STATIC_BUNDLE_DIR="${OUTPUT}/static"
fi
-STATIC_SETTINGS_DIRECTORY=
-if [[ ${PLACE_RESOURCE_BUNDLES_OUTSIDE_FRAMEWORK} ]]; then
- STATIC_SETTINGS_DIRECTORY="${OUTPUT}"
+STATIC_SETTINGS_DIR=
+if [[ ${SELF_CONTAINED} ]]; then
+ STATIC_SETTINGS_DIR="${OUTPUT}/static/${NAME}.framework"
else
- STATIC_SETTINGS_DIRECTORY="${OUTPUT}/static/${NAME}.framework"
+ STATIC_SETTINGS_DIR="${OUTPUT}"
fi
SDK=iphonesimulator
@@ -48,7 +42,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
fi
IOS_SDK_VERSION=`xcrun --sdk ${SDK} --show-sdk-version`
-echo "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK}; symbols: ${GCC_GENERATE_DEBUGGING_SYMBOLS}; Bitcode: ${ENABLE_BITCODE}; Mapbox.bundle: ${BUNDLE_RESOURCES} bundle.outside: ${PLACE_RESOURCE_BUNDLES_OUTSIDE_FRAMEWORK}"
+echo "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK}; symbols: ${GCC_GENERATE_DEBUGGING_SYMBOLS}; self-contained static framework: ${SELF_CONTAINED:-NO}"
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
function finish { >&2 echo -en "\033[0m"; }
@@ -63,7 +57,6 @@ if [[ ${BUILD_DYNAMIC} == true ]]; then
mkdir -p "${OUTPUT}"/dynamic
fi
-
step "Recording library version…"
VERSION="${OUTPUT}"/version.txt
echo -n "https://github.com/mapbox/mapbox-gl-native/commit/" > ${VERSION}
@@ -72,77 +65,50 @@ echo -n "mapbox-gl-native "
echo ${HASH}
echo ${HASH} >> ${VERSION}
-
-step "Creating build files…"
-export MASON_PLATFORM=ios
-export BUILDTYPE=${BUILDTYPE:-Release}
-export HOST=ios
-make Xcode/ios
-
PROJ_VERSION=$(git rev-list --count HEAD)
+SEM_VERSION=$( git describe --tags --match=ios-v*.*.* --abbrev=0 | sed 's/^ios-v//' )
+SHORT_VERSION=${SEM_VERSION%-*}
-if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
- if [[ ${BUILD_STATIC} == true ]]; then
- step "Building intermediate static libraries for iOS devices (build ${PROJ_VERSION})…"
- xcodebuild -sdk iphoneos${IOS_SDK_VERSION} \
- ARCHS="arm64 armv7 armv7s" \
- ONLY_ACTIVE_ARCH=NO \
- GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
- ENABLE_BITCODE=${ENABLE_BITCODE} \
- DEPLOYMENT_POSTPROCESSING=YES \
- CODE_SIGNING_REQUIRED=NO \
- CODE_SIGN_IDENTITY= \
- -project ./build/ios-all/gyp/mbgl.xcodeproj \
- -configuration ${BUILDTYPE} \
- -target everything \
- -jobs ${JOBS}
- fi
-
- if [[ ${BUILD_DYNAMIC} == true ]]; then
- step "Building dynamic framework for iOS devices (build ${PROJ_VERSION})…"
- xcodebuild -sdk iphoneos${IOS_SDK_VERSION} \
- ARCHS="arm64 armv7 armv7s" \
- ONLY_ACTIVE_ARCH=NO \
- GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
- ENABLE_BITCODE=${ENABLE_BITCODE} \
- DEPLOYMENT_POSTPROCESSING=YES \
- CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
- CODE_SIGNING_REQUIRED=NO \
- CODE_SIGN_IDENTITY= \
- -project ./build/ios-all/gyp/ios.xcodeproj \
- -configuration ${BUILDTYPE} \
- -target iossdk \
- -jobs ${JOBS}
- fi
-fi
+step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})…"
+
+SCHEME='dynamic'
+if [[ ${BUILD_DYNAMIC} == true && ${BUILD_STATIC} == true ]]; then
+ SCHEME+='+static'
+elif [[ ${BUILD_STATIC} == true ]]; then
+ SCHEME='static'
+fi
+
+xcodebuild \
+ GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
+ CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
+ CURRENT_SHORT_VERSION=${SHORT_VERSION} \
+ CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \
+ CURRENT_COMMIT_HASH=${HASH} \
+ ONLY_ACTIVE_ARCH=NO \
+ -derivedDataPath ${DERIVED_DATA} \
+ -workspace ./platform/ios/ios.xcworkspace \
+ -scheme ${SCHEME} \
+ -configuration ${BUILDTYPE} \
+ -sdk iphonesimulator \
+ -jobs ${JOBS} | xcpretty
-if [[ ${BUILD_STATIC} == true ]]; then
- step "Building intermediate static libraries for iOS Simulator (build ${PROJ_VERSION})…"
- xcodebuild -sdk iphonesimulator${IOS_SDK_VERSION} \
- ARCHS="x86_64 i386" \
- ONLY_ACTIVE_ARCH=NO \
- GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
- -project ./build/ios-all/gyp/mbgl.xcodeproj \
- -configuration ${BUILDTYPE} \
- -target everything \
- -jobs ${JOBS}
-fi
-
-if [[ ${BUILD_DYNAMIC} == true ]]; then
- step "Building dynamic framework for iOS Simulator (build ${PROJ_VERSION})…"
- xcodebuild -sdk iphonesimulator${IOS_SDK_VERSION} \
- ARCHS="x86_64 i386" \
- ONLY_ACTIVE_ARCH=NO \
+if [[ ${BUILD_FOR_DEVICE} == true ]]; then
+ xcodebuild \
GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
- ENABLE_BITCODE=${ENABLE_BITCODE} \
CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
- -project ./build/ios-all/gyp/ios.xcodeproj \
+ CURRENT_SHORT_VERSION=${SHORT_VERSION} \
+ CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \
+ CURRENT_COMMIT_HASH=${HASH} \
+ ONLY_ACTIVE_ARCH=NO \
+ -derivedDataPath ${DERIVED_DATA} \
+ -workspace ./platform/ios/ios.xcworkspace \
+ -scheme ${SCHEME} \
-configuration ${BUILDTYPE} \
- -target iossdk \
- -jobs ${JOBS}
+ -sdk iphoneos \
+ -jobs ${JOBS} | xcpretty
fi
-LIBS=(core.a platform-ios.a asset-fs.a http-nsurl.a)
+LIBS=(Mapbox.a mbgl-core.a mbgl-platform-ios.a)
# https://medium.com/@syshen/create-an-ios-universal-framework-148eb130a46c
if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
@@ -150,46 +116,56 @@ if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
step "Assembling static framework for iOS Simulator and devices…"
mkdir -p ${OUTPUT}/static/${NAME}.framework
libtool -static -no_warning_for_no_symbols \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a` \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a` \
-o ${OUTPUT}/static/${NAME}.framework/${NAME} \
- ${LIBS[@]/#/gyp/build/${BUILDTYPE}-iphoneos/libmbgl-} \
- ${LIBS[@]/#/gyp/build/${BUILDTYPE}-iphonesimulator/libmbgl-}
+ ${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphoneos/lib} \
+ ${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a`
+
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${STATIC_BUNDLE_DIR}
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
step "Copying dynamic framework into place for iOS devices"
cp -r \
- gyp/build/${BUILDTYPE}-iphoneos/${NAME}.framework \
- ${OUTPUT}/dynamic/
- cp -r gyp/build/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM \
+ ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework \
${OUTPUT}/dynamic/
+ if [[ -e ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM ]]; then
+ cp -r ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM \
+ ${OUTPUT}/dynamic/
+ fi
step "Merging simulator dynamic library into device dynamic library…"
lipo \
- gyp/build/${BUILDTYPE}-iphoneos/${NAME}.framework/${NAME} \
- gyp/build/${BUILDTYPE}-iphonesimulator/${NAME}.framework/${NAME} \
+ ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework/${NAME} \
+ ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework/${NAME} \
-create -output ${OUTPUT}/dynamic/${NAME}.framework/${NAME} | echo
fi
+
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/Settings.bundle ${STATIC_SETTINGS_DIR}
else
if [[ ${BUILD_STATIC} == true ]]; then
step "Assembling static library for iOS Simulator…"
mkdir -p ${OUTPUT}/static/${NAME}.framework
libtool -static -no_warning_for_no_symbols \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libuv.a` \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a` \
-o ${OUTPUT}/static/${NAME}.framework/${NAME} \
- ${LIBS[@]/#/gyp/build/${BUILDTYPE}-iphonesimulator/libmbgl-}
+ ${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a`
+
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${STATIC_BUNDLE_DIR}
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
step "Copying dynamic framework into place for iOS Simulator…"
cp -r \
- gyp/build/${BUILDTYPE}-iphonesimulator/${NAME}.framework \
+ ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework \
${OUTPUT}/dynamic/${NAME}.framework
- cp -r gyp/build/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM \
- ${OUTPUT}/dynamic/
+ if [[ -e ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM ]]; then
+ cp -r ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM \
+ ${OUTPUT}/dynamic/
+ fi
fi
+
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/Settings.bundle ${STATIC_SETTINGS_DIR}
fi
if [[ "${GCC_GENERATE_DEBUGGING_SYMBOLS}" == false ]]; then
@@ -211,48 +187,19 @@ fi
if [[ ${BUILD_STATIC} == true ]]; then
step "Copying static library headers…"
- mkdir -p "${OUTPUT}/static/${NAME}.framework/Headers"
- cp -pv platform/{darwin,ios}/include/*.h "${OUTPUT}/static/${NAME}.framework/Headers"
+ cp -rv "${PRODUCTS}/${BUILDTYPE}-iphoneos/Headers" "${OUTPUT}/static/${NAME}.framework/Headers"
cat platform/ios/framework/Mapbox-static.h > "${OUTPUT}/static/${NAME}.framework/Headers/Mapbox.h"
- cat platform/ios/framework/Mapbox.h >> "${OUTPUT}/static/${NAME}.framework/Headers/Mapbox.h"
+ cat "${PRODUCTS}/${BUILDTYPE}-iphoneos/Headers/Mapbox.h" >> "${OUTPUT}/static/${NAME}.framework/Headers/Mapbox.h"
fi
step "Copying library resources…"
-SEM_VERSION=$( git describe --tags --match=ios-v*.*.* --abbrev=0 | sed 's/^ios-v//' )
-SHORT_VERSION=${SEM_VERSION%-*}
-if [[ ${BUNDLE_RESOURCES} ]]; then
- cp -pv LICENSE.md ${STATIC_SETTINGS_DIRECTORY}
- cp -rv platform/ios/app/Settings.bundle ${STATIC_SETTINGS_DIRECTORY}
-else
- cp -pv LICENSE.md "${OUTPUT}"
- cp -rv platform/ios/app/Settings.bundle "${OUTPUT}"
-fi
+cp -pv LICENSE.md ${STATIC_SETTINGS_DIR}
if [[ ${BUILD_STATIC} == true ]]; then
- mkdir -p ${STATIC_BUNDLE_PATH}
- cp -pv platform/{default,ios}/resources/* ${STATIC_BUNDLE_PATH}
- INFO_PLIST_PATH="${OUTPUT}/static/${NAME}.framework/Info.plist"
- cp -pv platform/ios/framework/Info.plist "${INFO_PLIST_PATH}"
- plutil -remove CFBundleExecutable "${INFO_PLIST_PATH}"
- plutil -remove CFBundlePackageType "${INFO_PLIST_PATH}"
- plutil -replace CFBundleIdentifier -string com.mapbox.sdk.ios "${INFO_PLIST_PATH}"
- plutil -replace CFBundleName -string ${NAME} "${INFO_PLIST_PATH}"
- plutil -replace CFBundleShortVersionString -string "${SHORT_VERSION}" "${INFO_PLIST_PATH}"
- plutil -replace CFBundleVersion -string ${PROJ_VERSION} "${INFO_PLIST_PATH}"
- plutil -replace MGLSemanticVersionString -string "${SEM_VERSION}" "${INFO_PLIST_PATH}"
- plutil -replace MGLCommitHash -string "${HASH}" "${INFO_PLIST_PATH}"
- if [[ ${BUNDLE_RESOURCES} ]]; then
- cp -pv "${INFO_PLIST_PATH}" "${STATIC_BUNDLE_PATH}/Info.plist"
- fi
+ cp -pv "${STATIC_BUNDLE_DIR}/${NAME}.bundle/Info.plist" "${OUTPUT}/static/${NAME}.framework/Info.plist"
+ plutil -replace CFBundlePackageType -string FMWK "${OUTPUT}/static/${NAME}.framework/Info.plist"
mkdir "${OUTPUT}/static/${NAME}.framework/Modules"
cp -pv platform/ios/framework/modulemap "${OUTPUT}/static/${NAME}.framework/Modules/module.modulemap"
fi
-if [[ ${BUILD_DYNAMIC} == true ]]; then
- plutil -replace CFBundleShortVersionString -string "${SHORT_VERSION}" "${OUTPUT}/dynamic/${NAME}.framework/Info.plist"
- plutil -replace CFBundleVersion -string "${PROJ_VERSION}" "${OUTPUT}/dynamic/${NAME}.framework/Info.plist"
- plutil -replace MGLSemanticVersionString -string "${SEM_VERSION}" "${OUTPUT}/dynamic/${NAME}.framework/Info.plist"
- plutil -replace MGLCommitHash -string "${HASH}" "${OUTPUT}/dynamic/${NAME}.framework/Info.plist"
- cp -pv platform/ios/framework/strip-frameworks.sh "${OUTPUT}/dynamic/${NAME}.framework/strip-frameworks.sh"
-fi
sed -n -e '/^## /,$p' platform/ios/CHANGELOG.md > "${OUTPUT}/CHANGELOG.md"
rm -rf /tmp/mbgl
diff --git a/platform/ios/scripts/run.sh b/platform/ios/scripts/run.sh
deleted file mode 100755
index 44c21e7562..0000000000
--- a/platform/ios/scripts/run.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-set -u
-
-source ./platform/ios/scripts/setup.sh
-
-BUILDTYPE=${BUILDTYPE:-Release}
-
-PUBLISH_TAG=($(git show -s --format=%B | sed -n 's/.*\[publish \([a-z]\{1,\}\)-v\([0-9a-z.\-]\{1,\}\)\].*/\1 \2/p'))
-PUBLISH_PLATFORM=${PUBLISH_TAG[0],-}
-PUBLISH_VERSION=${PUBLISH_TAG[1],-}
-
-
-################################################################################
-# Build
-################################################################################
-
-if [[ ${PUBLISH_PLATFORM} = 'ios' ]]; then
- # default, with debug symbols
- mapbox_time "package_ios_symbols" \
- make ipackage
-
- mapbox_time "deploy_ios_symbols"
- ./platform/ios/scripts/publish.sh "${PUBLISH_VERSION}" symbols
-
- # no debug symbols, for smaller distribution
- mapbox_time "package_ios_stripped" \
- make ipackage-strip
-
- mapbox_time "deploy_ios_stripped"
- ./platform/ios/scripts/publish.sh "${PUBLISH_VERSION}"
-
- # dynamic, with debug symbols
- mapbox_time "package_ios_dynamic" \
- make iframework
-
- mapbox_time "deploy_ios_dynamic"
- ./platform/ios/scripts/publish.sh "${PUBLISH_VERSION}" symbols-dynamic
-
- # dynamic, without debug symbols
- mapbox_time "package_ios_dynamic_stripped" \
- make iframework SYMBOLS=NO
-
- mapbox_time "deploy_ios_dynamic_stripped"
- ./platform/ios/scripts/publish.sh "${PUBLISH_VERSION}" dynamic
-else
- # build & test iOS
- mapbox_time "run_ios_tests" \
- make itest
-fi
diff --git a/platform/ios/scripts/setup.sh b/platform/ios/scripts/setup.sh
deleted file mode 100644
index d115cf9020..0000000000
--- a/platform/ios/scripts/setup.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-# This script is sourced; do not set -e or -o pipefail here.
-
-# Ensure mason is on the PATH
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
diff --git a/platform/ios/scripts/test.sh b/platform/ios/scripts/test.sh
deleted file mode 100755
index 630a99686d..0000000000
--- a/platform/ios/scripts/test.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-set -u
-
-xcodebuild \
- -project ./platform/ios/test/ios-tests.xcodeproj \
- -scheme 'Mapbox GL Tests' \
- -sdk iphonesimulator \
- -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' \
- -destination 'platform=iOS Simulator,name=iPad 2,OS=latest' \
- test
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
index 63adb3c463..31fd39c83d 100644
--- a/platform/ios/src/MGLAPIClient.m
+++ b/platform/ios/src/MGLAPIClient.m
@@ -14,7 +14,7 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
@interface MGLAPIClient ()
@property (nonatomic, copy) NSURLSession *session;
-@property (nonatomic, copy) NSString *baseURL;
+@property (nonatomic, copy) NSURL *baseURL;
@property (nonatomic, copy) NSData *digicertCert;
@property (nonatomic, copy) NSData *geoTrustCert;
@property (nonatomic, copy) NSData *testServerCert;
@@ -47,14 +47,14 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSError *statusError = nil;
if (httpResponse.statusCode >= 400) {
- NSString *description = [NSString stringWithFormat:NSLocalizedString(@"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
- NSString *reason = [NSString stringWithFormat:NSLocalizedString(@"The status code was %ld", nil), (long)httpResponse.statusCode];
+ NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
+ NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode];
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description,
NSLocalizedFailureReasonErrorKey: reason};
statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
}
if (completionHandler) {
- error = error ? error : statusError;
+ error = error ?: statusError;
completionHandler(error);
}
[self.dataTasks removeObject:dataTask];
@@ -76,8 +76,9 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
#pragma mark Utilities
- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- NSString *url = [NSString stringWithFormat:@"%@/%@?access_token=%@", self.baseURL, MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
+ NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
+ NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
[request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
[request setHTTPMethod:MGLAPIClientHTTPMethodPost];
@@ -87,12 +88,13 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
}
- (void)setupBaseURL {
- NSString *testServerURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMetricsTestServerURL"];
- if (testServerURL) {
- _baseURL = testServerURL;
- _usesTestServer = YES;
+ NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"];
+ NSURL *testServerURL = [NSURL URLWithString:testServerURLString];
+ if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) {
+ self.baseURL = testServerURL;
+ self.usesTestServer = YES;
} else {
- _baseURL = MGLAPIClientBaseURL;
+ self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL];
}
}
@@ -120,7 +122,7 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
- NSString *sdkVersion = semanticVersion ? semanticVersion : shortVersion;
+ NSString *sdkVersion = semanticVersion ?: shortVersion;
_userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion];
}
diff --git a/platform/ios/src/MGLAnnotationContainerView.h b/platform/ios/src/MGLAnnotationContainerView.h
new file mode 100644
index 0000000000..90d2964831
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationContainerView.h
@@ -0,0 +1,17 @@
+#import <UIKit/UIKit.h>
+
+#import "MGLTypes.h"
+
+@class MGLAnnotationView;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLAnnotationContainerView : UIView
+
++ (instancetype)annotationContainerViewWithAnnotationContainerView:(MGLAnnotationContainerView *)annotationContainerView;
+
+- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAnnotationContainerView.m b/platform/ios/src/MGLAnnotationContainerView.m
new file mode 100644
index 0000000000..9a823c839c
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationContainerView.m
@@ -0,0 +1,52 @@
+#import "MGLAnnotationContainerView.h"
+#import "MGLAnnotationView.h"
+
+@interface MGLAnnotationContainerView ()
+
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews;
+
+@end
+
+@implementation MGLAnnotationContainerView
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self)
+ {
+ _annotationViews = [NSMutableArray array];
+ }
+ return self;
+}
+
++ (instancetype)annotationContainerViewWithAnnotationContainerView:(nonnull MGLAnnotationContainerView *)annotationContainerView
+{
+ MGLAnnotationContainerView *newAnnotationContainerView = [[MGLAnnotationContainerView alloc] initWithFrame:annotationContainerView.frame];
+ [newAnnotationContainerView addSubviews:annotationContainerView.subviews];
+ return newAnnotationContainerView;
+}
+
+- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews
+{
+ for (MGLAnnotationView *view in subviews)
+ {
+ [self addSubview:view];
+ [self.annotationViews addObject:view];
+ }
+}
+
+#pragma mark UIAccessibility methods
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return UIAccessibilityTraitAdjustable;
+}
+
+- (void)accessibilityIncrement {
+ [self.superview.superview accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement {
+ [self.superview.superview accessibilityDecrement];
+}
+
+@end
diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h
new file mode 100644
index 0000000000..fa2adb3830
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationImage.h
@@ -0,0 +1,44 @@
+#import <UIKit/UIKit.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** The MGLAnnotationImage class is responsible for presenting point-based annotations visually on a map view. Annotation image objects wrap `UIImage` objects and may be recycled later and put into a reuse queue that is maintained by the map view. */
+@interface MGLAnnotationImage : NSObject
+
+#pragma mark Initializing and Preparing the Image Object
+
+/**
+ Initializes and returns a new annotation image object.
+
+ @param image The image to be displayed for the annotation.
+ @param reuseIdentifier The string that identifies that this annotation image is reusable.
+ @return The initialized annotation image object or `nil` if there was a problem initializing the object.
+ */
++ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier;
+
+#pragma mark Getting and Setting Attributes
+
+/** The image to be displayed for the annotation. */
+@property (nonatomic, strong, nullable) UIImage *image;
+
+/**
+ The string that identifies that this annotation image is reusable. (read-only)
+
+ You specify the reuse identifier when you create the image object. You use this type later to retrieve an annotation image object that was created previously but which is currently unused because its annotation is not on screen.
+
+ If you define distinctly different types of annotations (with distinctly different annotation images to go with them), you can differentiate between the annotation types by specifying different reuse identifiers for each one.
+ */
+@property (nonatomic, readonly) NSString *reuseIdentifier;
+
+/**
+ A Boolean value indicating whether the annotation is enabled.
+
+ The default value of this property is `YES`. If the value of this property is `NO`, the annotation image ignores touch events and cannot be selected.
+ */
+@property (nonatomic, getter=isEnabled) BOOL enabled;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAnnotationImage.m b/platform/ios/src/MGLAnnotationImage.m
index 374ed162fb..e1085be98d 100644
--- a/platform/ios/src/MGLAnnotationImage.m
+++ b/platform/ios/src/MGLAnnotationImage.m
@@ -3,6 +3,8 @@
@interface MGLAnnotationImage ()
@property (nonatomic, strong) NSString *reuseIdentifier;
+@property (nonatomic, strong, nullable) NSString *styleIconIdentifier;
+
@property (nonatomic, weak) id<MGLAnnotationImageDelegate> delegate;
@end
diff --git a/platform/ios/src/MGLAnnotationImage_Private.h b/platform/ios/src/MGLAnnotationImage_Private.h
index f22a9ac4e2..dcd8a49bf9 100644
--- a/platform/ios/src/MGLAnnotationImage_Private.h
+++ b/platform/ios/src/MGLAnnotationImage_Private.h
@@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLAnnotationImage (Private)
+/// Unique identifier of the sprite image used by the style to represent the receiver’s `image`.
+@property (nonatomic, strong, nullable) NSString *styleIconIdentifier;
+
@property (nonatomic, weak) id<MGLAnnotationImageDelegate> delegate;
@end
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
new file mode 100644
index 0000000000..5b8091e7b4
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -0,0 +1,61 @@
+#import <UIKit/UIKit.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** The MGLAnnotationView class is responsible for representing point-based annotation markers as a view. Annotation views represent an annotation object, which is an object that corresponds to the MGLAnnotation protocol. When an annotation’s coordinate point is visible on the map view, the map view delegate is asked to provide a corresponding annotation view. If an annotation view is created with a reuse identifier, the map view may recycle the view when it goes offscreen. */
+@interface MGLAnnotationView : UIView
+
+/**
+ Initializes and returns a new annotation view object.
+
+ @param reuseIdentifier The string that identifies that this annotation view is reusable.
+ @return The initialized annotation view object.
+ */
+- (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier;
+
+/**
+ The string that identifies that this annotation view is reusable. (read-only)
+
+ You specify the reuse identifier when you create the view. You use the identifier later to retrieve an annotation view that was
+ created previously but which is currently unused because its annotation is not on screen.
+
+ If you define distinctly different types of annotations (with distinctly different annotation views to go with them), you can
+ differentiate between the annotation types by specifying different reuse identifiers for each one.
+ */
+@property (nonatomic, readonly, nullable) NSString *reuseIdentifier;
+
+/**
+ Annotation view is centered at the coordinate point of the associated annotation.
+
+ By changing this property you can reposition the view as needed. The offset is measured in points.
+ Positive offset moves the annotation view towards the bottom right, while negative offset moves it towards the top left.
+ */
+@property (nonatomic) CGVector centerOffset;
+
+
+/**
+ Setting this property to YES will force the annotation view to tilt according to the associated map view.
+ */
+@property (nonatomic, assign, getter=isFlat) BOOL flat;
+
+/**
+ Setting this property to YES will cause the annotation view to shrink as it approaches the horizon and grow as it moves away from the
+ horizon when the associated map view is tilted. Conversely, setting this property to NO will ensure that the annotation view maintains
+ a constant size even when the map view is tilted. To maintain consistency with annotation representations that are not backed by an
+ MGLAnnotationView object, the default value of this property is YES.
+ */
+@property (nonatomic, assign, getter=isScaledWithViewingDistance) BOOL scalesWithViewingDistance;
+
+/**
+ Called when the view is removed from the reuse queue.
+
+ The default implementation of this method does nothing. You can override it in your custom annotation views and use it to put the view
+ in a known state before it is returned to your map view delegate.
+ */
+- (void)prepareForReuse;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
new file mode 100644
index 0000000000..31657dbf4e
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -0,0 +1,147 @@
+#import "MGLAnnotationView.h"
+#import "MGLAnnotationView_Private.h"
+#import "MGLMapView_Internal.h"
+
+#import "NSBundle+MGLAdditions.h"
+
+#include <mbgl/util/constants.hpp>
+
+@interface MGLAnnotationView ()
+
+@property (nonatomic) id<MGLAnnotation> annotation;
+@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
+
+@end
+
+@implementation MGLAnnotationView
+
+- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
+{
+ self = [self initWithFrame:CGRectZero];
+ if (self)
+ {
+ _reuseIdentifier = [reuseIdentifier copy];
+ _scalesWithViewingDistance = YES;
+ }
+ return self;
+}
+
+- (void)prepareForReuse
+{
+ // Intentionally left blank. The default implementation of this method does nothing.
+}
+
+- (void)setCenterOffset:(CGVector)centerOffset
+{
+ _centerOffset = centerOffset;
+ self.center = self.center;
+}
+
+- (void)setCenter:(CGPoint)center
+{
+ [self setCenter:center pitch:0];
+}
+
+- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch
+{
+ center.x += _centerOffset.dx;
+ center.y += _centerOffset.dy;
+
+ [super setCenter:center];
+
+ if (self.flat)
+ {
+ [self updatePitch:pitch];
+ }
+
+ if (self.scalesWithViewingDistance)
+ {
+ [self updateScaleForPitch:pitch];
+ }
+}
+
+- (void)updatePitch:(CGFloat)pitch
+{
+ CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(pitch), 1.0, 0, 0);
+ self.layer.transform = t;
+}
+
+- (void)updateScaleForPitch:(CGFloat)pitch
+{
+ CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
+ if (superviewHeight > 0.0) {
+ // Find the maximum amount of scale reduction to apply as the view's center moves from the top
+ // of the superview to the bottom. For example, if this view's center has moved 25% of the way
+ // from the top of the superview towards the bottom then the maximum scale reduction is 1 - .25
+ // or 75%. The range goes from a maximum of 100% to 0% as the view moves from the top to the bottom
+ // along the y axis of its superview.
+ CGFloat maxScaleReduction = 1.0 - self.center.y / superviewHeight;
+
+ // The pitch intensity represents how much the map view is actually pitched compared to
+ // what is possible. The value will range from 0% (not pitched at all) to 100% (pitched as much
+ // as the map view will allow). The map view's maximum pitch is defined in `mbgl::util::PITCH_MAX`.
+ // Since it is possible for the map view to report a pitch less than 0 due to the nature of
+ // how the gesture information is captured, the value is guarded with MAX.
+ CGFloat pitchIntensity = MAX(pitch, 0) / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
+
+ // The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
+ // multiplied by the pitch intensity. For example, if the maximum scale reduction is 75% and the
+ // map view is 50% pitched then the annotation view should be reduced by 37.5% (.75 * .5). The
+ // reduction is then normalized for a scale of 1.0.
+ CGFloat pitchAdjustedScale = 1.0 - maxScaleReduction * pitchIntensity;
+
+ CATransform3D transform = self.flat ? self.layer.transform : CATransform3DIdentity;
+ self.layer.transform = CATransform3DScale(transform, pitchAdjustedScale, pitchAdjustedScale, 1);
+ }
+}
+
+- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
+{
+ // Allow mbgl to drive animation of this view’s bounds.
+ if ([event isEqualToString:@"bounds"])
+ {
+ return [NSNull null];
+ }
+ return [super actionForLayer:layer forKey:event];
+}
+
+#pragma mark UIAccessibility methods
+
+- (BOOL)isAccessibilityElement {
+ return !self.hidden;
+}
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable;
+}
+
+- (NSString *)accessibilityLabel {
+ return [self.annotation respondsToSelector:@selector(title)] ? self.annotation.title : super.accessibilityLabel;
+}
+
+- (NSString *)accessibilityValue {
+ return [self.annotation respondsToSelector:@selector(subtitle)] ? self.annotation.subtitle : super.accessibilityValue;
+}
+
+- (NSString *)accessibilityHint {
+ return NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
+}
+
+- (CGRect)accessibilityFrame {
+ CGRect accessibilityFrame = self.frame;
+ CGRect minimumFrame = CGRectInset({ self.center, CGSizeZero },
+ -MGLAnnotationAccessibilityElementMinimumSize.width / 2,
+ -MGLAnnotationAccessibilityElementMinimumSize.height / 2);
+ accessibilityFrame = CGRectUnion(accessibilityFrame, minimumFrame);
+ return accessibilityFrame;
+}
+
+- (void)accessibilityIncrement {
+ [self.superview accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement {
+ [self.superview accessibilityDecrement];
+}
+
+@end \ No newline at end of file
diff --git a/platform/ios/src/MGLAnnotationView_Private.h b/platform/ios/src/MGLAnnotationView_Private.h
new file mode 100644
index 0000000000..c5a65487a2
--- /dev/null
+++ b/platform/ios/src/MGLAnnotationView_Private.h
@@ -0,0 +1,15 @@
+#import "MGLAnnotationView.h"
+#import "MGLAnnotation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLAnnotationView (Private)
+
+@property (nonatomic) id<MGLAnnotation> annotation;
+@property (nonatomic, readwrite, nullable) NSString *reuseIdentifier;
+
+- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h
new file mode 100644
index 0000000000..641976dfee
--- /dev/null
+++ b/platform/ios/src/MGLCalloutView.h
@@ -0,0 +1,77 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol MGLCalloutViewDelegate;
+@protocol MGLAnnotation;
+
+/**
+ A protocol for a `UIView` subclass that displays information about a selected annotation near that annotation.
+ */
+@protocol MGLCalloutView <NSObject>
+
+/**
+ An object conforming to the `MGLAnnotation` protocol whose details this callout view displays.
+ */
+@property (nonatomic, strong) id <MGLAnnotation> representedObject;
+
+/**
+ A view that the user may tap to perform an action. This view is conventionally positioned on the left side of the callout view.
+ */
+@property (nonatomic, strong) UIView *leftAccessoryView;
+
+/**
+ A view that the user may tap to perform an action. This view is conventionally positioned on the right side of the callout view.
+ */
+@property (nonatomic, strong) UIView *rightAccessoryView;
+
+/**
+ An object conforming to the `MGLCalloutViewDelegate` method that receives messages related to the callout view’s interactive subviews.
+ */
+@property (nonatomic, weak) id<MGLCalloutViewDelegate> delegate;
+
+/**
+ Presents a callout view by adding it to `inView` and pointing at the given rect of `inView`’s bounds. Constrains the callout to the bounds of the given view.
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
+
+/**
+ Dismisses the callout view.
+ */
+- (void)dismissCalloutAnimated:(BOOL)animated;
+
+@end
+
+/**
+ The MGLCalloutViewDelegate protocol defines a set of optional methods that you can use to receive messages from an object that conforms to the MGLCalloutView protocol. The callout view uses these methods to inform the delegate that the user has interacted with the the callout view.
+ */
+@protocol MGLCalloutViewDelegate <NSObject>
+
+@optional
+/**
+ Returns a Boolean value indicating whether the entire callout view “highlights” when tapped. The default value is `YES`, which means the callout view highlights when tapped.
+
+ The return value of this method is ignored unless the delegate also responds to the `-calloutViewTapped` method.
+ */
+- (BOOL)calloutViewShouldHighlight:(UIView<MGLCalloutView> *)calloutView;
+
+/**
+ Tells the delegate that the callout view has been tapped.
+ */
+- (void)calloutViewTapped:(UIView<MGLCalloutView> *)calloutView;
+
+/**
+ Called before the callout view appears on screen, or before the appearance animation will start.
+ */
+- (void)calloutViewWillAppear:(UIView<MGLCalloutView> *)calloutView;
+
+/**
+ Called after the callout view appears on screen, or after the appearance animation is complete.
+ */
+- (void)calloutViewDidAppear:(UIView<MGLCalloutView> *)calloutView;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLLocationManager.m b/platform/ios/src/MGLLocationManager.m
index b5740e3547..7a9faf5c8d 100644
--- a/platform/ios/src/MGLLocationManager.m
+++ b/platform/ios/src/MGLLocationManager.m
@@ -61,12 +61,16 @@ static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManage
}
- (void)startLocationServices {
- if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized ||
- [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse) {
-
+ CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
+ BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorizedAlways;
+#else
+ BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorized;
+#endif
+ if (authorizedAlways || authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) {
// If the host app can run in the background with `always` location permissions then allow background
// updates and start the significant location change service and background timeout timer
- if (self.hostAppHasBackgroundCapability && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized) {
+ if (self.hostAppHasBackgroundCapability && authorizedAlways) {
[self.standardLocationManager startMonitoringSignificantLocationChanges];
[self startBackgroundTimeoutTimer];
// On iOS 9 and above also allow background location updates
@@ -121,7 +125,11 @@ static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManage
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
switch (status) {
- case kCLAuthorizationStatusAuthorized: // Also handles kCLAuthorizationStatusAuthorizedAlways
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
+ case kCLAuthorizationStatusAuthorizedAlways:
+#else
+ case kCLAuthorizationStatusAuthorized:
+#endif
case kCLAuthorizationStatusAuthorizedWhenInUse:
[self startUpdatingLocation];
break;
diff --git a/platform/ios/include/MGLMapView+IBAdditions.h b/platform/ios/src/MGLMapView+IBAdditions.h
index f18df56e01..f18df56e01 100644
--- a/platform/ios/include/MGLMapView+IBAdditions.h
+++ b/platform/ios/src/MGLMapView+IBAdditions.h
diff --git a/platform/ios/include/MGLMapView+MGLCustomStyleLayerAdditions.h b/platform/ios/src/MGLMapView+MGLCustomStyleLayerAdditions.h
index de4dc01f99..de4dc01f99 100644
--- a/platform/ios/include/MGLMapView+MGLCustomStyleLayerAdditions.h
+++ b/platform/ios/src/MGLMapView+MGLCustomStyleLayerAdditions.h
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
new file mode 100644
index 0000000000..63d799bda9
--- /dev/null
+++ b/platform/ios/src/MGLMapView.h
@@ -0,0 +1,1187 @@
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+
+#import <UIKit/UIKit.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class MGLAnnotationView;
+@class MGLAnnotationImage;
+@class MGLUserLocation;
+@class MGLPolyline;
+@class MGLPolygon;
+@class MGLShape;
+
+@protocol MGLMapViewDelegate;
+@protocol MGLAnnotation;
+@protocol MGLOverlay;
+@protocol MGLCalloutView;
+@protocol MGLFeature;
+
+/** The vertical alignment of an annotation within a map view. */
+typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
+ /** Aligns the annotation vertically in the center of the map view. */
+ MGLAnnotationVerticalAlignmentCenter = 0,
+ /** Aligns the annotation vertically at the top of the map view. */
+ MGLAnnotationVerticalAlignmentTop,
+ /** Aligns the annotation vertically at the bottom of the map view. */
+ MGLAnnotationVerticalAlignmentBottom,
+};
+
+/** Options for enabling debugging features in an MGLMapView instance. */
+typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
+ /** Edges of tile boundaries are shown as thick, red lines to help diagnose
+ tile clipping issues. */
+ MGLMapDebugTileBoundariesMask = 1 << 1,
+ /** Each tile shows its tile coordinate (x/y/z) in the upper-left corner. */
+ MGLMapDebugTileInfoMask = 1 << 2,
+ /** Each tile shows a timestamp indicating when it was loaded. */
+ MGLMapDebugTimestampsMask = 1 << 3,
+ /** Edges of glyphs and symbols are shown as faint, green lines to help
+ diagnose collision and label placement issues. */
+ MGLMapDebugCollisionBoxesMask = 1 << 4,
+ /** Line widths, backgrounds, and fill colors are ignored to create a
+ wireframe effect. */
+ MGLMapDebugWireframesMask = 1 << 5,
+};
+
+/**
+ An interactive, customizable map view with an interface similar to the one
+ provided by Apple's MapKit.
+
+ Using `MGLMapView`, you can embed the map inside a view, allow users to
+ manipulate it with standard gestures, animate the map between different
+ viewpoints, and present information in the form of annotations and overlays.
+
+ The map view loads scalable vector tiles that conform to the
+ <a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
+ It styles them with a style that conforms to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
+ Such styles can be designed in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
+ mapbox.com.
+
+ A collection of Mapbox-hosted styles is available through the `MGLStyle`
+ class. These basic styles use
+ <a href="https://www.mapbox.com/developers/vector-tiles/mapbox-streets">Mapbox Streets</a>
+ or <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a> data
+ sources, but you can specify a custom style that makes use of your own data.
+
+ Mapbox-hosted vector tiles and styles require an API access token, which you
+ can obtain from the
+ <a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>.
+ Access tokens associate requests to Mapbox's vector tile and style APIs with
+ your Mapbox account. They also deter other developers from using your styles
+ without your permission.
+
+ @note You are responsible for getting permission to use the map data and for
+ ensuring that your use adheres to the relevant terms of use.
+ */
+IB_DESIGNABLE
+@interface MGLMapView : UIView
+
+#pragma mark Creating Instances
+
+/**
+ Initializes and returns a newly allocated map view with the specified frame
+ and the default style.
+
+ @param frame The frame for the view, measured in points.
+ @return An initialized map view.
+ */
+- (instancetype)initWithFrame:(CGRect)frame;
+
+/**
+ Initializes and returns a newly allocated map view with the specified frame
+ and style URL.
+
+ @param frame The frame for the view, measured in points.
+ @param styleURL URL of the map style to display. The URL may be a full HTTP
+ or HTTPS URL, a Mapbox URL indicating the style's map ID
+ (`mapbox://styles/{user}/{style}`), or a path to a local file relative
+ to the application's resource path. Specify `nil` for the default style.
+ @return An initialized map view.
+ */
+- (instancetype)initWithFrame:(CGRect)frame styleURL:(nullable NSURL *)styleURL;
+
+#pragma mark Accessing the Delegate
+
+/**
+ The receiver's delegate.
+
+ A map view sends messages to its delegate to notify it of changes to its
+ contents or the viewpoint. The delegate also provides information about
+ annotations displayed on the map, such as the styles to apply to individual
+ annotations.
+ */
+@property(nonatomic, weak, nullable) IBOutlet id<MGLMapViewDelegate> delegate;
+
+#pragma mark Configuring the Map's Appearance
+
+/**
+ URLs of the styles bundled with the library.
+
+ @deprecated Call the relevant class method of `MGLStyle` for the URL of a
+ particular default style.
+ */
+@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((deprecated("Call the relevant class method of MGLStyle for the URL of a particular default style.")));
+
+/**
+ URL of the style currently displayed in the receiver.
+
+ The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style's
+ map ID (`mapbox://styles/{user}/{style}`), or a path to a local file
+ relative to the application's resource path.
+
+ If you set this property to `nil`, the receiver will use the default style
+ and this property will automatically be set to that style's URL.
+ */
+@property (nonatomic, null_resettable) NSURL *styleURL;
+
+/**
+ Reloads the style.
+
+ You do not normally need to call this method. The map view automatically
+ responds to changes in network connectivity by reloading the style. You may
+ need to call this method if you change the access token after a style has
+ loaded but before loading a style associated with a different Mapbox account.
+
+ This method does not bust the cache. Even if the style has recently changed on
+ the server, calling this method does not necessarily ensure that the map view
+ reflects those changes.
+ */
+- (IBAction)reloadStyle:(id)sender;
+
+/**
+ A control indicating the map's direction and allowing the user to manipulate
+ the direction, positioned in the upper-right corner.
+ */
+@property (nonatomic, readonly) UIImageView *compassView;
+
+/**
+ The Mapbox logo, positioned in the lower-left corner.
+
+ @note The Mapbox terms of service, which governs the use of Mapbox-hosted
+ vector tiles and styles,
+ <a href="https://www.mapbox.com/help/mapbox-logo/">requires</a> most Mapbox
+ customers to display the Mapbox logo. If this applies to you, do not
+ hide this view or change its contents.
+ */
+@property (nonatomic, readonly) UIImageView *logoView;
+
+/**
+ A view showing legally required copyright notices and telemetry settings,
+ positioned at the bottom-right of the map view.
+
+ @note The Mapbox terms of service, which governs the use of Mapbox-hosted
+ vector tiles and styles,
+ <a href="https://www.mapbox.com/help/attribution/">requires</a> these
+ copyright notices to accompany any map that features Mapbox-designed styles,
+ OpenStreetMap data, or other Mapbox data such as satellite or terrain
+ data. If that applies to this map view, do not hide this view or remove
+ any notices from it.
+
+ @note You are additionally
+ <a href="https://www.mapbox.com/help/telemetry-opt-out-for-users/">required</a>
+ to provide users with the option to disable anonymous usage and location
+ sharing (telemetry). If this view is hidden, you must implement this
+ setting elsewhere in your app or via `Settings.bundle`. See our
+ <a href="https://www.mapbox.com/ios-sdk/#telemetry_opt_out">website</a> for
+ implementation help.
+ */
+@property (nonatomic, readonly) UIButton *attributionButton;
+
+/**
+ Currently active style classes, represented as an array of string identifiers.
+ */
+@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses;
+
+/**
+ Returns a Boolean value indicating whether the style class with the given
+ identifier is currently active.
+
+ @param styleClass The style class to query for.
+ @return Whether the style class is currently active.
+ */
+- (BOOL)hasStyleClass:(NSString *)styleClass;
+
+/**
+ Activates the style class with the given identifier.
+
+ @param styleClass The style class to activate.
+ */
+- (void)addStyleClass:(NSString *)styleClass;
+
+/**
+ Deactivates the style class with the given identifier.
+
+ @param styleClass The style class to deactivate.
+ */
+- (void)removeStyleClass:(NSString *)styleClass;
+
+#pragma mark Displaying the User's Location
+
+/**
+ A Boolean value indicating whether the map may display the user location.
+
+ Setting this property to `YES` causes the map view to use the Core Location
+ framework to find the current location. As long as this property is `YES`, the
+ map view continues to track the user's location and update it periodically.
+
+ This property does not indicate whether the user's position is actually visible
+ on the map, only whether the map view is allowed to display it. To determine
+ whether the user's position is visible, use the `userLocationVisible` property.
+ The default value of this property is `NO`.
+
+ On iOS 8 and above, your app must specify a value for
+ `NSLocationWhenInUseUsageDescription` or `NSLocationAlwaysUsageDescription` in
+ its `Info.plist` to satisfy the requirements of the underlying Core Location
+ framework when enabling this property.
+ */
+@property (nonatomic, assign) BOOL showsUserLocation;
+
+/**
+ A Boolean value indicating whether the device's current location is visible in
+ the map view.
+
+ Use `showsUserLocation` to control the visibility of the on-screen user
+ location annotation.
+ */
+@property (nonatomic, assign, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
+
+/**
+ Returns the annotation object indicating the user's current location.
+ */
+@property (nonatomic, readonly, nullable) MGLUserLocation *userLocation;
+
+/**
+ The mode used to track the user location. The default value is
+ `MGLUserTrackingModeNone`.
+
+ Changing the value of this property updates the map view with an animated
+ transition. If you don’t want to animate the change, use the
+ `-setUserTrackingMode:animated:` method instead.
+ */
+@property (nonatomic, assign) MGLUserTrackingMode userTrackingMode;
+
+/**
+ Sets the mode used to track the user location, with an optional transition.
+
+ @param mode The mode used to track the user location.
+ @param animated If `YES`, there is an animated transition from the current
+ viewport to a viewport that results from the change to `mode`. If `NO`, the
+ map view instantaneously changes to the new viewport. This parameter only
+ affects the initial transition; subsequent changes to the user location or
+ heading are always animated.
+ */
+- (void)setUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
+
+/**
+ The vertical alignment of the user location annotation within the receiver. The
+ default value is `MGLAnnotationVerticalAlignmentCenter`.
+
+ Changing the value of this property updates the map view with an animated
+ transition. If you don’t want to animate the change, use the
+ `-setUserLocationVerticalAlignment:animated:` method instead.
+ */
+@property (nonatomic, assign) MGLAnnotationVerticalAlignment userLocationVerticalAlignment;
+
+/**
+ Sets the vertical alignment of the user location annotation within the
+ receiver, with an optional transition.
+
+ @param alignment The vertical alignment of the user location annotation.
+ @param animated If `YES`, the user location annotation animates to its new
+ position within the map view. If `NO`, the user location annotation
+ instantaneously moves to its new position.
+ */
+- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated;
+
+/**
+ Whether the map view should display a heading calibration alert when necessary.
+ The default value is `YES`.
+ */
+@property (nonatomic, assign) BOOL displayHeadingCalibration;
+
+/**
+ The geographic coordinate that is the subject of observation as the user
+ location is being tracked.
+
+ By default, this property is set to an invalid coordinate, indicating that
+ there is no target. In course tracking mode, the target forms one of two foci
+ in the viewport, the other being the user location annotation. Typically, this
+ property is set to a destination or waypoint in a real-time navigation scene.
+ As the user annotation moves toward the target, the map automatically zooms in
+ to fit both foci optimally within the viewport.
+
+ This property has no effect if the `userTrackingMode` property is set to a
+ value other than `MGLUserTrackingModeFollowWithCourse`.
+
+ Changing the value of this property updates the map view with an animated
+ transition. If you don’t want to animate the change, use the
+ `-setTargetCoordinate:animated:` method instead.
+ */
+@property (nonatomic, assign) CLLocationCoordinate2D targetCoordinate;
+
+/**
+ Sets the geographic coordinate that is the subject of observation as the user
+ location is being tracked, with an optional transition animation.
+
+ By default, the target coordinate is set to an invalid coordinate, indicating
+ that there is no target. In course tracking mode, the target forms one of two
+ foci in the viewport, the other being the user location annotation. Typically,
+ the target is set to a destination or waypoint in a real-time navigation scene.
+ As the user annotation moves toward the target, the map automatically zooms in
+ to fit both foci optimally within the viewport.
+
+ This method has no effect if the `userTrackingMode` property is set to a value
+ other than `MGLUserTrackingModeFollowWithCourse`.
+
+ @param targetCoordinate The target coordinate to fit within the viewport.
+ @param animated If `YES`, the map animates to fit the target within the map
+ view. If `NO`, the map fits the target instantaneously.
+ */
+- (void)setTargetCoordinate:(CLLocationCoordinate2D)targetCoordinate animated:(BOOL)animated;
+
+#pragma mark Configuring How the User Interacts with the Map
+
+/**
+ A Boolean value that determines whether the user may zoom the map in and
+ out, changing the zoom level.
+
+ When this property is set to `YES`, the default, the user may zoom the map
+ in and out by pinching two fingers or by double tapping, holding, and moving
+ the finger up and down.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the map zoom
+ programmatically.
+ */
+@property(nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
+
+/**
+ A Boolean value that determines whether the user may scroll around the map,
+ changing the center coordinate.
+
+ When this property is set to `YES`, the default, the user may scroll the map
+ by dragging or swiping with one finger.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the map location
+ programmatically.
+ */
+@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
+
+/**
+ A Boolean value that determines whether the user may rotate the map,
+ changing the direction.
+
+ When this property is set to `YES`, the default, the user may rotate the map
+ by moving two fingers in a circular motion.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still rotate the map
+ programmatically.
+ */
+@property(nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
+
+/**
+ A Boolean value that determines whether the user may change the pitch (tilt) of
+ the map.
+
+ When this property is set to `YES`, the default, the user may tilt the map by
+ vertically dragging two fingers.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the pitch of the map
+ programmatically.
+
+ The default value of this property is `YES`.
+ */
+@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
+
+#pragma mark Manipulating the Viewpoint
+
+/**
+ The geographic coordinate at the center of the map view.
+
+ Changing the value of this property centers the map on the new coordinate
+ without changing the current zoom level.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setCenterCoordinate:animated:` method
+ instead.
+ */
+@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
+
+/**
+ Changes the center coordinate of the map and optionally animates the change.
+
+ Changing the center coordinate centers the map on the new coordinate without
+ changing the current zoom level.
+
+ @param coordinate The new center coordinate for the map.
+ @param animated Specify `YES` if you want the map view to scroll to the new
+ location or `NO` if you want the map to display the new location
+ immediately.
+ */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
+
+/**
+ Changes the center coordinate and zoom level of the map and optionally animates
+ the change.
+
+ @param centerCoordinate The new center coordinate for the map.
+ @param zoomLevel The new zoom level for the map.
+ @param animated Specify `YES` if you want the map view to animate scrolling and
+ zooming to the new location or `NO` if you want the map to display the new
+ location immediately.
+ */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated;
+
+/**
+ Changes the center coordinate, zoom level, and direction of the map and
+ optionally animates the change.
+
+ @param centerCoordinate The new center coordinate for the map.
+ @param zoomLevel The new zoom level for the map.
+ @param direction The new direction for the map, measured in degrees relative to
+ true north.
+ @param animated Specify `YES` if you want the map view to animate scrolling,
+ zooming, and rotating to the new location or `NO` if you want the map to
+ display the new location immediately.
+ */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated;
+
+/**
+ Changes the center coordinate, zoom level, and direction of the map, calling a
+ completion handler at the end of an optional animation.
+
+ @param centerCoordinate The new center coordinate for the map.
+ @param zoomLevel The new zoom level for the map.
+ @param direction The new direction for the map, measured in degrees relative to
+ true north.
+ @param animated Specify `YES` if you want the map view to animate scrolling,
+ zooming, and rotating to the new location or `NO` if you want the map to
+ display the new location immediately.
+ @param completion The block executed after the animation finishes.
+ */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated completionHandler:(nullable void (^)(void))completion;
+
+/** The zoom level of the receiver.
+
+ In addition to affecting the visual size and detail of features on the map,
+ the zoom level affects the size of the vector tiles that are loaded. At zoom
+ level 0, each tile covers the entire world map; at zoom level 1, it covers ¼
+ of the world; at zoom level 2, <sup>1</sup>⁄<sub>16</sub> of the world, and
+ so on.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setZoomLevel:animated:` method instead.
+ */
+@property (nonatomic) double zoomLevel;
+
+/**
+ Changes the zoom level of the map and optionally animates the change.
+
+ Changing the zoom level scales the map without changing the current center
+ coordinate.
+
+ @param zoomLevel The new zoom level for the map.
+ @param animated Specify `YES` if you want the map view to animate the change
+ to the new zoom level or `NO` if you want the map to display the new
+ zoom level immediately.
+ */
+- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated;
+
+/**
+ * The minimum zoom level at which the map can be shown.
+ *
+ * Depending on the map view’s aspect ratio, the map view may be prevented
+ * from reaching the minimum zoom level, in order to keep the map from
+ * repeating within the current viewport.
+ *
+ * If the value of this property is greater than that of the
+ * maximumZoomLevel property, the behavior is undefined.
+ *
+ * The default minimumZoomLevel is 0.
+ */
+@property (nonatomic) double minimumZoomLevel;
+
+/**
+ * The maximum zoom level the map can be shown at.
+ *
+ * If the value of this property is smaller than that of the
+ * minimumZoomLevel property, the behavior is undefined.
+ *
+ * The default maximumZoomLevel is 20.
+ */
+@property (nonatomic) double maximumZoomLevel;
+
+/**
+ The heading of the map, measured in degrees clockwise from true north.
+
+ The value `0` means that the top edge of the map view corresponds to true
+ north. The value `90` means the top of the map is pointing due east. The
+ value `180` means the top of the map points due south, and so on.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setDirection:animated:` method instead.
+ */
+@property (nonatomic) CLLocationDirection direction;
+
+/**
+ Changes the heading of the map and optionally animates the change.
+
+ @param direction The heading of the map, measured in degrees clockwise from
+ true north.
+ @param animated Specify `YES` if you want the map view to animate the change
+ to the new heading or `NO` if you want the map to display the new
+ heading immediately.
+
+ Changing the heading rotates the map without changing the current center
+ coordinate or zoom level.
+ */
+- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated;
+
+/**
+ Resets the map rotation to a northern heading — a `direction` of `0` degrees.
+ */
+- (IBAction)resetNorth;
+
+/**
+ The coordinate bounds visible in the receiver's viewport.
+
+ Changing the value of this property updates the receiver immediately. If you
+ want to animate the change, call `-setVisibleCoordinateBounds:animated:`
+ instead.
+ */
+@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
+
+/**
+ Changes the receiver’s viewport to fit the given coordinate bounds,
+ optionally animating the change.
+
+ @param bounds The bounds that the viewport will show in its entirety.
+ @param animated Specify `YES` to animate the change by smoothly scrolling
+ and zooming or `NO` to immediately display the given bounds.
+ */
+- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
+
+/**
+ Changes the receiver's viewport to fit the given coordinate bounds and
+ optionally some additional padding on each side.
+
+ @param bounds The bounds that the viewport will show in its entirety.
+ @param insets The minimum padding (in screen points) that will be visible
+ around the given coordinate bounds.
+ @param animated Specify `YES` to animate the change by smoothly scrolling and
+ zooming or `NO` to immediately display the given bounds.
+ */
+- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
+
+/**
+ Changes the receiver's viewport to fit all of the given coordinates and
+ optionally some additional padding on each side.
+
+ @param coordinates The coordinates that the viewport will show.
+ @param count The number of coordinates. This number must not be greater than
+ the number of elements in `coordinates`.
+ @param insets The minimum padding (in screen points) that will be visible
+ around the given coordinate bounds.
+ @param animated Specify `YES` to animate the change by smoothly scrolling and
+ zooming or `NO` to immediately display the given bounds.
+ */
+- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
+
+/**
+ Changes the receiver's viewport to fit all of the given coordinates and
+ optionally some additional padding on each side.
+
+ @param coordinates The coordinates that the viewport will show.
+ @param count The number of coordinates. This number must not be greater than
+ the number of elements in `coordinates`.
+ @param insets The minimum padding (in screen points) that will be visible
+ around the given coordinate bounds.
+ @param direction The direction to rotate the map to, measured in degrees
+ relative to true north.
+ @param duration The duration to animate the change in seconds.
+ @param function The timing function to animate the change.
+ @param completion The block executed after the animation finishes.
+ */
+- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets direction:(CLLocationDirection)direction duration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Sets the visible region so that the map displays the specified annotations.
+
+ Calling this method updates the value in the `visibleCoordinateBounds` property
+ and potentially other properties to reflect the new map region. A small amount
+ of padding is reserved around the edges of the map view. To specify a different
+ amount of padding, use the `-showAnnotations:edgePadding:animated:` method.
+
+ @param annotations The annotations that you want to be visible in the map.
+ @param animated `YES` if you want the map region change to be animated, or `NO`
+ if you want the map to display the new region immediately without animations.
+ */
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated;
+
+/**
+ Sets the visible region so that the map displays the specified annotations with
+ the specified amount of padding on each side.
+
+ Calling this method updates the value in the visibleCoordinateBounds property
+ and potentially other properties to reflect the new map region.
+
+ @param annotations The annotations that you want to be visible in the map.
+ @param insets The minimum padding (in screen points) around the edges of the
+ map view to keep clear of annotations.
+ @param animated `YES` if you want the map region change to be animated, or `NO`
+ if you want the map to display the new region immediately without animations.
+ */
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
+
+/**
+ A camera representing the current viewpoint of the map.
+ */
+@property (nonatomic, copy) MGLMapCamera *camera;
+
+/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition animation.
+
+ @param camera The new viewpoint.
+ @param animated Specify `YES` if you want the map view to animate the change to
+ the new viewpoint or `NO` if you want the map to display the new viewpoint
+ immediately.
+ */
+- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
+
+/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function;
+
+/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and a default duration based on the length of the flight
+ path.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and an optional transition duration.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously. Specify a negative value to use the default duration, which
+ is based on the length of the flight path.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and an optional transition duration and peak altitude.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously. Specify a negative value to use the default duration, which
+ is based on the length of the flight path.
+ @param peakAltitude The altitude, measured in meters, at the midpoint of the
+ animation. The value of this parameter is ignored if it is negative or if
+ the animation transition resulting from a similar call to
+ `-setCamera:animated:` would have a midpoint at a higher altitude.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Returns the camera that best fits the given coordinate bounds.
+
+ @param bounds The coordinate bounds to fit to the receiver’s viewport.
+ @return A camera object centered on the same location as the coordinate
+ bounds with zoom level as high (close to the ground) as possible while still
+ including the entire coordinate bounds. The camera object uses the current
+ direction and pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds;
+
+/**
+ Returns the camera that best fits the given coordinate bounds, optionally with
+ some additional padding on each side.
+
+ @param bounds The coordinate bounds to fit to the receiver’s viewport.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the same location as the coordinate bounds
+ with zoom level as high (close to the ground) as possible while still
+ including the entire coordinate bounds. The camera object uses the current
+ direction and pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
+
+/**
+ 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 `UIEdgeInsetsZero`, 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.
+
+ When the map view’s superview is an instance of `UIViewController` whose
+ `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this
+ property may be overridden at any time.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setContentInset:animated:` method
+ instead.
+ */
+@property (nonatomic, assign) UIEdgeInsets contentInset;
+
+/**
+ Sets the distance from the edges of the map view’s frame to the edges of the
+ map view’s logical viewport with an optional transition animation.
+
+ When the value of this property is equal to `UIEdgeInsetsZero`, 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.
+
+ When the map view’s superview is an instance of `UIViewController` whose
+ `automaticallyAdjustsScrollViewInsets` property is `YES`, the value of this
+ property may be overridden at any time.
+
+ @param contentInset The new values to inset the content by.
+ @param animated Specify `YES` if you want the map view to animate the change to
+ the content inset or `NO` if you want the map to inset the content
+ immediately.
+ */
+- (void)setContentInset:(UIEdgeInsets)contentInset animated:(BOOL)animated;
+
+#pragma mark Converting Geographic Coordinates
+
+/**
+ Converts a point in the given view's coordinate system to a geographic
+ coordinate.
+
+ @param point The point to convert.
+ @param view The view in whose coordinate system the point is expressed.
+ @return The geographic coordinate at the given point.
+ */
+- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view;
+
+/**
+ Converts a geographic coordinate to a point in the given view's coordinate
+ system.
+
+ @param coordinate The geographic coordinate to convert.
+ @param view The view in whose coordinate system the returned point should be
+ expressed. If this parameter is `nil`, the returned point is expressed
+ in the window's coordinate system. If `view` is not `nil`, it must
+ belong to the same window as the map view.
+ @return The point (in the appropriate view or window coordinate system)
+ corresponding to the given geographic coordinate.
+ */
+- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable UIView *)view;
+
+/**
+ Converts a rectangle in the given view’s coordinate system to a geographic
+ bounding box.
+
+ @param rect The rectangle to convert.
+ @param view The view in whose coordinate system the rectangle is expressed.
+ @return The geographic bounding box coextensive with the given rectangle.
+ */
+- (MGLCoordinateBounds)convertRect:(CGRect)rect toCoordinateBoundsFromView:(nullable UIView *)view;
+
+/**
+ Converts a geographic bounding box to a rectangle in the given view’s
+ coordinate system.
+
+ @param bounds The geographic bounding box to convert.
+ @param view The view in whose coordinate system the returned rectangle should
+ be expressed. If this parameter is `nil`, the returned rectangle is
+ expressed in the window’s coordinate system. If `view` is not `nil`, it must
+ belong to the same window as the map view.
+ */
+- (CGRect)convertCoordinateBounds:(MGLCoordinateBounds)bounds toRectToView:(nullable UIView *)view;
+
+/**
+ Returns the distance spanned by one point in the map view's coordinate system
+ at the given latitude and current zoom level.
+
+ The distance between points decreases as the latitude approaches the poles.
+ This relationship parallels the relationship between longitudinal coordinates
+ at different latitudes.
+
+ @param latitude The latitude of the geographic coordinate represented by the
+ point.
+ @return The distance in meters spanned by a single point.
+ */
+- (CLLocationDistance)metersPerPointAtLatitude:(CLLocationDegrees)latitude;
+
+- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude __attribute__((deprecated("Use -metersPerPointAtLatitude:.")));
+
+#pragma mark Annotating the Map
+
+/**
+ The complete list of annotations associated with the receiver. (read-only)
+
+ The objects in this array must adopt the `MGLAnnotation` protocol. If no
+ annotations are associated with the map view, the value of this property is
+ `nil`.
+ */
+@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
+
+/**
+ Adds an annotation to the map view.
+
+ @note `MGLMultiPolyline`, `MGLMultiPolygon`, and `MGLShapeCollection` objects
+ cannot be added to the map view at this time. Nor can `MGLMultiPoint`
+ objects that are not instances of `MGLPolyline` or `MGLPolygon`. Any
+ multipoint, multipolyline, multipolygon, or shape collection object that is
+ specified is silently ignored.
+
+ @param annotation The annotation object to add to the receiver. This object
+ must conform to the `MGLAnnotation` protocol. The map view retains the
+ annotation object. */
+- (void)addAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Adds an array of annotations to the map view.
+
+ @note `MGLMultiPolyline`, `MGLMultiPolygon`, and `MGLShapeCollection` objects
+ cannot be added to the map view at this time. Nor can `MGLMultiPoint`
+ objects that are not instances of `MGLPolyline` or `MGLPolygon`. Any
+ multipoint, multipolyline, multipolygon, or shape collection objects that
+ are specified are silently ignored.
+
+ @param annotations An array of annotation objects. Each object in the array
+ must conform to the `MGLAnnotation` protocol. The map view retains each
+ individual annotation object.
+ */
+- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+
+/**
+ Removes an annotation from the map view, deselecting it if it is selected.
+
+ Removing an annotation object dissociates it from the map view entirely,
+ preventing it from being displayed on the map. Thus you would typically call
+ this method only when you want to hide or delete a given annotation.
+
+ @param annotation The annotation object to remove. This object must conform
+ to the `MGLAnnotation` protocol
+ */
+- (void)removeAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Removes an array of annotations from the map view, deselecting any selected
+ annotations in the array.
+
+ Removing annotation objects dissociates them from the map view entirely,
+ preventing them from being displayed on the map. Thus you would typically
+ call this method only when you want to hide or delete the given annotations.
+
+ @param annotations The array of annotation objects to remove. Objects in the
+ array must conform to the `MGLAnnotation` protocol.
+ */
+- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+
+/**
+ Returns a reusable annotation image object associated with its identifier.
+
+ For performance reasons, you should generally reuse `MGLAnnotationImage`
+ objects for identical-looking annotations in your map views. Dequeueing
+ saves time and memory during performance-critical operations such as
+ scrolling.
+
+ @param identifier A string identifying the annotation image to be reused.
+ This string is the same one you specify when initially returning the
+ annotation image object using the `-mapView:imageForAnnotation:` method.
+ @return An annotation image object with the given identifier, or `nil` if no
+ such object exists in the reuse queue.
+ */
+- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier;
+
+/**
+ Returns a reusable annotation view object associated with its identifier.
+
+ For performance reasons, you should generally reuse `MGLAnnotationView`
+ objects for identical-looking annotations in your map views. Dequeueing
+ saves time and memory during performance-critical operations such as
+ scrolling.
+
+ @param identifier A string identifying the annotation view to be reused.
+ This string is the same one you specify when initially returning the
+ annotation view object using the `-mapView:viewForAnnotation:` method.
+ @return An annotation view object with the given identifier, or `nil` if no
+ such object exists in the reuse queue.
+ */
+- (nullable MGLAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;
+
+#pragma mark Managing Annotation Selections
+
+/**
+ The currently selected annotations.
+
+ Assigning a new array to this property selects only the first annotation in
+ the array.
+ */
+@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
+
+/**
+ Selects an annotation and displays a callout view for it.
+
+ If the given annotation is not visible within the current viewport, this
+ method has no effect.
+
+ @param annotation The annotation object to select.
+ @param animated If `YES`, the callout view is animated into position.
+ */
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated;
+
+/**
+ Deselects an annotation and hides its callout view.
+
+ @param annotation The annotation object to deselect.
+ @param animated If `YES`, the callout view is animated offscreen.
+ */
+- (void)deselectAnnotation:(nullable id <MGLAnnotation>)annotation animated:(BOOL)animated;
+
+#pragma mark Overlaying the Map
+
+/**
+ Adds a single overlay object to the map.
+
+ To remove an overlay from a map, use the `-removeOverlay:` method.
+
+ @param overlay The overlay object to add. This object must conform to the
+ `MGLOverlay` protocol. */
+- (void)addOverlay:(id <MGLOverlay>)overlay;
+
+/**
+ Adds an array of overlay objects to the map.
+
+ To remove multiple overlays from a map, use the `-removeOverlays:` method.
+
+ @param overlays An array of objects, each of which must conform to the
+ `MGLOverlay` protocol.
+ */
+- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+
+/**
+ Removes a single overlay object from the map.
+
+ If the specified overlay is not currently associated with the map view, this
+ method does nothing.
+
+ @param overlay The overlay object to remove.
+ */
+- (void)removeOverlay:(id <MGLOverlay>)overlay;
+
+/**
+ Removes one or more overlay objects from the map.
+
+ If a given overlay object is not associated with the map view, it is ignored.
+
+ @param overlays An array of objects, each of which conforms to the `MGLOverlay`
+ protocol.
+ */
+- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+
+#pragma mark Accessing the Underlying Map Data
+
+/**
+ Returns an array of rendered map features that intersect with a given point.
+
+ This method may return features from any of the map’s style layers. To restrict
+ the search to a particular layer or layers, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:` method. For more
+ information about searching for map features, see that method’s documentation.
+
+ @param point A point expressed in the map view’s coordinate system.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:));
+
+/**
+ Returns an array of rendered map features that intersect with a given point,
+ restricted to the given style layers.
+
+ Each object in the returned array represents a feature rendered by the
+ current style and provides access to attributes specified by the relevant
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
+ The returned array includes features specified in vector and GeoJSON tile
+ sources but does not include anything from raster, image, or video sources.
+
+ Only visible features are returned. For example, suppose the current style uses
+ the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
+ but none of the specified style layers includes features that have the `maki`
+ property set to `bus`. If you pass a point corresponding to the location of a
+ bus stop into this method, the bus stop feature does not appear in the
+ resulting array. On the other hand, if the style does include bus stops, an
+ `MGLFeature` object representing that bus stop is returned and its
+ `featureAttributes` dictionary has the `maki` key set to `bus` (along with
+ other attributes). The dictionary contains only the attributes provided by the
+ tile source; it does not include computed attribute values or rules about how
+ the feature is rendered by the current style.
+
+ The returned array is sorted by z-order, starting with the topmost rendered
+ feature and ending with the bottommost rendered feature. A feature that is
+ rendered multiple times due to wrapping across the antimeridian at low zoom
+ levels is included only once, subject to the caveat that follows.
+
+ Features come from tiled vector data or GeoJSON data that is converted to tiles
+ internally, so feature geometries are clipped at tile boundaries and features
+ may appear duplicated across tiles. For example, suppose the specified point
+ lies along a road that spans the screen. The resulting array includes those
+ parts of the road that lie within the map tile that contain the specified
+ point, even if the road extends into other tiles.
+
+ To find out the layer names in a particular style, view the style in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+
+ @param point A point expressed in the map view’s coordinate system.
+ @param styleLayerIdentifiers A set of strings that correspond to the names of
+ layers defined in the current style. Only the features contained in these
+ layers are included in the returned array.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
+
+/**
+ Returns an array of rendered map features that intersect with the given
+ rectangle.
+
+ This method may return features from any of the map’s style layers. To restrict
+ the search to a particular layer or layers, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:` method. For more
+ information about searching for map features, see that method’s documentation.
+
+ @param rect A rectangle expressed in the map view’s coordinate system.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:));
+
+/**
+ Returns an array of rendered map features that intersect with the given
+ rectangle, restricted to the given style layers.
+
+ Each object in the returned array represents a feature rendered by the
+ current style and provides access to attributes specified by the relevant
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
+ The returned array includes features specified in vector and GeoJSON tile
+ sources but does not include anything from raster, image, or video sources.
+
+ Only visible features are returned. For example, suppose the current style uses
+ the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
+ but none of the specified style layers includes features that have the `maki`
+ property set to `bus`. If you pass a rectangle containing the location of a bus
+ stop into this method, the bus stop feature does not appear in the resulting
+ array. On the other hand, if the style does include bus stops, an `MGLFeature`
+ object representing that bus stop is returned and its `featureAttributes`
+ dictionary has the `maki` key set to `bus` (along with other attributes). The
+ dictionary contains only the attributes provided by the tile source; it does
+ not include computed attribute values or rules about how the feature is
+ rendered by the current style.
+
+ The returned array is sorted by z-order, starting with the topmost rendered
+ feature and ending with the bottommost rendered feature. A feature that is
+ rendered multiple times due to wrapping across the antimeridian at low zoom
+ levels is included only once, subject to the caveat that follows.
+
+ Features come from tiled vector data or GeoJSON data that is converted to tiles
+ internally, so feature geometries are clipped at tile boundaries and features
+ may appear duplicated across tiles. For example, suppose the specified
+ rectangle intersects with a road that spans the screen. The resulting array
+ includes those parts of the road that lie within the map tiles covering the
+ specified rectangle, even if the road extends into other tiles. The portion of
+ the road within each map tile is included individually.
+
+ To find out the layer names in a particular style, view the style in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+
+ @param rect A rectangle expressed in the map view’s coordinate system.
+ @param styleLayerIdentifiers A set of strings that correspond to the names of
+ layers defined in the current style. Only the features contained in these
+ layers are included in the returned array.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:));
+
+#pragma mark Debugging the Map
+
+/**
+ The options that determine which debugging aids are shown on the map.
+
+ These options are all disabled by default and should remain disabled in
+ released software for performance and aesthetic reasons.
+ */
+@property (nonatomic) MGLMapDebugMaskOptions debugMask;
+
+@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((deprecated("Use -debugMask and -setDebugMask:.")));
+
+- (void)toggleDebug __attribute__((deprecated("Use -setDebugMask:.")));
+
+- (void)emptyMemoryCache __attribute__((deprecated));
+
+/**
+ Resets the map to the minimum zoom level, a center coordinate of (0, 0), and
+ a northern heading.
+ */
+- (void)resetPosition;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 57e59be10d..5d1bcb1109 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -1,6 +1,4 @@
-#import "MGLMapView.h"
-#import "MGLMapView+IBAdditions.h"
-#import "MGLMapView+MGLCustomStyleLayerAdditions.h"
+#import "MGLMapView_Internal.h"
#import <mbgl/platform/log.hpp>
#import <mbgl/gl/gl.hpp>
@@ -9,8 +7,7 @@
#import <OpenGLES/EAGL.h>
#include <mbgl/mbgl.hpp>
-#include <mbgl/annotation/point_annotation.hpp>
-#include <mbgl/annotation/shape_annotation.hpp>
+#include <mbgl/annotation/annotation.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/mode.hpp>
@@ -18,19 +15,21 @@
#include <mbgl/platform/darwin/reachability.h>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/math/wrap.hpp>
#include <mbgl/util/geo.hpp>
-#include <mbgl/util/math.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/projection.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/default_styles.hpp>
#include <mbgl/util/chrono.hpp>
#import "Mapbox.h"
-#import "../../darwin/src/MGLGeometry_Private.h"
-#import "../../darwin/src/MGLMultiPoint_Private.h"
-#import "../../darwin/src/MGLOfflineStorage_Private.h"
+#import "MGLFeature_Private.h"
+#import "MGLGeometry_Private.h"
+#import "MGLMultiPoint_Private.h"
+#import "MGLOfflineStorage_Private.h"
#import "NSBundle+MGLAdditions.h"
#import "NSString+MGLAdditions.h"
@@ -39,8 +38,10 @@
#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation_Private.h"
#import "MGLAnnotationImage_Private.h"
+#import "MGLAnnotationView_Private.h"
#import "MGLMapboxEvents.h"
#import "MGLCompactCalloutView.h"
+#import "MGLAnnotationContainerView.h"
#import <algorithm>
#import <cstdlib>
@@ -60,8 +61,6 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {
MGLUserTrackingStateChanged,
};
-NSString *const MGLMapboxSetupDocumentationURLDisplayString = @"mapbox.com/help/first-steps-ios-sdk";
-
const NSTimeInterval MGLAnimationDuration = 0.3;
/// Duration of an animation due to a user location update, typically chosen to
@@ -99,6 +98,8 @@ const CGFloat MGLAnnotationImagePaddingForHitTest = 5;
/// Distance from the callout’s anchor point to the annotation it points to.
const CGFloat MGLAnnotationImagePaddingForCallout = 1;
+const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
+
/// Unique identifier representing a single annotation in mbgl.
typedef uint32_t MGLAnnotationTag;
@@ -132,15 +133,69 @@ mbgl::Color MGLColorObjectFromUIColor(UIColor *color)
return {{ (float)r, (float)g, (float)b, (float)a }};
}
+@interface MGLAnnotationAccessibilityElement : UIAccessibilityElement
+
+@property (nonatomic) MGLAnnotationTag tag;
+
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation MGLAnnotationAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag
+{
+ if (self = [super initWithAccessibilityContainer:container])
+ {
+ _tag = tag;
+ self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable;
+ }
+ return self;
+}
+
+- (void)accessibilityIncrement
+{
+ [self.accessibilityContainer accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement
+{
+ [self.accessibilityContainer accessibilityDecrement];
+}
+
+@end
+
/// Lightweight container for metadata about an annotation, including the annotation itself.
class MGLAnnotationContext {
public:
id <MGLAnnotation> annotation;
- /// mbgl-given identifier for the annotation image used by this annotation.
- /// Based on the annotation image’s reusable identifier.
- NSString *symbolIdentifier;
+ /// The annotation’s image’s reuse identifier.
+ NSString *imageReuseIdentifier;
+ MGLAnnotationAccessibilityElement *accessibilityElement;
+ MGLAnnotationView *annotationView;
+ NSString *viewReuseIdentifier;
};
+/** An accessibility element representing the MGLMapView at large. */
+@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement
+
+@end
+
+@implementation MGLMapViewProxyAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container
+{
+ if (self = [super initWithAccessibilityContainer:container])
+ {
+ self.accessibilityTraits = UIAccessibilityTraitButton;
+ self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
+ self.accessibilityHint = @"Returns to the map";
+ }
+ return self;
+}
+
+@end
+
#pragma mark - Private -
@interface MGLMapView () <UIGestureRecognizerDelegate,
@@ -180,6 +235,8 @@ public:
@property (nonatomic) CGFloat quickZoomStart;
@property (nonatomic, getter=isDormant) BOOL dormant;
@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
+@property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement;
+@property (nonatomic) MGLAnnotationContainerView *annotationContainerView;
@end
@@ -191,14 +248,18 @@ public:
BOOL _opaque;
NS_MUTABLE_ARRAY_OF(NSURL *) *_bundledStyleURLs;
-
+
MGLAnnotationContextMap _annotationContextsByAnnotationTag;
/// Tag of the selected annotation. If the user location annotation is selected, this ivar is set to `MGLAnnotationTagNotFound`.
MGLAnnotationTag _selectedAnnotationTag;
+ NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *_annotationViewReuseQueueByIdentifier;
+
BOOL _userLocationAnnotationIsSelected;
/// Size of the rectangle formed by unioning the maximum slop area around every annotation image.
CGSize _unionedAnnotationImageSize;
std::vector<MGLAnnotationTag> _annotationsNearbyLastTap;
+ CGPoint _initialImplicitCalloutViewOffset;
+ NSDate *_userLocationAnimationCompletionDate;
BOOL _isWaitingForRedundantReachableNotification;
BOOL _isTargetingInterfaceBuilder;
@@ -219,6 +280,10 @@ public:
BOOL _delegateHasStrokeColorsForShapeAnnotations;
BOOL _delegateHasFillColorsForShapeAnnotations;
BOOL _delegateHasLineWidthsForShapeAnnotations;
+
+ MGLCompassDirectionFormatter *_accessibilityCompassFormatter;
+
+ CGSize _largestAnnotationViewSize;
}
#pragma mark - Setup & Teardown -
@@ -288,6 +353,12 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
_mbglMap->setStyleURL([[styleURL absoluteString] UTF8String]);
}
+- (IBAction)reloadStyle:(__unused id)sender {
+ NSURL *styleURL = self.styleURL;
+ _mbglMap->setStyleURL("");
+ self.styleURL = styleURL;
+}
+
- (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
@@ -299,7 +370,14 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self createGLView];
}
- self.accessibilityLabel = @"Map";
+ // setup accessibility
+ //
+// self.isAccessibilityElement = YES;
+ self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"MAP_A11Y_LABEL", nil, nil, @"Map", @"Accessibility label");
+ self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction | UIAccessibilityTraitAdjustable;
+ _accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init];
+ _accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong;
+
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
@@ -314,12 +392,12 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// setup mbgl map
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
- _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);
+ _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
+ [self validateTileCacheSize];
// start paused if in IB
if (_isTargetingInterfaceBuilder || background) {
self.dormant = YES;
- _mbglMap->pause();
}
// Notify map object when network reachability status changes.
@@ -338,6 +416,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// Set up annotation management and selection state.
_annotationImagesByIdentifier = [NSMutableDictionary dictionary];
_annotationContextsByAnnotationTag = {};
+ _annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary];
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
@@ -345,7 +424,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
//
UIImage *logo = [[MGLMapView resourceImageNamed:@"mapbox.png"] imageWithAlignmentRectInsets:UIEdgeInsetsMake(1.5, 4, 3.5, 2)];
_logoView = [[UIImageView alloc] initWithImage:logo];
- _logoView.accessibilityLabel = @"Mapbox logo";
+ _logoView.accessibilityTraits = UIAccessibilityTraitStaticText;
+ _logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label");
_logoView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_logoView];
_logoViewConstraints = [NSMutableArray array];
@@ -353,7 +433,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// setup attribution
//
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
- _attributionButton.accessibilityLabel = @"Attribution info";
+ _attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label");
+ _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint");
[_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside];
_attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_attributionButton];
@@ -362,12 +443,14 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// setup compass
//
- _compassView = [[UIImageView alloc] initWithImage:[MGLMapView resourceImageNamed:@"Compass.png"]];
- _compassView.accessibilityLabel = @"Compass";
- _compassView.frame = CGRectMake(0, 0, _compassView.image.size.width, _compassView.image.size.height);
+ _compassView = [[UIImageView alloc] initWithImage:self.compassImage];
+ _compassView.frame = { CGPointZero, _compassView.image.size };
_compassView.alpha = 0;
_compassView.userInteractionEnabled = YES;
[_compassView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleCompassTapGesture:)]];
+ _compassView.accessibilityTraits = UIAccessibilityTraitButton;
+ _compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label");
+ _compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint");
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
[container addSubview:_compassView];
container.translatesAutoresizingMaskIntoConstraints = NO;
@@ -469,7 +552,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
_glView.delegate = self;
[_glView bindDrawable];
[self insertSubview:_glView atIndex:0];
-
_glView.contentMode = UIViewContentModeCenter;
// load extensions
@@ -488,6 +570,26 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
});
}
+- (UIImage *)compassImage
+{
+ UIImage *scaleImage = [MGLMapView resourceImageNamed:@"Compass.png"];
+ UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale);
+ [scaleImage drawInRect:{ CGPointZero, scaleImage.size }];
+
+ NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{
+ NSFontAttributeName: [UIFont systemFontOfSize:9 weight:UIFontWeightUltraLight],
+ NSForegroundColorAttributeName: [UIColor whiteColor],
+ }];
+ CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2,
+ scaleImage.size.height * 0.45,
+ north.size.width, north.size.height);
+ [north drawInRect:stringRect];
+
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return image;
+}
+
- (void)reachabilityChanged:(NSNotification *)notification
{
MGLReachability *reachability = [notification object];
@@ -503,6 +605,13 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_attributionButton removeObserver:self forKeyPath:@"hidden"];
+ // Removing the annotations unregisters any outstanding KVO observers.
+ NSArray *annotations = self.annotations;
+ if (annotations)
+ {
+ [self removeAnnotations:annotations];
+ }
+
[self validateDisplayLink];
if (_mbglMap)
@@ -533,14 +642,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (_delegate == delegate) return;
_delegate = delegate;
-
- if ([delegate respondsToSelector:@selector(mapView:symbolNameForAnnotation:)])
- {
- [NSException raise:@"Method unavailable" format:
- @"-mapView:symbolNameForAnnotation: has been removed from the MGLMapViewDelegate protocol, but %@ still implements it. "
- @"Implement -[%@ mapView:imageForAnnotation:] instead.",
- NSStringFromClass([delegate class]), NSStringFromClass([delegate class])];
- }
_delegateHasAlphasForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:alphaForShapeAnnotation:)];
_delegateHasStrokeColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:strokeColorForShapeAnnotation:)];
@@ -558,15 +659,37 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
-
- [self setNeedsLayout];
+ if ( ! CGRectEqualToRect(frame, self.frame))
+ {
+ [self validateTileCacheSize];
+ }
}
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
+ if ( ! CGRectEqualToRect(bounds, self.bounds))
+ {
+ [self validateTileCacheSize];
+ }
+}
+
+- (void)validateTileCacheSize
+{
+ if ( ! _mbglMap)
+ {
+ return;
+ }
+
+ CGFloat zoomFactor = self.maximumZoomLevel - self.minimumZoomLevel + 1;
+ CGFloat cpuFactor = [NSProcessInfo processInfo].processorCount;
+ CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000;
+ CGFloat sizeFactor = (CGRectGetWidth(self.bounds) / mbgl::util::tileSize) *
+ (CGRectGetHeight(self.bounds) / mbgl::util::tileSize);
+
+ NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
- [self setNeedsLayout];
+ _mbglMap->setSourceTileCacheSize(cacheSize);
}
+ (BOOL)requiresConstraintBasedLayout
@@ -636,6 +759,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
multiplier:1
constant:5]];
+ UIImage *compassImage = self.compassView.image;
[compassContainerConstraints addObject:
[NSLayoutConstraint constraintWithItem:compassContainer
attribute:NSLayoutAttributeWidth
@@ -643,7 +767,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
- constant:self.compassView.image.size.width]];
+ constant:compassImage.size.width]];
[compassContainerConstraints addObject:
[NSLayoutConstraint constraintWithItem:compassContainer
@@ -652,7 +776,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
- constant:self.compassView.image.size.height]];
+ constant:compassImage.size.height]];
[constraintParentView addConstraints:compassContainerConstraints];
// logo bug
@@ -741,17 +865,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ( ! self.dormant)
{
- CGFloat zoomFactor = _mbglMap->getMaxZoom() - _mbglMap->getMinZoom() + 1;
- CGFloat cpuFactor = (CGFloat)[[NSProcessInfo processInfo] processorCount];
- CGFloat memoryFactor = (CGFloat)[[NSProcessInfo processInfo] physicalMemory] / 1000 / 1000 / 1000;
- CGFloat sizeFactor = ((CGFloat)_mbglMap->getWidth() / mbgl::util::tileSize) *
- ((CGFloat)_mbglMap->getHeight() / mbgl::util::tileSize);
-
- NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
-
- _mbglMap->setSourceTileCacheSize(cacheSize);
-
- _mbglMap->renderSync();
+ _mbglMap->render();
[self updateUserLocationAnnotationView];
}
@@ -894,7 +1008,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
[self validateDisplayLink];
self.dormant = YES;
- _mbglMap->pause();
[self.glView deleteDrawable];
}
}
@@ -967,8 +1080,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.glSnapshotView addSubview:snapshotTint];
}
- _mbglMap->pause();
-
[self.glView deleteDrawable];
}
}
@@ -988,8 +1099,6 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.glSnapshotView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.glView bindDrawable];
-
- _mbglMap->resume();
_displayLink.paused = NO;
@@ -1017,6 +1126,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
for (UIView *subview in view.subviews) [self updateTintColorForView:subview];
}
+- (BOOL)canBecomeFirstResponder {
+ return YES;
+}
+
#pragma mark - Gestures -
- (void)handleCompassTapGesture:(__unused id)sender
@@ -1142,7 +1255,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (log2(newScale) < _mbglMap->getMinZoom()) return;
- _mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y });
+ _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
// The gesture recognizer only reports the gesture’s current center
// point, so use the previous center point to anchor the transition.
@@ -1152,7 +1265,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
CLLocationCoordinate2D centerCoordinate = _previousPinchCenterCoordinate;
_mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate),
- { centerPoint.x, centerPoint.y });
+ mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
}
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
@@ -1190,7 +1303,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (velocity)
{
- _mbglMap->setScale(newScale, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
+ _mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
}
[self notifyGestureDidEndWithDrift:velocity];
@@ -1239,7 +1352,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
newDegrees = fmaxf(newDegrees, -30);
}
- _mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y });
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1254,7 +1367,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
CGFloat newRadians = radians + velocity * duration * 0.1;
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
- _mbglMap->setBearing(newDegrees, { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
[self notifyGestureDidEndWithDrift:YES];
@@ -1280,24 +1393,40 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
return;
}
[self trackGestureEvent:MGLEventGestureSingleTap forRecognizer:singleTap];
+
+ if (self.mapViewProxyAccessibilityElement.accessibilityElementIsFocused)
+ {
+ id nextElement;
+ if (_userLocationAnnotationIsSelected)
+ {
+ nextElement = self.userLocationAnnotationView;
+ }
+ else
+ {
+ nextElement = _annotationContextsByAnnotationTag[_selectedAnnotationTag].accessibilityElement;
+ }
+ [self deselectAnnotation:self.selectedAnnotation animated:YES];
+ UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nextElement);
+ return;
+ }
CGPoint tapPoint = [singleTap locationInView:self];
- if (self.userLocationVisible)
+ if (self.userLocationVisible
+ && [self.userLocationAnnotationView.layer.presentationLayer hitTest:tapPoint])
{
- // Assume that the user is fat-fingering an annotation.
- CGRect hitRect = CGRectInset({ tapPoint, CGSizeZero },
- -MGLAnnotationImagePaddingForHitTest,
- -MGLAnnotationImagePaddingForHitTest);
-
- if (CGRectIntersectsRect(hitRect, self.userLocationAnnotationView.frame))
+ if ( ! _userLocationAnnotationIsSelected)
{
- if ( ! _userLocationAnnotationIsSelected)
- {
- [self selectAnnotation:self.userLocation animated:YES];
- }
- return;
+ [self selectAnnotation:self.userLocation animated:YES];
}
+ return;
+ }
+
+ MGLAnnotationView *hitAnnotationView = [self annotationViewAtPoint:tapPoint];
+ if (hitAnnotationView)
+ {
+ [self selectAnnotation:hitAnnotationView.annotation animated:YES];
+ return;
}
MGLAnnotationTag hitAnnotationTag = [self annotationTagAtPoint:tapPoint persistingResults:YES];
@@ -1405,7 +1534,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
centerPoint = self.userLocationAnnotationViewCenter;
}
_mbglMap->scaleBy(powf(2, newZoom) / _mbglMap->getScale(),
- { centerPoint.x, centerPoint.y });
+ mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1440,7 +1569,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
centerPoint = self.userLocationAnnotationViewCenter;
}
- _mbglMap->setPitch(pitchNew, centerPoint);
+ _mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
[self notifyMapChange:mbgl::MapChangeRegionIsChanging];
}
@@ -1479,7 +1608,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)])
{
NSAssert([tap.view isKindOfClass:[UIControl class]], @"Tapped view %@ is not a UIControl", tap.view);
- [self.delegate mapView:self annotation:self.selectedAnnotation
+ id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
+ NSAssert(selectedAnnotation, @"Selected annotation should not be nil.");
+ [self.delegate mapView:self annotation:selectedAnnotation
calloutAccessoryControlTapped:(UIControl *)tap.view];
}
}
@@ -1493,7 +1624,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)])
{
- [self.delegate mapView:self tapOnCalloutForAnnotation:self.selectedAnnotation];
+ id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
+ NSAssert(selectedAnnotation, @"Selected annotation should not be nil.");
+ [self.delegate mapView:self tapOnCalloutForAnnotation:selectedAnnotation];
}
}
@@ -1501,10 +1634,18 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)])
{
- [self.delegate mapView:self tapOnCalloutForAnnotation:self.selectedAnnotation];
+ id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
+ NSAssert(selectedAnnotation, @"Selected annotation should not be nil.");
+ [self.delegate mapView:self tapOnCalloutForAnnotation:selectedAnnotation];
}
}
+- (void)calloutViewDidAppear:(UIView<MGLCalloutView> *)calloutView
+{
+ UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, calloutView);
+}
+
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ];
@@ -1532,15 +1673,15 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ( ! self.attributionSheet)
{
- self.attributionSheet = [[UIActionSheet alloc] initWithTitle:@"Mapbox iOS SDK"
+ self.attributionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title")
delegate:self
- cancelButtonTitle:@"Cancel"
+ cancelButtonTitle:NSLocalizedStringWithDefaultValue(@"CANCEL", nil, nil, @"Cancel", @"")
destructiveButtonTitle:nil
otherButtonTitles:
- @"© Mapbox",
- @"© OpenStreetMap",
- @"Improve This Map",
- @"Mapbox Telemetry",
+ NSLocalizedStringWithDefaultValue(@"COPY_MAPBOX", nil, nil, @"© Mapbox", @"Copyright notice in attribution sheet"),
+ NSLocalizedStringWithDefaultValue(@"COPY_OSM", nil, nil, @"© OpenStreetMap", @"Copyright notice in attribution sheet"),
+ NSLocalizedStringWithDefaultValue(@"MAP_FEEDBACK", nil, nil, @"Improve This Map", @"Action in attribution sheet"),
+ NSLocalizedStringWithDefaultValue(@"TELEMETRY_NAME", nil, nil, @"Mapbox Telemetry", @"Action in attribution sheet"),
nil];
}
@@ -1575,22 +1716,22 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"])
{
- message = @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
- participate = @"Keep Participating";
- optOut = @"Stop Participating";
+ message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_MSG", nil, nil, @"You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message");
+ participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_ON", nil, nil, @"Keep Participating", @"Telemetry prompt button");
+ optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_ENABLED_OFF", nil, nil, @"Stop Participating", @"Telemetry prompt button");
}
else
{
- message = @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
- participate = @"Participate";
- optOut = @"Don’t Participate";
+ message = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_MSG", nil, nil, @"You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.", @"Telemetry prompt message");
+ participate = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_ON", nil, nil, @"Participate", @"Telemetry prompt button");
+ optOut = NSLocalizedStringWithDefaultValue(@"TELEMETRY_DISABLED_OFF", nil, nil, @"Don’t Participate", @"Telemetry prompt button");
}
- UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Make Mapbox Maps Better"
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringWithDefaultValue(@"TELEMETRY_TITLE", nil, nil, @"Make Mapbox Maps Better", @"Telemetry prompt title")
message:message
delegate:self
cancelButtonTitle:participate
- otherButtonTitles:@"Tell Me More", optOut, nil];
+ otherButtonTitles:NSLocalizedStringWithDefaultValue(@"TELEMETRY_MORE", nil, nil, @"Tell Me More", @"Telemetry prompt button"), optOut, nil];
[alert show];
}
}
@@ -1604,7 +1745,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
else if (buttonIndex == alertView.firstOtherButtonIndex)
{
[[UIApplication sharedApplication] openURL:
- [NSURL URLWithString:@"https://mapbox.com/telemetry/"]];
+ [NSURL URLWithString:@"https://www.mapbox.com/telemetry/"]];
}
else if (buttonIndex == alertView.firstOtherButtonIndex + 1)
{
@@ -1614,7 +1755,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
#pragma mark - Properties -
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(__unused void *)context
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"hidden"] && object == _attributionButton)
{
@@ -1625,6 +1766,35 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[MGLMapboxEvents ensureMetricsOptoutExists];
}
}
+ else if ([keyPath isEqualToString:@"coordinate"] && [object conformsToProtocol:@protocol(MGLAnnotation)] && ![object isKindOfClass:[MGLMultiPoint class]])
+ {
+ id <MGLAnnotation> annotation = object;
+ MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
+ // We can get here because a subclass registered itself as an observer
+ // of the coordinate key path of a non-multipoint annotation but failed
+ // to handle the change. This check deters us from treating the
+ // subclass’s context as an annotation tag. If the context happens to
+ // match a valid annotation tag, the annotation will be unnecessarily
+ // but safely updated.
+ if (annotation == [self annotationWithTag:annotationTag])
+ {
+ const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(annotation.coordinate);
+
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+ NSString *symbolName;
+ if (!annotationContext.annotationView)
+ {
+ MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
+ symbolName = annotationImage.styleIconIdentifier;
+ }
+
+ _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String ?: "" });
+ if (annotationTag == _selectedAnnotationTag)
+ {
+ [self deselectAnnotation:annotation animated:YES];
+ }
+ }
+ }
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled
@@ -1667,6 +1837,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
mask |= MGLMapDebugCollisionBoxesMask;
}
+ if (options & mbgl::MapDebugOptions::Wireframe)
+ {
+ mask |= MGLMapDebugWireframesMask;
+ }
return mask;
}
@@ -1689,6 +1863,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
options |= mbgl::MapDebugOptions::Collision;
}
+ if (debugMask & MGLMapDebugWireframesMask)
+ {
+ options |= mbgl::MapDebugOptions::Wireframe;
+ }
_mbglMap->setDebug(options);
}
@@ -1731,6 +1909,242 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
_mbglMap->onLowMemory();
}
+#pragma mark - Accessibility -
+
+- (NSString *)accessibilityValue
+{
+ double zoomLevel = round(self.zoomLevel + 1);
+ return [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE", nil, nil, @"Zoom %dx\n%ld annotation(s) visible", @"Map accessibility value"), (int)zoomLevel, (long)self.accessibilityAnnotationCount];
+}
+
+- (CGRect)accessibilityFrame
+{
+ CGRect frame = [super accessibilityFrame];
+ UIViewController *viewController = self.viewControllerForLayoutGuides;
+ if (viewController)
+ {
+ CGFloat topInset = viewController.topLayoutGuide.length;
+ frame.origin.y += topInset;
+ frame.size.height -= topInset + viewController.bottomLayoutGuide.length;
+ }
+ return frame;
+}
+
+- (UIBezierPath *)accessibilityPath
+{
+ UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.accessibilityFrame];
+
+ // Exclude any visible annotation callout view.
+ if (self.calloutViewForSelectedAnnotation)
+ {
+ UIBezierPath *calloutViewPath = [UIBezierPath bezierPathWithRect:self.calloutViewForSelectedAnnotation.frame];
+ [path appendPath:calloutViewPath];
+ }
+
+ return path;
+}
+
+- (NSInteger)accessibilityElementCount
+{
+ if (self.calloutViewForSelectedAnnotation)
+ {
+ return 2 /* selectedAnnotationCalloutView, mapViewProxyAccessibilityElement */;
+ }
+ NSInteger count = self.accessibilityAnnotationCount + 2 /* compass, attributionButton */;
+ if (self.userLocationAnnotationView)
+ {
+ count++;
+ }
+ return count;
+}
+
+- (NSInteger)accessibilityAnnotationCount
+{
+ std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
+ return visibleAnnotations.size();
+}
+
+- (id)accessibilityElementAtIndex:(NSInteger)index
+{
+ if (self.calloutViewForSelectedAnnotation)
+ {
+ if (index == 0)
+ {
+ return self.calloutViewForSelectedAnnotation;
+ }
+ if (index == 1)
+ {
+ self.mapViewProxyAccessibilityElement.accessibilityFrame = self.accessibilityFrame;
+ self.mapViewProxyAccessibilityElement.accessibilityPath = self.accessibilityPath;
+ return self.mapViewProxyAccessibilityElement;
+ }
+ return nil;
+ }
+ std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
+
+ // Ornaments
+ if (index == 0)
+ {
+ return self.compassView;
+ }
+ if ( ! self.userLocationAnnotationView)
+ {
+ index++;
+ }
+ else if (index == 1)
+ {
+ return self.userLocationAnnotationView;
+ }
+ if (index > 0 && (NSUInteger)index == visibleAnnotations.size() + 2 /* compass, userLocationAnnotationView */)
+ {
+ return self.attributionButton;
+ }
+
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
+ CGPoint centerPoint = self.contentCenter;
+ if (self.userTrackingMode != MGLUserTrackingModeNone)
+ {
+ centerPoint = self.userLocationAnnotationViewCenter;
+ }
+ CLLocationCoordinate2D currentCoordinate = [self convertPoint:centerPoint toCoordinateFromView:self];
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
+ CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
+ CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
+ CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude,
+ coordinateA.longitude - currentCoordinate.longitude);
+ CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude,
+ coordinateB.longitude - currentCoordinate.longitude);
+ return deltaA < deltaB;
+ });
+
+ NSUInteger annotationIndex = MGLAnnotationTagNotFound;
+ if (index >= 0 && (NSUInteger)(index - 2) < visibleAnnotations.size())
+ {
+ annotationIndex = index - 2 /* compass, userLocationAnnotationView */;
+ }
+ MGLAnnotationTag annotationTag = visibleAnnotations[annotationIndex];
+ NSAssert(annotationTag != MGLAnnotationTagNotFound, @"Can’t get accessibility element for nonexistent or invisible annotation at index %li.", (long)index);
+ NSAssert(_annotationContextsByAnnotationTag.count(annotationTag), @"Missing annotation for tag %u.", annotationTag);
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+ id <MGLAnnotation> annotation = annotationContext.annotation;
+
+ // Let the annotation view serve as its own accessibility element.
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+ if (annotationView && annotationView.superview)
+ {
+ return annotationView;
+ }
+
+ // Lazily create an accessibility element for the found annotation.
+ if ( ! annotationContext.accessibilityElement)
+ {
+ annotationContext.accessibilityElement = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self tag:annotationTag];
+ }
+
+ // Update the accessibility element.
+ MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
+ CGRect annotationFrame = [self frameOfImage:annotationImage.image centeredAtCoordinate:annotation.coordinate];
+ CGPoint annotationFrameCenter = CGPointMake(CGRectGetMidX(annotationFrame), CGRectGetMidY(annotationFrame));
+ CGRect minimumFrame = CGRectInset({ annotationFrameCenter, CGSizeZero },
+ -MGLAnnotationAccessibilityElementMinimumSize.width / 2,
+ -MGLAnnotationAccessibilityElementMinimumSize.height / 2);
+ annotationFrame = CGRectUnion(annotationFrame, minimumFrame);
+ CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self);
+ annotationContext.accessibilityElement.accessibilityFrame = screenRect;
+ annotationContext.accessibilityElement.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
+
+ if ([annotation respondsToSelector:@selector(title)])
+ {
+ annotationContext.accessibilityElement.accessibilityLabel = annotation.title;
+ }
+ if ([annotation respondsToSelector:@selector(subtitle)])
+ {
+ annotationContext.accessibilityElement.accessibilityValue = annotation.subtitle;
+ }
+
+ return annotationContext.accessibilityElement;
+}
+
+- (NSInteger)indexOfAccessibilityElement:(id)element
+{
+ if (self.calloutViewForSelectedAnnotation)
+ {
+ return [@[self.calloutViewForSelectedAnnotation, self.mapViewProxyAccessibilityElement]
+ indexOfObject:element];
+ }
+ if (element == self.compassView)
+ {
+ return 0;
+ }
+ if (element == self.userLocationAnnotationView)
+ {
+ return 1;
+ }
+
+ std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
+
+ MGLAnnotationTag tag = MGLAnnotationTagNotFound;
+ if ([element isKindOfClass:[MGLAnnotationView class]])
+ {
+ id <MGLAnnotation> annotation = [(MGLAnnotationView *)element annotation];
+ tag = [self annotationTagForAnnotation:annotation];
+ }
+ else if ([element isKindOfClass:[MGLAnnotationAccessibilityElement class]])
+ {
+ tag = [(MGLAnnotationAccessibilityElement *)element tag];
+ }
+ else if (element == self.attributionButton)
+ {
+ return !!self.userLocationAnnotationView + visibleAnnotations.size();
+ }
+ else
+ {
+ return NSNotFound;
+ }
+
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
+ auto foundElement = std::find(visibleAnnotations.begin(), visibleAnnotations.end(), tag);
+ if (foundElement == visibleAnnotations.end())
+ {
+ return NSNotFound;
+ }
+ return !!self.userLocationAnnotationView + std::distance(visibleAnnotations.begin(), foundElement) + 1 /* compass */;
+}
+
+- (MGLMapViewProxyAccessibilityElement *)mapViewProxyAccessibilityElement
+{
+ if ( ! _mapViewProxyAccessibilityElement)
+ {
+ _mapViewProxyAccessibilityElement = [[MGLMapViewProxyAccessibilityElement alloc] initWithAccessibilityContainer:self];
+ }
+ return _mapViewProxyAccessibilityElement;
+}
+
+- (void)accessibilityIncrement
+{
+ // Swipe up to zoom out.
+ [self accessibilityScaleBy:0.5];
+}
+
+- (void)accessibilityDecrement
+{
+ // Swipe down to zoom in.
+ [self accessibilityScaleBy:2];
+}
+
+- (void)accessibilityScaleBy:(double)scaleFactor
+{
+ CGPoint centerPoint = self.contentCenter;
+ if (self.userTrackingMode != MGLUserTrackingModeNone)
+ {
+ centerPoint = self.userLocationAnnotationViewCenter;
+ }
+ _mbglMap->scaleBy(scaleFactor, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ [self unrotateIfNeededForGesture];
+
+ UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.accessibilityValue);
+}
+
#pragma mark - Geography -
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate
@@ -1837,6 +2251,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
_mbglMap->setMinZoom(minimumZoomLevel);
+ [self validateTileCacheSize];
}
- (double)minimumZoomLevel
@@ -1847,6 +2262,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
+ [self validateTileCacheSize];
}
- (double)maximumZoomLevel
@@ -1925,14 +2341,14 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self willChangeValueForKey:@"visibleCoordinateBounds"];
mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
- mbgl::AnnotationSegment segment;
- segment.reserve(count);
+ std::vector<mbgl::LatLng> latLngs;
+ latLngs.reserve(count);
for (NSUInteger i = 0; i < count; i++)
{
- segment.push_back({coordinates[i].latitude, coordinates[i].longitude});
+ latLngs.push_back({coordinates[i].latitude, coordinates[i].longitude});
}
- mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngs(segment, padding);
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngs(latLngs, padding);
if (direction >= 0)
{
cameraOptions.angle = MGLRadiansFromDegrees(-direction);
@@ -1994,7 +2410,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
else
{
CGPoint centerPoint = self.userLocationAnnotationViewCenter;
- _mbglMap->setBearing(direction, { centerPoint.x, centerPoint.y },
+ _mbglMap->setBearing(direction, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y },
MGLDurationInSeconds(duration));
}
}
@@ -2016,14 +2432,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (MGLMapCamera *)camera
{
- CGFloat pitch = _mbglMap->getPitch();
- CLLocationDistance altitude = MGLAltitudeForZoomLevel(self.zoomLevel, pitch,
- self.centerCoordinate.latitude,
- self.frame.size);
- return [MGLMapCamera cameraLookingAtCenterCoordinate:self.centerCoordinate
- fromDistance:altitude
- pitch:pitch
- heading:self.direction];
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ return [self cameraForCameraOptions:_mbglMap->getCameraOptions(padding)];
}
- (void)setCamera:(MGLMapCamera *)camera
@@ -2123,6 +2533,29 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self didChangeValueForKey:@"camera"];
}
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds
+{
+ return [self cameraThatFitsCoordinateBounds:bounds edgePadding:UIEdgeInsetsZero];
+}
+
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets
+{
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), padding);
+ return [self cameraForCameraOptions:cameraOptions];
+}
+
+- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions
+{
+ CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
+ double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel;
+ CLLocationDirection direction = cameraOptions.angle ? -MGLDegreesFromRadians(*cameraOptions.angle) : self.direction;
+ CGFloat pitch = cameraOptions.pitch ? MGLDegreesFromRadians(*cameraOptions.pitch) : _mbglMap->getPitch();
+ CLLocationDistance altitude = MGLAltitudeForZoomLevel(zoomLevel, pitch, centerCoordinate.latitude, self.frame.size);
+ return [MGLMapCamera cameraLookingAtCenterCoordinate:centerCoordinate fromDistance:altitude pitch:pitch heading:direction];
+}
+
/// Returns a CameraOptions object that specifies parameters for animating to
/// the given camera.
- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets
@@ -2299,8 +2732,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
newAppliedClasses.insert(newAppliedClasses.end(), [appliedClass UTF8String]);
}
- _mbglMap->setDefaultTransitionDuration(MGLDurationInSeconds(transitionDuration));
- _mbglMap->setClasses(newAppliedClasses);
+ mbgl::style::TransitionOptions transition { { MGLDurationInSeconds(transitionDuration) } };
+ _mbglMap->setClasses(newAppliedClasses, transition);
}
- (BOOL)hasStyleClass:(NSString *)styleClass
@@ -2342,6 +2775,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
return pair.second.annotation;
});
+
+ annotations.erase(std::remove_if(annotations.begin(), annotations.end(),
+ [](const id <MGLAnnotation> annotation) { return annotation == nullptr; }),
+ annotations.end());
+
return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()];
}
@@ -2390,10 +2828,13 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if ( ! annotations) return;
[self willChangeValueForKey:@"annotations"];
- std::vector<mbgl::PointAnnotation> points;
- std::vector<mbgl::ShapeAnnotation> shapes;
+ NSMutableDictionary *annotationImagesForAnnotation = [NSMutableDictionary dictionary];
+ NSMutableDictionary *annotationViewsForAnnotation = [NSMutableDictionary dictionary];
+ BOOL delegateImplementsViewForAnnotation = [self.delegate respondsToSelector:@selector(mapView:viewForAnnotation:)];
BOOL delegateImplementsImageForPoint = [self.delegate respondsToSelector:@selector(mapView:imageForAnnotation:)];
+
+ NSMutableArray *newAnnotationViews = [[NSMutableArray alloc] initWithCapacity:annotations.count];
for (id <MGLAnnotation> annotation in annotations)
{
@@ -2401,66 +2842,153 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if ([annotation isKindOfClass:[MGLMultiPoint class]])
{
- [(MGLMultiPoint *)annotation addShapeAnnotationObjectToCollection:shapes withDelegate:self];
- }
- else
- {
- MGLAnnotationImage *annotationImage = delegateImplementsImageForPoint ? [self.delegate mapView:self imageForAnnotation:annotation] : nil;
- if ( ! annotationImage)
+ // Actual multipoints aren’t supported as annotations.
+ if ([annotation isMemberOfClass:[MGLMultiPoint class]]
+ || [annotation isMemberOfClass:[MGLMultiPointFeature class]])
{
- annotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName];
+ continue;
}
- if ( ! annotationImage)
- {
- // Create a default annotation image that depicts a round pin
- // rising from the center, with a shadow slightly below center.
- // The alignment rect therefore excludes the bottom half.
- UIImage *defaultAnnotationImage = [MGLMapView resourceImageNamed:MGLDefaultStyleMarkerSymbolName];
- defaultAnnotationImage = [defaultAnnotationImage imageWithAlignmentRectInsets:
- UIEdgeInsetsMake(0, 0, defaultAnnotationImage.size.height / 2, 0)];
- annotationImage = [MGLAnnotationImage annotationImageWithImage:defaultAnnotationImage
- reuseIdentifier:MGLDefaultStyleMarkerSymbolName];
+
+ // The polyline or polygon knows how to style itself (with the map view’s help).
+ MGLMultiPoint *multiPoint = (MGLMultiPoint *)annotation;
+ if (!multiPoint.pointCount) {
+ continue;
}
+
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation([multiPoint annotationObjectWithDelegate:self]);
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ _annotationContextsByAnnotationTag[annotationTag] = context;
+ }
+ else if ( ! [annotation isKindOfClass:[MGLMultiPolyline class]]
+ || ![annotation isKindOfClass:[MGLMultiPolygon class]]
+ || ![annotation isKindOfClass:[MGLShapeCollection class]])
+ {
+ MGLAnnotationView *annotationView;
+ NSString *symbolName;
+ NSValue *annotationValue = [NSValue valueWithNonretainedObject:annotation];
- if ( ! self.annotationImagesByIdentifier[annotationImage.reuseIdentifier])
+ if (delegateImplementsViewForAnnotation)
{
- self.annotationImagesByIdentifier[annotationImage.reuseIdentifier] = annotationImage;
- [self installAnnotationImage:annotationImage];
- annotationImage.delegate = self;
+ annotationView = [self annotationViewForAnnotation:annotation];
+ if (annotationView)
+ {
+ annotationViewsForAnnotation[annotationValue] = annotationView;
+ annotationView.center = [self convertCoordinate:annotation.coordinate toPointToView:self];
+ [newAnnotationViews addObject:annotationView];
+ }
+ }
+
+ if ( ! annotationView) {
+ MGLAnnotationImage *annotationImage;
+
+ if (delegateImplementsImageForPoint)
+ {
+ annotationImage = [self.delegate mapView:self imageForAnnotation:annotation];
+ }
+ if ( ! annotationImage)
+ {
+ annotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName];
+ }
+ if ( ! annotationImage)
+ {
+ annotationImage = self.defaultAnnotationImage;
+ }
+
+ symbolName = annotationImage.styleIconIdentifier;
+
+ if ( ! symbolName)
+ {
+ symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
+ annotationImage.styleIconIdentifier = symbolName;
+ }
+ if ( ! self.annotationImagesByIdentifier[annotationImage.reuseIdentifier])
+ {
+ [self installAnnotationImage:annotationImage];
+ }
+
+ annotationImagesForAnnotation[annotationValue] = annotationImage;
}
- NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation(mbgl::SymbolAnnotation {
+ MGLPointFromLocationCoordinate2D(annotation.coordinate),
+ symbolName.UTF8String ?: ""
+ });
- points.emplace_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate), symbolName ? [symbolName UTF8String] : "");
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ MGLAnnotationImage *annotationImage = annotationImagesForAnnotation[annotationValue];
+
+ if (annotationImage) {
+ context.imageReuseIdentifier = annotationImage.reuseIdentifier;
+ }
+ if (annotationView) {
+ context.annotationView = annotationView;
+ context.viewReuseIdentifier = annotationView.reuseIdentifier;
+ }
+
+ _annotationContextsByAnnotationTag[annotationTag] = context;
+ if ([annotation isKindOfClass:[NSObject class]]) {
+ NSAssert(![annotation isKindOfClass:[MGLMultiPoint class]], @"Point annotation should not be MGLMultiPoint.");
+ [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:(void *)(NSUInteger)annotationTag];
+ }
}
}
- if (points.size())
+ [self updateAnnotationContainerViewWithAnnotationViews:newAnnotationViews];
+
+ [self didChangeValueForKey:@"annotations"];
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+}
+
+- (void)updateAnnotationContainerViewWithAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews
+{
+ if (annotationViews.count == 0) return;
+
+ MGLAnnotationContainerView *newAnnotationContainerView;
+ if (self.annotationContainerView)
{
- std::vector<MGLAnnotationTag> pointAnnotationTags = _mbglMap->addPointAnnotations(points);
-
- for (size_t i = 0; i < pointAnnotationTags.size(); ++i)
- {
- MGLAnnotationContext context;
- context.annotation = annotations[i];
- context.symbolIdentifier = @(points[i].icon.c_str());
- _annotationContextsByAnnotationTag[pointAnnotationTags[i]] = context;
- }
+ // reload any previously added views
+ newAnnotationContainerView = [MGLAnnotationContainerView annotationContainerViewWithAnnotationContainerView:self.annotationContainerView];
+ [self.annotationContainerView removeFromSuperview];
+ }
+ else
+ {
+ newAnnotationContainerView = [[MGLAnnotationContainerView alloc] initWithFrame:self.bounds];
}
+ newAnnotationContainerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ newAnnotationContainerView.contentMode = UIViewContentModeCenter;
+ [newAnnotationContainerView addSubviews:annotationViews];
+ [_glView insertSubview:newAnnotationContainerView atIndex:0];
+ self.annotationContainerView = newAnnotationContainerView;
+}
+
+/// Initialize and return a default annotation image that depicts a round pin
+/// rising from the center, with a shadow slightly below center. The alignment
+/// rect therefore excludes the bottom half.
+- (MGLAnnotationImage *)defaultAnnotationImage
+{
+ UIImage *image = [MGLMapView resourceImageNamed:MGLDefaultStyleMarkerSymbolName];
+ image = [image imageWithAlignmentRectInsets:
+ UIEdgeInsetsMake(0, 0, image.size.height / 2, 0)];
+ MGLAnnotationImage *annotationImage = [MGLAnnotationImage annotationImageWithImage:image
+ reuseIdentifier:MGLDefaultStyleMarkerSymbolName];
+ annotationImage.styleIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
+ return annotationImage;
+}
- if (shapes.size())
+- (MGLAnnotationView *)annotationViewForAnnotation:(id<MGLAnnotation>)annotation
+{
+ MGLAnnotationView *annotationView = [self.delegate mapView:self viewForAnnotation:annotation];
+
+ if (annotationView)
{
- std::vector<MGLAnnotationTag> shapeAnnotationTags = _mbglMap->addShapeAnnotations(shapes);
-
- for (size_t i = 0; i < shapeAnnotationTags.size(); ++i)
- {
- MGLAnnotationContext context;
- context.annotation = annotations[i];
- _annotationContextsByAnnotationTag[shapeAnnotationTags[i]] = context;
- }
+ annotationView.annotation = annotation;
+ CGRect bounds = UIEdgeInsetsInsetRect({ CGPointZero, annotationView.frame.size }, annotationView.alignmentRectInsets);
+ _largestAnnotationViewSize = CGSizeMake(bounds.size.width / 2.0, bounds.size.height / 2.0);
}
- [self didChangeValueForKey:@"annotations"];
+ return annotationView;
}
- (double)alphaForShapeAnnotation:(MGLShape *)annotation
@@ -2499,6 +3027,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage
{
+ NSString *iconIdentifier = annotationImage.styleIconIdentifier;
+ self.annotationImagesByIdentifier[annotationImage.reuseIdentifier] = annotationImage;
+ annotationImage.delegate = self;
+
// retrieve pixels
CGImageRef image = annotationImage.image.CGImage;
size_t width = CGImageGetWidth(image);
@@ -2519,8 +3051,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
float(annotationImage.image.scale));
// sprite upload
- NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
- _mbglMap->addAnnotationIcon(symbolName.UTF8String, cSpriteImage);
+ _mbglMap->addAnnotationIcon(iconIdentifier.UTF8String, cSpriteImage);
// Create a slop area with a “radius” equal in size to the annotation
// image’s alignment rect, allowing the eventual tap to be on any point
@@ -2546,8 +3077,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ( ! annotations) return;
- std::vector<MGLAnnotationTag> annotationTagsToRemove;
- annotationTagsToRemove.reserve(annotations.count);
+ [self willChangeValueForKey:@"annotations"];
for (id <MGLAnnotation> annotation in annotations)
{
@@ -2558,7 +3088,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
continue;
}
- annotationTagsToRemove.push_back(annotationTag);
+
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+ [annotationView removeFromSuperview];
if (annotationTag == _selectedAnnotationTag)
{
@@ -2566,14 +3099,17 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
_annotationContextsByAnnotationTag.erase(annotationTag);
- }
+
+ if ([annotation isKindOfClass:[NSObject class]] && ![annotation isKindOfClass:[MGLMultiPoint class]])
+ {
+ [(NSObject *)annotation removeObserver:self forKeyPath:@"coordinate" context:(void *)(NSUInteger)annotationTag];
+ }
- if ( ! annotationTagsToRemove.empty())
- {
- [self willChangeValueForKey:@"annotations"];
- _mbglMap->removeAnnotations(annotationTagsToRemove);
- [self didChangeValueForKey:@"annotations"];
+ _mbglMap->removeAnnotation(annotationTag);
}
+
+ [self didChangeValueForKey:@"annotations"];
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
- (void)addOverlay:(id <MGLOverlay>)overlay
@@ -2583,10 +3119,12 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays
{
+#if DEBUG
for (id <MGLOverlay> overlay in overlays)
{
NSAssert([overlay conformsToProtocol:@protocol(MGLOverlay)], @"overlay should conform to MGLOverlay");
}
+#endif
[self addAnnotations:overlays];
}
@@ -2598,23 +3136,48 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays
{
+#if DEBUG
for (id <MGLOverlay> overlay in overlays)
{
NSAssert([overlay conformsToProtocol:@protocol(MGLOverlay)], @"overlay should conform to MGLOverlay");
}
+#endif
[self removeAnnotations:overlays];
}
- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier
{
- // This prefix is used to avoid collisions with style-defined sprites in
- // mbgl, but reusable identifiers are never prefixed.
- if ([identifier hasPrefix:MGLAnnotationSpritePrefix])
+ return self.annotationImagesByIdentifier[identifier];
+}
+
+- (nullable MGLAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier
+{
+ NSMutableArray *annotationViewReuseQueue = [self annotationViewReuseQueueForIdentifier:identifier];
+ MGLAnnotationView *reusableView = annotationViewReuseQueue.firstObject;
+ [reusableView prepareForReuse];
+ [annotationViewReuseQueue removeObject:reusableView];
+
+ return reusableView;
+}
+
+- (MGLAnnotationView *)annotationViewAtPoint:(CGPoint)point
+{
+ std::vector<MGLAnnotationTag> annotationTags = [self annotationTagsInRect:self.bounds];
+
+ for(auto const& annotationTag: annotationTags)
{
- identifier = [identifier substringFromIndex:MGLAnnotationSpritePrefix.length];
+ auto &annotationContext = _annotationContextsByAnnotationTag[annotationTag];
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+ CGPoint convertedPoint = [self convertPoint:point toView:annotationView];
+
+ if ([annotationView pointInside:convertedPoint withEvent:nil])
+ {
+ return annotationView;
+ }
}
- return self.annotationImagesByIdentifier[identifier];
+
+ return nil;
}
/**
@@ -2648,6 +3211,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
-MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
+ MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName];
+ UIImage *fallbackImage = fallbackAnnotationImage.image;
+
// Filter out any annotation whose image is unselectable or for which
// hit testing fails.
auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(),
@@ -2656,6 +3222,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag];
NSAssert(annotation, @"Unknown annotation found nearby tap");
+ MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag];
+
MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
if ( ! annotationImage.enabled)
{
@@ -2664,10 +3232,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// Filter out the annotation if the fattened finger didn’t land
// within the image’s alignment rect.
- CGRect annotationRect = [self frameOfImage:annotationImage.image
- centeredAtCoordinate:annotation.coordinate];
+ CGRect annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate];
+
return !!!CGRectIntersectsRect(annotationRect, hitRect);
});
+
nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end));
}
@@ -2732,9 +3301,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
// Choose the first nearby annotation.
- if (_annotationsNearbyLastTap.size())
+ if (nearbyAnnotations.size())
{
- hitAnnotationTag = _annotationsNearbyLastTap.front();
+ hitAnnotationTag = nearbyAnnotations.front();
}
}
}
@@ -2773,7 +3342,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations
{
- return (self.selectedAnnotation ? @[ self.selectedAnnotation ] : @[]);
+ id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
+ return (selectedAnnotation ? @[ selectedAnnotation ] : @[]);
}
- (void)setSelectedAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations
@@ -2813,10 +3383,29 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (annotationTag == MGLAnnotationTagNotFound && annotation != self.userLocation)
{
[self addAnnotation:annotation];
+ annotationTag = [self annotationTagForAnnotation:annotation];
+ if (annotationTag == MGLAnnotationTagNotFound) return;
}
- // The annotation can’t be selected if no part of it is hittable.
+ // By default attempt to use the GL annotation image frame as the positioning rect.
CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
+
+ if (annotation != self.userLocation)
+ {
+ MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
+
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+ if (annotationView)
+ {
+ // Annotations represented by views use the view frame as the positioning rect.
+ positioningRect = annotationView.frame;
+
+ [annotationView.superview bringSubviewToFront:annotationView];
+ }
+ }
+
+ // The client can request that any annotation be selected (even ones that are offscreen).
+ // The annotation can’t be selected if no part of it is hittable.
if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation)
{
return;
@@ -2830,59 +3419,62 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.delegate mapView:self annotationCanShowCallout:annotation])
{
// build the callout
+ UIView <MGLCalloutView> *calloutView;
if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)])
{
- self.calloutViewForSelectedAnnotation = [self.delegate mapView:self calloutViewForAnnotation:annotation];
+ calloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation];
}
- if (!self.calloutViewForSelectedAnnotation)
+ if (!calloutView)
{
- self.calloutViewForSelectedAnnotation = [self calloutViewForAnnotation:annotation];
+ calloutView = [self calloutViewForAnnotation:annotation];
}
+ self.calloutViewForSelectedAnnotation = calloutView;
if (_userLocationAnnotationIsSelected)
{
- positioningRect = CGRectInset(self.userLocationAnnotationView.frame,
- -MGLAnnotationImagePaddingForCallout,
- -MGLAnnotationImagePaddingForCallout);
+ positioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame];
+
+ CGRect implicitAnnotationFrame = [self.userLocationAnnotationView.layer.presentationLayer frame];
+ CGRect explicitAnnotationFrame = self.userLocationAnnotationView.frame;
+ _initialImplicitCalloutViewOffset = CGPointMake(CGRectGetMinX(explicitAnnotationFrame) - CGRectGetMinX(implicitAnnotationFrame),
+ CGRectGetMinY(explicitAnnotationFrame) - CGRectGetMinY(implicitAnnotationFrame));
}
// consult delegate for left and/or right accessory views
if ([self.delegate respondsToSelector:@selector(mapView:leftCalloutAccessoryViewForAnnotation:)])
{
- self.calloutViewForSelectedAnnotation.leftAccessoryView =
- [self.delegate mapView:self leftCalloutAccessoryViewForAnnotation:annotation];
+ calloutView.leftAccessoryView = [self.delegate mapView:self leftCalloutAccessoryViewForAnnotation:annotation];
- if ([self.calloutViewForSelectedAnnotation.leftAccessoryView isKindOfClass:[UIControl class]])
+ if ([calloutView.leftAccessoryView isKindOfClass:[UIControl class]])
{
UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleCalloutAccessoryTapGesture:)];
- [self.calloutViewForSelectedAnnotation.leftAccessoryView addGestureRecognizer:calloutAccessoryTap];
+ [calloutView.leftAccessoryView addGestureRecognizer:calloutAccessoryTap];
}
}
if ([self.delegate respondsToSelector:@selector(mapView:rightCalloutAccessoryViewForAnnotation:)])
{
- self.calloutViewForSelectedAnnotation.rightAccessoryView =
- [self.delegate mapView:self rightCalloutAccessoryViewForAnnotation:annotation];
+ calloutView.rightAccessoryView = [self.delegate mapView:self rightCalloutAccessoryViewForAnnotation:annotation];
- if ([self.calloutViewForSelectedAnnotation.rightAccessoryView isKindOfClass:[UIControl class]])
+ if ([calloutView.rightAccessoryView isKindOfClass:[UIControl class]])
{
UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleCalloutAccessoryTapGesture:)];
- [self.calloutViewForSelectedAnnotation.rightAccessoryView addGestureRecognizer:calloutAccessoryTap];
+ [calloutView.rightAccessoryView addGestureRecognizer:calloutAccessoryTap];
}
}
// set annotation delegate to handle taps on the callout view
- self.calloutViewForSelectedAnnotation.delegate = self;
+ calloutView.delegate = self;
// present popup
- [self.calloutViewForSelectedAnnotation presentCalloutFromRect:positioningRect
- inView:self.glView
- constrainedToView:self.glView
- animated:animated];
+ [calloutView presentCalloutFromRect:positioningRect
+ inView:self.glView
+ constrainedToView:self.glView
+ animated:animated];
}
// notify delegate
@@ -2906,6 +3498,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
/// and is appropriate for positioning a popover.
- (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annotationTag
{
+ MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag[annotationTag];
+
id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag];
if ( ! annotation)
{
@@ -2914,11 +3508,16 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
UIImage *image = [self imageOfAnnotationWithTag:annotationTag].image;
if ( ! image)
{
+ image = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName].image;
+ }
+ if ( ! image)
+ {
return CGRectZero;
}
CGRect positioningRect = [self frameOfImage:image centeredAtCoordinate:annotation.coordinate];
positioningRect.origin.x -= 0.5;
+
return CGRectInset(positioningRect, -MGLAnnotationImagePaddingForCallout,
-MGLAnnotationImagePaddingForCallout);
}
@@ -2941,7 +3540,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
return nil;
}
- NSString *customSymbol = _annotationContextsByAnnotationTag.at(annotationTag).symbolIdentifier;
+ NSString *customSymbol = _annotationContextsByAnnotationTag.at(annotationTag).imageReuseIdentifier;
NSString *symbolName = customSymbol.length ? customSymbol : MGLDefaultStyleMarkerSymbolName;
return [self dequeueReusableAnnotationImageWithIdentifier:symbolName];
@@ -2951,7 +3550,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
if ( ! annotation) return;
- if ([self.selectedAnnotation isEqual:annotation])
+ if (self.selectedAnnotation == annotation)
{
// dismiss popup
[self.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated];
@@ -2968,6 +3567,35 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
}
+- (void)calloutViewWillAppear:(UIView <MGLCalloutView> *)calloutView
+{
+ if (_userLocationAnnotationIsSelected ||
+ CGPointEqualToPoint(_initialImplicitCalloutViewOffset, CGPointZero))
+ {
+ return;
+ }
+
+ // The user location callout view initially points to the user location
+ // annotation’s implicit (visual) frame, which is offset from the
+ // annotation’s explicit frame. Now the callout view needs to rendezvous
+ // with the explicit frame. Then,
+ // -updateUserLocationAnnotationViewAnimatedWithDuration: will take over the
+ // next time an updated location arrives.
+ [UIView animateWithDuration:_userLocationAnimationCompletionDate.timeIntervalSinceNow
+ delay:0
+ options:(UIViewAnimationOptionCurveLinear |
+ UIViewAnimationOptionAllowUserInteraction |
+ UIViewAnimationOptionBeginFromCurrentState)
+ animations:^
+ {
+ calloutView.frame = CGRectOffset(calloutView.frame,
+ _initialImplicitCalloutViewOffset.x,
+ _initialImplicitCalloutViewOffset.y);
+ _initialImplicitCalloutViewOffset = CGPointZero;
+ }
+ completion:NULL];
+}
+
- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated
{
CGFloat maximumPadding = 100;
@@ -3006,11 +3634,53 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)annotationImageNeedsRedisplay:(MGLAnnotationImage *)annotationImage
{
- // remove sprite
- NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
- _mbglMap->removeAnnotationIcon(symbolName.UTF8String);
- [self installAnnotationImage:annotationImage];
- _mbglMap->update(mbgl::Update::Annotations);
+ NSString *reuseIdentifier = annotationImage.reuseIdentifier;
+ NSString *iconIdentifier = annotationImage.styleIconIdentifier;
+ NSString *fallbackReuseIdentifier = MGLDefaultStyleMarkerSymbolName;
+ NSString *fallbackIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:fallbackReuseIdentifier];
+
+ // Remove the old icon from the style.
+ if ( ! [iconIdentifier isEqualToString:fallbackIconIdentifier]) {
+ _mbglMap->removeAnnotationIcon(iconIdentifier.UTF8String);
+ }
+
+ if (annotationImage.image)
+ {
+ // Add the new icon to the style.
+ NSString *updatedIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
+ annotationImage.styleIconIdentifier = updatedIconIdentifier;
+ [self installAnnotationImage:annotationImage];
+
+ if ([iconIdentifier isEqualToString:fallbackIconIdentifier])
+ {
+ // Update any annotations associated with the annotation image.
+ [self applyIconIdentifier:updatedIconIdentifier toAnnotationsWithImageReuseIdentifier:reuseIdentifier];
+ }
+ }
+ else
+ {
+ // Add the default icon to the style if necessary.
+ annotationImage.styleIconIdentifier = fallbackIconIdentifier;
+ if ( ! [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName])
+ {
+ [self installAnnotationImage:self.defaultAnnotationImage];
+ }
+
+ // Update any annotations associated with the annotation image.
+ [self applyIconIdentifier:fallbackIconIdentifier toAnnotationsWithImageReuseIdentifier:reuseIdentifier];
+ }
+}
+
+- (void)applyIconIdentifier:(NSString *)iconIdentifier toAnnotationsWithImageReuseIdentifier:(NSString *)reuseIdentifier
+{
+ for (auto &pair : _annotationContextsByAnnotationTag)
+ {
+ if ([pair.second.imageReuseIdentifier isEqualToString:reuseIdentifier])
+ {
+ const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(pair.second.annotation.coordinate);
+ _mbglMap->updateAnnotation(pair.first, mbgl::SymbolAnnotation { point, iconIdentifier.UTF8String ?: "" });
+ }
+ }
}
#pragma mark - User Location -
@@ -3149,7 +3819,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
MGLUserTrackingMode oldMode = _userTrackingMode;
+ [self willChangeValueForKey:@"userTrackingMode"];
_userTrackingMode = mode;
+ [self didChangeValueForKey:@"userTrackingMode"];
switch (_userTrackingMode)
{
@@ -3173,9 +3845,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.locationManager stopUpdatingHeading];
- if (self.userLocationAnnotationView)
+ CLLocation *location = self.userLocation.location;
+ if (location && self.userLocationAnnotationView)
{
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
}
break;
@@ -3223,7 +3896,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
_userLocationVerticalAlignment = alignment;
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
+ CLLocation *location = self.userLocation.location;
+ if (location)
+ {
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
+ }
}
}
@@ -3280,7 +3957,19 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
self.userLocationAnnotationView.haloLayer.hidden = ! CLLocationCoordinate2DIsValid(self.userLocation.coordinate) ||
newLocation.horizontalAccuracy > 10;
- [self updateUserLocationAnnotationView];
+ NSTimeInterval duration = MGLAnimationDuration;
+ if (oldLocation && ! CGPointEqualToPoint(self.userLocationAnnotationView.center, CGPointZero))
+ {
+ duration = MIN([newLocation.timestamp timeIntervalSinceDate:oldLocation.timestamp], MGLUserLocationAnimationDuration);
+ }
+ [self updateUserLocationAnnotationViewAnimatedWithDuration:duration];
+
+ if (self.userTrackingMode == MGLUserTrackingModeNone &&
+ self.userLocationAnnotationView.accessibilityElementIsFocused &&
+ [UIApplication sharedApplication].applicationState == UIApplicationStateActive)
+ {
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, self.userLocationAnnotationView);
+ }
}
- (void)didUpdateLocationWithUserTrackingAnimated:(BOOL)animated
@@ -3541,6 +4230,57 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
}
+#pragma mark Data
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point
+{
+ return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil];
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers
+{
+ mbgl::ScreenCoordinate screenCoordinate = { point.x, point.y };
+
+ mbgl::optional<std::vector<std::string>> optionalLayerIDs;
+ if (styleLayerIdentifiers)
+ {
+ __block std::vector<std::string> layerIDs;
+ layerIDs.reserve(styleLayerIdentifiers.count);
+ [styleLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop)
+ {
+ layerIDs.push_back(identifier.UTF8String);
+ }];
+ optionalLayerIDs = layerIDs;
+ }
+
+ std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenCoordinate, optionalLayerIDs);
+ return MGLFeaturesFromMBGLFeatures(features);
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect {
+ return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil];
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+ mbgl::ScreenBox screenBox = {
+ { CGRectGetMinX(rect), CGRectGetMinY(rect) },
+ { CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
+ };
+
+ mbgl::optional<std::vector<std::string>> optionalLayerIDs;
+ if (styleLayerIdentifiers) {
+ __block std::vector<std::string> layerIDs;
+ layerIDs.reserve(styleLayerIdentifiers.count);
+ [styleLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
+ layerIDs.push_back(identifier.UTF8String);
+ }];
+ optionalLayerIDs = layerIDs;
+ }
+
+ std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenBox, optionalLayerIDs);
+ return MGLFeaturesFromMBGLFeatures(features);
+}
+
#pragma mark - Utility -
- (void)animateWithDelay:(NSTimeInterval)delay animations:(void (^)(void))animations
@@ -3652,6 +4392,10 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)])
{
+ if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
+ {
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+ }
BOOL animated = change == mbgl::MapChangeRegionDidChangeAnimated;
[self.delegate mapView:self regionDidChangeAnimated:animated];
}
@@ -3714,6 +4458,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
[self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:(change == mbgl::MapChangeDidFinishRenderingFrameFullyRendered)];
}
+ [self updateAnnotationViews];
break;
}
}
@@ -3721,14 +4466,81 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)updateUserLocationAnnotationView
{
+ [self updateUserLocationAnnotationViewAnimatedWithDuration:0];
+}
+
+- (void)updateAnnotationViews
+{
+ BOOL delegateImplementsViewForAnnotation = [self.delegate respondsToSelector:@selector(mapView:viewForAnnotation:)];
+
+ if (!delegateImplementsViewForAnnotation)
+ {
+ return;
+ }
+
+ for (auto &pair : _annotationContextsByAnnotationTag)
+ {
+ CGRect viewPort = CGRectInset(self.bounds, -_largestAnnotationViewSize.width - MGLAnnotationUpdateViewportOutset.width, -_largestAnnotationViewSize.height - MGLAnnotationUpdateViewportOutset.width);
+
+ MGLAnnotationContext &annotationContext = pair.second;
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+
+ if (!annotationView)
+ {
+ MGLAnnotationView *annotationView = [self annotationViewForAnnotation:annotationContext.annotation];
+ if (annotationView)
+ {
+ // If the annotation view has no superview it means it was never used before so add it
+ if (!annotationView.superview)
+ {
+ [self.glView addSubview:annotationView];
+ }
+
+ CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
+ [annotationView setCenter:center pitch:self.camera.pitch];
+
+ annotationContext.annotationView = annotationView;
+ }
+ }
+
+ bool annotationViewIsVisible = CGRectContainsRect(viewPort, annotationView.frame);
+ if (!annotationViewIsVisible)
+ {
+ [self enqueueAnnotationViewForAnnotationContext:annotationContext];
+ }
+ else
+ {
+ CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
+ [annotationView setCenter:center pitch:self.camera.pitch];
+ }
+ }
+}
+
+- (void)enqueueAnnotationViewForAnnotationContext:(MGLAnnotationContext &)annotationContext
+{
+ MGLAnnotationView *annotationView = annotationContext.annotationView;
+
+ if (!annotationView) return;
+
+ if (annotationContext.viewReuseIdentifier)
+ {
+ NSMutableArray *annotationViewReuseQueue = [self annotationViewReuseQueueForIdentifier:annotationContext.viewReuseIdentifier];
+ if (![annotationViewReuseQueue containsObject:annotationView])
+ {
+ [annotationViewReuseQueue addObject:annotationView];
+ annotationContext.annotationView = nil;
+ }
+ }
+}
+
+- (void)updateUserLocationAnnotationViewAnimatedWithDuration:(NSTimeInterval)duration
+{
MGLUserLocationAnnotationView *annotationView = self.userLocationAnnotationView;
if ( ! CLLocationCoordinate2DIsValid(self.userLocation.coordinate)) {
annotationView.hidden = YES;
return;
}
- if ( ! annotationView.superview) [self.glView addSubview:annotationView];
-
CGPoint userPoint;
if (self.userTrackingMode != MGLUserTrackingModeNone
&& self.userTrackingState == MGLUserTrackingStateChanged)
@@ -3739,11 +4551,36 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
userPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self];
}
+
+ if ( ! annotationView.superview)
+ {
+ [self.glView addSubview:annotationView];
+ // Prevents the view from sliding in from the origin.
+ annotationView.center = userPoint;
+ }
if (CGRectContainsPoint(CGRectInset(self.bounds, -MGLAnnotationUpdateViewportOutset.width,
-MGLAnnotationUpdateViewportOutset.height), userPoint))
{
- annotationView.center = userPoint;
+ // Smoothly move the user location annotation view and callout view to
+ // the new location.
+ [UIView animateWithDuration:duration
+ delay:0
+ options:(UIViewAnimationOptionCurveLinear |
+ UIViewAnimationOptionAllowUserInteraction |
+ UIViewAnimationOptionBeginFromCurrentState)
+ animations:^{
+ if (self.selectedAnnotation == self.userLocation)
+ {
+ UIView <MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation;
+ calloutView.frame = CGRectOffset(calloutView.frame,
+ userPoint.x - annotationView.center.x,
+ userPoint.y - annotationView.center.y);
+ }
+ annotationView.center = userPoint;
+ } completion:NULL];
+ _userLocationAnimationCompletionDate = [NSDate dateWithTimeIntervalSinceNow:duration];
+
annotationView.hidden = NO;
[annotationView setupLayers];
@@ -3802,11 +4639,14 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
- (void)updateCompass
{
- CLLocationDirection degrees = mbgl::util::wrap(-self.direction, 0., 360.);
-
- self.compassView.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(degrees));
+ CLLocationDirection direction = self.direction;
+ CLLocationDirection plateDirection = mbgl::util::wrap(-direction, 0., 360.);
+ self.compassView.transform = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(plateDirection));
+
+ self.compassView.isAccessibilityElement = direction > 0;
+ self.compassView.accessibilityValue = [_accessibilityCompassFormatter stringFromDirection:direction];
- if (_mbglMap->getBearing() && self.compassView.alpha < 1)
+ if (direction > 0 && self.compassView.alpha < 1)
{
[UIView animateWithDuration:MGLAnimationDuration
delay:0
@@ -3817,7 +4657,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
completion:nil];
}
- else if (_mbglMap->getBearing() == 0 && self.compassView.alpha > 0)
+ else if (direction == 0 && self.compassView.alpha > 0)
{
[UIView animateWithDuration:MGLAnimationDuration
delay:0
@@ -3868,7 +4708,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// Headline
UILabel *headlineLabel = [[UILabel alloc] init];
- headlineLabel.text = @"MGLMapView";
+ headlineLabel.text = NSStringFromClass([self class]);
headlineLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
headlineLabel.textAlignment = NSTextAlignmentCenter;
headlineLabel.numberOfLines = 1;
@@ -3879,8 +4719,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// Explanation
UILabel *explanationLabel = [[UILabel alloc] init];
- explanationLabel.text = (@"To display a Mapbox-hosted map here, set MGLMapboxAccessToken to your access token in Info.plist\n\n"
- @"For detailed instructions, see:");
+ explanationLabel.text = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"DESIGNABLE", nil, nil, @"To display a Mapbox-hosted map here, set %@ to your access token in %@\n\nFor detailed instructions, see:", @"Instructions in Interface Builder designable; {key}, {plist file name}"), @"MGLMapboxAccessToken", @"Info.plist"];
explanationLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
explanationLabel.numberOfLines = 0;
explanationLabel.translatesAutoresizingMaskIntoConstraints = NO;
@@ -3890,7 +4729,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
// Link
UIButton *linkButton = [UIButton buttonWithType:UIButtonTypeSystem];
- [linkButton setTitle:MGLMapboxSetupDocumentationURLDisplayString forState:UIControlStateNormal];
+ [linkButton setTitle:NSLocalizedStringWithDefaultValue(@"FIRST_STEPS_URL", nil, nil, @"mapbox.com/help/first-steps-ios-sdk", @"Setup documentation URL display string; keep as short as possible") forState:UIControlStateNormal];
linkButton.translatesAutoresizingMaskIntoConstraints = NO;
linkButton.titleLabel.numberOfLines = 0;
[linkButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
@@ -3955,14 +4794,21 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
views:views]];
}
+- (NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *)annotationViewReuseQueueForIdentifier:(NSString *)identifier {
+ if (!_annotationViewReuseQueueByIdentifier[identifier])
+ {
+ _annotationViewReuseQueueByIdentifier[identifier] = [NSMutableArray array];
+ }
+
+ return _annotationViewReuseQueueByIdentifier[identifier];
+}
+
class MBGLView : public mbgl::View
{
- public:
- MBGLView(MGLMapView* nativeView_, const float scaleFactor_)
- : nativeView(nativeView_), scaleFactor(scaleFactor_) {
- }
- virtual ~MBGLView() {}
-
+public:
+ MBGLView(MGLMapView* nativeView_, const float scaleFactor_)
+ : nativeView(nativeView_), scaleFactor(scaleFactor_) {
+ }
float getPixelRatio() const override {
return scaleFactor;
@@ -3970,7 +4816,7 @@ class MBGLView : public mbgl::View
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>([nativeView bounds].size.width),
- static_cast<uint16_t>([nativeView bounds].size.height) }};
+ static_cast<uint16_t>([nativeView bounds].size.height) }};
}
std::array<uint16_t, 2> getFramebufferSize() const override {
@@ -3978,15 +4824,14 @@ class MBGLView : public mbgl::View
static_cast<uint16_t>([[nativeView glView] drawableHeight]) }};
}
- void notify() override
+ void notifyMapChange(mbgl::MapChange change) override
{
- // no-op
+ [nativeView notifyMapChange:change];
}
- void notifyMapChange(mbgl::MapChange change) override
+ void invalidate() override
{
- assert([[NSThread currentThread] isMainThread]);
- [nativeView notifyMapChange:change];
+ [nativeView setNeedsGLDisplay];
}
void activate() override
@@ -3999,26 +4844,9 @@ class MBGLView : public mbgl::View
[EAGLContext setCurrentContext:nil];
}
- void invalidate() override
- {
- [nativeView performSelectorOnMainThread:@selector(setNeedsGLDisplay)
- withObject:nil
- waitUntilDone:NO];
- }
-
- void beforeRender() override
- {
- // no-op
- }
-
- void afterRender() override
- {
- // no-op
- }
-
- private:
- __weak MGLMapView *nativeView = nullptr;
- const float scaleFactor;
+private:
+ __weak MGLMapView *nativeView = nullptr;
+ const float scaleFactor;
};
@end
@@ -4188,7 +5016,7 @@ void MGLPrepareCustomStyleLayer(void *context)
}
}
-void MGLDrawCustomStyleLayer(void *context, const mbgl::CustomLayerRenderParameters &params)
+void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRenderParameters &params)
{
CGSize size = CGSizeMake(params.width, params.height);
CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude);
@@ -4220,14 +5048,14 @@ void MGLFinishCustomStyleLayer(void *context)
{
NSAssert(identifier, @"Style layer needs an identifier");
MGLCustomStyleLayerHandlers *context = new MGLCustomStyleLayerHandlers(preparation, drawing, completion);
- _mbglMap->addCustomLayer(identifier.UTF8String, MGLPrepareCustomStyleLayer,
- MGLDrawCustomStyleLayer, MGLFinishCustomStyleLayer,
- context, otherIdentifier.UTF8String);
+ _mbglMap->addLayer(std::make_unique<mbgl::style::CustomLayer>(identifier.UTF8String, MGLPrepareCustomStyleLayer,
+ MGLDrawCustomStyleLayer, MGLFinishCustomStyleLayer, context),
+ otherIdentifier ? mbgl::optional<std::string>(otherIdentifier.UTF8String) : mbgl::optional<std::string>());
}
- (void)removeCustomStyleLayerWithIdentifier:(NSString *)identifier
{
- _mbglMap->removeCustomLayer(identifier.UTF8String);
+ _mbglMap->removeLayer(identifier.UTF8String);
}
- (void)setCustomStyleLayersNeedDisplay
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
new file mode 100644
index 0000000000..39eb43d4ca
--- /dev/null
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -0,0 +1,284 @@
+#import <UIKit/UIKit.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class MGLMapView;
+
+/** The MGLMapViewDelegate protocol defines a set of optional methods that you can use to receive map-related update messages. Because many map operations require the `MGLMapView` class to load data asynchronously, the map view calls these methods to notify your application when specific operations complete. The map view also uses these methods to request annotation marker symbology and to manage interactions with those markers. */
+@protocol MGLMapViewDelegate <NSObject>
+
+@optional
+
+#pragma mark Responding to Map Position Changes
+
+/**
+ Tells the delegate that the region displayed by the map view is about to change.
+
+ This method is called whenever the currently displayed map region will start changing.
+
+ @param mapView The map view whose visible region will change.
+ @param animated Whether the change will cause an animated effect on the map.
+ */
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
+
+/**
+ Tells the delegate that the region displayed by the map view is changing.
+
+ This method is called whenever the currently displayed map region changes. During movement, this method may be called many times to report updates to the map position. Therefore, your implementation of this method should be as lightweight as possible to avoid affecting performance.
+
+ @param mapView The map view whose visible region is changing.
+ */
+- (void)mapViewRegionIsChanging:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the region displayed by the map view just changed.
+
+ This method is called whenever the currently displayed map region has finished changing.
+
+ @param mapView The map view whose visible region changed.
+ @param animated Whether the change caused an animated effect on the map.
+ */
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
+
+#pragma mark Loading the Map
+
+/**
+ Tells the delegate that the map view will begin to load.
+
+ This method is called whenever the map view starts loading, including when a new style has been set and the map must reload.
+
+ @param mapView The map view that is starting to load.
+ */
+- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the map view has finished loading.
+
+ This method is called whenever the map view finishes loading, either after the initial load or after a style change has forced a reload.
+
+ @param mapView The map view that has finished loading.
+ */
+- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFailLoadingMap:(MGLMapView *)mapView withError:(NSError *)error;
+
+// TODO
+- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+
+// TODO
+- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
+
+// TODO
+- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+
+#pragma mark Tracking User Location
+
+/**
+ Tells the delegate that the map view will begin tracking the user's location.
+
+ This method is called when the value of the `showsUserLocation` property changes to `YES`.
+
+ @param mapView The map view that is tracking the user's location.
+ */
+- (void)mapViewWillStartLocatingUser:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the map view has stopped tracking the user's location.
+
+ This method is called when the value of the `showsUserLocation` property changes to `NO`.
+
+ @param mapView The map view that is tracking the user's location.
+ */
+- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the location of the user was updated.
+
+ While the `showsUserLocation` property is set to `YES`, this method is called whenever a new location update is received by the map view. This method is also called if the map view's user tracking mode is set to `MGLUserTrackingModeFollowWithHeading` and the heading changes, or if it is set to `MGLUserTrackingModeFollowWithCourse` and the course changes.
+
+ This method is not called if the application is currently running in the background. If you want to receive location updates while running in the background, you must use the Core Location framework.
+
+ @param mapView The map view that is tracking the user's location.
+ @param userLocation The location object representing the user's latest location. This property may be `nil`.
+ */
+- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(nullable MGLUserLocation *)userLocation;
+
+/**
+ Tells the delegate that an attempt to locate the user's position failed.
+
+ @param mapView The map view that is tracking the user's location.
+ @param error An error object containing the reason why location tracking failed.
+ */
+- (void)mapView:(MGLMapView *)mapView didFailToLocateUserWithError:(NSError *)error;
+
+/**
+ Tells the delegate that the map view's user tracking mode has changed.
+
+ This method is called after the map view asynchronously changes to reflect the new user tracking mode, for example by beginning to zoom or rotate.
+
+ @param mapView The map view that changed its tracking mode.
+ @param mode The new tracking mode.
+ @param animated Whether the change caused an animated effect on the map.
+ */
+- (void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
+
+#pragma mark Managing the Display of Annotations
+
+/**
+ Returns a view object to use for the marker for the specified point annotation object.
+
+ @param mapView The map view that requested the annotation view.
+ @param annotation The object representing the annotation that is about to be displayed.
+ @return The view object to display for the specified annotation or `nil` if you want to display the default marker image.
+ */
+- (nullable MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns an image object to use for the marker for the specified point annotation object.
+
+ @param mapView The map view that requested the annotation image.
+ @param annotation The object representing the annotation that is about to be displayed.
+ @return The image object to display for the specified annotation or `nil` if you want to display the default marker image.
+ */
+- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns the alpha value to use when rendering a shape annotation. Defaults to `1.0`.
+
+ @param mapView The map view rendering the shape annotation.
+ @param annotation The annotation being rendered.
+ @return An alpha value between `0` and `1.0`.
+ */
+- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
+
+/**
+ Returns the stroke color to use when rendering a shape annotation. Defaults to the map view’s tint color.
+
+ @param mapView The map view rendering the shape annotation.
+ @param annotation The annotation being rendered.
+ @return A color to use for the shape outline.
+ */
+- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
+
+/**
+ Returns the fill color to use when rendering a polygon annotation. Defaults to the map view’s tint color.
+
+ @param mapView The map view rendering the polygon annotation.
+ @param annotation The annotation being rendered.
+ @return A color to use for the polygon interior.
+ */
+- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
+
+/**
+ Returns the line width to use when rendering a polyline annotation. Defaults to `3.0`.
+
+ @param mapView The map view rendering the polygon annotation.
+ @param annotation The annotation being rendered.
+ @return A line width for the polyline.
+ */
+- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation;
+
+/**
+ Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble.
+
+ If the value returned is `YES`, a standard callout bubble is shown when the user taps a selected annotation. The callout uses the title and subtitle text from the associated annotation object. If there is no title text, though, the annotation will not show a callout. The callout also displays any custom callout views returned by the delegate for the left and right callout accessory views.
+
+ If the value returned is `NO`, the value of the title and subtitle strings are ignored.
+
+ @param mapView The map view that requested the annotation callout ability.
+ @param annotation The object representing the annotation.
+ @return A Boolean indicating whether the annotation should show a callout.
+ */
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns a callout view to display for the specified annotation.
+
+ If this method is present in the delegate, it must return a new instance of a view dedicated to display the callout bubble. It will be configured by the map view. If this method is not present, or if it returns `nil`, a standard, two-line, bubble-like callout view is displayed by default.
+
+ @param mapView The map view that requested the callout view.
+ @param annotation The object representing the annotation.
+ @return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the default callout view.
+ */
+- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns the view to display on the left side of the standard callout bubble.
+
+ The default value is treated as if `nil`. The left callout view is typically used to display information about the annotation or to link to custom information provided by your application.
+
+ If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
+
+ @param mapView The map view presenting the annotation callout.
+ @param annotation The object representing the annotation with the callout.
+ @return The accessory view to display.
+ */
+- (nullable UIView *)mapView:(MGLMapView *)mapView leftCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns the view to display on the right side of the standard callout bubble.
+
+ The default value is treated is if `nil`. The right callout view is typically used to link to more detailed information about the annotation. A common view to specify for this property is `UIButton` object whose type is set to `UIButtonTypeDetailDisclosure`.
+
+ If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
+
+ @param mapView The map view presenting the annotation callout.
+ @param annotation The object representing the annotation with the callout.
+ @return The accessory view to display.
+ */
+- (nullable UIView *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
+
+#pragma mark Managing Annotations
+
+/**
+ Tells the delegate that the user tapped one of the annotation's accessory buttons.
+
+ Accessory views contain custom content and are positioned on either side of the annotation title text. If a view you specify is a descendant of the `UIControl` class, the map view calls this method as a convenience whenever the user taps your view. You can use this method to respond to taps and perform any actions associated with that control. For example, if your control displayed additional information about the annotation, you could use this method to present a modal panel with that information.
+
+ If your custom accessory views are not descendants of the `UIControl` class, the map view does not call this method.
+
+ @param mapView The map view containing the specified annotation.
+ @param annotation The annotation whose button was tapped.
+ @param control The control that was tapped.
+ */
+- (void)mapView:(MGLMapView *)mapView annotation:(id <MGLAnnotation>)annotation calloutAccessoryControlTapped:(UIControl *)control;
+
+/**
+ Tells the delegate that the user tapped on an annotation's callout view.
+
+ @param mapView The map view containing the specified annotation.
+ @param annotation The annotation whose callout was tapped.
+ */
+- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotation>)annotation;
+
+#pragma mark Selecting Annotations
+
+/**
+ Tells the delegate that one of its annotations was selected.
+
+ You can use this method to track changes in the selection state of annotations.
+
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was selected.
+ */
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Tells the delegate that one of its annotations was deselected.
+
+ You can use this method to track changes in the selection state of annotations.
+
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was deselected.
+ */
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLMapView_Internal.h b/platform/ios/src/MGLMapView_Internal.h
new file mode 100644
index 0000000000..6225e11749
--- /dev/null
+++ b/platform/ios/src/MGLMapView_Internal.h
@@ -0,0 +1,17 @@
+#import <Mapbox/Mapbox.h>
+
+/// Minimum size of an annotation’s accessibility element.
+extern const CGSize MGLAnnotationAccessibilityElementMinimumSize;
+
+@interface MGLMapView (Internal)
+
+/** Triggers another render pass even when it is not necessary. */
+- (void)setNeedsGLDisplay;
+
+/** Returns whether the map view is currently loading or processing any assets required to render the map */
+- (BOOL)isFullyLoaded;
+
+/** Empties the in-memory tile cache. */
+- (void)didReceiveMemoryWarning;
+
+@end
diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m
index 2dc7fee280..5ddf4e2b57 100644
--- a/platform/ios/src/MGLMapboxEvents.m
+++ b/platform/ios/src/MGLMapboxEvents.m
@@ -123,7 +123,7 @@ const NSTimeInterval MGLFlushInterval = 180;
@property (nonatomic) MGLLocationManager *locationManager;
@property (nonatomic) NSTimer *timer;
@property (nonatomic) NSDate *instanceIDRotationDate;
-@property (nonatomic) NSDate *turnstileSendDate;
+@property (nonatomic) NSDate *nextTurnstileSendDate;
@end
@@ -136,7 +136,7 @@ const NSTimeInterval MGLFlushInterval = 180;
NSBundle *bundle = [NSBundle mainBundle];
NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:@"MGLMapboxAccountType"];
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
- @"MGLMapboxAccountType": accountTypeNumber ? accountTypeNumber : @0,
+ @"MGLMapboxAccountType": accountTypeNumber ?: @0,
@"MGLMapboxMetricsEnabled": @YES,
@"MGLMapboxMetricsDebugLoggingEnabled": @NO,
}];
@@ -144,8 +144,12 @@ const NSTimeInterval MGLFlushInterval = 180;
}
+ (BOOL)isEnabled {
+#if TARGET_OS_SIMULATOR
+ return NO;
+#else
return ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsEnabled"] &&
[[NSUserDefaults standardUserDefaults] integerForKey:@"MGLMapboxAccountType"] == 0);
+#endif
}
- (BOOL)debugLoggingEnabled {
@@ -311,7 +315,7 @@ const NSTimeInterval MGLFlushInterval = 180;
}
- (void)pushTurnstileEvent {
- if (self.turnstileSendDate && [[NSDate date] timeIntervalSinceDate:self.turnstileSendDate] < 0) {
+ if (self.nextTurnstileSendDate && [[NSDate date] timeIntervalSinceDate:self.nextTurnstileSendDate] < 0) {
return;
}
@@ -338,11 +342,25 @@ const NSTimeInterval MGLFlushInterval = 180;
return;
}
[strongSelf writeEventToLocalDebugLog:turnstileEventAttributes];
- NSTimeInterval twentyFourHourTimeInterval = 24 * 3600;
- strongSelf.turnstileSendDate = [[NSDate date] dateByAddingTimeInterval:twentyFourHourTimeInterval];
+ [strongSelf updateNextTurnstileSendDate];
}];
}
+- (void)updateNextTurnstileSendDate {
+ // Find the time a day from now (sometime tomorrow)
+ NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
+ NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
+ dayComponent.day = 1;
+ NSDate *sometimeTomorrow = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
+
+ // Find the start of tomorrow and use that as the next turnstile send date. The effect of this is that
+ // turnstile events can be sent as much as once per calendar day and always at the start of a session
+ // when a map load happens.
+ NSDate *startOfTomorrow = nil;
+ [calendar rangeOfUnit:NSCalendarUnitDay startDate:&startOfTomorrow interval:nil forDate:sometimeTomorrow];
+ self.nextTurnstileSendDate = startOfTomorrow;
+}
+
+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
[[MGLMapboxEvents sharedManager] pushEvent:event withAttributes:attributeDictionary];
}
@@ -356,7 +374,7 @@ const NSTimeInterval MGLFlushInterval = 180;
[self pushTurnstileEvent];
}
- if ([self isPaused]) {
+ if (self.paused) {
return;
}
@@ -446,7 +464,7 @@ const NSTimeInterval MGLFlushInterval = 180;
// Called implicitly from public use of +flush.
//
- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- if ([self isPaused]) {
+ if (self.paused) {
return;
}
diff --git a/platform/ios/include/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h
index 6160413510..6160413510 100644
--- a/platform/ios/include/MGLUserLocation.h
+++ b/platform/ios/src/MGLUserLocation.h
diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m
index 0ee90a3c2c..a568ec8be1 100644
--- a/platform/ios/src/MGLUserLocation.m
+++ b/platform/ios/src/MGLUserLocation.m
@@ -1,6 +1,7 @@
#import "MGLUserLocation_Private.h"
#import "MGLMapView.h"
+#import "NSBundle+MGLAdditions.h"
NS_ASSUME_NONNULL_BEGIN
@@ -68,7 +69,7 @@ NS_ASSUME_NONNULL_END
- (NSString *)title
{
- return (_title ? _title : @"You Are Here");
+ return _title ?: NSLocalizedStringWithDefaultValue(@"USER_DOT_TITLE", nil, nil, @"You Are Here", @"Default user location annotation title");
}
- (NSString *)description
diff --git a/platform/ios/src/MGLUserLocationAnnotationView.m b/platform/ios/src/MGLUserLocationAnnotationView.m
index a7ca352bc0..c514026e42 100644
--- a/platform/ios/src/MGLUserLocationAnnotationView.m
+++ b/platform/ios/src/MGLUserLocationAnnotationView.m
@@ -4,6 +4,8 @@
#import "MGLUserLocation_Private.h"
#import "MGLAnnotation.h"
#import "MGLMapView.h"
+#import "MGLCoordinateFormatter.h"
+#import "NSBundle+MGLAdditions.h"
const CGFloat MGLUserLocationAnnotationDotSize = 22.0;
const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
@@ -36,6 +38,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
CLLocationAccuracy _oldHorizontalAccuracy;
double _oldZoom;
double _oldPitch;
+
+ MGLCoordinateFormatter *_accessibilityCoordinateFormatter;
}
- (instancetype)initWithFrame:(CGRect)frame
@@ -53,7 +57,10 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
self.annotation = [[MGLUserLocation alloc] initWithMapView:mapView];
_mapView = mapView;
[self setupLayers];
- self.accessibilityLabel = @"User location";
+ self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable | UIAccessibilityTraitUpdatesFrequently;
+
+ _accessibilityCoordinateFormatter = [[MGLCoordinateFormatter alloc] init];
+ _accessibilityCoordinateFormatter.unitStyle = NSFormattingUnitStyleLong;
}
return self;
}
@@ -64,6 +71,62 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
return [self initInMapView:mapView];
}
+- (BOOL)isAccessibilityElement
+{
+ return !self.hidden;
+}
+
+- (NSString *)accessibilityLabel
+{
+ return self.annotation.title;
+}
+
+- (NSString *)accessibilityValue
+{
+ if (self.annotation.subtitle)
+ {
+ return self.annotation.subtitle;
+ }
+
+ // Each arcminute of longitude is at most about 1 nmi, too small for low zoom levels.
+ // Each arcsecond of longitude is at most about 30 m, too small for all but the very highest of zoom levels.
+ double zoomLevel = self.mapView.zoomLevel;
+ _accessibilityCoordinateFormatter.allowsMinutes = zoomLevel > 8;
+ _accessibilityCoordinateFormatter.allowsSeconds = zoomLevel > 20;
+
+ return [_accessibilityCoordinateFormatter stringFromCoordinate:self.mapView.centerCoordinate];
+}
+
+- (CGRect)accessibilityFrame
+{
+ return CGRectInset(self.frame, -15, -15);
+}
+
+- (UIBezierPath *)accessibilityPath
+{
+ return [UIBezierPath bezierPathWithOvalInRect:self.frame];
+}
+
+- (void)accessibilityIncrement
+{
+ [self.mapView accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement
+{
+ [self.mapView accessibilityDecrement];
+}
+
+- (void)setHidden:(BOOL)hidden
+{
+ BOOL oldValue = super.hidden;
+ [super setHidden:hidden];
+ if (oldValue != hidden)
+ {
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+ }
+}
+
- (void)setTintColor:(UIColor *)tintColor
{
if (_puckModeActivated)
@@ -128,6 +191,22 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
}
}
+- (void)updateFrameWithSize:(CGFloat)size
+{
+ CGSize newSize = CGSizeMake(size, size);
+ if (CGSizeEqualToSize(self.frame.size, newSize))
+ {
+ return;
+ }
+
+ // Update frame size, keeping the existing center point.
+ CGPoint oldCenter = self.center;
+ CGRect newFrame = self.frame;
+ newFrame.size = newSize;
+ [self setFrame:newFrame];
+ [self setCenter:oldCenter];
+}
+
- (void)drawPuck
{
if ( ! _puckModeActivated)
@@ -140,6 +219,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
_haloLayer = nil;
_dotBorderLayer = nil;
_dotLayer = nil;
+
+ [self updateFrameWithSize:MGLUserLocationAnnotationPuckSize];
}
// background dot (white with black shadow)
@@ -215,6 +296,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
_puckDot = nil;
_puckArrow = nil;
+
+ [self updateFrameWithSize:MGLUserLocationAnnotationDotSize];
}
BOOL showHeadingIndicator = _mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
@@ -471,7 +554,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
CGContextDrawRadialGradient(context, gradient,
centerPoint, 0.0,
centerPoint, haloRadius,
- nil);
+ kNilOptions);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
new file mode 100644
index 0000000000..bc22a5f955
--- /dev/null
+++ b/platform/ios/src/Mapbox.h
@@ -0,0 +1,38 @@
+#import <Foundation/Foundation.h>
+
+/// Project version number for Mapbox.
+FOUNDATION_EXPORT double MapboxVersionNumber;
+
+/// Project version string for Mapbox.
+FOUNDATION_EXPORT const unsigned char MapboxVersionString[];
+
+#import "MGLAnnotationView.h"
+#import "MGLAccountManager.h"
+#import "MGLAnnotation.h"
+#import "MGLAnnotationImage.h"
+#import "MGLCalloutView.h"
+#import "MGLClockDirectionFormatter.h"
+#import "MGLCompassDirectionFormatter.h"
+#import "MGLCoordinateFormatter.h"
+#import "MGLFeature.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+#import "MGLMapView.h"
+#import "MGLMapView+IBAdditions.h"
+#import "MGLMapView+MGLCustomStyleLayerAdditions.h"
+#import "MGLMapViewDelegate.h"
+#import "MGLMultiPoint.h"
+#import "MGLOfflinePack.h"
+#import "MGLOfflineRegion.h"
+#import "MGLOfflineStorage.h"
+#import "MGLOverlay.h"
+#import "MGLPointAnnotation.h"
+#import "MGLPolygon.h"
+#import "MGLPolyline.h"
+#import "MGLShape.h"
+#import "MGLShapeCollection.h"
+#import "MGLStyle.h"
+#import "MGLTilePyramidOfflineRegion.h"
+#import "MGLTypes.h"
+#import "MGLUserLocation.h"
+#import "NSValue+MGLAdditions.h"
diff --git a/platform/ios/test/App-Info.plist b/platform/ios/test/App-Info.plist
deleted file mode 100644
index 8753067af1..0000000000
--- a/platform/ios/test/App-Info.plist
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>CFBundleDevelopmentRegion</key>
- <string>en</string>
- <key>CFBundleDisplayName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundleExecutable</key>
- <string>${EXECUTABLE_NAME}</string>
- <key>CFBundleIdentifier</key>
- <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
- <key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
- <key>CFBundleName</key>
- <string>${PRODUCT_NAME}</string>
- <key>CFBundlePackageType</key>
- <string>APPL</string>
- <key>CFBundleShortVersionString</key>
- <string>1.0</string>
- <key>CFBundleSignature</key>
- <string>????</string>
- <key>CFBundleVersion</key>
- <string>1.0</string>
- <key>LSRequiresIPhoneOS</key>
- <true/>
- <key>MGLMapboxAccessToken</key>
- <string>pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg</string>
- <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
- <true/>
- <key>MGLMetricsTestServerURL</key>
- <string>https://cloudfront-staging.tilestream.net</string>
- <key>NSLocationAlwaysUsageDescription</key>
- <string>Strictly for testing purposes, promise!</string>
- <key>UILaunchStoryboardName</key>
- <string>LaunchScreen</string>
- <key>UIRequiredDeviceCapabilities</key>
- <array>
- <string>armv7</string>
- </array>
- <key>UISupportedInterfaceOrientations</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
- <key>UISupportedInterfaceOrientations~ipad</key>
- <array>
- <string>UIInterfaceOrientationPortrait</string>
- <string>UIInterfaceOrientationPortraitUpsideDown</string>
- <string>UIInterfaceOrientationLandscapeLeft</string>
- <string>UIInterfaceOrientationLandscapeRight</string>
- </array>
-</dict>
-</plist>
diff --git a/platform/ios/test/Images.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/test/Images.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 5008911f9e..0000000000
--- a/platform/ios/test/Images.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,162 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "1x"
- },
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "57x57",
- "scale" : "1x"
- },
- {
- "idiom" : "iphone",
- "size" : "57x57",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "3x"
- },
- {
- "idiom" : "ipad",
- "size" : "29x29",
- "scale" : "1x"
- },
- {
- "idiom" : "ipad",
- "size" : "29x29",
- "scale" : "2x"
- },
- {
- "idiom" : "ipad",
- "size" : "40x40",
- "scale" : "1x"
- },
- {
- "idiom" : "ipad",
- "size" : "40x40",
- "scale" : "2x"
- },
- {
- "idiom" : "ipad",
- "size" : "50x50",
- "scale" : "1x"
- },
- {
- "idiom" : "ipad",
- "size" : "50x50",
- "scale" : "2x"
- },
- {
- "idiom" : "ipad",
- "size" : "72x72",
- "scale" : "1x"
- },
- {
- "idiom" : "ipad",
- "size" : "72x72",
- "scale" : "2x"
- },
- {
- "idiom" : "ipad",
- "size" : "76x76",
- "scale" : "1x"
- },
- {
- "idiom" : "ipad",
- "size" : "76x76",
- "scale" : "2x"
- },
- {
- "idiom" : "car",
- "size" : "120x120",
- "scale" : "1x"
- },
- {
- "size" : "24x24",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "notificationCenter",
- "subtype" : "38mm"
- },
- {
- "size" : "27.5x27.5",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "notificationCenter",
- "subtype" : "42mm"
- },
- {
- "size" : "29x29",
- "idiom" : "watch",
- "role" : "companionSettings",
- "scale" : "2x"
- },
- {
- "size" : "29x29",
- "idiom" : "watch",
- "role" : "companionSettings",
- "scale" : "3x"
- },
- {
- "size" : "40x40",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "appLauncher",
- "subtype" : "38mm"
- },
- {
- "size" : "44x44",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "longLook",
- "subtype" : "42mm"
- },
- {
- "size" : "86x86",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "quickLook",
- "subtype" : "38mm"
- },
- {
- "size" : "98x98",
- "idiom" : "watch",
- "scale" : "2x",
- "role" : "quickLook",
- "subtype" : "42mm"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-} \ No newline at end of file
diff --git a/platform/ios/test/Info.plist b/platform/ios/test/Info.plist
new file mode 100644
index 0000000000..ba72822e87
--- /dev/null
+++ b/platform/ios/test/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/platform/ios/test/KIF b/platform/ios/test/KIF
deleted file mode 160000
-Subproject 0e56388a71b0fce94e0df58fa33f81287fa27a7
diff --git a/platform/ios/test/MapViewTests.m b/platform/ios/test/MapViewTests.m
deleted file mode 100644
index c8fe862e0a..0000000000
--- a/platform/ios/test/MapViewTests.m
+++ /dev/null
@@ -1,554 +0,0 @@
-#import <KIF/KIF.h>
-#import <KIF/KIFTestStepValidation.h>
-
-#import "KIFTestActor+MapboxGL.h"
-
-#import <Mapbox/Mapbox.h>
-#import "MGLTViewController.h"
-
-#import <CoreLocation/CoreLocation.h>
-#import <KIF/UIAutomationHelper.h>
-
-@interface MapViewTests : KIFTestCase <MGLMapViewDelegate>
-
-@end
-
-@implementation MapViewTests
-
-- (NSNotification *)waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:(void (^)())block {
- [[NSNotificationCenter defaultCenter] addObserverForName:@"regionDidChangeAnimated"
- object:tester.mapView
- queue:nil
- usingBlock:^(NSNotification * _Nonnull note) {}];
- NSNotification *notification = [system waitForNotificationName:@"regionDidChangeAnimated"
- object:tester.mapView whileExecutingBlock:block];
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:@"regionDidChangeAnimated"
- object:tester.mapView];
- return notification;
-}
-
-- (void)beforeEach {
- [system simulateDeviceRotationToOrientation:UIDeviceOrientationPortrait];
- [tester.viewController resetMapView];
-
- tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.913175, -77.032458);
- tester.mapView.zoomLevel = 14;
- tester.mapView.direction = 0;
-
- tester.mapView.zoomEnabled = YES;
- tester.mapView.scrollEnabled = YES;
- tester.mapView.rotateEnabled = YES;
-
- [tester.mapView removeAnnotations:tester.mapView.annotations];
-
- tester.viewController.navigationController.navigationBarHidden = YES;
- tester.viewController.navigationController.toolbarHidden = YES;
-
- tester.mapView.delegate = self;
-}
-
-- (void)approveLocationIfNeeded {
- if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
- [UIAutomationHelper acknowledgeSystemAlert];
- [tester waitForTimeInterval:1];
- }
- XCTAssertTrue([CLLocationManager locationServicesEnabled]);
- XCTAssertEqual([CLLocationManager authorizationStatus], kCLAuthorizationStatusAuthorizedAlways);
-}
-
-- (void)testDirectionSet {
- [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
- [tester.mapView setDirection:270 animated:YES];
- }];
-
- XCTAssertEqual(tester.mapView.direction,
- 270,
- @"setting direction should take effect");
-
- [tester waitForAnimationsToFinish];
- XCTAssertEqual(tester.compass.alpha,
- 1,
- @"compass should be visible when map is rotated");
-
- XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
- [NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_PI * 0.5)],
- @"compass rotation should indicate map rotation");
-}
-
-- (void)testDirectionReset {
- [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
- [tester.mapView setDirection:90 animated:YES];
- }];
-
- XCTAssertEqual(tester.mapView.direction,
- 90,
- @"setting direction should take effect");
-
- [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
- [tester.mapView resetNorth];
- }];
-
- XCTAssertEqual(tester.mapView.direction,
- 0,
- @"resetting north should reset map direction");
-
- [tester waitForAnimationsToFinish];
- XCTAssertEqual(tester.compass.alpha,
- 0,
- @"compass should not be visible when map is unrotated");
-
- XCTAssert(CGAffineTransformEqualToTransform(tester.compass.transform, CGAffineTransformIdentity),
- @"compass rotation should indicate map rotation");
-}
-
-- (void)testZoom {
- double zoom = tester.mapView.zoomLevel;
-
- [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
- tester.mapView.bounds.size.height / 2)
- distance:50
- steps:10];
-
- XCTAssertGreaterThan(tester.mapView.zoomLevel,
- zoom,
- @"zoom gesture should increase zoom level");
-
- zoom = tester.mapView.zoomLevel;
-
- [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
- tester.mapView.bounds.size.height / 2)
- distance:50
- steps:10];
-
- XCTAssertLessThan(tester.mapView.zoomLevel,
- zoom,
- @"pinch gesture should decrease zoom level");
-}
-
-- (void)testZoomDisabled {
- tester.mapView.zoomEnabled = NO;
- double zoom = tester.mapView.zoomLevel;
-
- [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
- tester.mapView.bounds.size.height / 2)
- distance:50
- steps:10];
-
- XCTAssertEqual(tester.mapView.zoomLevel,
- zoom,
- @"disabling zoom gesture should disallow zooming");
-
- [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
- tester.mapView.bounds.size.height / 2)
- distance:50
- steps:10];
-
- XCTAssertEqual(tester.mapView.zoomLevel,
- zoom,
- @"disabling zoom gesture should disallow pinching");
-}
-
-- (void)testFitToBounds {
- // No-op
- MGLCoordinateBounds initialBounds = tester.mapView.visibleCoordinateBounds;
- [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
- XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
- @"setting visible coordinate bounds to currently visible coordinate bounds should be a no-op");
-
- // Roundtrip after zooming
- tester.mapView.zoomLevel -= 3;
- [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
- XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
- @"after zooming out, setting visible coordinate bounds back to %@ should not leave them at %@",
- MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
- tester.mapView.zoomLevel += 3;
- [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
- XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
- @"after zooming in, setting visible coordinate bounds back to %@ should not leave them at %@",
- MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
-
- // Roundtrip after panning
- MGLCoordinateBounds offsetBounds = MGLCoordinateBoundsOffset(initialBounds, MGLCoordinateSpanMake(0, 30));
- [tester.mapView setVisibleCoordinateBounds:offsetBounds animated:NO];
- [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
- XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
- @"after panning 30° to the east, setting visible coordinate bounds back to %@ should not leave them at %@",
- MGLStringFromCoordinateBounds(initialBounds),
- MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
-
- // Inscribed shapes with rotation
- tester.mapView.direction = 45;
- // https://en.wikipedia.org/wiki/Boundary_Markers_of_the_Original_District_of_Columbia
- CLLocationCoordinate2D dcCoordinates[] = {
- {38.790339, -77.040583},
- {38.893219, -77.172304},
- {38.995946, -77.040947},
- {38.892829, -76.909229},
- };
- MGLCoordinateBounds dcBounds = {{38.790339, -77.172304}, {38.995946, -76.909229}};
- [tester.mapView setVisibleCoordinateBounds:dcBounds
- animated:NO];
- double zoomLevel = tester.mapView.zoomLevel;
- [tester.mapView setVisibleCoordinates:dcCoordinates
- count:sizeof(dcCoordinates) / sizeof(dcCoordinates[0])
- edgePadding:UIEdgeInsetsZero
- animated:NO];
- XCTAssertGreaterThan(tester.mapView.zoomLevel, zoomLevel,
- @"when the map is rotated, DC should fit at a zoom level higher than %f, but instead the zoom level is %f",
- zoomLevel, tester.mapView.zoomLevel);
-}
-
-- (void)testPan {
- CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
-
- [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
-
- XCTAssertGreaterThan(tester.mapView.centerCoordinate.latitude,
- centerCoordinate.latitude,
- @"panning map down should increase center latitude");
-
- XCTAssertLessThan(tester.mapView.centerCoordinate.longitude,
- centerCoordinate.longitude,
- @"panning map right should decrease center longitude");
-}
-
-- (void)testSetCenterCancelsTransitions {
- XCTestExpectation *cameraIsInDCExpectation = [self expectationWithDescription:@"camera reset to DC"];
-
- CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487);
- CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487);
- [tester.mapView setCenterCoordinate:dc animated:NO];
- [tester.mapView setCenterCoordinate:dc_west animated:YES];
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC),
- dispatch_get_main_queue(),
- ^{
- [tester.mapView setCenterCoordinate:dc animated:NO];
- XCTAssertEqualWithAccuracy(dc.latitude,
- tester.mapView.centerCoordinate.latitude,
- 0.0005,
- @"setting center coordinate should cancel transitions");
- XCTAssertEqualWithAccuracy(dc.longitude,
- tester.mapView.centerCoordinate.longitude,
- 0.0005,
- @"setting center coordinate should cancel transitions");
- [cameraIsInDCExpectation fulfill];
- });
-
- [self waitForExpectationsWithTimeout:1.0 handler:nil];
-}
-
-- (void)testPanDisabled {
- tester.mapView.scrollEnabled = NO;
- CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
-
- [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
-
- XCTAssertEqualWithAccuracy(centerCoordinate.latitude,
- tester.mapView.centerCoordinate.latitude,
- 0.005,
- @"disabling pan gesture should disallow vertical panning");
-
- XCTAssertEqualWithAccuracy(centerCoordinate.longitude,
- tester.mapView.centerCoordinate.longitude,
- 0.005,
- @"disabling pan gesture should disallow horizontal panning");
-}
-
-- (void)testRotate {
- CLLocationDirection startAngle = tester.mapView.direction;
-
- XCTAssertNotEqual(startAngle,
- 45,
- @"start angle must not be destination angle");
-
- [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
-
- XCTAssertGreaterThanOrEqual(fabs(tester.mapView.direction - startAngle),
- 20,
- @"rotating map should change angle");
-}
-
-- (void)testRotateDisabled {
- tester.mapView.rotateEnabled = NO;
- CLLocationDirection startAngle = tester.mapView.direction;
-
- XCTAssertNotEqual(startAngle,
- 45,
- @"start angle must not be destination angle");
-
- [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
-
- XCTAssertEqualWithAccuracy(tester.mapView.direction,
- startAngle,
- 0.005,
- @"disabling rotation show disallow rotation gestures");
-}
-
-- (void)testZoomSet {
- double newZoom = 11.65;
-
- XCTAssertNotEqual(tester.mapView.zoomLevel,
- newZoom,
- @"initial setup should have differing zoom");
-
- tester.mapView.zoomLevel = newZoom;
-
- XCTAssertEqualWithAccuracy(tester.mapView.zoomLevel,
- newZoom,
- 0.01,
- @"setting zoom should take effect");
-}
-
-- (void)testMarkerSelection {
- CGPoint point = CGPointMake(100, 100);
- MGLPointAnnotation *marker = [MGLPointAnnotation new];
- marker.coordinate = [tester.mapView convertPoint:point toCoordinateFromView:tester.mapView];
- marker.title = @"test"; // title required for callout
- [tester.mapView addAnnotation:marker];
-
- XCTAssertEqual(tester.mapView.selectedAnnotations.count, 0);
-
- [tester.mapView selectAnnotation:marker animated:NO];
- XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
-
- [tester.mapView deselectAnnotation:marker animated:NO];
- XCTAssertEqual(tester.mapView.selectedAnnotations.count, 0);
-}
-
-- (void)testMarkerAddWithoutDelegate {
- XCTAssertFalse([tester.viewController respondsToSelector:@selector(mapView:imageForAnnotation:)]);
-
- MGLPointAnnotation *marker = [MGLPointAnnotation new];
- marker.coordinate = tester.mapView.centerCoordinate;
- [tester.mapView addAnnotation:marker];
-
- [tester.mapView selectAnnotation:marker animated:NO];
- XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
- XCTAssertEqual([[tester.mapView subviewsWithClassNamePrefix:@"SM"] count], 0); // no callout for no title
-
- [tester.mapView deselectAnnotation:marker animated:NO];
- marker.title = @"test";
- [tester.mapView selectAnnotation:marker animated:NO];
- XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
- XCTAssertGreaterThan([[tester.mapView subviewsWithClassNamePrefix:@"SM"] count], 0);
-}
-
-- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id<MGLAnnotation>)annotation {
- return YES;
-}
-
-- (void)testTopLayoutGuide {
- CGRect statusBarFrame, navigationBarFrame, compassFrame;
- UINavigationBar *navigationBar = tester.viewController.navigationController.navigationBar;
-
- compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
- statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
- @"compass should not be under status bar");
-
- tester.viewController.navigationController.navigationBarHidden = NO;
- compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
- navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
- @"compass should not be under navigation bar");
-
- [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeLeft];
-
- compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
- navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
- @"rotated device should not have compass under navigation bar");
-
- tester.viewController.navigationController.navigationBarHidden = YES;
- compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
- statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
- @"rotated device should not have compass under status bar");
-}
-
-- (void)testBottomLayoutGuide {
- CGRect logoBugFrame, toolbarFrame, attributionButtonFrame;
- UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox logo"];
- UIToolbar *toolbar = tester.viewController.navigationController.toolbar;
- UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Attribution info"];
-
- tester.viewController.navigationController.toolbarHidden = NO;
-
- logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
- toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
- @"logo bug should not be under toolbar");
-
- attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
- @"attribution button should not be under toolbar");
-
- [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeRight];
-
- logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
- toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
- @"rotated device should not have logo bug under toolbar");
-
- attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
- XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
- @"rotated device should not have attribution button under toolbar");
-}
-
-- (void)testInsetMapView {
- [tester.viewController insetMapView];
- [tester waitForAnimationsToFinish];
-
- UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox logo"];
- UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Attribution info"];
-
- CGRect mapViewFrame = [tester.mapView.superview convertRect:tester.mapView.frame toView:nil];
-
- CGRect logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
- XCTAssertTrue(CGRectIntersectsRect(logoBugFrame, mapViewFrame),
- @"logo bug should lie inside shrunken map view");
-
- CGRect attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
- XCTAssertTrue(CGRectIntersectsRect(attributionButtonFrame, mapViewFrame),
- @"attribution button should lie inside shrunken map view");
-
- CGRect compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
- XCTAssertTrue(CGRectIntersectsRect(compassFrame, mapViewFrame),
- @"compass should lie inside shrunken map view");
-}
-
-- (void)testContentInsetsWithTinyMapView {
- [tester.viewController tinyMapView];
- [self keyValueObservingExpectationForObject:tester.mapView keyPath:@"contentInset" handler:^BOOL(id observedObject, NSDictionary *change) {
- XCTAssertEqual(tester.mapView.contentInset.top,
- 0,
- @"map should not have top content inset");
- XCTAssertEqual(tester.mapView.contentInset.bottom,
- 0,
- @"map should not have bottom content inset");
- return YES;
- }];
- [self waitForExpectationsWithTimeout:2.0 handler:nil];
-
- tester.mapView.frame = CGRectMake(0, 0, tester.mapView.frame.size.width, tester.mapView.frame.size.height);
- [self keyValueObservingExpectationForObject:tester.mapView keyPath:@"contentInset" handler:^BOOL(id observedObject, NSDictionary *change) {
- XCTAssertEqual(tester.mapView.contentInset.top,
- tester.viewController.topLayoutGuide.length,
- @"map should have top content inset equal to the top layout guide");
- return YES;
- }];
- [self waitForExpectationsWithTimeout:2.0 handler:nil];
-}
-
-- (void)testDelegateRegionWillChange {
- __block NSUInteger unanimatedCount;
- __block NSUInteger animatedCount;
- [[NSNotificationCenter defaultCenter] addObserverForName:@"regionWillChangeAnimated"
- object:tester.mapView
- queue:nil
- usingBlock:^(NSNotification *note) {
- if ([note.userInfo[@"animated"] boolValue]) {
- animatedCount++;
- } else {
- unanimatedCount++;
- }
- }];
-
- [tester waitForTimeInterval:1];
-
- unanimatedCount = 0;
- animatedCount = 0;
-
- NSNotification *notification = [system waitForNotificationName:@"regionWillChangeAnimated"
- object:tester.mapView
- whileExecutingBlock:^{
- tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
- }];
-
- [tester waitForTimeInterval:1];
-
- XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
- NO,
- @"regionWillChange delegate should not indicate animated change");
- XCTAssertEqual(unanimatedCount,
- 1,
- @"regionWillChange delegate should indicate one unanimated change");
-
- notification = [system waitForNotificationName:@"regionWillChangeAnimated"
- object:tester.mapView
- whileExecutingBlock:^{
- [tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
- }];
-
- [tester waitForTimeInterval:1];
-
- XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
- YES,
- @"regionWillChange delegate should indicate an animated change");
- XCTAssertEqual(animatedCount,
- 1,
- @"regionWillChange delegate should indicate one animated change");
-
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:@"regionWillChangeAnimated"
- object:tester.mapView];
-}
-
-- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
- [[NSNotificationCenter defaultCenter] postNotificationName:@"regionWillChangeAnimated"
- object:mapView
- userInfo:@{ @"animated" : @(animated) }];
-}
-
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
- [[NSNotificationCenter defaultCenter] postNotificationName:@"regionDidChangeAnimated"
- object:mapView
- userInfo:@{ @"animated" : @(animated) }];
-}
-
-- (void)testDelegatesStartStopLocatingUser {
- NSNotification *notification = [system waitForNotificationName:@"mapViewWillStartLocatingUser"
- object:tester.mapView
- whileExecutingBlock:^{
- tester.mapView.showsUserLocation = YES;
- [self approveLocationIfNeeded];
- }];
-
- XCTAssertEqualObjects(notification.name,
- @"mapViewWillStartLocatingUser",
- @"mapViewWillStartLocatingUser delegate should receive message");
- XCTAssertNotNil([tester.mapView valueForKeyPath:@"locationManager"],
- "map view location manager should not be nil");
-
- notification = [system waitForNotificationName:@"mapViewDidStopLocatingUser"
- object:tester.mapView
- whileExecutingBlock:^{
- tester.mapView.showsUserLocation = NO;
- }];
-
- XCTAssertEqualObjects(notification.name,
- @"mapViewDidStopLocatingUser",
- @"mapViewDidStopLocatingUser delegate should receive message");
- XCTAssertEqual(tester.mapView.userTrackingMode,
- MGLUserTrackingModeNone,
- @"user tracking mode should be none");
- XCTAssertNil([tester.mapView valueForKeyPath:@"locationManager"],
- "map view location manager should be nil");
-}
-
-- (void)mapViewWillStartLocatingUser:(MGLMapView *)mapView {
- [[NSNotificationCenter defaultCenter] postNotificationName:@"mapViewWillStartLocatingUser" object:mapView];
-}
-
-- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView {
- [[NSNotificationCenter defaultCenter] postNotificationName:@"mapViewDidStopLocatingUser" object:mapView];
-}
-
-@end
diff --git a/platform/ios/test/ios-tests.xcodeproj/project.pbxproj b/platform/ios/test/ios-tests.xcodeproj/project.pbxproj
deleted file mode 100644
index 09cc7f3c31..0000000000
--- a/platform/ios/test/ios-tests.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,688 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 46;
- objects = {
-
-/* Begin PBXBuildFile section */
- 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96567A221B0E84CD00D78776 /* LaunchScreen.xib */; };
- 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96567A301B0E8BB900D78776 /* Images.xcassets */; };
- DA482C801C12582600772FE3 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA482C7F1C12582600772FE3 /* Mapbox.framework */; };
- DA482C811C12582600772FE3 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA482C7F1C12582600772FE3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
- DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */; };
- DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043360196DBBD500E6F39D /* MGLTViewController.m */; };
- DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; };
- DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0580E71ACB628200B112C9 /* IOKit.framework */; };
- DD0E6F841B0190E200DC035A /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E6F701B0190E200DC035A /* libOCMock.a */; };
- DD0E6F981B01B68E00DC035A /* OHHTTPStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */; };
- DD0E6F991B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */; };
- DD0E6F9A1B01B68E00DC035A /* OHHTTPStubsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */; };
- DD0E6F9B1B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */; };
- DD0E6F9C1B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */; };
- DD0E6F9D1B01B68E00DC035A /* OHPathHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */; };
- DDBD016C196DC4740033959E /* MapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD0168196DC4740033959E /* MapViewTests.m */; };
- DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */; };
- DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DDBD0144196DC3AE0033959E /* libKIF.a */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
- DA3BDF011BA2326400553BD2 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 9CC9673B1AD4B1B600576D13;
- remoteInfo = KIFFramework;
- };
- DDBD0143196DC3AE0033959E /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = EABD46AA1857A0C700A5F081;
- remoteInfo = KIF;
- };
- DDBD0147196DC3AE0033959E /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = EB60ECC1177F8C83005A041A;
- remoteInfo = "Test Host";
- };
- DDBD0149196DC3AE0033959E /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = EABD46CD1857A0F300A5F081;
- remoteInfo = "KIF Tests";
- };
- DDBD0160196DC3D70033959E /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = DD04331B196DB9BC00E6F39D /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = DD043322196DB9BC00E6F39D;
- remoteInfo = "Mapbox GL Tests";
- };
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXCopyFilesBuildPhase section */
- DA482C821C12582600772FE3 /* Embed Frameworks */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = "";
- dstSubfolderSpec = 10;
- files = (
- DA482C811C12582600772FE3 /* Mapbox.framework in Embed Frameworks */,
- );
- name = "Embed Frameworks";
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
- 96567A221B0E84CD00D78776 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = SOURCE_ROOT; };
- 96567A301B0E8BB900D78776 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = SOURCE_ROOT; };
- DA482C7F1C12582600772FE3 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mapbox.framework; path = ../../../build/ios/pkg/dynamic/Mapbox.framework; sourceTree = "<group>"; };
- DACAD7111B08719F009119DC /* MGLMapboxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGLMapboxEvents.h; path = ../../platform/ios/MGLMapboxEvents.h; sourceTree = SOURCE_ROOT; };
- DADD9EB51BD16D8B00DA9161 /* Compatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Compatibility.h; path = OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h; sourceTree = SOURCE_ROOT; };
- DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL Tests.app"; sourceTree = BUILT_PRODUCTS_DIR; };
- DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTAppDelegate.m; sourceTree = SOURCE_ROOT; };
- DD043360196DBBD500E6F39D /* MGLTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTViewController.m; sourceTree = SOURCE_ROOT; };
- DD043361196DBBD500E6F39D /* MGLTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTViewController.h; sourceTree = SOURCE_ROOT; };
- DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTAppDelegate.h; sourceTree = SOURCE_ROOT; };
- DD043365196DBBE000E6F39D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; };
- DD043367196DBCC200E6F39D /* App-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "App-Info.plist"; sourceTree = SOURCE_ROOT; };
- DD0580E71ACB628200B112C9 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
- DD0E6F701B0190E200DC035A /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOCMock.a; path = OCMock/libOCMock.a; sourceTree = SOURCE_ROOT; };
- DD0E6F721B0190E200DC035A /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OCMAdditions.h"; sourceTree = "<group>"; };
- DD0E6F731B0190E200DC035A /* OCMArg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMArg.h; sourceTree = "<group>"; };
- DD0E6F741B0190E200DC035A /* OCMConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMConstraint.h; sourceTree = "<group>"; };
- DD0E6F751B0190E200DC035A /* OCMLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMLocation.h; sourceTree = "<group>"; };
- DD0E6F761B0190E200DC035A /* OCMMacroState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMMacroState.h; sourceTree = "<group>"; };
- DD0E6F771B0190E200DC035A /* OCMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMock.h; sourceTree = "<group>"; };
- DD0E6F781B0190E200DC035A /* OCMockObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMockObject.h; sourceTree = "<group>"; };
- DD0E6F791B0190E200DC035A /* OCMRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMRecorder.h; sourceTree = "<group>"; };
- DD0E6F7A1B0190E200DC035A /* OCMStubRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMStubRecorder.h; sourceTree = "<group>"; };
- DD0E6F8D1B01B68E00DC035A /* OHHTTPStubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubs.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h; sourceTree = SOURCE_ROOT; };
- DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubs.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m; sourceTree = SOURCE_ROOT; };
- DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubs+NSURLSessionConfiguration.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m"; sourceTree = SOURCE_ROOT; };
- DD0E6F901B01B68E00DC035A /* OHHTTPStubsResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubsResponse.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h; sourceTree = SOURCE_ROOT; };
- DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubsResponse.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m; sourceTree = SOURCE_ROOT; };
- DD0E6F921B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+HTTPMessage.h"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h"; sourceTree = SOURCE_ROOT; };
- DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+HTTPMessage.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m"; sourceTree = SOURCE_ROOT; };
- DD0E6F941B01B68E00DC035A /* OHHTTPStubsResponse+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+JSON.h"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h"; sourceTree = SOURCE_ROOT; };
- DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+JSON.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m"; sourceTree = SOURCE_ROOT; };
- DD0E6F961B01B68E00DC035A /* OHPathHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHPathHelpers.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h; sourceTree = SOURCE_ROOT; };
- DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHPathHelpers.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m; sourceTree = SOURCE_ROOT; };
- DDBD013A196DC3AE0033959E /* KIF.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KIF.xcodeproj; path = KIF/KIF.xcodeproj; sourceTree = SOURCE_ROOT; };
- DDBD0152196DC3D70033959E /* Test Bundle.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test Bundle.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
- DDBD0165196DC4560033959E /* Bundle-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Bundle-Info.plist"; sourceTree = SOURCE_ROOT; };
- DDBD0168196DC4740033959E /* MapViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewTests.m; sourceTree = SOURCE_ROOT; };
- DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KIFTestActor+MapboxGL.m"; sourceTree = SOURCE_ROOT; };
- DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KIFTestActor+MapboxGL.h"; sourceTree = SOURCE_ROOT; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- DD043320196DB9BC00E6F39D /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DA482C801C12582600772FE3 /* Mapbox.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DDBD014F196DC3D70033959E /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */,
- DDBD016E196DC4A10033959E /* libKIF.a in Frameworks */,
- DD0E6F841B0190E200DC035A /* libOCMock.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- DD04331A196DB9BC00E6F39D = {
- isa = PBXGroup;
- children = (
- DA482C7F1C12582600772FE3 /* Mapbox.framework */,
- DD04332C196DB9BC00E6F39D /* App */,
- DDBD0139196DC38D0033959E /* Tests */,
- DD043325196DB9BC00E6F39D /* Frameworks */,
- DD043324196DB9BC00E6F39D /* Products */,
- );
- sourceTree = "<group>";
- };
- DD043324196DB9BC00E6F39D /* Products */ = {
- isa = PBXGroup;
- children = (
- DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */,
- DDBD0152196DC3D70033959E /* Test Bundle.xctest */,
- );
- name = Products;
- sourceTree = "<group>";
- };
- DD043325196DB9BC00E6F39D /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- DD0580E71ACB628200B112C9 /* IOKit.framework */,
- );
- name = Frameworks;
- sourceTree = "<group>";
- };
- DD04332C196DB9BC00E6F39D /* App */ = {
- isa = PBXGroup;
- children = (
- DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */,
- DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */,
- DD043361196DBBD500E6F39D /* MGLTViewController.h */,
- DD043360196DBBD500E6F39D /* MGLTViewController.m */,
- DDAE739B1ACB557500E1A793 /* GL Library */,
- DD04332D196DB9BC00E6F39D /* Supporting Files */,
- );
- name = App;
- path = "Mapbox GL Tests";
- sourceTree = "<group>";
- };
- DD04332D196DB9BC00E6F39D /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- 96567A301B0E8BB900D78776 /* Images.xcassets */,
- DD043367196DBCC200E6F39D /* App-Info.plist */,
- DD043365196DBBE000E6F39D /* main.m */,
- 96567A221B0E84CD00D78776 /* LaunchScreen.xib */,
- );
- name = "Supporting Files";
- sourceTree = "<group>";
- };
- DD0E6F6B1B01906600DC035A /* OCMock */ = {
- isa = PBXGroup;
- children = (
- DD0E6F701B0190E200DC035A /* libOCMock.a */,
- DD0E6F711B0190E200DC035A /* OCMock */,
- );
- name = OCMock;
- sourceTree = "<group>";
- };
- DD0E6F711B0190E200DC035A /* OCMock */ = {
- isa = PBXGroup;
- children = (
- DD0E6F721B0190E200DC035A /* NSNotificationCenter+OCMAdditions.h */,
- DD0E6F731B0190E200DC035A /* OCMArg.h */,
- DD0E6F741B0190E200DC035A /* OCMConstraint.h */,
- DD0E6F751B0190E200DC035A /* OCMLocation.h */,
- DD0E6F761B0190E200DC035A /* OCMMacroState.h */,
- DD0E6F771B0190E200DC035A /* OCMock.h */,
- DD0E6F781B0190E200DC035A /* OCMockObject.h */,
- DD0E6F791B0190E200DC035A /* OCMRecorder.h */,
- DD0E6F7A1B0190E200DC035A /* OCMStubRecorder.h */,
- );
- name = OCMock;
- path = OCMock/OCMock;
- sourceTree = SOURCE_ROOT;
- };
- DD0E6F861B01B67100DC035A /* OHHTTPStubs */ = {
- isa = PBXGroup;
- children = (
- DADD9EB51BD16D8B00DA9161 /* Compatibility.h */,
- DD0E6F8D1B01B68E00DC035A /* OHHTTPStubs.h */,
- DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */,
- DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */,
- DD0E6F901B01B68E00DC035A /* OHHTTPStubsResponse.h */,
- DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */,
- DD0E6F921B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.h */,
- DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */,
- DD0E6F941B01B68E00DC035A /* OHHTTPStubsResponse+JSON.h */,
- DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */,
- DD0E6F961B01B68E00DC035A /* OHPathHelpers.h */,
- DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */,
- );
- name = OHHTTPStubs;
- sourceTree = "<group>";
- };
- DDAE739B1ACB557500E1A793 /* GL Library */ = {
- isa = PBXGroup;
- children = (
- DACAD7111B08719F009119DC /* MGLMapboxEvents.h */,
- );
- name = "GL Library";
- sourceTree = "<group>";
- };
- DDBD0139196DC38D0033959E /* Tests */ = {
- isa = PBXGroup;
- children = (
- DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */,
- DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */,
- DDBD0168196DC4740033959E /* MapViewTests.m */,
- DDBD0167196DC46B0033959E /* Supporting Files */,
- DDBD014D196DC3B00033959E /* KIF */,
- DD0E6F6B1B01906600DC035A /* OCMock */,
- DD0E6F861B01B67100DC035A /* OHHTTPStubs */,
- );
- name = Tests;
- path = "Mapbox GL Tests";
- sourceTree = "<group>";
- };
- DDBD013B196DC3AE0033959E /* Products */ = {
- isa = PBXGroup;
- children = (
- DDBD0144196DC3AE0033959E /* libKIF.a */,
- DDBD0148196DC3AE0033959E /* Test Host.app */,
- DDBD014A196DC3AE0033959E /* KIF Tests - XCTest.xctest */,
- DA3BDF021BA2326400553BD2 /* KIF.framework */,
- );
- name = Products;
- sourceTree = "<group>";
- };
- DDBD014D196DC3B00033959E /* KIF */ = {
- isa = PBXGroup;
- children = (
- DDBD013A196DC3AE0033959E /* KIF.xcodeproj */,
- );
- name = KIF;
- sourceTree = "<group>";
- };
- DDBD0167196DC46B0033959E /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- DDBD0165196DC4560033959E /* Bundle-Info.plist */,
- );
- name = "Supporting Files";
- sourceTree = "<group>";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- DD043322196DB9BC00E6F39D /* Mapbox GL Tests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */;
- buildPhases = (
- DD04331F196DB9BC00E6F39D /* Sources */,
- DD043320196DB9BC00E6F39D /* Frameworks */,
- DD043321196DB9BC00E6F39D /* Resources */,
- DA482C821C12582600772FE3 /* Embed Frameworks */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = "Mapbox GL Tests";
- productName = "Mapbox GL Tests";
- productReference = DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */;
- productType = "com.apple.product-type.application";
- };
- DDBD0151196DC3D70033959E /* Test Bundle */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */;
- buildPhases = (
- DDBD014E196DC3D70033959E /* Sources */,
- DDBD014F196DC3D70033959E /* Frameworks */,
- DDBD0150196DC3D70033959E /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- DDBD0161196DC3D70033959E /* PBXTargetDependency */,
- );
- name = "Test Bundle";
- productName = "Test Bundle";
- productReference = DDBD0152196DC3D70033959E /* Test Bundle.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- DD04331B196DB9BC00E6F39D /* Project object */ = {
- isa = PBXProject;
- attributes = {
- CLASSPREFIX = MGLT;
- LastUpgradeCheck = 0700;
- ORGANIZATIONNAME = Mapbox;
- TargetAttributes = {
- DDBD0151196DC3D70033959E = {
- TestTargetID = DD043322196DB9BC00E6F39D;
- };
- };
- };
- buildConfigurationList = DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */;
- compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- Base,
- );
- mainGroup = DD04331A196DB9BC00E6F39D;
- productRefGroup = DD043324196DB9BC00E6F39D /* Products */;
- projectDirPath = "";
- projectReferences = (
- {
- ProductGroup = DDBD013B196DC3AE0033959E /* Products */;
- ProjectRef = DDBD013A196DC3AE0033959E /* KIF.xcodeproj */;
- },
- );
- projectRoot = "";
- targets = (
- DD043322196DB9BC00E6F39D /* Mapbox GL Tests */,
- DDBD0151196DC3D70033959E /* Test Bundle */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXReferenceProxy section */
- DA3BDF021BA2326400553BD2 /* KIF.framework */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.framework;
- path = KIF.framework;
- remoteRef = DA3BDF011BA2326400553BD2 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- DDBD0144196DC3AE0033959E /* libKIF.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libKIF.a;
- remoteRef = DDBD0143196DC3AE0033959E /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- DDBD0148196DC3AE0033959E /* Test Host.app */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.application;
- path = "Test Host.app";
- remoteRef = DDBD0147196DC3AE0033959E /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- DDBD014A196DC3AE0033959E /* KIF Tests - XCTest.xctest */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.cfbundle;
- path = "KIF Tests - XCTest.xctest";
- remoteRef = DDBD0149196DC3AE0033959E /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
-/* End PBXReferenceProxy section */
-
-/* Begin PBXResourcesBuildPhase section */
- DD043321196DB9BC00E6F39D /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */,
- 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DDBD0150196DC3D70033959E /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- DD04331F196DB9BC00E6F39D /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DD043366196DBBE000E6F39D /* main.m in Sources */,
- DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */,
- DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- DDBD014E196DC3D70033959E /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- DD0E6F981B01B68E00DC035A /* OHHTTPStubs.m in Sources */,
- DD0E6F9C1B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m in Sources */,
- DD0E6F9B1B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m in Sources */,
- DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */,
- DD0E6F991B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */,
- DD0E6F9A1B01B68E00DC035A /* OHHTTPStubsResponse.m in Sources */,
- DDBD016C196DC4740033959E /* MapViewTests.m in Sources */,
- DD0E6F9D1B01B68E00DC035A /* OHPathHelpers.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXTargetDependency section */
- DDBD0161196DC3D70033959E /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = DD043322196DB9BC00E6F39D /* Mapbox GL Tests */;
- targetProxy = DDBD0160196DC3D70033959E /* PBXContainerItemProxy */;
- };
-/* End PBXTargetDependency section */
-
-/* Begin XCBuildConfiguration section */
- DD043356196DB9BC00E6F39D /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- COPY_PHASE_STRIP = NO;
- ENABLE_TESTABILITY = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_SYMBOLS_PRIVATE_EXTERN = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
- };
- name = Debug;
- };
- DD043357196DB9BC00E6F39D /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- COPY_PHASE_STRIP = YES;
- ENABLE_NS_ASSERTIONS = NO;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_SYMBOLS_PRIVATE_EXTERN = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
- VALIDATE_PRODUCT = YES;
- };
- name = Release;
- };
- DD043359196DB9BC00E6F39D /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/../../../build/ios/pkg/dynamic/",
- );
- HEADER_SEARCH_PATHS = "";
- INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "../../mason_packages/**",
- );
- OTHER_LDFLAGS = "";
- PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
- PRODUCT_NAME = "$(TARGET_NAME)";
- TARGETED_DEVICE_FAMILY = "1,2";
- WRAPPER_EXTENSION = app;
- };
- name = Debug;
- };
- DD04335A196DB9BC00E6F39D /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/../../../build/ios/pkg/dynamic/",
- );
- HEADER_SEARCH_PATHS = "";
- INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "../../mason_packages/**",
- );
- OTHER_LDFLAGS = "";
- PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
- PRODUCT_NAME = "$(TARGET_NAME)";
- TARGETED_DEVICE_FAMILY = "1,2";
- WRAPPER_EXTENSION = app;
- };
- name = Release;
- };
- DDBD0163196DC3D70033959E /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/../../../build/ios/pkg/dynamic/",
- );
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- "KIF_XCTEST=1",
- );
- HEADER_SEARCH_PATHS = (
- "$(inherited)",
- /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- OCMock,
- OHHTTPStubs/OHHTTPStubs/Sources,
- );
- INFOPLIST_FILE = "Bundle-Info.plist";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/OCMock",
- );
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-framework",
- XCTest,
- "-ObjC",
- );
- PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
- PRODUCT_NAME = "$(TARGET_NAME)";
- TEST_HOST = "$(BUNDLE_LOADER)";
- WRAPPER_EXTENSION = xctest;
- };
- name = Debug;
- };
- DDBD0164196DC3D70033959E /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/../../../build/ios/pkg/dynamic/",
- );
- GCC_PREPROCESSOR_DEFINITIONS = "KIF_XCTEST=1";
- HEADER_SEARCH_PATHS = (
- "$(inherited)",
- /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
- OCMock,
- OHHTTPStubs/OHHTTPStubs/Sources,
- );
- INFOPLIST_FILE = "Bundle-Info.plist";
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/OCMock",
- );
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-framework",
- XCTest,
- "-ObjC",
- );
- PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
- PRODUCT_NAME = "$(TARGET_NAME)";
- TEST_HOST = "$(BUNDLE_LOADER)";
- WRAPPER_EXTENSION = xctest;
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DD043356196DB9BC00E6F39D /* Debug */,
- DD043357196DB9BC00E6F39D /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DD043359196DB9BC00E6F39D /* Debug */,
- DD04335A196DB9BC00E6F39D /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- DDBD0163196DC3D70033959E /* Debug */,
- DDBD0164196DC3D70033959E /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = DD04331B196DB9BC00E6F39D /* Project object */;
-}
diff --git a/platform/ios/test/.gitignore b/platform/ios/uitest/.gitignore
index 516812b72d..516812b72d 100644
--- a/platform/ios/test/.gitignore
+++ b/platform/ios/uitest/.gitignore
diff --git a/platform/ios/uitest/App-Info.plist b/platform/ios/uitest/App-Info.plist
new file mode 100644
index 0000000000..ab5ac7ec8c
--- /dev/null
+++ b/platform/ios/uitest/App-Info.plist
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>MGLMapboxAccessToken</key>
+ <string>pk.eyJ1IjoianVzdGluIiwiYSI6Ik9RX3RRQzAifQ.dmOg_BAp1ywuDZMM7YsXRg</string>
+ <key>MGLMapboxMetricsEnabledSettingShownInApp</key>
+ <true/>
+ <key>NSLocationAlwaysUsageDescription</key>
+ <string>Strictly for testing purposes, promise!</string>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/platform/ios/test/Bundle-Info.plist b/platform/ios/uitest/Bundle-Info.plist
index 169b6f710e..169b6f710e 100644
--- a/platform/ios/test/Bundle-Info.plist
+++ b/platform/ios/uitest/Bundle-Info.plist
diff --git a/platform/ios/uitest/Images.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/uitest/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..eeea76c2db
--- /dev/null
+++ b/platform/ios/uitest/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,73 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "40x40",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "60x60",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "76x76",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "83.5x83.5",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Contents.json b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Contents.json
index 628027f247..628027f247 100644
--- a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Contents.json
+++ b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Contents.json
diff --git a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
index 208eea205a..208eea205a 100644
--- a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
+++ b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default@2x.png b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default@2x.png
index 4438336215..4438336215 100644
--- a/platform/ios/test/Images.xcassets/LaunchImage.launchimage/Default@2x.png
+++ b/platform/ios/uitest/Images.xcassets/LaunchImage.launchimage/Default@2x.png
Binary files differ
diff --git a/platform/ios/uitest/KIF b/platform/ios/uitest/KIF
new file mode 160000
+Subproject 973a4cb653b54c3e8b2c0681f4097568ff0ac34
diff --git a/platform/ios/test/KIFTestActor+MapboxGL.h b/platform/ios/uitest/KIFTestActor+MapboxGL.h
index d16e348486..d16e348486 100644
--- a/platform/ios/test/KIFTestActor+MapboxGL.h
+++ b/platform/ios/uitest/KIFTestActor+MapboxGL.h
diff --git a/platform/ios/test/KIFTestActor+MapboxGL.m b/platform/ios/uitest/KIFTestActor+MapboxGL.m
index 6c5e53f40d..6c5e53f40d 100644
--- a/platform/ios/test/KIFTestActor+MapboxGL.m
+++ b/platform/ios/uitest/KIFTestActor+MapboxGL.m
diff --git a/platform/ios/test/LaunchScreen.xib b/platform/ios/uitest/LaunchScreen.xib
index 238fb51b50..238fb51b50 100644
--- a/platform/ios/test/LaunchScreen.xib
+++ b/platform/ios/uitest/LaunchScreen.xib
diff --git a/platform/ios/test/MGLTAppDelegate.h b/platform/ios/uitest/MGLTAppDelegate.h
index c0025582ee..c0025582ee 100644
--- a/platform/ios/test/MGLTAppDelegate.h
+++ b/platform/ios/uitest/MGLTAppDelegate.h
diff --git a/platform/ios/test/MGLTAppDelegate.m b/platform/ios/uitest/MGLTAppDelegate.m
index b79c2f4abb..b79c2f4abb 100644
--- a/platform/ios/test/MGLTAppDelegate.m
+++ b/platform/ios/uitest/MGLTAppDelegate.m
diff --git a/platform/ios/test/MGLTViewController.h b/platform/ios/uitest/MGLTViewController.h
index 349c216008..349c216008 100644
--- a/platform/ios/test/MGLTViewController.h
+++ b/platform/ios/uitest/MGLTViewController.h
diff --git a/platform/ios/test/MGLTViewController.m b/platform/ios/uitest/MGLTViewController.m
index 451dea9292..451dea9292 100644
--- a/platform/ios/test/MGLTViewController.m
+++ b/platform/ios/uitest/MGLTViewController.m
diff --git a/platform/ios/uitest/MapViewTests.m b/platform/ios/uitest/MapViewTests.m
new file mode 100644
index 0000000000..41ea5446ef
--- /dev/null
+++ b/platform/ios/uitest/MapViewTests.m
@@ -0,0 +1,553 @@
+#import <KIF/KIF.h>
+#import <KIF/KIFTestStepValidation.h>
+
+#import "KIFTestActor+MapboxGL.h"
+
+#import <Mapbox/Mapbox.h>
+#import "MGLTViewController.h"
+
+#import <CoreLocation/CoreLocation.h>
+
+@interface MapViewTests : KIFTestCase <MGLMapViewDelegate>
+
+@end
+
+@implementation MapViewTests
+
+- (NSNotification *)waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:(void (^)())block {
+ [[NSNotificationCenter defaultCenter] addObserverForName:@"regionDidChangeAnimated"
+ object:tester.mapView
+ queue:nil
+ usingBlock:^(NSNotification * _Nonnull note) {}];
+ NSNotification *notification = [system waitForNotificationName:@"regionDidChangeAnimated"
+ object:tester.mapView whileExecutingBlock:block];
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"regionDidChangeAnimated"
+ object:tester.mapView];
+ return notification;
+}
+
+- (void)beforeEach {
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationPortrait];
+ [tester.viewController resetMapView];
+
+ tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(38.913175, -77.032458);
+ tester.mapView.zoomLevel = 14;
+ tester.mapView.direction = 0;
+
+ tester.mapView.zoomEnabled = YES;
+ tester.mapView.scrollEnabled = YES;
+ tester.mapView.rotateEnabled = YES;
+
+ [tester.mapView removeAnnotations:tester.mapView.annotations];
+
+ tester.viewController.navigationController.navigationBarHidden = YES;
+ tester.viewController.navigationController.toolbarHidden = YES;
+
+ tester.mapView.delegate = self;
+}
+
+- (void)approveLocationIfNeeded {
+ if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
+ [tester acknowledgeSystemAlert];
+ [tester waitForTimeInterval:1];
+ }
+ XCTAssertTrue([CLLocationManager locationServicesEnabled]);
+ XCTAssertEqual([CLLocationManager authorizationStatus], kCLAuthorizationStatusAuthorizedAlways);
+}
+
+- (void)testDirectionSet {
+ [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
+ [tester.mapView setDirection:270 animated:YES];
+ }];
+
+ XCTAssertEqual(tester.mapView.direction,
+ 270,
+ @"setting direction should take effect");
+
+ [tester waitForAnimationsToFinish];
+ XCTAssertEqual(tester.compass.alpha,
+ 1,
+ @"compass should be visible when map is rotated");
+
+ XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
+ [NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_PI * 0.5)],
+ @"compass rotation should indicate map rotation");
+}
+
+- (void)testDirectionReset {
+ [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
+ [tester.mapView setDirection:90 animated:YES];
+ }];
+
+ XCTAssertEqual(tester.mapView.direction,
+ 90,
+ @"setting direction should take effect");
+
+ [self waitForNotificationThatRegionDidChangeAnimatedWhileExecutingBlock:^{
+ [tester.mapView resetNorth];
+ }];
+
+ XCTAssertEqual(tester.mapView.direction,
+ 0,
+ @"resetting north should reset map direction");
+
+ [tester waitForAnimationsToFinish];
+ XCTAssertEqual(tester.compass.alpha,
+ 0,
+ @"compass should not be visible when map is unrotated");
+
+ XCTAssert(CGAffineTransformEqualToTransform(tester.compass.transform, CGAffineTransformIdentity),
+ @"compass rotation should indicate map rotation");
+}
+
+- (void)testZoom {
+ double zoom = tester.mapView.zoomLevel;
+
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertGreaterThan(tester.mapView.zoomLevel,
+ zoom,
+ @"zoom gesture should increase zoom level");
+
+ zoom = tester.mapView.zoomLevel;
+
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertLessThan(tester.mapView.zoomLevel,
+ zoom,
+ @"pinch gesture should decrease zoom level");
+}
+
+- (void)testZoomDisabled {
+ tester.mapView.zoomEnabled = NO;
+ double zoom = tester.mapView.zoomLevel;
+
+ [tester.mapView zoomAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow zooming");
+
+ [tester.mapView pinchAtPoint:CGPointMake(tester.mapView.bounds.size.width / 2,
+ tester.mapView.bounds.size.height / 2)
+ distance:50
+ steps:10];
+
+ XCTAssertEqual(tester.mapView.zoomLevel,
+ zoom,
+ @"disabling zoom gesture should disallow pinching");
+}
+
+- (void)testFitToBounds {
+ // No-op
+ MGLCoordinateBounds initialBounds = tester.mapView.visibleCoordinateBounds;
+ [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
+ XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
+ @"setting visible coordinate bounds to currently visible coordinate bounds should be a no-op");
+
+ // Roundtrip after zooming
+ tester.mapView.zoomLevel -= 3;
+ [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
+ XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
+ @"after zooming out, setting visible coordinate bounds back to %@ should not leave them at %@",
+ MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
+ tester.mapView.zoomLevel += 3;
+ [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
+ XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
+ @"after zooming in, setting visible coordinate bounds back to %@ should not leave them at %@",
+ MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
+
+ // Roundtrip after panning
+ MGLCoordinateBounds offsetBounds = MGLCoordinateBoundsOffset(initialBounds, MGLCoordinateSpanMake(0, 30));
+ [tester.mapView setVisibleCoordinateBounds:offsetBounds animated:NO];
+ [tester.mapView setVisibleCoordinateBounds:initialBounds animated:NO];
+ XCTAssertEqualObjects(MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds),
+ @"after panning 30° to the east, setting visible coordinate bounds back to %@ should not leave them at %@",
+ MGLStringFromCoordinateBounds(initialBounds),
+ MGLStringFromCoordinateBounds(tester.mapView.visibleCoordinateBounds));
+
+ // Inscribed shapes with rotation
+ tester.mapView.direction = 45;
+ // https://en.wikipedia.org/wiki/Boundary_Markers_of_the_Original_District_of_Columbia
+ CLLocationCoordinate2D dcCoordinates[] = {
+ {38.790339, -77.040583},
+ {38.893219, -77.172304},
+ {38.995946, -77.040947},
+ {38.892829, -76.909229},
+ };
+ MGLCoordinateBounds dcBounds = {{38.790339, -77.172304}, {38.995946, -76.909229}};
+ [tester.mapView setVisibleCoordinateBounds:dcBounds
+ animated:NO];
+ double zoomLevel = tester.mapView.zoomLevel;
+ [tester.mapView setVisibleCoordinates:dcCoordinates
+ count:sizeof(dcCoordinates) / sizeof(dcCoordinates[0])
+ edgePadding:UIEdgeInsetsZero
+ animated:NO];
+ XCTAssertGreaterThan(tester.mapView.zoomLevel, zoomLevel,
+ @"when the map is rotated, DC should fit at a zoom level higher than %f, but instead the zoom level is %f",
+ zoomLevel, tester.mapView.zoomLevel);
+}
+
+- (void)testPan {
+ CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
+
+ [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
+
+ XCTAssertGreaterThan(tester.mapView.centerCoordinate.latitude,
+ centerCoordinate.latitude,
+ @"panning map down should increase center latitude");
+
+ XCTAssertLessThan(tester.mapView.centerCoordinate.longitude,
+ centerCoordinate.longitude,
+ @"panning map right should decrease center longitude");
+}
+
+- (void)testSetCenterCancelsTransitions {
+ XCTestExpectation *cameraIsInDCExpectation = [self expectationWithDescription:@"camera reset to DC"];
+
+ CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487);
+ CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487);
+ [tester.mapView setCenterCoordinate:dc animated:NO];
+ [tester.mapView setCenterCoordinate:dc_west animated:YES];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC),
+ dispatch_get_main_queue(),
+ ^{
+ [tester.mapView setCenterCoordinate:dc animated:NO];
+ XCTAssertEqualWithAccuracy(dc.latitude,
+ tester.mapView.centerCoordinate.latitude,
+ 0.0005,
+ @"setting center coordinate should cancel transitions");
+ XCTAssertEqualWithAccuracy(dc.longitude,
+ tester.mapView.centerCoordinate.longitude,
+ 0.0005,
+ @"setting center coordinate should cancel transitions");
+ [cameraIsInDCExpectation fulfill];
+ });
+
+ [self waitForExpectationsWithTimeout:1.0 handler:nil];
+}
+
+- (void)testPanDisabled {
+ tester.mapView.scrollEnabled = NO;
+ CLLocationCoordinate2D centerCoordinate = tester.mapView.centerCoordinate;
+
+ [tester.mapView dragFromPoint:CGPointMake(10, 10) toPoint:CGPointMake(300, 300) steps:10];
+
+ XCTAssertEqualWithAccuracy(centerCoordinate.latitude,
+ tester.mapView.centerCoordinate.latitude,
+ 0.005,
+ @"disabling pan gesture should disallow vertical panning");
+
+ XCTAssertEqualWithAccuracy(centerCoordinate.longitude,
+ tester.mapView.centerCoordinate.longitude,
+ 0.005,
+ @"disabling pan gesture should disallow horizontal panning");
+}
+
+- (void)testRotate {
+ CLLocationDirection startAngle = tester.mapView.direction;
+
+ XCTAssertNotEqual(startAngle,
+ 45,
+ @"start angle must not be destination angle");
+
+ [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
+
+ XCTAssertGreaterThanOrEqual(fabs(tester.mapView.direction - startAngle),
+ 20,
+ @"rotating map should change angle");
+}
+
+- (void)testRotateDisabled {
+ tester.mapView.rotateEnabled = NO;
+ CLLocationDirection startAngle = tester.mapView.direction;
+
+ XCTAssertNotEqual(startAngle,
+ 45,
+ @"start angle must not be destination angle");
+
+ [tester.mapView twoFingerRotateAtPoint:tester.mapView.center angle:45];
+
+ XCTAssertEqualWithAccuracy(tester.mapView.direction,
+ startAngle,
+ 0.005,
+ @"disabling rotation show disallow rotation gestures");
+}
+
+- (void)testZoomSet {
+ double newZoom = 11.65;
+
+ XCTAssertNotEqual(tester.mapView.zoomLevel,
+ newZoom,
+ @"initial setup should have differing zoom");
+
+ tester.mapView.zoomLevel = newZoom;
+
+ XCTAssertEqualWithAccuracy(tester.mapView.zoomLevel,
+ newZoom,
+ 0.01,
+ @"setting zoom should take effect");
+}
+
+- (void)testMarkerSelection {
+ CGPoint point = CGPointMake(100, 100);
+ MGLPointAnnotation *marker = [MGLPointAnnotation new];
+ marker.coordinate = [tester.mapView convertPoint:point toCoordinateFromView:tester.mapView];
+ marker.title = @"test"; // title required for callout
+ [tester.mapView addAnnotation:marker];
+
+ XCTAssertEqual(tester.mapView.selectedAnnotations.count, 0);
+
+ [tester.mapView selectAnnotation:marker animated:NO];
+ XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
+
+ [tester.mapView deselectAnnotation:marker animated:NO];
+ XCTAssertEqual(tester.mapView.selectedAnnotations.count, 0);
+}
+
+- (void)testMarkerAddWithoutDelegate {
+ XCTAssertFalse([tester.viewController respondsToSelector:@selector(mapView:imageForAnnotation:)]);
+
+ MGLPointAnnotation *marker = [MGLPointAnnotation new];
+ marker.coordinate = tester.mapView.centerCoordinate;
+ [tester.mapView addAnnotation:marker];
+
+ [tester.mapView selectAnnotation:marker animated:NO];
+ XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
+ XCTAssertEqual([[tester.mapView subviewsWithClassNamePrefix:@"SM"] count], 0); // no callout for no title
+
+ [tester.mapView deselectAnnotation:marker animated:NO];
+ marker.title = @"test";
+ [tester.mapView selectAnnotation:marker animated:NO];
+ XCTAssertEqualObjects(tester.mapView.selectedAnnotations.firstObject, marker);
+ XCTAssertGreaterThan([[tester.mapView subviewsWithClassNamePrefix:@"SM"] count], 0);
+}
+
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id<MGLAnnotation>)annotation {
+ return YES;
+}
+
+- (void)testTopLayoutGuide {
+ CGRect statusBarFrame, navigationBarFrame, compassFrame;
+ UINavigationBar *navigationBar = tester.viewController.navigationController.navigationBar;
+
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
+ @"compass should not be under status bar");
+
+ tester.viewController.navigationController.navigationBarHidden = NO;
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
+ @"compass should not be under navigation bar");
+
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeLeft];
+
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ navigationBarFrame = [tester.window convertRect:navigationBar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, navigationBarFrame),
+ @"rotated device should not have compass under navigation bar");
+
+ tester.viewController.navigationController.navigationBarHidden = YES;
+ compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ statusBarFrame = [tester.window convertRect:[[UIApplication sharedApplication] statusBarFrame] toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(compassFrame, statusBarFrame),
+ @"rotated device should not have compass under status bar");
+}
+
+- (void)testBottomLayoutGuide {
+ CGRect logoBugFrame, toolbarFrame, attributionButtonFrame;
+ UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox"];
+ UIToolbar *toolbar = tester.viewController.navigationController.toolbar;
+ UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"About this map"];
+
+ tester.viewController.navigationController.toolbarHidden = NO;
+
+ logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
+ @"logo bug should not be under toolbar");
+
+ attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
+ @"attribution button should not be under toolbar");
+
+ [system simulateDeviceRotationToOrientation:UIDeviceOrientationLandscapeRight];
+
+ logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ toolbarFrame = [tester.window convertRect:toolbar.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(logoBugFrame, toolbarFrame),
+ @"rotated device should not have logo bug under toolbar");
+
+ attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertFalse(CGRectIntersectsRect(attributionButtonFrame, toolbarFrame),
+ @"rotated device should not have attribution button under toolbar");
+}
+
+- (void)testInsetMapView {
+ [tester.viewController insetMapView];
+ [tester waitForAnimationsToFinish];
+
+ UIView *logoBug = (UIView *)[tester waitForViewWithAccessibilityLabel:@"Mapbox"];
+ UIView *attributionButton = (UIView *)[tester waitForViewWithAccessibilityLabel:@"About this map"];
+
+ CGRect mapViewFrame = [tester.mapView.superview convertRect:tester.mapView.frame toView:nil];
+
+ CGRect logoBugFrame = [logoBug.superview convertRect:logoBug.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(logoBugFrame, mapViewFrame),
+ @"logo bug should lie inside shrunken map view");
+
+ CGRect attributionButtonFrame = [attributionButton.superview convertRect:attributionButton.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(attributionButtonFrame, mapViewFrame),
+ @"attribution button should lie inside shrunken map view");
+
+ CGRect compassFrame = [tester.compass.superview convertRect:tester.compass.frame toView:nil];
+ XCTAssertTrue(CGRectIntersectsRect(compassFrame, mapViewFrame),
+ @"compass should lie inside shrunken map view");
+}
+
+- (void)testContentInsetsWithTinyMapView {
+ [tester.viewController tinyMapView];
+ [self keyValueObservingExpectationForObject:tester.mapView keyPath:@"contentInset" handler:^BOOL(id observedObject, NSDictionary *change) {
+ XCTAssertEqual(tester.mapView.contentInset.top,
+ 0,
+ @"map should not have top content inset");
+ XCTAssertEqual(tester.mapView.contentInset.bottom,
+ 0,
+ @"map should not have bottom content inset");
+ return YES;
+ }];
+ [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+ tester.mapView.frame = CGRectMake(0, 0, tester.mapView.frame.size.width, tester.mapView.frame.size.height);
+ [self keyValueObservingExpectationForObject:tester.mapView keyPath:@"contentInset" handler:^BOOL(id observedObject, NSDictionary *change) {
+ XCTAssertEqual(tester.mapView.contentInset.top,
+ tester.viewController.topLayoutGuide.length,
+ @"map should have top content inset equal to the top layout guide");
+ return YES;
+ }];
+ [self waitForExpectationsWithTimeout:2.0 handler:nil];
+}
+
+- (void)testDelegateRegionWillChange {
+ __block NSUInteger unanimatedCount;
+ __block NSUInteger animatedCount;
+ [[NSNotificationCenter defaultCenter] addObserverForName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ queue:nil
+ usingBlock:^(NSNotification *note) {
+ if ([note.userInfo[@"animated"] boolValue]) {
+ animatedCount++;
+ } else {
+ unanimatedCount++;
+ }
+ }];
+
+ [tester waitForTimeInterval:1];
+
+ unanimatedCount = 0;
+ animatedCount = 0;
+
+ NSNotification *notification = [system waitForNotificationName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ tester.mapView.centerCoordinate = CLLocationCoordinate2DMake(0, 0);
+ }];
+
+ [tester waitForTimeInterval:1];
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ NO,
+ @"regionWillChange delegate should not indicate animated change");
+ XCTAssertEqual(unanimatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one unanimated change");
+
+ notification = [system waitForNotificationName:@"regionWillChangeAnimated"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ [tester.mapView setCenterCoordinate:CLLocationCoordinate2DMake(45, 100) animated:YES];
+ }];
+
+ [tester waitForTimeInterval:1];
+
+ XCTAssertEqual([notification.userInfo[@"animated"] boolValue],
+ YES,
+ @"regionWillChange delegate should indicate an animated change");
+ XCTAssertEqual(animatedCount,
+ 1,
+ @"regionWillChange delegate should indicate one animated change");
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"regionWillChangeAnimated"
+ object:tester.mapView];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"regionWillChangeAnimated"
+ object:mapView
+ userInfo:@{ @"animated" : @(animated) }];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"regionDidChangeAnimated"
+ object:mapView
+ userInfo:@{ @"animated" : @(animated) }];
+}
+
+- (void)testDelegatesStartStopLocatingUser {
+ NSNotification *notification = [system waitForNotificationName:@"mapViewWillStartLocatingUser"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ tester.mapView.showsUserLocation = YES;
+ [self approveLocationIfNeeded];
+ }];
+
+ XCTAssertEqualObjects(notification.name,
+ @"mapViewWillStartLocatingUser",
+ @"mapViewWillStartLocatingUser delegate should receive message");
+ XCTAssertNotNil([tester.mapView valueForKeyPath:@"locationManager"],
+ "map view location manager should not be nil");
+
+ notification = [system waitForNotificationName:@"mapViewDidStopLocatingUser"
+ object:tester.mapView
+ whileExecutingBlock:^{
+ tester.mapView.showsUserLocation = NO;
+ }];
+
+ XCTAssertEqualObjects(notification.name,
+ @"mapViewDidStopLocatingUser",
+ @"mapViewDidStopLocatingUser delegate should receive message");
+ XCTAssertEqual(tester.mapView.userTrackingMode,
+ MGLUserTrackingModeNone,
+ @"user tracking mode should be none");
+ XCTAssertNil([tester.mapView valueForKeyPath:@"locationManager"],
+ "map view location manager should be nil");
+}
+
+- (void)mapViewWillStartLocatingUser:(MGLMapView *)mapView {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"mapViewWillStartLocatingUser" object:mapView];
+}
+
+- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"mapViewDidStopLocatingUser" object:mapView];
+}
+
+@end
diff --git a/platform/ios/test/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h b/platform/ios/uitest/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h
index c20a9c2b20..c20a9c2b20 100644
--- a/platform/ios/test/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h
+++ b/platform/ios/uitest/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMArg.h b/platform/ios/uitest/OCMock/OCMock/OCMArg.h
index d53437cb7d..d53437cb7d 100644
--- a/platform/ios/test/OCMock/OCMock/OCMArg.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMArg.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMConstraint.h b/platform/ios/uitest/OCMock/OCMock/OCMConstraint.h
index 777966ab7d..777966ab7d 100644
--- a/platform/ios/test/OCMock/OCMock/OCMConstraint.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMConstraint.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMLocation.h b/platform/ios/uitest/OCMock/OCMock/OCMLocation.h
index e510db7aaf..e510db7aaf 100644
--- a/platform/ios/test/OCMock/OCMock/OCMLocation.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMLocation.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMMacroState.h b/platform/ios/uitest/OCMock/OCMock/OCMMacroState.h
index 4b2d635086..4b2d635086 100644
--- a/platform/ios/test/OCMock/OCMock/OCMMacroState.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMMacroState.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMRecorder.h b/platform/ios/uitest/OCMock/OCMock/OCMRecorder.h
index f56d2ca4c0..f56d2ca4c0 100644
--- a/platform/ios/test/OCMock/OCMock/OCMRecorder.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMRecorder.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMStubRecorder.h b/platform/ios/uitest/OCMock/OCMock/OCMStubRecorder.h
index 890c9ef3bc..890c9ef3bc 100644
--- a/platform/ios/test/OCMock/OCMock/OCMStubRecorder.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMStubRecorder.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMock.h b/platform/ios/uitest/OCMock/OCMock/OCMock.h
index f0083b3507..f0083b3507 100644
--- a/platform/ios/test/OCMock/OCMock/OCMock.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMock.h
diff --git a/platform/ios/test/OCMock/OCMock/OCMockObject.h b/platform/ios/uitest/OCMock/OCMock/OCMockObject.h
index 63f2bae2be..63f2bae2be 100644
--- a/platform/ios/test/OCMock/OCMock/OCMockObject.h
+++ b/platform/ios/uitest/OCMock/OCMock/OCMockObject.h
diff --git a/platform/ios/test/OCMock/libOCMock.a b/platform/ios/uitest/OCMock/libOCMock.a
index 9bb38e21a3..9bb38e21a3 100644
--- a/platform/ios/test/OCMock/libOCMock.a
+++ b/platform/ios/uitest/OCMock/libOCMock.a
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/.gitignore b/platform/ios/uitest/OHHTTPStubs/.gitignore
index a27dff4c47..a27dff4c47 100644
--- a/platform/ios/test/OHHTTPStubs/.gitignore
+++ b/platform/ios/uitest/OHHTTPStubs/.gitignore
diff --git a/platform/ios/test/OHHTTPStubs/.travis.yml b/platform/ios/uitest/OHHTTPStubs/.travis.yml
index 45bfb794fd..45bfb794fd 100644
--- a/platform/ios/test/OHHTTPStubs/.travis.yml
+++ b/platform/ios/uitest/OHHTTPStubs/.travis.yml
diff --git a/platform/ios/test/OHHTTPStubs/CHANGELOG.md b/platform/ios/uitest/OHHTTPStubs/CHANGELOG.md
index 34f2e98990..34f2e98990 100644
--- a/platform/ios/test/OHHTTPStubs/CHANGELOG.md
+++ b/platform/ios/uitest/OHHTTPStubs/CHANGELOG.md
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.h
index bf1a8f5fce..bf1a8f5fce 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.m b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.m
index 327a4d2d20..327a4d2d20 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.xib b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.xib
index bab95b860c..bab95b860c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/MainViewController.xib
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/MainViewController.xib
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj
index d79722eabb..d79722eabb 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 68ace104de..68ace104de 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme
index 41f5f1bbc6..41f5f1bbc6 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Demo.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
index e23833bf24..e23833bf24 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
index cfc0e73f1c..cfc0e73f1c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile
index 30504595e8..30504595e8 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile.lock b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile.lock
index 0d7e09fc56..0d7e09fc56 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Podfile.lock
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Podfile.lock
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
index 7a6d07a0a9..7a6d07a0a9 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
index e466a30930..e466a30930 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
index 55a0a6a468..55a0a6a468 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
index f976df50d7..f976df50d7 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
index 773f3f322e..773f3f322e 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
index 81aae9624d..81aae9624d 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h
index 7a6d07a0a9..7a6d07a0a9 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/Compatibility.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
index e466a30930..e466a30930 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
index 55a0a6a468..55a0a6a468 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
index f976df50d7..f976df50d7 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
index 773f3f322e..773f3f322e 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
index 81aae9624d..81aae9624d 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json
index a1bed26164..a1bed26164 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Local Podspecs/OHHTTPStubs.podspec.json
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock
index 0d7e09fc56..0d7e09fc56 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Manifest.lock
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj
index cf0b52702f..cf0b52702f 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
index 39df59c3c9..39df59c3c9 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
index b9b8578c7f..b9b8578c7f 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
index 4deafde22c..4deafde22c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
index aa992a4adb..aa992a4adb 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
index a14c030722..a14c030722 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
index 8fb40d2e94..8fb40d2e94 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
index 6e93a65cc8..6e93a65cc8 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
index 95cf11d9fb..95cf11d9fb 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
index de1d241adc..de1d241adc 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
index 79d3603ad7..79d3603ad7 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
index 756aa4392a..756aa4392a 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m
index ade64bd1a9..ade64bd1a9 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h
index 5918510c4e..5918510c4e 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-environment.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh
index ea685a22b1..ea685a22b1 100755
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig
index 722a231f1e..722a231f1e 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig
index 722a231f1e..722a231f1e 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Pods/Target Support Files/Pods/Pods.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg
index c42a21cd74..c42a21cd74 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.jpg
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt
index e662f91b23..e662f91b23 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Stubs/stub.txt
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png
index 0891b7aabf..0891b7aabf 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/Default-568h@2x.png
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist
index 25bfd55966..25bfd55966 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch
index 20ef719789..20ef719789 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/OHHTTPStubsDemo-Prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m
index 478bfb3959..478bfb3959 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/ObjC/Supporting Files/main.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/AppDelegate.swift b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/AppDelegate.swift
index 58f489a7d7..58f489a7d7 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/AppDelegate.swift
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/AppDelegate.swift
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Main.storyboard b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Main.storyboard
index 438521ca5c..438521ca5c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Main.storyboard
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Main.storyboard
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/MainViewController.swift b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/MainViewController.swift
index 9133e20d46..9133e20d46 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/MainViewController.swift
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/MainViewController.swift
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj
index 48a07864e3..48a07864e3 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 919434a625..919434a625 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
index 870383aff8..870383aff8 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcodeproj/xcshareddata/xcschemes/OHHTTPStubsDemo.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
index cfc0e73f1c..cfc0e73f1c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/OHHTTPStubsDemo.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile
index 17dfc0103f..17dfc0103f 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile.lock b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile.lock
index 834a7de58b..834a7de58b 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Podfile.lock
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Podfile.lock
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
index 7a6d07a0a9..7a6d07a0a9 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/Compatibility.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
index e466a30930..e466a30930 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
index 55a0a6a468..55a0a6a468 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
index f976df50d7..f976df50d7 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
index 773f3f322e..773f3f322e 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
index 81aae9624d..81aae9624d 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
index e466a30930..e466a30930 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubs.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
index 55a0a6a468..55a0a6a468 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
index 247257b1d8..247257b1d8 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
index 773f3f322e..773f3f322e 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHHTTPStubsResponse.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
index 6a6273c69c..6a6273c69c 120000
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Headers/Public/OHHTTPStubs/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json
index a1bed26164..a1bed26164 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Local Podspecs/OHHTTPStubs.podspec.json
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock
index 834a7de58b..834a7de58b 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Manifest.lock
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj
index c30d5eaa4d..c30d5eaa4d 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
index 699b998d1c..699b998d1c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Pods.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist
index cea441462f..cea441462f 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
index cf3a26e1ef..cf3a26e1ef 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
index 4deafde22c..4deafde22c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
index aa992a4adb..aa992a4adb 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h
index d95666a8a9..d95666a8a9 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap
index 268a7c33d4..268a7c33d4 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
index a14c030722..a14c030722 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist
index c10e7eab4f..c10e7eab4f 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
index 56b72965aa..56b72965aa 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
index 6e93a65cc8..6e93a65cc8 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
index 95cf11d9fb..95cf11d9fb 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h
index 28cfb1c4c0..28cfb1c4c0 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs-umbrella.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap
index efe706e390..efe706e390 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.modulemap
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
index de1d241adc..de1d241adc 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods-OHHTTPStubs/Pods-OHHTTPStubs.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist
index 6974542586..6974542586 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
index 79d3603ad7..79d3603ad7 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
index 756aa4392a..756aa4392a 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m
index ade64bd1a9..ade64bd1a9 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h
index 5918510c4e..5918510c4e 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-environment.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh
index 91e9c70b4d..91e9c70b4d 100755
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-frameworks.sh
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh
index ea685a22b1..ea685a22b1 100755
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h
index 21dcfd2c21..21dcfd2c21 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods-umbrella.h
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig
index a5a034c3e4..a5a034c3e4 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap
index 8413413077..8413413077 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.modulemap
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig
index a5a034c3e4..a5a034c3e4 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Pods/Target Support Files/Pods/Pods.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg
index c42a21cd74..c42a21cd74 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.jpg
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.txt b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.txt
index e662f91b23..e662f91b23 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Stubs/stub.txt
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Stubs/stub.txt
diff --git a/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png
new file mode 100644
index 0000000000..0891b7aabf
--- /dev/null
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Default-568h@2x.png
Binary files differ
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json
index 36d2c80d88..36d2c80d88 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist
index 0ca4d80efa..0ca4d80efa 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib
index a9be7a994c..a9be7a994c 100644
--- a/platform/ios/test/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib
+++ b/platform/ios/uitest/OHHTTPStubs/Examples/Swift/Supporting Files/LaunchScreen.xib
diff --git a/platform/ios/test/OHHTTPStubs/LICENSE b/platform/ios/uitest/OHHTTPStubs/LICENSE
index a83928dd2e..a83928dd2e 100644
--- a/platform/ios/test/OHHTTPStubs/LICENSE
+++ b/platform/ios/uitest/OHHTTPStubs/LICENSE
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs.podspec b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs.podspec
index 7ddbe58e23..7ddbe58e23 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs.podspec
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs.podspec
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj
index 7b1a4fa4db..7b1a4fa4db 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 25fccc6ba4..25fccc6ba4 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme
index f5a14781a1..f5a14781a1 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs Mac Framework.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme
index d82274e7af..d82274e7af 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS Framework.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme
index 998a5b942f..998a5b942f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcodeproj/xcshareddata/xcschemes/OHHTTPStubs iOS StaticLib.xcscheme
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata
index 06ceb62b61..06ceb62b61 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
index 08de0be8d3..08de0be8d3 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/OHHTTPStubs.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile
index c20f4c541f..c20f4c541f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile.lock b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile.lock
index 5896b3b3b0..5896b3b3b0 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Podfile.lock
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Podfile.lock
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h
index dfa82f6691..dfa82f6691 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m
index 1de5812ca1..1de5812ca1 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h
index 9f390294e2..9f390294e2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m
index 4ae7275480..4ae7275480 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
index 8fed5a6891..8fed5a6891 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
index 6413297d72..6413297d72 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h
index 5e610d887f..5e610d887f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m
index 1da148282d..1da148282d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h
index 68273da58e..68273da58e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
index a880d75191..a880d75191 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
index 1f97f914f3..1f97f914f3 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h
index 85435564c3..85435564c3 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
index cfe609a2de..cfe609a2de 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
index aed4549815..aed4549815 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
index a56e8542e1..a56e8542e1 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
index 030e8faed7..030e8faed7 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
index 083d30c266..083d30c266 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
index f475773d02..f475773d02 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
index d53e6ec2db..d53e6ec2db 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE
index 0616192d00..0616192d00 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/LICENSE
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md
index 13e080c096..13e080c096 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/README.md
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h
index 312d680e0d..312d680e0d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
index c2d855a59c..c2d855a59c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h
index 1c1f8dd6f8..1c1f8dd6f8 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m
index 6627dbb1b8..6627dbb1b8 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h
index b94f1cb8e0..b94f1cb8e0 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m
index b7e2a26cb2..b7e2a26cb2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIAlertView+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h
index e6ed6de04c..e6ed6de04c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
index a225290a25..a225290a25 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h
index 5d523636e8..5d523636e8 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
index c06aa9ca4c..c06aa9ca4c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
index 94082f6cb5..94082f6cb5 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h
index 3f1bc086f5..3f1bc086f5 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m
index 927f56d843..927f56d843 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h
index 37ce772de0..37ce772de0 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m
index e266451fe5..e266451fe5 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h
index 202e8f4ea2..202e8f4ea2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m
index 525d02aead..525d02aead 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h
index ac762c8238..ac762c8238 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h
index 9dcc623c63..9dcc623c63 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPRequestOperationManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h
index 56feb9fb85..56feb9fb85 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFHTTPSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h
index 67519d9848..67519d9848 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkActivityIndicatorManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h
index 68fc7744f2..68fc7744f2 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworkReachabilityManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h
index a5a38da7dc..a5a38da7dc 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h
index fd1322db9c..fd1322db9c 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFSecurityPolicy.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h
index d9b35fb754..d9b35fb754 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLConnectionOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h
index ca8209b81f..ca8209b81f 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLRequestSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h
index e36a765d82..e36a765d82 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLResponseSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h
index 835101de7b..835101de7b 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/AFURLSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h
index c534ebfb02..c534ebfb02 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIActivityIndicatorView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h
index f992813385..f992813385 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIAlertView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h
index 8f2e221939..8f2e221939 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIButton+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h
index a95d67380f..a95d67380f 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIImageView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h
index 95017cce57..95017cce57 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIKit+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h
index 730b167dcd..730b167dcd 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIProgressView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h
index 8efd826209..8efd826209 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIRefreshControl+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h
index c8df6ef17b..c8df6ef17b 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Private/AFNetworking/UIWebView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h
index ac762c8238..ac762c8238 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h
index 9dcc623c63..9dcc623c63 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPRequestOperationManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h
index 56feb9fb85..56feb9fb85 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFHTTPSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h
index 67519d9848..67519d9848 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkActivityIndicatorManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h
index 68fc7744f2..68fc7744f2 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworkReachabilityManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h
index a5a38da7dc..a5a38da7dc 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h
index fd1322db9c..fd1322db9c 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFSecurityPolicy.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h
index d9b35fb754..d9b35fb754 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLConnectionOperation.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h
index ca8209b81f..ca8209b81f 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLRequestSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h
index e36a765d82..e36a765d82 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLResponseSerialization.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h
index 835101de7b..835101de7b 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/AFURLSessionManager.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h
index c534ebfb02..c534ebfb02 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIActivityIndicatorView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h
index f992813385..f992813385 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIAlertView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h
index 8f2e221939..8f2e221939 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIButton+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h
index a95d67380f..a95d67380f 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIImageView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h
index 95017cce57..95017cce57 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIKit+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h
index 730b167dcd..730b167dcd 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIProgressView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h
index 8efd826209..8efd826209 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIRefreshControl+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h
index c8df6ef17b..c8df6ef17b 120000
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Headers/Public/AFNetworking/UIWebView+AFNetworking.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock
index 5896b3b3b0..5896b3b3b0 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Manifest.lock
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj
index 7c52607f05..7c52607f05 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Pods.xcodeproj/project.pbxproj
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig
index 3487d75ca9..3487d75ca9 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m
index 2de591a2e2..2de591a2e2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch
index b9c163b498..b9c163b498 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig
index 1fb0c387d4..1fb0c387d4 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests-AFNetworking/Pods-OHHTTPStubs Mac Tests-AFNetworking.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown
index 59a175087d..59a175087d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist
index 899e8ae43e..899e8ae43e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m
index f124d0bf53..f124d0bf53 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h
index 42c1cb9116..42c1cb9116 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-environment.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh
index ea685a22b1..ea685a22b1 100755
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig
index 6514eaa721..6514eaa721 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig
index 6514eaa721..6514eaa721 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs Mac Tests/Pods-OHHTTPStubs Mac Tests.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig
index 8eb1a3989e..8eb1a3989e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m
index 8fb666b3d8..8fb666b3d8 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch
index aa992a4adb..aa992a4adb 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig
index 7f67782003..7f67782003 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking/Pods-OHHTTPStubs iOS Fmk Tests-AFNetworking.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown
index 59a175087d..59a175087d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist
index 899e8ae43e..899e8ae43e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m
index dbb179ac46..dbb179ac46 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh
index ea685a22b1..ea685a22b1 100755
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig
index 72ec462315..72ec462315 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig
index 72ec462315..72ec462315 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Fmk Tests/Pods-OHHTTPStubs iOS Fmk Tests.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig
index 927eecdfe5..927eecdfe5 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m
index 174408f7fa..174408f7fa 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch
index aa992a4adb..aa992a4adb 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig
index 758125c1eb..758125c1eb 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking/Pods-OHHTTPStubs iOS Lib Tests-AFNetworking.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown
index 59a175087d..59a175087d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist
index 899e8ae43e..899e8ae43e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m
index 391f5bc28d..391f5bc28d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh
index ea685a22b1..ea685a22b1 100755
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig
index 19fb024a98..19fb024a98 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig
index 19fb024a98..19fb024a98 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Lib Tests/Pods-OHHTTPStubs iOS Lib Tests.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig
index 6f43611720..6f43611720 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-Private.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m
index 8aa49d1389..8aa49d1389 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch
index 80fbb8dc3c..80fbb8dc3c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking-prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig
index b8aeea85b3..b8aeea85b3 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests-AFNetworking/Pods-OHHTTPStubs iOS Tests-AFNetworking.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown
index 59a175087d..59a175087d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.markdown
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist
index 899e8ae43e..899e8ae43e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-acknowledgements.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m
index 0fb6509a93..0fb6509a93 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-dummy.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h
index e1a5d47d3b..e1a5d47d3b 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-environment.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh
index 43f08523e1..43f08523e1 100755
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests-resources.sh
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig
index 89450f5b6a..89450f5b6a 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.debug.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig
index 89450f5b6a..89450f5b6a 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Pods/Target Support Files/Pods-OHHTTPStubs iOS Tests/Pods-OHHTTPStubs iOS Tests.release.xcconfig
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h
index b41ddda60f..b41ddda60f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h
index 2776a4f3b7..2776a4f3b7 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m
index 06d7363f23..06d7363f23 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/HTTPMessage/OHHTTPStubsResponse+HTTPMessage.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h
index 3069ff32ec..3069ff32ec 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m
index d88fee9bdd..d88fee9bdd 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h
index 77fe65e892..77fe65e892 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m
index 4de8996ae0..4de8996ae0 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Mocktail/OHHTTPStubs+Mocktail.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m
index b0e14a093c..b0e14a093c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m
index 1184dcad1f..1184dcad1f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h
index f4c2a07a98..f4c2a07a98 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m
index 4fa31f907b..4fa31f907b 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h
index 0ac0f6e124..0ac0f6e124 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m
index 03d0e7a6db..03d0e7a6db 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h
index fc8719df0d..fc8719df0d 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m
index aebcb99080..aebcb99080 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h
index 27f487f9fa..27f487f9fa 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m
index db8d7efa79..db8d7efa79 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h
index aa250547b8..aa250547b8 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m
index cac24a636a..cac24a636a 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h
index b3c301acdd..b3c301acdd 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m
index 18b769e45a..18b769e45a 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift
index 5229dcb808..5229dcb808 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Sources/Swift/OHHTTPStubsSwift.swift
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist
index bcee41c04a..bcee41c04a 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs Mac-Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist
index d3de8eefb6..d3de8eefb6 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/Supporting Files/OHHTTPStubs iOS-Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json
index e69de29bb2..e69de29bb2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/empty.bundle/nothingtoseehere.json
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json
index e69de29bb2..e69de29bb2 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Fixtures/emptyfile.json
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail
index 596a2ecdb4..596a2ecdb4 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/cards.tail
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail
index 1755e71d57..1755e71d57 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/login.tail
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail
index a21bc088c6..a21bc088c6 100755
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/MocktailFolder/logos_ebay.tail
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m
index 98086feeb1..98086feeb1 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/OHPathHelpersTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m
index a4ee670a6e..a4ee670a6e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/AFNetworkingTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m
index db7725703c..db7725703c 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/MocktailTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m
index 1321854169..1321854169 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionDelegateTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m
index 1fe6c91e87..1fe6c91e87 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLConnectionTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m
index 2e829ca432..2e829ca432 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NSURLSessionTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m
index ec263d543f..ec263d543f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/NilValuesTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift
index 3d2f1c3577..3d2f1c3577 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/SwiftHelpersTests.swift
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m
index b2e820f80f..b2e820f80f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/TimingTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m
index 0e34aea54f..0e34aea54f 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/Test Suites/WithContentsOfURLTests.m
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist
index 169b6f710e..169b6f710e 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Info.plist
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch
index a0a31a66c6..a0a31a66c6 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/UnitTests-Prefix.pch
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail
index 283ca0271b..283ca0271b 100644
--- a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail
+++ b/platform/ios/uitest/OHHTTPStubs/OHHTTPStubs/UnitTests/login.tail
diff --git a/platform/ios/test/OHHTTPStubs/README.md b/platform/ios/uitest/OHHTTPStubs/README.md
index 2c9c831393..2c9c831393 100644
--- a/platform/ios/test/OHHTTPStubs/README.md
+++ b/platform/ios/uitest/OHHTTPStubs/README.md
diff --git a/platform/ios/test/OHHTTPStubs/Rakefile b/platform/ios/uitest/OHHTTPStubs/Rakefile
index a7c7397fba..a7c7397fba 100644
--- a/platform/ios/test/OHHTTPStubs/Rakefile
+++ b/platform/ios/uitest/OHHTTPStubs/Rakefile
diff --git a/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..0d6f89234d
--- /dev/null
+++ b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj
@@ -0,0 +1,574 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 96567A221B0E84CD00D78776 /* LaunchScreen.xib */; };
+ 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96567A301B0E8BB900D78776 /* Images.xcassets */; };
+ DA482C801C12582600772FE3 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA482C7F1C12582600772FE3 /* Mapbox.framework */; };
+ DA482C811C12582600772FE3 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA482C7F1C12582600772FE3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ DA9C551D1CD9DFCD000A15C6 /* libKIF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9C551B1CD9DFA7000A15C6 /* libKIF.a */; };
+ DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */; };
+ DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043360196DBBD500E6F39D /* MGLTViewController.m */; };
+ DD043366196DBBE000E6F39D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DD043365196DBBE000E6F39D /* main.m */; };
+ DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0580E71ACB628200B112C9 /* IOKit.framework */; };
+ DD0E6F841B0190E200DC035A /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E6F701B0190E200DC035A /* libOCMock.a */; };
+ DD0E6F981B01B68E00DC035A /* OHHTTPStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */; };
+ DD0E6F991B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */; };
+ DD0E6F9A1B01B68E00DC035A /* OHHTTPStubsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */; };
+ DD0E6F9B1B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */; };
+ DD0E6F9C1B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */; };
+ DD0E6F9D1B01B68E00DC035A /* OHPathHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */; };
+ DDBD016C196DC4740033959E /* MapViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD0168196DC4740033959E /* MapViewTests.m */; };
+ DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */ = {isa = PBXBuildFile; fileRef = DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ DDBD0160196DC3D70033959E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DD04331B196DB9BC00E6F39D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DD043322196DB9BC00E6F39D;
+ remoteInfo = "Mapbox GL Tests";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ DA482C821C12582600772FE3 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ DA482C811C12582600772FE3 /* Mapbox.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 96567A221B0E84CD00D78776 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = SOURCE_ROOT; };
+ 96567A301B0E8BB900D78776 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = SOURCE_ROOT; };
+ DA482C7F1C12582600772FE3 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA9C551B1CD9DFA7000A15C6 /* libKIF.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libKIF.a; path = "../../../build/ios/Build/Products/Debug-iphonesimulator/libKIF.a"; sourceTree = "<group>"; };
+ DADD9EB51BD16D8B00DA9161 /* Compatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Compatibility.h; path = OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h; sourceTree = SOURCE_ROOT; };
+ DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL Tests.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTAppDelegate.m; sourceTree = SOURCE_ROOT; };
+ DD043360196DBBD500E6F39D /* MGLTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTViewController.m; sourceTree = SOURCE_ROOT; };
+ DD043361196DBBD500E6F39D /* MGLTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTViewController.h; sourceTree = SOURCE_ROOT; };
+ DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTAppDelegate.h; sourceTree = SOURCE_ROOT; };
+ DD043365196DBBE000E6F39D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; };
+ DD043367196DBCC200E6F39D /* App-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "App-Info.plist"; sourceTree = SOURCE_ROOT; };
+ DD0580E71ACB628200B112C9 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = DEVELOPER_DIR; };
+ DD0E6F701B0190E200DC035A /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOCMock.a; path = OCMock/libOCMock.a; sourceTree = SOURCE_ROOT; };
+ DD0E6F721B0190E200DC035A /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+OCMAdditions.h"; sourceTree = "<group>"; };
+ DD0E6F731B0190E200DC035A /* OCMArg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMArg.h; sourceTree = "<group>"; };
+ DD0E6F741B0190E200DC035A /* OCMConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMConstraint.h; sourceTree = "<group>"; };
+ DD0E6F751B0190E200DC035A /* OCMLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMLocation.h; sourceTree = "<group>"; };
+ DD0E6F761B0190E200DC035A /* OCMMacroState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMMacroState.h; sourceTree = "<group>"; };
+ DD0E6F771B0190E200DC035A /* OCMock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMock.h; sourceTree = "<group>"; };
+ DD0E6F781B0190E200DC035A /* OCMockObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMockObject.h; sourceTree = "<group>"; };
+ DD0E6F791B0190E200DC035A /* OCMRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMRecorder.h; sourceTree = "<group>"; };
+ DD0E6F7A1B0190E200DC035A /* OCMStubRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMStubRecorder.h; sourceTree = "<group>"; };
+ DD0E6F8D1B01B68E00DC035A /* OHHTTPStubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubs.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h; sourceTree = SOURCE_ROOT; };
+ DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubs.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.m; sourceTree = SOURCE_ROOT; };
+ DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubs+NSURLSessionConfiguration.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs+NSURLSessionConfiguration.m"; sourceTree = SOURCE_ROOT; };
+ DD0E6F901B01B68E00DC035A /* OHHTTPStubsResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubsResponse.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h; sourceTree = SOURCE_ROOT; };
+ DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubsResponse.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m; sourceTree = SOURCE_ROOT; };
+ DD0E6F921B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+HTTPMessage.h"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.h"; sourceTree = SOURCE_ROOT; };
+ DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+HTTPMessage.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+HTTPMessage.m"; sourceTree = SOURCE_ROOT; };
+ DD0E6F941B01B68E00DC035A /* OHHTTPStubsResponse+JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+JSON.h"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.h"; sourceTree = SOURCE_ROOT; };
+ DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+JSON.m"; path = "OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse+JSON.m"; sourceTree = SOURCE_ROOT; };
+ DD0E6F961B01B68E00DC035A /* OHPathHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OHPathHelpers.h; path = OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.h; sourceTree = SOURCE_ROOT; };
+ DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OHPathHelpers.m; path = OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers.m; sourceTree = SOURCE_ROOT; };
+ DDBD0152196DC3D70033959E /* Test Bundle.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Test Bundle.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DDBD0165196DC4560033959E /* Bundle-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Bundle-Info.plist"; sourceTree = SOURCE_ROOT; };
+ DDBD0168196DC4740033959E /* MapViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MapViewTests.m; sourceTree = SOURCE_ROOT; };
+ DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "KIFTestActor+MapboxGL.m"; sourceTree = SOURCE_ROOT; };
+ DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KIFTestActor+MapboxGL.h"; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ DD043320196DB9BC00E6F39D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA482C801C12582600772FE3 /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD014F196DC3D70033959E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD0580E81ACB628200B112C9 /* IOKit.framework in Frameworks */,
+ DA9C551D1CD9DFCD000A15C6 /* libKIF.a in Frameworks */,
+ DD0E6F841B0190E200DC035A /* libOCMock.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ DD04331A196DB9BC00E6F39D = {
+ isa = PBXGroup;
+ children = (
+ DD04332C196DB9BC00E6F39D /* App */,
+ DDBD0139196DC38D0033959E /* Tests */,
+ DD043325196DB9BC00E6F39D /* Frameworks */,
+ DD043324196DB9BC00E6F39D /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ DD043324196DB9BC00E6F39D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */,
+ DDBD0152196DC3D70033959E /* Test Bundle.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ DD043325196DB9BC00E6F39D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ DA9C551B1CD9DFA7000A15C6 /* libKIF.a */,
+ DA482C7F1C12582600772FE3 /* Mapbox.framework */,
+ DD0580E71ACB628200B112C9 /* IOKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ DD04332C196DB9BC00E6F39D /* App */ = {
+ isa = PBXGroup;
+ children = (
+ DD043362196DBBD500E6F39D /* MGLTAppDelegate.h */,
+ DD04335F196DBBD500E6F39D /* MGLTAppDelegate.m */,
+ DD043361196DBBD500E6F39D /* MGLTViewController.h */,
+ DD043360196DBBD500E6F39D /* MGLTViewController.m */,
+ DD04332D196DB9BC00E6F39D /* Supporting Files */,
+ );
+ name = App;
+ path = "Mapbox GL Tests";
+ sourceTree = "<group>";
+ };
+ DD04332D196DB9BC00E6F39D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 96567A301B0E8BB900D78776 /* Images.xcassets */,
+ DD043367196DBCC200E6F39D /* App-Info.plist */,
+ DD043365196DBBE000E6F39D /* main.m */,
+ 96567A221B0E84CD00D78776 /* LaunchScreen.xib */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ DD0E6F6B1B01906600DC035A /* OCMock */ = {
+ isa = PBXGroup;
+ children = (
+ DD0E6F701B0190E200DC035A /* libOCMock.a */,
+ DD0E6F711B0190E200DC035A /* OCMock */,
+ );
+ name = OCMock;
+ sourceTree = "<group>";
+ };
+ DD0E6F711B0190E200DC035A /* OCMock */ = {
+ isa = PBXGroup;
+ children = (
+ DD0E6F721B0190E200DC035A /* NSNotificationCenter+OCMAdditions.h */,
+ DD0E6F731B0190E200DC035A /* OCMArg.h */,
+ DD0E6F741B0190E200DC035A /* OCMConstraint.h */,
+ DD0E6F751B0190E200DC035A /* OCMLocation.h */,
+ DD0E6F761B0190E200DC035A /* OCMMacroState.h */,
+ DD0E6F771B0190E200DC035A /* OCMock.h */,
+ DD0E6F781B0190E200DC035A /* OCMockObject.h */,
+ DD0E6F791B0190E200DC035A /* OCMRecorder.h */,
+ DD0E6F7A1B0190E200DC035A /* OCMStubRecorder.h */,
+ );
+ name = OCMock;
+ path = OCMock/OCMock;
+ sourceTree = SOURCE_ROOT;
+ };
+ DD0E6F861B01B67100DC035A /* OHHTTPStubs */ = {
+ isa = PBXGroup;
+ children = (
+ DADD9EB51BD16D8B00DA9161 /* Compatibility.h */,
+ DD0E6F8D1B01B68E00DC035A /* OHHTTPStubs.h */,
+ DD0E6F8E1B01B68E00DC035A /* OHHTTPStubs.m */,
+ DD0E6F8F1B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m */,
+ DD0E6F901B01B68E00DC035A /* OHHTTPStubsResponse.h */,
+ DD0E6F911B01B68E00DC035A /* OHHTTPStubsResponse.m */,
+ DD0E6F921B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.h */,
+ DD0E6F931B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m */,
+ DD0E6F941B01B68E00DC035A /* OHHTTPStubsResponse+JSON.h */,
+ DD0E6F951B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m */,
+ DD0E6F961B01B68E00DC035A /* OHPathHelpers.h */,
+ DD0E6F971B01B68E00DC035A /* OHPathHelpers.m */,
+ );
+ name = OHHTTPStubs;
+ sourceTree = "<group>";
+ };
+ DDBD0139196DC38D0033959E /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD016B196DC4740033959E /* KIFTestActor+MapboxGL.h */,
+ DDBD016A196DC4740033959E /* KIFTestActor+MapboxGL.m */,
+ DDBD0168196DC4740033959E /* MapViewTests.m */,
+ DDBD0167196DC46B0033959E /* Supporting Files */,
+ DD0E6F6B1B01906600DC035A /* OCMock */,
+ DD0E6F861B01B67100DC035A /* OHHTTPStubs */,
+ );
+ name = Tests;
+ path = "Mapbox GL Tests";
+ sourceTree = "<group>";
+ };
+ DDBD0167196DC46B0033959E /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DDBD0165196DC4560033959E /* Bundle-Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ DD043322196DB9BC00E6F39D /* Mapbox GL Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */;
+ buildPhases = (
+ DD04331F196DB9BC00E6F39D /* Sources */,
+ DD043320196DB9BC00E6F39D /* Frameworks */,
+ DD043321196DB9BC00E6F39D /* Resources */,
+ DA482C821C12582600772FE3 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Mapbox GL Tests";
+ productName = "Mapbox GL Tests";
+ productReference = DD043323196DB9BC00E6F39D /* Mapbox GL Tests.app */;
+ productType = "com.apple.product-type.application";
+ };
+ DDBD0151196DC3D70033959E /* Test Bundle */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */;
+ buildPhases = (
+ DDBD014E196DC3D70033959E /* Sources */,
+ DDBD014F196DC3D70033959E /* Frameworks */,
+ DDBD0150196DC3D70033959E /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DDBD0161196DC3D70033959E /* PBXTargetDependency */,
+ );
+ name = "Test Bundle";
+ productName = "Test Bundle";
+ productReference = DDBD0152196DC3D70033959E /* Test Bundle.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ DD04331B196DB9BC00E6F39D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = MGLT;
+ LastUpgradeCheck = 0700;
+ ORGANIZATIONNAME = Mapbox;
+ TargetAttributes = {
+ DDBD0151196DC3D70033959E = {
+ TestTargetID = DD043322196DB9BC00E6F39D;
+ };
+ };
+ };
+ buildConfigurationList = DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = DD04331A196DB9BC00E6F39D;
+ productRefGroup = DD043324196DB9BC00E6F39D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ DD043322196DB9BC00E6F39D /* Mapbox GL Tests */,
+ DDBD0151196DC3D70033959E /* Test Bundle */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ DD043321196DB9BC00E6F39D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 96567A311B0E8BB900D78776 /* Images.xcassets in Resources */,
+ 96567A231B0E84CD00D78776 /* LaunchScreen.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD0150196DC3D70033959E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ DD04331F196DB9BC00E6F39D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD043366196DBBE000E6F39D /* main.m in Sources */,
+ DD043363196DBBD500E6F39D /* MGLTAppDelegate.m in Sources */,
+ DD043364196DBBD500E6F39D /* MGLTViewController.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DDBD014E196DC3D70033959E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DD0E6F981B01B68E00DC035A /* OHHTTPStubs.m in Sources */,
+ DD0E6F9C1B01B68E00DC035A /* OHHTTPStubsResponse+JSON.m in Sources */,
+ DD0E6F9B1B01B68E00DC035A /* OHHTTPStubsResponse+HTTPMessage.m in Sources */,
+ DDBD016D196DC4740033959E /* KIFTestActor+MapboxGL.m in Sources */,
+ DD0E6F991B01B68E00DC035A /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */,
+ DD0E6F9A1B01B68E00DC035A /* OHHTTPStubsResponse.m in Sources */,
+ DDBD016C196DC4740033959E /* MapViewTests.m in Sources */,
+ DD0E6F9D1B01B68E00DC035A /* OHPathHelpers.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ DDBD0161196DC3D70033959E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DD043322196DB9BC00E6F39D /* Mapbox GL Tests */;
+ targetProxy = DDBD0160196DC3D70033959E /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ DD043356196DB9BC00E6F39D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SYMROOT = ../../../build/ios;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ DD043357196DB9BC00E6F39D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ SDKROOT = iphoneos;
+ SYMROOT = ../../../build/ios;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ DD043359196DB9BC00E6F39D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+ HEADER_SEARCH_PATHS = "";
+ INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ OTHER_LDFLAGS = "";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ DD04335A196DB9BC00E6F39D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ FRAMEWORK_SEARCH_PATHS = "$(inherited)";
+ HEADER_SEARCH_PATHS = "";
+ INFOPLIST_FILE = "$(SRCROOT)/App-Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ OTHER_LDFLAGS = "";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ DDBD0163196DC3D70033959E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ "KIF_XCTEST=1",
+ );
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ OCMock,
+ OHHTTPStubs/OHHTTPStubs/Sources,
+ );
+ INFOPLIST_FILE = "Bundle-Info.plist";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/OCMock",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ XCTest,
+ "-ObjC",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Debug;
+ };
+ DDBD0164196DC3D70033959E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Mapbox GL Tests.app/Mapbox GL Tests";
+ GCC_PREPROCESSOR_DEFINITIONS = "KIF_XCTEST=1";
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+ OCMock,
+ OHHTTPStubs/OHHTTPStubs/Sources,
+ );
+ INFOPLIST_FILE = "Bundle-Info.plist";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/OCMock",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-framework",
+ XCTest,
+ "-ObjC",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUNDLE_LOADER)";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ DD04331E196DB9BC00E6F39D /* Build configuration list for PBXProject "ios-tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD043356196DB9BC00E6F39D /* Debug */,
+ DD043357196DB9BC00E6F39D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DD043358196DB9BC00E6F39D /* Build configuration list for PBXNativeTarget "Mapbox GL Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DD043359196DB9BC00E6F39D /* Debug */,
+ DD04335A196DB9BC00E6F39D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DDBD0162196DC3D70033959E /* Build configuration list for PBXNativeTarget "Test Bundle" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DDBD0163196DC3D70033959E /* Debug */,
+ DDBD0164196DC3D70033959E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = DD04331B196DB9BC00E6F39D /* Project object */;
+}
diff --git a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 4cad7961db..4cad7961db 100644
--- a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout
index 68c68a2234..68c68a2234 100644
--- a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout
+++ b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/Mapbox GL Tests.xccheckout
diff --git a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
index 33408b6083..33408b6083 100644
--- a/platform/ios/test/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
+++ b/platform/ios/uitest/ios-tests.xcodeproj/project.xcworkspace/xcshareddata/ios-tests.xccheckout
diff --git a/platform/ios/test/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
index df15849ee0..df15849ee0 100644
--- a/platform/ios/test/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
+++ b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme
diff --git a/platform/ios/test/main.m b/platform/ios/uitest/main.m
index d79750dcdd..d79750dcdd 100644
--- a/platform/ios/test/main.m
+++ b/platform/ios/uitest/main.m
diff --git a/platform/linux/README.md b/platform/linux/README.md
index 55f3e71dba..bb05284609 100644
--- a/platform/linux/README.md
+++ b/platform/linux/README.md
@@ -8,12 +8,12 @@ This process gives you a Linux desktop app built on a Linux host system.
### Build
-Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
+Install GCC 5+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
sudo apt-get update
- sudo apt-get install gcc-4.9 g++-4.9
- export CXX=g++-4.9
+ sudo apt-get install gcc-5 g++-5
+ export CXX=g++-5
Ensure you have git and other build essentials:
@@ -33,14 +33,13 @@ Set the environment variable `MAPBOX_ACCESS_TOKEN` to your [Mapbox access token]
export MAPBOX_ACCESS_TOKEN=MYTOKEN
-Then, you can then proceed to build the library:
+Then, you can then proceed to build the test application:
- git submodule update --init
- make linux
+ make glfw-app
Set an access token as described below, and then run:
- make run-linux
+ make run-glfw-app
### Test
diff --git a/platform/linux/main.cpp b/platform/linux/main.cpp
deleted file mode 100644
index 98fb32075e..0000000000
--- a/platform/linux/main.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-#include <mbgl/mbgl.hpp>
-#include <mbgl/util/default_styles.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/platform/platform.hpp>
-#include <mbgl/platform/default/settings_json.hpp>
-#include <mbgl/platform/default/glfw_view.hpp>
-#include <mbgl/storage/default_file_source.hpp>
-
-#include <signal.h>
-#include <getopt.h>
-#include <fstream>
-#include <sstream>
-#include <cstdlib>
-#include <cstdio>
-
-namespace {
-
-std::unique_ptr<GLFWView> view;
-
-}
-
-void quit_handler(int) {
- if (view) {
- mbgl::Log::Info(mbgl::Event::Setup, "waiting for quit...");
- view->setShouldClose();
- } else {
- exit(0);
- }
-}
-
-int main(int argc, char *argv[]) {
- bool fullscreen = false;
- bool benchmark = false;
- std::string style;
- double latitude = 0, longitude = 0;
- double bearing = 0, zoom = 1, pitch = 0;
- bool skipConfig = false;
-
- const struct option long_options[] = {
- {"fullscreen", no_argument, 0, 'f'},
- {"benchmark", no_argument, 0, 'b'},
- {"style", required_argument, 0, 's'},
- {"lon", required_argument, 0, 'x'},
- {"lat", required_argument, 0, 'y'},
- {"zoom", required_argument, 0, 'z'},
- {"bearing", required_argument, 0, 'r'},
- {"pitch", required_argument, 0, 'p'},
- {0, 0, 0, 0}
- };
-
- while (true) {
- int option_index = 0;
- int opt = getopt_long(argc, argv, "fbs:", long_options, &option_index);
- if (opt == -1) break;
- switch (opt)
- {
- case 0:
- if (long_options[option_index].flag != 0)
- break;
- case 'f':
- fullscreen = true;
- break;
- case 'b':
- benchmark = true;
- break;
- case 's':
- style = std::string("asset://") + std::string(optarg);
- break;
- case 'x':
- longitude = atof(optarg);
- skipConfig = true;
- break;
- case 'y':
- latitude = atof(optarg);
- skipConfig = true;
- break;
- case 'z':
- zoom = atof(optarg);
- skipConfig = true;
- break;
- case 'r':
- bearing = atof(optarg);
- skipConfig = true;
- break;
- case 'p':
- pitch = atof(optarg);
- skipConfig = true;
- break;
- default:
- break;
- }
-
- }
-
- // sigint handling
- struct sigaction sigIntHandler;
- sigIntHandler.sa_handler = quit_handler;
- sigemptyset(&sigIntHandler.sa_mask);
- sigIntHandler.sa_flags = 0;
- sigaction(SIGINT, &sigIntHandler, NULL);
-
- if (benchmark) {
- mbgl::Log::Info(mbgl::Event::General, "BENCHMARK MODE: Some optimizations are disabled.");
- }
-
- view = std::make_unique<GLFWView>(fullscreen, benchmark);
-
- mbgl::DefaultFileSource fileSource("/tmp/mbgl-cache.db", ".");
-
- // Set access token if present
- const char *token = getenv("MAPBOX_ACCESS_TOKEN");
- if (token == nullptr) {
- mbgl::Log::Warning(mbgl::Event::Setup, "no access token set. mapbox.com tiles won't work.");
- } else {
- fileSource.setAccessToken(std::string(token));
- }
-
- mbgl::Map map(*view, fileSource);
-
- // Load settings
- mbgl::Settings_JSON settings;
-
- if (skipConfig) {
- map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
- map.setBearing(bearing);
- map.setPitch(pitch);
- mbgl::Log::Info(mbgl::Event::General, "Location: %f/%f (z%.2f, %.2f deg)", latitude, longitude, zoom, bearing);
- } else {
- map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom);
- map.setBearing(settings.bearing);
- map.setPitch(settings.pitch);
- map.setDebug(mbgl::MapDebugOptions(settings.debug));
- }
-
- view->setChangeStyleCallback([&map] () {
- static uint8_t currentStyleIndex;
-
- if (++currentStyleIndex == mbgl::util::default_styles::numOrderedStyles) {
- currentStyleIndex = 0;
- }
-
- mbgl::util::default_styles::DefaultStyle newStyle = mbgl::util::default_styles::orderedStyles[currentStyleIndex];
- map.setStyleURL(newStyle.url);
- view->setWindowTitle(newStyle.name);
-
- mbgl::Log::Info(mbgl::Event::Setup, "Changed style to: %s", newStyle.name);
- });
-
- // Load style
- if (style.empty()) {
- mbgl::util::default_styles::DefaultStyle newStyle = mbgl::util::default_styles::orderedStyles[0];
- style = newStyle.url;
- view->setWindowTitle(newStyle.name);
- }
-
- map.setStyleURL(style);
-
- view->run();
-
- // Save settings
- mbgl::LatLng latLng = map.getLatLng();
- settings.latitude = latLng.latitude;
- settings.longitude = latLng.longitude;
- settings.zoom = map.getZoom();
- settings.bearing = map.getBearing();
- settings.pitch = map.getPitch();
- settings.debug = mbgl::EnumType(map.getDebug());
- if (!skipConfig) {
- settings.save();
- }
- mbgl::Log::Info(mbgl::Event::General,
- "Exit location: --lat=\"%f\" --lon=\"%f\" --zoom=\"%f\" --bearing \"%f\"",
- settings.latitude, settings.longitude, settings.zoom, settings.bearing);
-
- return 0;
-}
diff --git a/platform/linux/mapboxgl-app.gypi b/platform/linux/mapboxgl-app.gypi
deleted file mode 100644
index 533f0b85d2..0000000000
--- a/platform/linux/mapboxgl-app.gypi
+++ /dev/null
@@ -1,66 +0,0 @@
-{
- 'includes': [
- '../../gyp/common.gypi',
- ],
- 'targets': [
- { 'target_name': 'linuxapp',
- 'product_name': 'mapbox-gl',
- 'type': 'executable',
-
- 'dependencies': [
- 'mbgl.gyp:core',
- 'mbgl.gyp:platform-<(platform_lib)',
- 'mbgl.gyp:http-<(http_lib)',
- 'mbgl.gyp:asset-<(asset_lib)',
- 'mbgl.gyp:copy_certificate_bundle',
- ],
-
- 'sources': [
- 'main.cpp',
- '../default/settings_json.cpp',
- '../default/glfw_view.hpp',
- '../default/glfw_view.cpp',
- '../default/log_stderr.cpp',
- ],
-
- 'variables' : {
- 'cflags_cc': [
- '<@(opengl_cflags)',
- '<@(boost_cflags)',
- '<@(glfw_cflags)',
- '<@(variant_cflags)',
- ],
- 'ldflags': [
- '<@(glfw_ldflags)',
- ],
- 'libraries': [
- '<@(glfw_static_libs)',
- ],
- },
-
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'SDKROOT': 'macosx',
- 'SUPPORTED_PLATFORMS':'macosx',
- 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
- 'MACOSX_DEPLOYMENT_TARGET': '10.10',
- },
- }, {
- 'cflags_cc': [ '<@(cflags_cc)' ],
- }]
- ],
-
- 'link_settings': {
- 'conditions': [
- ['OS == "mac"', {
- 'libraries': [ '<@(libraries)' ],
- 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(libraries)', '<@(ldflags)' ],
- }]
- ],
- },
- },
- ],
-}
diff --git a/platform/linux/platform.gyp b/platform/linux/platform.gyp
new file mode 100644
index 0000000000..7adf5337c4
--- /dev/null
+++ b/platform/linux/platform.gyp
@@ -0,0 +1,125 @@
+{
+ 'variables': {
+ 'loop_lib': 'uv',
+ 'headless_lib': 'glx',
+ 'coverage': '<!(echo $ENABLE_COVERAGE)>',
+ },
+ 'conditions': [
+ ['OS == "mac"', {
+ 'variables': {
+ 'headless_lib': 'cgl',
+ }
+ }],
+ ],
+ 'includes': [
+ '../../mbgl.gypi',
+ '../../test/test.gypi',
+ '../../benchmark/benchmark.gypi',
+ '../../bin/glfw.gypi',
+ '../../bin/render.gypi',
+ '../../bin/offline.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test',
+ 'type': 'executable',
+
+ 'dependencies': [
+ 'test-lib',
+ 'platform-lib',
+ 'copy_certificate_bundle',
+ ],
+
+ 'sources': [
+ '../../test/src/main.cpp',
+ ],
+ },
+ {
+ 'target_name': 'benchmark',
+ 'type': 'executable',
+
+ 'dependencies': [
+ 'benchmark-lib',
+ 'platform-lib',
+ ],
+
+ 'sources': [
+ '../../benchmark/src/main.cpp',
+ ],
+ },
+ {
+ 'target_name': 'platform-lib',
+ 'product_name': 'mbgl-platform-linux',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'core',
+ ],
+
+ 'include_dirs': [
+ '../default',
+ '../../include',
+ '../../src', # TODO: eliminate
+ ],
+
+ 'sources': [
+ '../default/log_stderr.cpp',
+ '../default/string_stdlib.cpp',
+ '../default/thread.cpp',
+ '../default/image.cpp',
+ '../default/webp_reader.cpp',
+ '../default/png_reader.cpp',
+ '../default/jpeg_reader.cpp',
+ '../default/asset_file_source.cpp',
+ '../default/http_file_source.cpp',
+ '../default/default_file_source.cpp',
+ '../default/online_file_source.cpp',
+ '../default/mbgl/storage/offline.hpp',
+ '../default/mbgl/storage/offline.cpp',
+ '../default/mbgl/storage/offline_database.hpp',
+ '../default/mbgl/storage/offline_database.cpp',
+ '../default/mbgl/storage/offline_download.hpp',
+ '../default/mbgl/storage/offline_download.cpp',
+ '../default/sqlite3.hpp',
+ '../default/sqlite3.cpp',
+ ],
+
+ 'cflags_cc': [
+ '<@(boost_cflags)',
+ '<@(nunicode_cflags)',
+ '<@(sqlite_cflags)',
+ '<@(rapidjson_cflags)',
+ '<@(libcurl_cflags)',
+ '<@(libpng_cflags)',
+ '<@(libjpeg-turbo_cflags)',
+ '<@(webp_cflags)',
+ ],
+
+ 'link_settings': {
+ 'libraries': [
+ '<@(nunicode_static_libs)',
+ '<@(nunicode_ldflags)',
+ '<@(sqlite_static_libs)',
+ '<@(sqlite_ldflags)',
+ '<@(zlib_static_libs)',
+ '<@(zlib_ldflags)',
+ '<@(libcurl_static_libs)',
+ '<@(libcurl_ldflags)',
+ '<@(libpng_static_libs)',
+ '<@(libpng_ldflags)',
+ '<@(libjpeg-turbo_static_libs)',
+ '<@(libjpeg-turbo_ldflags)',
+ '<@(webp_static_libs)',
+ '<@(webp_ldflags)',
+ ],
+ },
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../include',
+ ],
+ },
+ },
+ ],
+}
diff --git a/platform/linux/scripts/after_script.sh b/platform/linux/scripts/after_script.sh
index b5397f1df2..4989f0c444 100755
--- a/platform/linux/scripts/after_script.sh
+++ b/platform/linux/scripts/after_script.sh
@@ -3,13 +3,9 @@
set -e
set -o pipefail
-if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- # Install and add awscli to PATH for uploading the results
- pip install --user awscli
- export PATH="`python -m site --user-base`/bin:${PATH}"
-
- REPO_NAME=$(basename $TRAVIS_REPO_SLUG)
+JOB=$1
+if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
aws s3 cp --recursive --acl public-read --exclude "*" --include "*/actual.png" test/fixtures \
- s3://mapbox/$REPO_NAME/render-tests/$TRAVIS_JOB_NUMBER
+ s3://mapbox/mapbox-gl-native/render-tests/$JOB
fi
diff --git a/platform/linux/scripts/configure.sh b/platform/linux/scripts/configure.sh
index 69d3f22bff..8f041d589a 100644
--- a/platform/linux/scripts/configure.sh
+++ b/platform/linux/scripts/configure.sh
@@ -1,7 +1,11 @@
#!/usr/bin/env bash
-BOOST_VERSION=1.59.0
-BOOST_LIBPROGRAM_OPTIONS_VERSION=1.59.0
+CXX11ABI=$(scripts/check-cxx11abi.sh)
+
+UNIQUE_RESOURCE_VERSION=dev
+PROTOZERO_VERSION=1.3.0
+BOOST_VERSION=1.60.0
+BOOST_LIBPROGRAM_OPTIONS_VERSION=1.60.0
LIBCURL_VERSION=system
GLFW_VERSION=3.1.2
LIBPNG_VERSION=1.6.20
@@ -10,12 +14,15 @@ SQLITE_VERSION=3.9.1
LIBUV_VERSION=1.7.5
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
-GEOJSONVT_VERSION=3.1.0
-VARIANT_VERSION=1.0
+GEOMETRY_VERSION=0.5.0
+GEOJSONVT_VERSION=4.1.2${CXX11ABI:-}
+VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
-GTEST_VERSION=1.7.0
+GTEST_VERSION=1.7.0${CXX11ABI:-}
PIXELMATCH_VERSION=0.9.0
WEBP_VERSION=0.5.0
+EARCUT_VERSION=0.11
+BENCHMARK_VERSION=1.0.0
function print_opengl_flags {
CONFIG+=" 'opengl_cflags%': $(quote_flags $(pkg-config gl x11 --cflags)),"$LN
diff --git a/platform/linux/scripts/coveralls.sh b/platform/linux/scripts/coveralls.sh
index 468fa4774b..8d5903de6c 100755
--- a/platform/linux/scripts/coveralls.sh
+++ b/platform/linux/scripts/coveralls.sh
@@ -3,11 +3,22 @@
set -e
set -o pipefail
-source ./platform/linux/scripts/setup.sh
+mapbox_time "install_lcov" \
+mason install lcov 1.12
-################################################################################
-# Coveralls
-################################################################################
+# Collect coverage data and save it into coverage.info
+mapbox_time "lcov_capture" \
+`mason prefix lcov 1.12`/usr/bin/lcov \
+ --quiet \
+ --capture \
+ --no-external \
+ --gcov-tool "gcov-5" \
+ --directory "src/mbgl" \
+ --directory "platform" \
+ --directory "include/mbgl" \
+ --directory "build/linux-x86_64/${BUILDTYPE}" \
+ --base-directory "build/linux-x86_64/${BUILDTYPE}" \
+ --output-file "build/linux-x86_64/${BUILDTYPE}/coverage.info"
-mapbox_time "make_coveralls" \
-make coveralls -j${JOBS}
+mapbox_time "coveralls_upload" \
+coveralls-lcov "build/linux-x86_64/${BUILDTYPE}/coverage.info"
diff --git a/platform/linux/scripts/defaults.mk b/platform/linux/scripts/defaults.mk
deleted file mode 100644
index c387135bb1..0000000000
--- a/platform/linux/scripts/defaults.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-HEADLESS ?= glx
-PLATFORM ?= linux
-ASSET ?= fs
-HTTP ?= curl
diff --git a/platform/linux/scripts/install.sh b/platform/linux/scripts/install.sh
deleted file mode 100755
index a254d312ec..0000000000
--- a/platform/linux/scripts/install.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-mapbox_time "checkout_mason" \
-git submodule update --init .mason
-
-PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason" \
-mapbox_time "install_mesa" \
-mason install mesa 10.4.3
diff --git a/platform/linux/scripts/run.sh b/platform/linux/scripts/run.sh
deleted file mode 100755
index 1c0c13968a..0000000000
--- a/platform/linux/scripts/run.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-source ./platform/linux/scripts/setup.sh
-
-BUILDTYPE=${BUILDTYPE:-Release}
-
-################################################################################
-# Build
-################################################################################
-
-mapbox_time "compile_program" \
-make linux -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "compile_render_binary" \
-make render -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "compile_offline_binary" \
-make offline -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "compile_tests" \
-make test -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-################################################################################
-# Test
-################################################################################
-
-mapbox_time "run_tests" \
-make test-* BUILDTYPE=${BUILDTYPE}
diff --git a/platform/linux/scripts/setup.sh b/platform/linux/scripts/setup.sh
deleted file mode 100755
index af0eafb5cf..0000000000
--- a/platform/linux/scripts/setup.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# This script is sourced; do not set -e or -o pipefail here.
-
-# Ensure mason is on the PATH
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-
-# Set the core file limit to unlimited so a core file is generated upon crash
-ulimit -c unlimited -S
-
-################################################################################
-# X Server setup
-################################################################################
-
-# Start the mock X server
-if [ -f /etc/init.d/xvfb ] ; then
- mapbox_time "start_xvfb" \
- sh -e /etc/init.d/xvfb start
- sleep 2 # sometimes, xvfb takes some time to start up
-fi
-
-# Make sure we're connecting to xvfb
-export DISPLAY=:99.0
-
-# Make sure we're loading the 10.4.3 libs we installed manually
-export LD_LIBRARY_PATH="`mason prefix mesa 10.4.3`/lib:${LD_LIBRARY_PATH:-}"
-
-mapbox_time "glxinfo" \
-glxinfo
diff --git a/platform/linux/scripts/tidy.sh b/platform/linux/scripts/tidy.sh
deleted file mode 100755
index 424c82c3cd..0000000000
--- a/platform/linux/scripts/tidy.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-# Ensure mason is on the PATH
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-
-BUILDTYPE=${BUILDTYPE:-Release}
-
-export CLANG_TIDY=clang-tidy-3.8
-
-mapbox_time "config" \
-make config
-
-mapbox_time "tidy" \
-make tidy
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index 25e46b3e50..e1bff6cc28 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,34 @@
+# 3.2.1
+
+- Fixes a memory leak in raster image data ([#5269](https://github.com/mapbox/mapbox-gl-native/pull/5269))
+
+# 3.2.0
+
+- Switches to [earcut.hpp](https://github.com/mapbox/earcut.hpp) for tessellation ([#2444](https://github.com/mapbox/mapbox-gl-native/pull/2444))
+
+# 3.1.3
+
+- Fixes a leak in TexturePoolHolder ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
+- Fixes a bug where a callback would be fired after an AsyncRequest had been cancelled ([#5162](https://github.com/mapbox/mapbox-gl-native/pull/5162))
+
+# 3.1.2
+
+- Fixes a race condition with animated transitions ([#4836](https://github.com/mapbox/mapbox-gl-native/pull/4836))
+
+# 3.1.1
+
+- Moves node-pre-gyp from `bundledDependencies` to `preinstall` ([#4680](https://github.com/mapbox/mapbox-gl-native/pull/4680))
+
+# 3.1.0
+
+- Adds debug render options ([#3840](https://github.com/mapbox/mapbox-gl-native/pull/3840))
+- Fixes circle bucket rendering on tile boundaries ([#3764](https://github.com/mapbox/mapbox-gl-native/issues/3764))
+- Fixes a segfault caused by improperly disposing the entire module in the `NodeLog` destructor ([#4639](https://github.com/mapbox/mapbox-gl-native/pull/4639))
+- Fixes an issue with vanishing GeoJSON layers at high zoom levels ([#4632](https://github.com/mapbox/mapbox-gl-native/issues/4632))
+- Fixes inheritance from EventEmitter ([#4567](https://github.com/mapbox/mapbox-gl-native/pull/4576))
+- Fixes intermittent `stencil mask overflow` error ([#962](https://github.com/mapbox/mapbox-gl-native/issues/962))
+- Drops support for Node.js v5.x prebuilt binaries due to ongoing npm3 instability ([#4370](https://github.com/mapbox/mapbox-gl-native/issues/4370))
+
# 3.0.2
- Fixes a memory leak in `NodeMap::request` ([#3829](https://github.com/mapbox/mapbox-gl-native/pull/3829))
diff --git a/platform/node/README.md b/platform/node/README.md
index 706fcf86cd..50f53d72eb 100644
--- a/platform/node/README.md
+++ b/platform/node/README.md
@@ -1,7 +1,6 @@
# node-mapbox-gl-native
[![NPM](https://nodei.co/npm/mapbox-gl-native.png)](https://npmjs.org/package/mapbox-gl-native)
-[![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds)
## Installing
@@ -10,30 +9,49 @@ Requires a modern C++ runtime that supports C++14.
By default, installs binaries. On these platforms no additional dependencies are needed.
- 64 bit OS X or 64 bit Linux
-- Node.js v4+
+- Node.js v4.x _(note: v5+ is known to have issues)_
-Just run:
+Run:
```
npm install mapbox-gl-native
```
-Other platforms will fall back to a source compile with `make node`. To compile this module, make sure all submodules are initialized with `git submodule update --init` and install the [external dependencies required to build from source](https://github.com/mapbox/mapbox-gl-native/blob/node-v2.1.0/INSTALL.md#2-installing-dependencies).
+Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites.
## Testing
```
npm test
+npm run test-suite
```
## Rendering a map tile
```js
+var mbgl = require('mapbox-gl-native');
+var sharp = require('sharp');
var map = new mbgl.Map({ request: function() {} });
+
map.load(require('./test/fixtures/style.json'));
-map.render({}, function(err, image) {
+
+map.render({}, function(err, buffer) {
if (err) throw err;
- fs.writeFileSync('image.png', image);
+
+ map.release();
+
+ var image = sharp(buffer, {
+ raw: {
+ width: 512,
+ height: 512,
+ channels: 4
+ }
+ });
+
+ // Convert raw image buffer to PNG
+ image.toFile('image.png', function(err) {
+ if (err) throw err;
+ });
});
```
@@ -46,11 +64,12 @@ The first argument passed to `map.render` is an options object, all keys are opt
height: {height}, // number (px), defaults to 512
center: [{longitude}, {latitude}], // array of numbers (coordinates), defaults to [0,0]
bearing: {bearing}, // number (in degrees, counter-clockwise from north), defaults to 0
+ pitch: {pitch}, // number (in degrees, arcing towards the horizon), defaults to 0
classes: {classes} // array of strings
}
```
-When you are finished using a map object, you can call `map.release()` to dispose the internal map resources manually. This is not necessary, but can be helpful to optimize resource usage (memory, file sockets) on a more granualar level than v8's garbage collector.
+When you are finished using a map object, you can call `map.release()` to permanently dispose the internal map resources. This is not necessary, but can be helpful to optimize resource usage (memory, file sockets) on a more granualar level than V8's garbage collector. Calling `map.release()` will prevent a map object from being used for any further render calls, but can be safely called as soon as the `map.render()` callback returns, as the returned pixel buffer will always be retained for the scope of the callback.
## Implementing a file source
@@ -65,7 +84,7 @@ var map = new mbgl.Map({
});
```
-The `request()` method starts a new request to a file. The `ratio` sets the scale at which the map will render tiles, such as `2.0` for rendering images for high pixel density displays. The `req` parameter has two properties:
+The `request()` method handles a request for a resource. The `ratio` sets the scale at which the map will render tiles, such as `2.0` for rendering images for high pixel density displays. The `req` parameter has two properties:
```json
{
@@ -74,7 +93,7 @@ The `request()` method starts a new request to a file. The `ratio` sets the scal
}
```
-The `kind` is an enum and defined in [`mbgl.Resource`](https://github.com/mapbox/mapbox-gl-native/blob/node/include/mbgl/storage/resource.hpp):
+The `kind` is an enum and defined in [`mbgl.Resource`](https://github.com/mapbox/mapbox-gl-native/blob/master/include/mbgl/storage/resource.hpp):
```json
{
@@ -88,7 +107,7 @@ The `kind` is an enum and defined in [`mbgl.Resource`](https://github.com/mapbox
}
```
-It has no significance for anything but serves as a hint to your implemention as to what sort of resource to expect. E.g., your implementation could choose caching strategies based on the expected file type.
+The `kind` enum has no significance for anything but serves as a hint to your implemention as to what sort of resource to expect. E.g., your implementation could choose caching strategies based on the expected file type.
The `request` implementation should pass uncompressed data to `callback`. If you are downloading assets from a source that applies gzip transport encoding, the implementation must decompress the results before passing them on.
@@ -104,7 +123,7 @@ var map = new mbgl.Map({
});
```
-This is a very barebones implementation and you'll probably want a better implementation. E.g. it passes the url verbatim to the file system, but you'd want add some logic that normalizes `http` URLs. You'll notice that once your implementation has obtained the requested file, you have to deliver it to the requestee by calling `callback()`, which takes either an error object or `null` and an object with several settings:
+This is a very barebones implementation and you'll probably want a better implementation. E.g. it passes the url verbatim to the file system, but you'd want add some logic that normalizes `http` URLs. You'll notice that once your implementation has obtained the requested file, you have to deliver it to the requestee by calling `callback()`, which takes either an error object or `null` and an object with several keys:
```js
{
@@ -112,10 +131,10 @@ This is a very barebones implementation and you'll probably want a better implem
expires: new Date(),
etag: "string",
data: new Buffer()
-};
+}
```
-A sample implementation that uses [`request`](https://github.com/request/request) to query data from HTTP:
+A sample implementation that uses [`request`](https://github.com/request/request) to fetch data from a remote source:
```js
var mbgl = require('mapbox-gl-native');
@@ -148,43 +167,12 @@ var map = new mbgl.Map({
});
```
-Mapbox GL uses two types of protocols: `asset://` for files that should be loaded from some local static system, and `http://` (and `https://`), which should be loaded from the internet. However, stylesheets are free to use other protocols too, if your implementation of `request` supports these; e.g. you could use `s3://` to indicate that files are supposed to be loaded from S3.
+Stylesheets are free to use any protocols, but your implementation of `request` must support these; e.g. you could use `s3://` to indicate that files are supposed to be loaded from S3.
## Listening for log events
The module imported with `require('mapbox-gl-native')` inherits from [`EventEmitter`](https://nodejs.org/api/events.html), and the `NodeLogObserver` will push log events to this. Log messages can have [`class`](https://github.com/mapbox/mapbox-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L43-L60), [`severity`](https://github.com/mapbox/mapbox-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L17-L23), `code` ([HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)), and `text` parameters.
-```
-MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
- { Event::General, "General" },
- { Event::Setup, "Setup" },
- { Event::Shader, "Shader" },
- { Event::ParseStyle, "ParseStyle" },
- { Event::ParseTile, "ParseTile" },
- { Event::Render, "Render" },
- { Event::Style, "Style" },
- { Event::Database, "Database" },
- { Event::HttpRequest, "HttpRequest" },
- { Event::Sprite, "Sprite" },
- { Event::Image, "Image" },
- { Event::OpenGL, "OpenGL" },
- { Event::JNI, "JNI" },
- { Event::Android, "Android" },
- { Event::Crash, "Crash" },
- { Event(-1), "Unknown" },
-});
-```
-
-```
-MBGL_DEFINE_ENUM_CLASS(EventSeverityClass, EventSeverity, {
- { EventSeverity::Debug, "DEBUG" },
- { EventSeverity::Info, "INFO" },
- { EventSeverity::Warning, "WARNING" },
- { EventSeverity::Error, "ERROR" },
- { EventSeverity(-1), "UNKNOWN" },
-});
-```
-
```js
var mbgl = require('mapbox-gl-native');
mbgl.on('message', function(msg) {
@@ -195,58 +183,6 @@ mbgl.on('message', function(msg) {
});
```
-## Mapbox API Access tokens
-
-To use styles that rely on Mapbox vector tiles, you must pass an [API access token](https://www.mapbox.com/developers/api/#access-tokens) in your `request` implementation with requests to `mapbox://` protocols.
-
-```js
-var mbgl = require('mapbox-gl-native');
-var request = require('request');
-var url = require('url');
-
-var map = new mbgl.Map({
- request: function(req, callback) {
- var opts = {
- url: req.url,
- encoding: null,
- gzip: true
- };
-
- if (url.parse(req.url).protocol === 'mapbox:') {
- opts.qs = { access_token: process.env.MAPBOX_ACCESS_TOKEN};
- }
-
- request(opts, function (err, res, body) {
- if (err) {
- callback(err);
- } else if (res.statusCode == 200) {
- var response = {};
-
- if (res.headers.modified) { response.modified = new Date(res.headers.modified); }
- if (res.headers.expires) { response.expires = new Date(res.headers.expires); }
- if (res.headers.etag) { response.etag = res.headers.etag; }
-
- response.data = body;
-
- callback(null, response);
- } else {
- callback(new Error(JSON.parse(body).message));
- }
- });
- }
-});
-
-// includes a datasource with a reference to something like `mapbox://mapbox.mapbox-streets-v6`
-var style = mapboxStyle;
-
-map.load(style);
-map.render({}, function(err, image) {
- if (err) throw err;
- fs.writeFileSync('image.png', image);
-});
-
-```
-
## Contributing
See [DEVELOPING.md](DEVELOPING.md) for instructions on building this module for development.
diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml
index 134925d6e8..dde34c6c6a 100644
--- a/platform/node/bitrise.yml
+++ b/platform/node/bitrise.yml
@@ -22,28 +22,21 @@ workflows:
else
envman add --key SKIPCI --value false
fi
- - select-xcode-version:
- title: Select Xcode version
- run_if: '{{enveq "SKIPCI" "false"}}'
- script:
title: Run build script
run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- content: |-
#!/bin/bash
- set -e
- set -o pipefail
- export TRAVIS_OS_NAME=osx
- export TRAVIS_TAG=$BITRISE_GIT_TAG
- export NODE_VERSION=4
- export CXX=clang++
- export CC=clang
- source ./scripts/set_compiler.sh
- ./platform/node/scripts/install.sh
- if command -v ccache >/dev/null 2>&1; then ccache --zero-stats ; fi
- ./platform/node/scripts/run.sh
- if command -v ccache >/dev/null 2>&1; then ccache --show-stats ; fi
- ./platform/node/scripts/after_script.sh
+ set -eu -o pipefail
+ brew unlink node
+ brew install awscli homebrew/versions/node4-lts
+ brew link homebrew/versions/node4-lts
+ gem install xcpretty --no-rdoc --no-ri
+ make node
+ make test-node || result=$?
+ ./platform/node/scripts/after_script.sh ${BITRISE_BUILD_NUMBER} ${BITRISE_GIT_TAG:-}
+ exit ${result:-0}
- slack:
title: Post to Slack
run_if: '{{enveq "SKIPCI" "false"}}'
diff --git a/platform/node/scripts/after_script.sh b/platform/node/scripts/after_script.sh
index 8cbfa4d8ff..905055ad11 100755
--- a/platform/node/scripts/after_script.sh
+++ b/platform/node/scripts/after_script.sh
@@ -3,48 +3,20 @@
set -e
set -o pipefail
-# Inspect binary.
-if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- ldd ./lib/mapbox-gl-native.node
-else
- otool -L ./lib/mapbox-gl-native.node
-fi
+JOB=$1
+TAG=$2
-PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
+if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+ gzip --stdout node_modules/mapbox-gl-test-suite/render-tests/index.html | \
+ aws s3 cp --acl public-read --content-encoding gzip --content-type text/html \
+ - s3://mapbox/mapbox-gl-native/render-tests/$JOB/index.html
-if [[ ${TRAVIS_TAG} == node-v${PACKAGE_JSON_VERSION} ]]; then
- source ~/.nvm/nvm.sh
- nvm use $NODE_VERSION
+ echo http://mapbox.s3.amazonaws.com/mapbox-gl-native/render-tests/$JOB/index.html
+fi
- npm install aws-sdk
+PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
+if [[ $TAG == node-v${PACKAGE_JSON_VERSION} ]]; then
./node_modules/.bin/node-pre-gyp package
-
- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- ./node_modules/.bin/node-pre-gyp testpackage
- fi
-
./node_modules/.bin/node-pre-gyp publish info
-
- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- source ./platform/linux/scripts/setup.sh
-
- rm -rf build
- rm -rf lib
- npm install --fallback-to-build=false
- npm test
- fi
-fi
-
-if [[ ${TRAVIS_OS_NAME} == "linux" ]] && [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- # Install and add awscli to PATH for uploading the results
- pip install --user awscli
- export PATH="`python -m site --user-base`/bin:${PATH}"
-
- REPO_NAME=$(basename $TRAVIS_REPO_SLUG)
- gzip --stdout node_modules/mapbox-gl-test-suite/render-tests/index.html | \
- aws s3 cp --acl public-read --content-encoding gzip --content-type text/html \
- - s3://mapbox/$REPO_NAME/render-tests/$TRAVIS_JOB_NUMBER/index.html
-
- echo http://mapbox.s3.amazonaws.com/$REPO_NAME/render-tests/$TRAVIS_JOB_NUMBER/index.html
fi
diff --git a/platform/node/scripts/install.sh b/platform/node/scripts/install.sh
deleted file mode 100755
index b550933cd9..0000000000
--- a/platform/node/scripts/install.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-git submodule update --init .mason
-
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-
-if [ ${TRAVIS_OS_NAME} == "linux" ]; then
- mason install mesa 10.4.3
-fi
-
-if [ ! -d ~/.nvm ]; then
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.28.0/install.sh | bash
-fi
-
-source ~/.nvm/nvm.sh
-
-nvm install $NODE_VERSION
-nvm alias default $NODE_VERSION
-
-node --version
-npm --version
diff --git a/platform/node/scripts/run.sh b/platform/node/scripts/run.sh
deleted file mode 100755
index fdebaaeb94..0000000000
--- a/platform/node/scripts/run.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-source ./platform/${TRAVIS_OS_NAME}/scripts/setup.sh
-
-BUILDTYPE=${BUILDTYPE:-Release}
-
-################################################################################
-# Build
-################################################################################
-
-source ~/.nvm/nvm.sh
-nvm use $NODE_VERSION
-npm install --build-from-source
-
-################################################################################
-# Test
-################################################################################
-
-# https://github.com/mapbox/mapbox-gl-native/issues/2150
-if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
- npm test
- npm run test-suite
-fi
diff --git a/platform/node/src/node_feature.cpp b/platform/node/src/node_feature.cpp
new file mode 100644
index 0000000000..1a5e31fe97
--- /dev/null
+++ b/platform/node/src/node_feature.cpp
@@ -0,0 +1,168 @@
+#include "node_feature.hpp"
+
+namespace node_mbgl {
+
+using namespace mapbox::geometry;
+
+using Value = mbgl::Value;
+using Feature = mbgl::Feature;
+using Geometry = mbgl::Feature::geometry_type;
+using GeometryCollection = mapbox::geometry::geometry_collection<double>;
+using Properties = mbgl::Feature::property_map;
+
+template <class T>
+struct ToType {
+public:
+ v8::Local<v8::String> operator()(const point<T>&) {
+ return type("Point");
+ }
+
+ v8::Local<v8::String> operator()(const line_string<T>&) {
+ return type("LineString");
+ }
+
+ v8::Local<v8::String> operator()(const polygon<T>&) {
+ return type("Polygon");
+ }
+
+ v8::Local<v8::String> operator()(const multi_point<T>&) {
+ return type("MultiPoint");
+ }
+
+ v8::Local<v8::String> operator()(const multi_line_string<T>&) {
+ return type("MultiLineString");
+ }
+
+ v8::Local<v8::String> operator()(const multi_polygon<T>&) {
+ return type("MultiPolygon");
+ }
+
+ v8::Local<v8::String> operator()(const geometry_collection<T>&) {
+ return type("GeometryCollection");
+ }
+
+private:
+ v8::Local<v8::String> type(const char* type) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(type).ToLocalChecked());
+ }
+};
+
+template <class T>
+struct ToCoordinatesOrGeometries {
+public:
+ // Handles line_string, polygon, multi_point, multi_line_string, multi_polygon, and geometry_collection.
+ template <class E>
+ v8::Local<v8::Object> operator()(const std::vector<E>& vector) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Array> result = Nan::New<v8::Array>(vector.size());
+ for (std::size_t i = 0; i < vector.size(); ++i) {
+ Nan::Set(result, i, operator()(vector[i]));
+ }
+ return scope.Escape(result);
+ }
+
+ v8::Local<v8::Object> operator()(const point<T>& point) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Array> result = Nan::New<v8::Array>(2);
+ Nan::Set(result, 0, Nan::New(point.x));
+ Nan::Set(result, 1, Nan::New(point.y));
+ return scope.Escape(result);
+ }
+
+ v8::Local<v8::Object> operator()(const geometry<T>& geometry) {
+ return toJS(geometry);
+ }
+};
+
+struct ToValue {
+ v8::Local<v8::Value> operator()(std::nullptr_t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::Null());
+ }
+
+ v8::Local<v8::Value> operator()(bool t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(int64_t t) {
+ return operator()(double(t));
+ }
+
+ v8::Local<v8::Value> operator()(uint64_t t) {
+ return operator()(double(t));
+ }
+
+ v8::Local<v8::Value> operator()(double t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(const std::string& t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t).ToLocalChecked());
+ }
+
+ v8::Local<v8::Value> operator()(const std::vector<mbgl::Value>& array) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Array> result = Nan::New<v8::Array>();
+ for (unsigned int i = 0; i < array.size(); i++) {
+ result->Set(i, toJS(array[i]));
+ }
+ return scope.Escape(result);
+ }
+
+ v8::Local<v8::Value> operator()(const std::unordered_map<std::string, mbgl::Value>& map) {
+ return toJS(map);
+ }
+};
+
+v8::Local<v8::Object> toJS(const Geometry& geometry) {
+ Nan::EscapableHandleScope scope;
+
+ v8::Local<v8::Object> result = Nan::New<v8::Object>();
+
+ Nan::Set(result,
+ Nan::New("type").ToLocalChecked(),
+ Geometry::visit(geometry, ToType<double>()));
+
+ Nan::Set(result,
+ Nan::New(geometry.is<GeometryCollection>() ? "geometries" : "coordinates").ToLocalChecked(),
+ Geometry::visit(geometry, ToCoordinatesOrGeometries<double>()));
+
+ return scope.Escape(result);
+}
+
+v8::Local<v8::Value> toJS(const Value& value) {
+ return Value::visit(value, ToValue());
+}
+
+v8::Local<v8::Object> toJS(const Properties& properties) {
+ Nan::EscapableHandleScope scope;
+
+ v8::Local<v8::Object> result = Nan::New<v8::Object>();
+ for (const auto& property : properties) {
+ Nan::Set(result, Nan::New(property.first).ToLocalChecked(), toJS(property.second));
+ }
+
+ return scope.Escape(result);
+}
+
+v8::Local<v8::Object> toJS(const Feature& feature) {
+ Nan::EscapableHandleScope scope;
+
+ v8::Local<v8::Object> result = Nan::New<v8::Object>();
+
+ Nan::Set(result, Nan::New("type").ToLocalChecked(), Nan::New("Feature").ToLocalChecked());
+ Nan::Set(result, Nan::New("geometry").ToLocalChecked(), toJS(feature.geometry));
+ Nan::Set(result, Nan::New("properties").ToLocalChecked(), toJS(feature.properties));
+
+ if (feature.id) {
+ Nan::Set(result, Nan::New("id").ToLocalChecked(), Nan::New(double(*feature.id)));
+ }
+
+ return scope.Escape(result);
+}
+
+}
diff --git a/platform/node/src/node_feature.hpp b/platform/node/src/node_feature.hpp
new file mode 100644
index 0000000000..7973ee19d4
--- /dev/null
+++ b/platform/node/src/node_feature.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/util/feature.hpp>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <nan.h>
+#pragma GCC diagnostic pop
+
+namespace node_mbgl {
+
+v8::Local<v8::Value> toJS(const mbgl::Value&);
+v8::Local<v8::Object> toJS(const mbgl::Feature&);
+v8::Local<v8::Object> toJS(const mbgl::Feature::geometry_type&);
+v8::Local<v8::Object> toJS(const mbgl::Feature::property_map&);
+
+}
diff --git a/platform/node/src/node_log.cpp b/platform/node/src/node_log.cpp
index 8230bb36ce..a741109b27 100644
--- a/platform/node/src/node_log.cpp
+++ b/platform/node/src/node_log.cpp
@@ -52,7 +52,6 @@ NodeLogObserver::NodeLogObserver(v8::Local<v8::Object> target)
NodeLogObserver::~NodeLogObserver() {
queue->stop();
- module.Reset();
}
bool NodeLogObserver::onRecord(mbgl::EventSeverity severity, mbgl::Event event, int64_t code, const std::string &text) {
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 5eefe402d6..165e27b669 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -1,10 +1,9 @@
#include "node_map.hpp"
#include "node_request.hpp"
-#include "node_mapbox_gl_native.hpp"
+#include "node_feature.hpp"
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/util/exception.hpp>
-#include <mbgl/util/work_request.hpp>
#include <unistd.h>
@@ -52,6 +51,7 @@ NAN_MODULE_INIT(NodeMap::Init) {
Nan::SetPrototypeMethod(tpl, "render", Render);
Nan::SetPrototypeMethod(tpl, "release", Release);
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
+ Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
constructor.Reset(tpl->GetFunction());
Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction());
@@ -188,7 +188,7 @@ NAN_METHOD(NodeMap::Load) {
}
try {
- nodeMap->map->setStyleJSON(style, ".");
+ nodeMap->map->setStyleJSON(style);
} catch (const std::exception &ex) {
return Nan::ThrowError(ex.what());
}
@@ -450,6 +450,56 @@ NAN_METHOD(NodeMap::DumpDebugLogs) {
info.GetReturnValue().SetUndefined();
}
+NAN_METHOD(NodeMap::QueryRenderedFeatures) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ Nan::HandleScope scope;
+
+ if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsArray()) {
+ return Nan::ThrowTypeError("First argument must be an array");
+ }
+
+ auto posOrBox = info[0].As<v8::Array>();
+ if (posOrBox->Length() != 2) {
+ return Nan::ThrowTypeError("First argument must have two components");
+ }
+
+ try {
+ std::vector<mbgl::Feature> result;
+
+ if (Nan::Get(posOrBox, 0).ToLocalChecked()->IsArray()) {
+
+ auto pos0 = Nan::Get(posOrBox, 0).ToLocalChecked().As<v8::Array>();
+ auto pos1 = Nan::Get(posOrBox, 1).ToLocalChecked().As<v8::Array>();
+
+ result = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox {
+ {
+ Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(),
+ Nan::Get(pos0, 1).ToLocalChecked()->NumberValue()
+ }, {
+ Nan::Get(pos1, 0).ToLocalChecked()->NumberValue(),
+ Nan::Get(pos1, 1).ToLocalChecked()->NumberValue()
+ }
+ });
+
+ } else {
+ result = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate {
+ Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(),
+ Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue()
+ });
+ }
+
+ auto array = Nan::New<v8::Array>();
+ for (unsigned int i = 0; i < result.size(); i++) {
+ array->Set(i, toJS(result[i]));
+ }
+ info.GetReturnValue().Set(array);
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////
// Instance
@@ -474,27 +524,17 @@ NodeMap::~NodeMap() {
if (valid) release();
}
-class NodeFileSourceRequest : public mbgl::FileRequest {
-public:
- std::unique_ptr<mbgl::WorkRequest> workRequest;
-};
-
-std::unique_ptr<mbgl::FileRequest> NodeMap::request(const mbgl::Resource& resource, Callback cb1) {
- auto req = std::make_unique<NodeFileSourceRequest>();
-
- // This function can be called from any thread. Make sure we're executing the
- // JS implementation in the node event loop.
- req->workRequest = NodeRunLoop().invokeWithCallback([this] (mbgl::Resource res, Callback cb2) {
- Nan::HandleScope scope;
+std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, Callback callback_) {
+ Nan::HandleScope scope;
- auto requestHandle = NodeRequest::Create(res, cb2)->ToObject();
- auto callbackHandle = Nan::New<v8::Function>(NodeRequest::Respond, requestHandle);
+ auto requestHandle = NodeRequest::Create(resource, callback_)->ToObject();
+ auto request = Nan::ObjectWrap::Unwrap<NodeRequest>(requestHandle);
+ auto callbackHandle = Nan::New<v8::Function>(NodeRequest::Respond, requestHandle);
- v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
- Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
- }, cb1, resource);
+ v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
+ Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
- return std::move(req);
+ return std::make_unique<NodeRequest::NodeAsyncRequest>(request);
}
-}
+} // namespace node_mbgl
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index fe36ae7ed0..6e28eb541e 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -27,6 +27,7 @@ public:
static NAN_METHOD(Render);
static NAN_METHOD(Release);
static NAN_METHOD(DumpDebugLogs);
+ static NAN_METHOD(QueryRenderedFeatures);
void startRender(RenderOptions options);
void renderFinished();
@@ -42,7 +43,7 @@ public:
NodeMap(v8::Local<v8::Object>);
~NodeMap();
- std::unique_ptr<mbgl::FileRequest> request(const mbgl::Resource&, Callback);
+ std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, Callback);
mbgl::HeadlessView view;
std::unique_ptr<mbgl::Map> map;
diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp
index e0b094d570..26c49918be 100644
--- a/platform/node/src/node_mapbox_gl_native.cpp
+++ b/platform/node/src/node_mapbox_gl_native.cpp
@@ -5,25 +5,18 @@
#include <nan.h>
#pragma GCC diagnostic pop
-#include "node_mapbox_gl_native.hpp"
+#include <mbgl/util/run_loop.hpp>
+
#include "node_map.hpp"
#include "node_log.hpp"
#include "node_request.hpp"
-namespace node_mbgl {
-
-mbgl::util::RunLoop& NodeRunLoop() {
- static mbgl::util::RunLoop nodeRunLoop;
- return nodeRunLoop;
-}
-
-}
-
-NAN_MODULE_INIT(RegisterModule) {
+void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module) {
// This has the effect of:
// a) Ensuring that the static local variable is initialized before any thread contention.
// b) unreffing an async handle, which otherwise would keep the default loop running.
- node_mbgl::NodeRunLoop().stop();
+ static mbgl::util::RunLoop nodeRunLoop;
+ nodeRunLoop.stop();
node_mbgl::NodeMap::Init(target);
node_mbgl::NodeRequest::Init(target);
@@ -63,18 +56,21 @@ NAN_MODULE_INIT(RegisterModule) {
Nan::New("Resource").ToLocalChecked(),
resource);
- // Make the exported object inherit from process.EventEmitter
- v8::Local<v8::Object> process = Nan::Get(
- Nan::GetCurrentContext()->Global(),
- Nan::New("process").ToLocalChecked()).ToLocalChecked()->ToObject();
+ // Make the exported object inherit from EventEmitter
+ v8::Local<v8::Function> require = Nan::Get(module,
+ Nan::New("require").ToLocalChecked()).ToLocalChecked().As<v8::Function>();
+
+ v8::Local<v8::Value> eventsString = Nan::New("events").ToLocalChecked();
+ v8::Local<v8::Object> events = Nan::Call(require, module, 1, &eventsString).ToLocalChecked()->ToObject();
- v8::Local<v8::Object> EventEmitter = Nan::Get(process,
+ v8::Local<v8::Object> EventEmitter = Nan::Get(events,
Nan::New("EventEmitter").ToLocalChecked()).ToLocalChecked()->ToObject();
Nan::SetPrototype(target,
Nan::Get(EventEmitter, Nan::New("prototype").ToLocalChecked()).ToLocalChecked());
+ Nan::CallAsFunction(EventEmitter, target, 0, nullptr);
- mbgl::Log::setObserver(std::make_unique<node_mbgl::NodeLogObserver>(target->ToObject()));
+ mbgl::Log::setObserver(std::make_unique<node_mbgl::NodeLogObserver>(target));
}
NODE_MODULE(mapbox_gl_native, RegisterModule)
diff --git a/platform/node/src/node_mapbox_gl_native.hpp b/platform/node/src/node_mapbox_gl_native.hpp
deleted file mode 100644
index b98b035fea..0000000000
--- a/platform/node/src/node_mapbox_gl_native.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include <mbgl/util/run_loop.hpp>
-
-namespace node_mbgl {
-
-mbgl::util::RunLoop& NodeRunLoop();
-
-}
diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp
index 50d7628a2b..fa560ed4e7 100644
--- a/platform/node/src/node_request.cpp
+++ b/platform/node/src/node_request.cpp
@@ -45,6 +45,14 @@ v8::Handle<v8::Object> NodeRequest::Create(const mbgl::Resource& resource, mbgl:
NAN_METHOD(NodeRequest::Respond) {
using Error = mbgl::Response::Error;
+ // Move out of the object so callback() can only be fired once.
+ auto request = Nan::ObjectWrap::Unwrap<NodeRequest>(info.Data().As<v8::Object>());
+ auto callback = std::move(request->callback);
+ if (!callback) {
+ info.GetReturnValue().SetUndefined();
+ return;
+ }
+
mbgl::Response response;
if (info.Length() < 1) {
@@ -79,14 +87,16 @@ NAN_METHOD(NodeRequest::Respond) {
if (Nan::Has(res, Nan::New("modified").ToLocalChecked()).FromJust()) {
const double modified = Nan::Get(res, Nan::New("modified").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value();
if (!std::isnan(modified)) {
- response.modified = mbgl::SystemClock::from_time_t(modified / 1000);
+ response.modified = mbgl::Timestamp{ mbgl::Seconds(
+ static_cast<mbgl::Seconds::rep>(modified / 1000)) };
}
}
if (Nan::Has(res, Nan::New("expires").ToLocalChecked()).FromJust()) {
const double expires = Nan::Get(res, Nan::New("expires").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value();
if (!std::isnan(expires)) {
- response.expires = mbgl::SystemClock::from_time_t(expires / 1000);
+ response.expires = mbgl::Timestamp{ mbgl::Seconds(
+ static_cast<mbgl::Seconds::rep>(expires / 1000)) };
}
}
@@ -112,14 +122,39 @@ NAN_METHOD(NodeRequest::Respond) {
}
// Send the response object to the NodeFileSource object
- Nan::ObjectWrap::Unwrap<NodeRequest>(info.Data().As<v8::Object>())->callback(response);
+ callback(response);
info.GetReturnValue().SetUndefined();
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Instance
+NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) {
+ assert(request);
+ // Make sure the JS object has a pointer to this so that it can remove its pointer in the
+ // destructor
+ request->asyncRequest = this;
+}
+
+NodeRequest::NodeAsyncRequest::~NodeAsyncRequest() {
+ if (request) {
+ // Remove the callback function because the AsyncRequest was canceled and we are no longer
+ // interested in the result.
+ request->callback = {};
+ request->asyncRequest = nullptr;
+ }
+}
+
NodeRequest::NodeRequest(mbgl::FileSource::Callback callback_)
- : callback(callback_) {}
+ : callback(callback_) {
+}
+NodeRequest::~NodeRequest() {
+ // When this object gets garbage collected, make sure that the AsyncRequest can no longer
+ // attempt to remove the callback function this object was holding (it can't be fired anymore).
+ if (asyncRequest) {
+ asyncRequest->request = nullptr;
+ }
}
+
+} // namespace node_mbgl
diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp
index 8e57cb30ec..2d307a3f19 100644
--- a/platform/node/src/node_request.hpp
+++ b/platform/node/src/node_request.hpp
@@ -12,6 +12,7 @@
namespace node_mbgl {
class NodeFileSource;
+class NodeRequest;
class NodeRequest : public Nan::ObjectWrap {
public:
@@ -24,9 +25,17 @@ public:
static Nan::Persistent<v8::Function> constructor;
NodeRequest(mbgl::FileSource::Callback);
+ ~NodeRequest();
+
+ struct NodeAsyncRequest : public mbgl::AsyncRequest {
+ NodeAsyncRequest(NodeRequest*);
+ ~NodeAsyncRequest() override;
+ NodeRequest* request;
+ };
private:
mbgl::FileSource::Callback callback;
+ NodeAsyncRequest* asyncRequest = nullptr;
};
}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 1228900940..e8434bc774 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -220,6 +220,26 @@ test('Map', function(t) {
t.end();
});
+ t.test('returns an error delayed', function(t) {
+ var delay = 0;
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ delay += 100;
+ setTimeout(function() {
+ callback(new Error('not found'));
+ }, delay);
+ },
+ ratio: 1
+ });
+ map.load(style);
+ map.render({ zoom: 1 }, function(err, data) {
+ map.release();
+
+ t.ok(err, 'returns error');
+ t.end();
+ });
+ });
+
t.test('returns an error', function(t) {
var map = new mbgl.Map(options);
map.load(style);
@@ -298,7 +318,9 @@ test('Map', function(t) {
t.test('returning an error', function(t) {
var map = new mbgl.Map({
request: function(req, callback) {
- callback(new Error('request error'));
+ setImmediate(function () {
+ callback(new Error('request error'));
+ });
},
});
map.load(style);
diff --git a/platform/node/test/js/require.js b/platform/node/test/js/require.js
new file mode 100644
index 0000000000..1528002c20
--- /dev/null
+++ b/platform/node/test/js/require.js
@@ -0,0 +1 @@
+var mbgl = require('../../../../lib/mapbox-gl-native');
diff --git a/platform/node/test/query.test.js b/platform/node/test/query.test.js
new file mode 100644
index 0000000000..1309c03467
--- /dev/null
+++ b/platform/node/test/query.test.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var suite = require('mapbox-gl-test-suite').query;
+var suiteImplementation = require('./suite_implementation');
+
+var tests;
+
+if (process.argv[1] === __filename && process.argv.length > 2) {
+ tests = process.argv.slice(2);
+}
+
+suite.run('native', {tests: tests}, suiteImplementation);
diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js
index 05a6b2ba68..0527a19070 100644
--- a/platform/node/test/render.test.js
+++ b/platform/node/test/render.test.js
@@ -1,8 +1,7 @@
'use strict';
-var mbgl = require('../../../lib/mapbox-gl-native');
var suite = require('mapbox-gl-test-suite').render;
-var request = require('request');
+var suiteImplementation = require('./suite_implementation');
var tests;
@@ -10,47 +9,4 @@ if (process.argv[1] === __filename && process.argv.length > 2) {
tests = process.argv.slice(2);
}
-mbgl.on('message', function(msg) {
- console.log('%s (%s): %s', msg.severity, msg.class, msg.text);
-});
-
-suite.run('native', {tests: tests}, function (style, options, callback) {
- var map = new mbgl.Map({
- ratio: options.pixelRatio,
- request: function(req, callback) {
- request(req.url, {encoding: null}, function (err, response, body) {
- if (err) {
- callback(err);
- } else if (response.statusCode != 200) {
- callback(new Error(response.statusMessage));
- } else {
- callback(null, {data: body});
- }
- });
- }
- });
-
- var timedOut = false;
- var watchdog = setTimeout(function () {
- timedOut = true;
- map.dumpDebugLogs();
- callback(new Error('timed out after 20 seconds'));
- }, 20000);
-
- options.center = style.center;
- options.zoom = style.zoom;
- options.bearing = style.bearing;
- options.pitch = style.pitch;
- options.debug = {
- tileBorders: options.debug,
- collision: options.collisionDebug
- };
-
- map.load(style);
- map.render(options, function (err, pixels) {
- map.release();
- if (timedOut) return;
- clearTimeout(watchdog);
- callback(err, pixels);
- });
-});
+suite.run('native', {tests: tests}, suiteImplementation);
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
new file mode 100644
index 0000000000..da226a68f4
--- /dev/null
+++ b/platform/node/test/suite_implementation.js
@@ -0,0 +1,58 @@
+'use strict';
+
+var mbgl = require('../../../lib/mapbox-gl-native');
+var request = require('request');
+
+mbgl.on('message', function(msg) {
+ console.log('%s (%s): %s', msg.severity, msg.class, msg.text);
+});
+
+module.exports = function (style, options, callback) {
+ var map = new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: function(req, callback) {
+ request(req.url, {encoding: null}, function (err, response, body) {
+ if (err) {
+ callback(err);
+ } else if (response.statusCode != 200) {
+ callback(new Error(response.statusMessage));
+ } else {
+ callback(null, {data: body});
+ }
+ });
+ }
+ });
+
+ var timedOut = false;
+ var watchdog = setTimeout(function () {
+ timedOut = true;
+ map.dumpDebugLogs();
+ callback(new Error('timed out after 20 seconds'));
+ }, 20000);
+
+ options.center = style.center;
+ options.zoom = style.zoom;
+ options.bearing = style.bearing;
+ options.pitch = style.pitch;
+ options.debug = {
+ tileBorders: options.debug,
+ collision: options.collisionDebug
+ };
+
+ map.load(style);
+
+ map.render(options, function (err, pixels) {
+ var results = options.queryGeometry ?
+ map.queryRenderedFeatures(options.queryGeometry) :
+ [];
+ map.release();
+ if (timedOut) return;
+ clearTimeout(watchdog);
+ callback(err, pixels, results.map(prepareFeatures));
+ });
+
+ function prepareFeatures(r) {
+ delete r.layer;
+ return r;
+ }
+};
diff --git a/platform/osx/CHANGELOG.md b/platform/osx/CHANGELOG.md
new file mode 100644
index 0000000000..f2e1be966f
--- /dev/null
+++ b/platform/osx/CHANGELOG.md
@@ -0,0 +1,14 @@
+# Changelog for Mapbox OS X SDK
+
+## master
+
+* Fixed an issue in which Mapbox.framework was nested inside another folder named Mapbox.framework. ([#4998](https://github.com/mapbox/mapbox-gl-native/pull/4998))
+* Added methods to MGLMapView for obtaining the underlying map data rendered by the current style, along with additional classes to represent complex geometry in that data. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+* An MGLPolygon can now have interior polygons, representing holes knocked out of the overall shape. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+* Fixed a crash passing a mixture of point and shape annotations into `-[MGLMapView addAnnotations:]`. ([#5097](https://github.com/mapbox/mapbox-gl-native/pull/5097))
+* Added new options to `MGLMapDebugMaskOptions` that show wireframes and the stencil buffer instead of the color buffer. ([#4359](https://github.com/mapbox/mapbox-gl-native/pull/4359))
+* Fixed a memory leak when using raster resources. ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
+
+## 0.1.0
+
+* This version of the Mapbox OS X SDK roughly corresponds to version 3.3.0-alpha.2 of the Mapbox iOS SDK. The two SDKs have very similar feature sets. The main difference is the lack of user location tracking. Some APIs have been adapted to OS X conventions, particularly the use of NSPopover for callout views.
diff --git a/platform/osx/DEVELOPING.md b/platform/osx/DEVELOPING.md
index f75fa86db3..dcba9db1a1 100644
--- a/platform/osx/DEVELOPING.md
+++ b/platform/osx/DEVELOPING.md
@@ -2,21 +2,51 @@
This document explains how to build the Mapbox OS X SDK from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox iOS SDK.
-### Requirements
+## Requirements
The Mapbox OS X SDK and the osxapp demo application run on OS X 10.10.0 and above.
-### Build
+## Building the SDK
1. [Install core dependencies](../../INSTALL.md).
1. Run `make xproj`.
-1. Switch to the osxsdk or osxapp scheme. The former builds just the Cocoa framework, while the latter also builds a Cocoa demo application based on it.
+1. Switch to the “dynamic” or “osxapp” scheme. The former builds just the Cocoa framework, while the latter also builds a Cocoa demo application based on it.
-### Access tokens
+## Contributing
+
+### Adding a source code file
+
+To add an Objective-C header or implementation file to the OS X SDK:
+
+1. Add the file to the “dynamic” target’s Headers or Compile Sources build phase, as appropriate. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
+1. _(Optional.)_ If it’s a public header, change its visibility from Project to Public and import it in [the OS X SDK’s umbrella header](./src/Mapbox.h).
+1. _(Optional.)_ If the file would also be used by the iOS SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-source-code-file) for further instructions.
+
+### Adding a resource
+
+To add a resource (such as an image, SSL certificate, property list, or strings table) to the OS X SDK:
+
+1. Add the header to the Copy Bundle Resources build phase of the “dynamic” target. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
+1. _(Optional.)_ If the resource would also be used by the iOS SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-resource) for further instructions.
+
+### Adding user-facing text
+
+To add or update text that the user may see in the OS X SDK:
+
+1. Make sure the implementation file imports [NSBundle+MGLAdditions.h](../darwin/src/NSBundle+MGLAdditions.h).
+1. Use the `NSLocalizedStringWithDefaultValue()` macro:
+ * `key` is a unique identifier that won’t change if the user-facing text ever needs to change.
+ * `tbl` is `Foundation` in code shared between the iOS and OS X SDKs, or `nil` otherwise.
+ * `bundle` is `nil`; the redefined macro looks for the SDK bundle at runtime and ignores this argument.
+ * `val` is the English string.
+1. _(Optional.)_ When dealing with a number followed by a pluralized word, do not split the string. Instead, use a format string and make `val` ambiguous, like `%d file(s)`. Then pluralize for English in the appropriate [.stringsdict file](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html). See [platform/darwin/resources/en.lproj/Foundation.stringsdict](../darwin/resources/en.lproj/Foundation.stringsdict) for an example. Localizers should do likewise for their languages.
+1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the iOS SDK’s strings tables.
+
+## Access tokens
The demo applications use Mapbox vector tiles, which require a Mapbox account and API access token. Obtain an access token on the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). You will be prompted for this access token the first time you launch the demo application.
-### Usage
+## Using osxapp
Through the OS X SDK, the demo application supports a variety of standard gestures and keyboard shortcuts. For more details, open Mapbox GL Help from the Help menu.
diff --git a/platform/osx/INSTALL.md b/platform/osx/INSTALL.md
index e4566625eb..8a4323aa8e 100644
--- a/platform/osx/INSTALL.md
+++ b/platform/osx/INSTALL.md
@@ -6,25 +6,45 @@ This document explains how to build the Mapbox OS X SDK and integrate it into yo
The Mapbox OS X SDK requires the OS X 10.10.0 SDK or above.
-### Build
+### Building the SDK
+
+Grab a [prebuilt release](https://github.com/mapbox/mapbox-gl-native/releases/) – look for the releases that begin with “osx-” – or build the SDK from source:
1. [Install core dependencies](../../INSTALL.md).
-1. Run `make xpackage`, which produces a `Mapbox.framework` in the `gyp/build/Release/` folder.
+1. Run `make xpackage`, which produces a `Mapbox.framework` in the `build/osx/pkg/` folder.
+
+### Installation
+
+1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
+
+1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom OS X Application Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+
+## Usage
-### Install
+In a storyboard or XIB, add a view to your view controller. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically:
-1. Copy `gyp/build/Release/Mapbox.framework` into your project.
+1. Switch to the Assistant Editor.
+1. Import the `Mapbox` module.
+1. Connect the map view to a new outlet in your view controller class. (Control-drag from the map view in Interface Builder to a valid location in your view controller implementation.) The resulting outlet declaration should look something like this:
-1. In the project editor, select your application target, go to the General tab, and add `Mapbox.framework` to the *Embedded Binaries* section.
+```objc
+// ViewController.m
+@import Mapbox;
-1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target. In the Info tab, set `MGLMapboxAccessToken` to your access token. You can obtain one from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+@interface ViewController : NSViewController
-1. In a XIB or storyboard, add a Custom View and set its custom class to `MGLMapView`. If you need to manipulate the map view programmatically, import the `Mapbox` module (Swift) or `Mapbox.h` umbrella header (Objective-C).
+@property (strong) IBOutlet MGLMapView *mapView;
-## Use
+@end
+```
-The [Mapbox iOS SDK’s API documentation](https://www.mapbox.com/ios-sdk/api/) applies to the Mapbox OS X SDK with few differences, mostly around unimplemented features like user location tracking.
+```swift
+// ViewController.swift
+import Mapbox
-## Troubleshooting
+class ViewController: NSViewController {
+ @IBOutlet var mapView: MGLMapView!
+}
+```
-You can also try clearing the Xcode cache with `make clear_xcode_cache`.
+Run `make xdocument` to generate complete API documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox OS X SDK with few differences, mostly around unimplemented features like user location tracking.
diff --git a/platform/osx/README.md b/platform/osx/README.md
index 3f9a5cd62e..75c8c3453a 100644
--- a/platform/osx/README.md
+++ b/platform/osx/README.md
@@ -8,3 +8,5 @@ This SDK is analogous to the Mapbox iOS SDK, and much of the iOS SDK documentati
* [Integrating the Mapbox OS X SDK into your application](INSTALL.md)
* [Contributing to the Mapbox OS X SDK](DEVELOPING.md)
+
+<img alt="" src="screenshot.png" width="645">
diff --git a/platform/osx/WorkspaceSettings.xcsettings b/platform/osx/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000000..a2d959210c
--- /dev/null
+++ b/platform/osx/WorkspaceSettings.xcsettings
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>BuildLocationStyle</key>
+ <string>UseAppPreferences</string>
+ <key>CustomBuildLocationType</key>
+ <string>RelativeToDerivedData</string>
+ <key>DerivedDataCustomLocation</key>
+ <string>../../build</string>
+ <key>DerivedDataLocationStyle</key>
+ <string>WorkspaceRelativePath</string>
+ <key>IssueFilterStyle</key>
+ <string>ShowActiveSchemeOnly</string>
+ <key>LiveSourceIssuesEnabled</key>
+ <true/>
+</dict>
+</plist>
diff --git a/platform/osx/app/AppDelegate.h b/platform/osx/app/AppDelegate.h
index ca9edab773..a1d9297b2f 100644
--- a/platform/osx/app/AppDelegate.h
+++ b/platform/osx/app/AppDelegate.h
@@ -16,6 +16,8 @@ extern NSString * const MGLMapboxAccessTokenDefaultsKey;
@property (assign) double pendingZoomLevel;
@property (copy) MGLMapCamera *pendingCamera;
@property (assign) MGLCoordinateBounds pendingVisibleCoordinateBounds;
+@property (assign) double pendingMinimumZoomLevel;
+@property (assign) double pendingMaximumZoomLevel;
@property (copy) NSURL *pendingStyleURL;
@property (assign) MGLMapDebugMaskOptions pendingDebugMask;
diff --git a/platform/osx/app/AppDelegate.m b/platform/osx/app/AppDelegate.m
index d9fd967410..b7860cf130 100644
--- a/platform/osx/app/AppDelegate.m
+++ b/platform/osx/app/AppDelegate.m
@@ -204,6 +204,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
if ([pack.region isKindOfClass:[MGLTilePyramidOfflineRegion class]]) {
MGLTilePyramidOfflineRegion *region = (MGLTilePyramidOfflineRegion *)pack.region;
self.pendingVisibleCoordinateBounds = region.bounds;
+ self.pendingMinimumZoomLevel = region.minimumZoomLevel;
+ self.pendingMaximumZoomLevel = region.maximumZoomLevel;
[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:NULL];
}
break;
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon128x128.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon128x128.png
new file mode 100644
index 0000000000..145d5a7d85
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon128x128.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon16x16.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon16x16.png
new file mode 100644
index 0000000000..fa2588dec3
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon16x16.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256-1.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256-1.png
new file mode 100644
index 0000000000..18fec77f84
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256-1.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256.png
new file mode 100644
index 0000000000..18fec77f84
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon256x256.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32-1.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32-1.png
new file mode 100644
index 0000000000..bf3acc1282
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32-1.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32.png
new file mode 100644
index 0000000000..bf3acc1282
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon32x32.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512-1.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512-1.png
new file mode 100644
index 0000000000..1ea7683696
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512-1.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512.png
new file mode 100644
index 0000000000..1ea7683696
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/AppIcon512x512.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..58e739d056
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "AppIcon16x16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "AppIcon32x32-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "AppIcon32x32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "icon-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "AppIcon128x128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "AppIcon256x256-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "AppIcon256x256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "AppIcon512x512-1.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "AppIcon512x512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "icon.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon-1.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon-1.png
new file mode 100644
index 0000000000..36dd7acf90
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon-1.png
Binary files differ
diff --git a/platform/ios/app/img/iTunesArtwork@2x.png b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon.png
index fdee900aa4..fdee900aa4 100644
--- a/platform/ios/app/img/iTunesArtwork@2x.png
+++ b/platform/osx/app/Assets.xcassets/AppIcon.appiconset/icon.png
Binary files differ
diff --git a/platform/osx/app/Assets.xcassets/Contents.json b/platform/osx/app/Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/platform/osx/app/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/platform/osx/app/Base.lproj/MainMenu.xib b/platform/osx/app/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000000..c80428ff00
--- /dev/null
+++ b/platform/osx/app/Base.lproj/MainMenu.xib
@@ -0,0 +1,845 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="z9N-Fm-MUP"/>
+ </connections>
+ </customObject>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
+ <connections>
+ <outlet property="offlinePacksArrayController" destination="dWe-R6-sRz" id="Ar5-xu-ABm"/>
+ <outlet property="offlinePacksPanel" destination="Jjv-gs-Tx6" id="0vK-rR-3ZX"/>
+ <outlet property="preferencesWindow" destination="UWc-yQ-qda" id="Ota-aT-Mz2"/>
+ </connections>
+ </customObject>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="Mapbox GL" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Mapbox GL" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About Mapbox GL" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
+ <connections>
+ <action selector="showPreferences:" target="-1" id="Llx-Uy-HTS"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide Mapbox GL" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit Mapbox GL" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save Offline Pack…" id="UXB-sj-C7i">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="addOfflinePack:" target="-1" id="Usu-xO-QEx"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" id="KaW-ft-85H">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Streets" state="on" tag="1" keyEquivalent="1" id="17N-yz-NNo">
+ <connections>
+ <action selector="setStyle:" target="-1" id="I4L-Wx-UXA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Outdoors" tag="2" keyEquivalent="2" id="BBa-Qa-SQr">
+ <connections>
+ <action selector="setStyle:" target="-1" id="rM1-yG-t5u"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Light" tag="3" keyEquivalent="3" id="HWe-7u-UVJ">
+ <connections>
+ <action selector="setStyle:" target="-1" id="Q9V-O1-oRz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Dark" tag="4" keyEquivalent="4" id="6HI-q6-AeV">
+ <connections>
+ <action selector="setStyle:" target="-1" id="YfH-1I-G50"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Satellite" tag="5" keyEquivalent="5" id="h0J-5X-kgF">
+ <connections>
+ <action selector="setStyle:" target="-1" id="GXt-oK-Hy1"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Satellite Streets" tag="6" keyEquivalent="6" id="9BL-00-HFt">
+ <connections>
+ <action selector="setStyle:" target="-1" id="oL4-AC-waq"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Custom Style…" id="L0h-86-2cU">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="chooseCustomStyle:" target="-1" id="QJF-fM-Ty3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="BMF-ml-0Bd"/>
+ <menuItem title="Zoom In" keyEquivalent="+" id="W82-WO-xvB">
+ <connections>
+ <action selector="zoomIn:" target="-1" id="g33-vK-zUu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom Out" keyEquivalent="-" id="j7h-PY-edM">
+ <connections>
+ <action selector="zoomOut:" target="-1" id="0pP-tO-9ex"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Snap to North" keyEquivalent="" id="Zss-3w-wkz">
+ <connections>
+ <action selector="snapToNorth:" target="-1" id="Ayq-GE-Lb5"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="mkP-YN-G0w"/>
+ <menuItem title="Reload" keyEquivalent="r" id="JvI-nv-KaE">
+ <connections>
+ <action selector="reload:" target="-1" id="xkh-9F-mOe"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="CyM-Wv-Bnc"/>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Debug" id="ZNC-5r-eBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Debug" id="McE-ka-r79">
+ <items>
+ <menuItem title="Show Tile Boundaries" id="rDE-dG-rTR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleTileBoundaries:" target="-1" id="LAO-88-F7h"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Tile Info" id="LoH-qD-kb0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleTileInfo:" target="-1" id="KCn-0G-V87"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Tile Timestamps" id="bY0-2E-LZ7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleTileTimestamps:" target="-1" id="tBs-2N-KEG"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Collision Boxes" id="Y0b-3K-mJE">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleCollisionBoxes:" target="-1" id="EYa-7n-iWZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Wireframes" id="hSX-Be-8xC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleWireframes:" target="-1" id="usj-ug-upt"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="2EG-Hp-4FA"/>
+ <menuItem title="Show Color Buffer" id="Eao-WE-BWz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="showColorBuffer:" target="-1" id="Nuq-Qs-98g"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show Stencil Buffer" id="LlS-Yh-RkN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="showStencilBuffer:" target="-1" id="WkN-t9-Mpv"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="dYw-bb-tr1"/>
+ <menuItem title="Show Tooltips on Dropped Pins" id="uir-Rx-zmw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleShowsToolTipsOnDroppedPins:" target="-1" id="1YC-Co-QQ6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Random Cursors for Dropped Pins" id="ZTk-lc-Jgu">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRandomizesCursorsOnDroppedPins:" target="-1" id="Mpw-b8-oub"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Sl5-nE-kHd"/>
+ <menuItem title="Blanket Map With Pins" id="LMZ-oe-Ngh">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="dropManyPins:" target="-1" id="Rtv-8N-3Z8"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Add Polygon and Polyline" id="DVr-vT-lpe">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="drawPolygonAndPolyLineAnnotations:" target="-1" id="EhT-CB-gee"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove All Annotations" id="6rC-68-vk0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="wQq-Mx-QY0"/>
+ <menuItem title="Start World Tour" id="VFo-Jh-2sw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startWorldTour:" target="-1" id="66Y-Gm-Yn1"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop World Tour" id="Pa8-qU-xfr">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopWorldTour:" target="-1" id="aq0-7t-AGi"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" keyEquivalent="z" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Uix-g7-fAt"/>
+ <menuItem title="Offline Packs" id="YW3-jR-knj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="showOfflinePacksPanel:" target="Voe-Tx-rLC" id="kj9-ht-KmF"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="Mapbox GL Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showShortcuts:" target="-1" id="hNZ-sm-X2q"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="EpY-wQ-SjH"/>
+ <menuItem title="Improve This Map" id="xu5-WN-qYK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="giveFeedback:" target="-1" id="cil-i9-r39"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ <window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="Preferences" animationBehavior="default" id="UWc-yQ-qda">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="109" y="131" width="350" height="84"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <view key="contentView" id="eA4-n3-qPe">
+ <rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0IK-AW-Gg3">
+ <rect key="frame" x="18" y="45" width="89" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Access token:" id="Ptd-FI-M5A">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <connections>
+ <accessibilityConnection property="link" destination="7sb-sf-oJU" id="U0t-jC-oQ7"/>
+ </connections>
+ </textField>
+ <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7sb-sf-oJU">
+ <rect key="frame" x="113" y="42" width="197" height="22"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="jlV-TC-NUv">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <connections>
+ <binding destination="45S-yT-WUN" name="value" keyPath="values.MGLMapboxAccessToken" id="iJE-S2-ALY"/>
+ </connections>
+ </textField>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="c3S-LC-PoX">
+ <rect key="frame" x="318" y="47" width="12" height="12"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="12" id="M3J-pU-gKn"/>
+ </constraints>
+ <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="NSFollowLinkFreestandingTemplate" imagePosition="only" alignment="center" controlSize="small" imageScaling="proportionallyUpOrDown" inset="2" id="38x-37-Ay0">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="smallSystem"/>
+ </buttonCell>
+ <connections>
+ <action selector="openAccessTokenManager:" target="-1" id="1LX-4G-roC"/>
+ </connections>
+ </button>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="7IZ-zl-iT1">
+ <rect key="frame" x="18" y="18" width="109" height="18"/>
+ <buttonCell key="cell" type="check" title="Scroll to zoom" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="hVR-66-JSh">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <binding destination="45S-yT-WUN" name="value" keyPath="values.MGLScrollWheelZoomsMapView" id="2AZ-bk-DM5"/>
+ </connections>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="trailing" secondItem="c3S-LC-PoX" secondAttribute="trailing" constant="20" symbolic="YES" id="7QU-Jd-Rg6"/>
+ <constraint firstItem="c3S-LC-PoX" firstAttribute="top" secondItem="eA4-n3-qPe" secondAttribute="top" constant="25" id="JOS-HU-27c"/>
+ <constraint firstItem="7sb-sf-oJU" firstAttribute="leading" secondItem="0IK-AW-Gg3" secondAttribute="trailing" constant="8" symbolic="YES" id="SS6-VQ-sLK"/>
+ <constraint firstItem="0IK-AW-Gg3" firstAttribute="leading" secondItem="eA4-n3-qPe" secondAttribute="leading" constant="20" symbolic="YES" id="TYG-io-qfV"/>
+ <constraint firstItem="7sb-sf-oJU" firstAttribute="top" secondItem="eA4-n3-qPe" secondAttribute="top" constant="20" symbolic="YES" id="Vzb-q8-ecP"/>
+ <constraint firstItem="7IZ-zl-iT1" firstAttribute="leading" secondItem="0IK-AW-Gg3" secondAttribute="leading" id="aIY-WX-AW9"/>
+ <constraint firstItem="7IZ-zl-iT1" firstAttribute="top" secondItem="7sb-sf-oJU" secondAttribute="bottom" constant="8" symbolic="YES" id="ide-24-GqL"/>
+ <constraint firstItem="c3S-LC-PoX" firstAttribute="leading" secondItem="7sb-sf-oJU" secondAttribute="trailing" constant="8" symbolic="YES" id="pjl-9u-IgM"/>
+ <constraint firstItem="7sb-sf-oJU" firstAttribute="baseline" secondItem="0IK-AW-Gg3" secondAttribute="baseline" id="qIY-Jr-9Ws"/>
+ <constraint firstAttribute="bottom" secondItem="7IZ-zl-iT1" secondAttribute="bottom" constant="20" symbolic="YES" id="wng-pn-VIz"/>
+ <constraint firstItem="7sb-sf-oJU" firstAttribute="centerY" secondItem="c3S-LC-PoX" secondAttribute="centerY" id="zej-gw-fC0"/>
+ </constraints>
+ </view>
+ <connections>
+ <outlet property="initialFirstResponder" destination="7sb-sf-oJU" id="UZe-di-dnA"/>
+ </connections>
+ <point key="canvasLocation" x="754" y="221"/>
+ </window>
+ <userDefaultsController representsSharedInstance="YES" id="45S-yT-WUN"/>
+ <window title="Offline Packs" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MBXOfflinePacksPanel" animationBehavior="default" id="Jjv-gs-Tx6" customClass="NSPanel">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="830" y="430" width="400" height="300"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <view key="contentView" id="8ha-hw-zOD">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
+ <rect key="frame" x="-1" y="20" width="402" height="281"/>
+ <clipView key="contentView" id="J9U-Yx-o2S">
+ <rect key="frame" x="1" y="0.0" width="400" height="280"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="257"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <size key="intercellSpacing" width="3" height="2"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+ <tableColumns>
+ <tableColumn identifier="" editable="NO" width="16" minWidth="10" maxWidth="3.4028234663852886e+38" id="xtw-hQ-8C5" userLabel="State">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <imageCell key="dataCell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="edU-Yw-20f"/>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.stateImage" id="2wd-1J-TZt"/>
+ </connections>
+ </tableColumn>
+ <tableColumn editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="oys-QZ-34I">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.context" id="NtD-s5-ZUq">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">OfflinePackNameValueTransformer</string>
+ </dictionary>
+ </binding>
+ </connections>
+ </tableColumn>
+ <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WfC-qb-HsW">
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="sNm-Qn-ne6"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesCompleted" id="mu6-Jg-GiU"/>
+ </connections>
+ </tableColumn>
+ <tableColumn identifier="" editable="NO" width="50" minWidth="10" maxWidth="3.4028234663852886e+38" id="Rrd-A9-jqc">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="mHy-qJ-rOA">
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="kyx-ZP-OBH"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
+ </connections>
+ </tableColumn>
+ <tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="h7m-6l-KaS">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Size">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="701-bg-k6L">
+ <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" id="IXV-J9-sP3"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfBytesCompleted" id="Zsa-Na-yFN"/>
+ </connections>
+ </tableColumn>
+ </tableColumns>
+ <connections>
+ <action trigger="doubleAction" selector="chooseOfflinePack:" target="-1" id="pUN-eT-zRT"/>
+ </connections>
+ </tableView>
+ </subviews>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
+ <rect key="frame" x="1" y="7" width="0.0" height="16"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
+ <rect key="frame" x="224" y="17" width="15" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <tableHeaderView key="headerView" id="MAZ-Iq-hBi">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="23"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </tableHeaderView>
+ </scrollView>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wzf-ce-Spm">
+ <rect key="frame" x="0.0" y="-1" width="21" height="21"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="21" id="5ST-tY-8Ph"/>
+ </constraints>
+ <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="sew-F7-i5T">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="addOfflinePack:" target="-1" id="SN0-PM-HoU"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7L7-hr-zId">
+ <rect key="frame" x="20" y="0.0" width="21" height="19"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="21" id="JYb-AF-8gZ"/>
+ </constraints>
+ <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="oTF-3m-6qT">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent" base64-UTF8="YES">
+CA
+</string>
+ </buttonCell>
+ <connections>
+ <action selector="delete:" target="-1" id="EGL-bf-yUD"/>
+ </connections>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstItem="7L7-hr-zId" firstAttribute="centerY" secondItem="wzf-ce-Spm" secondAttribute="centerY" id="7TI-6w-bf1"/>
+ <constraint firstAttribute="bottom" secondItem="Q8b-0e-dLv" secondAttribute="bottom" constant="20" symbolic="YES" id="DZa-ly-bhV"/>
+ <constraint firstItem="wzf-ce-Spm" firstAttribute="top" secondItem="Q8b-0e-dLv" secondAttribute="bottom" id="LhK-5z-CQA"/>
+ <constraint firstItem="Q8b-0e-dLv" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="-1" id="Oyo-ch-rZo"/>
+ <constraint firstAttribute="bottom" secondItem="7L7-hr-zId" secondAttribute="bottom" id="TtY-j1-T5h"/>
+ <constraint firstItem="Q8b-0e-dLv" firstAttribute="top" secondItem="8ha-hw-zOD" secondAttribute="top" constant="-1" id="WDk-Ig-Grr"/>
+ <constraint firstAttribute="trailing" secondItem="Q8b-0e-dLv" secondAttribute="trailing" constant="-1" id="hHf-rd-Wcv"/>
+ <constraint firstItem="7L7-hr-zId" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="20" symbolic="YES" id="iKJ-ph-ACS"/>
+ <constraint firstAttribute="bottom" secondItem="wzf-ce-Spm" secondAttribute="bottom" constant="-1" id="jFV-Xi-fWr"/>
+ <constraint firstItem="wzf-ce-Spm" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" id="kJt-oJ-72R"/>
+ </constraints>
+ </view>
+ <point key="canvasLocation" x="720" y="317"/>
+ </window>
+ <arrayController objectClassName="MGLOfflinePack" editable="NO" avoidsEmptySelection="NO" id="dWe-R6-sRz" userLabel="Offline Packs Array Controller">
+ <declaredKeys>
+ <string>context</string>
+ <string>countOfResourcesCompleted</string>
+ <string>countOfResourcesExpected</string>
+ <string>countOfBytesCompleted</string>
+ <string>stateImage</string>
+ </declaredKeys>
+ </arrayController>
+ </objects>
+ <resources>
+ <image name="NSAddTemplate" width="11" height="11"/>
+ <image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
+ <image name="NSRemoveTemplate" width="11" height="11"/>
+ </resources>
+</document>
diff --git a/platform/osx/app/Base.lproj/MapDocument.xib b/platform/osx/app/Base.lproj/MapDocument.xib
new file mode 100644
index 0000000000..55d82d21d0
--- /dev/null
+++ b/platform/osx/app/Base.lproj/MapDocument.xib
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="MapDocument">
+ <connections>
+ <outlet property="mapView" destination="q4d-kF-8Hi" id="7hI-dS-A5R"/>
+ <outlet property="mapViewContextMenu" destination="XbX-6a-Mgy" id="YD0-1r-5N2"/>
+ <outlet property="window" destination="cSv-fg-MAQ" id="TBu-Mu-79N"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="MBXMapWindow" animationBehavior="default" id="cSv-fg-MAQ">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="388" y="211" width="512" height="480"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <view key="contentView" id="TuG-C5-zLS">
+ <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <customView translatesAutoresizingMaskIntoConstraints="NO" id="q4d-kF-8Hi" customClass="MGLMapView">
+ <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
+ <connections>
+ <outlet property="delegate" destination="-2" id="dh2-0H-jFZ"/>
+ <outlet property="menu" destination="XbX-6a-Mgy" id="dSu-HR-Kq2"/>
+ </connections>
+ </customView>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="q4d-kF-8Hi" secondAttribute="bottom" id="L2t-Be-qWL"/>
+ <constraint firstItem="q4d-kF-8Hi" firstAttribute="top" secondItem="TuG-C5-zLS" secondAttribute="top" id="T8A-o3-Bhq"/>
+ <constraint firstItem="q4d-kF-8Hi" firstAttribute="leading" secondItem="TuG-C5-zLS" secondAttribute="leading" id="fGH-YW-Qd3"/>
+ <constraint firstAttribute="trailing" secondItem="q4d-kF-8Hi" secondAttribute="trailing" id="yfG-iG-K4C"/>
+ </constraints>
+ </view>
+ <toolbar key="toolbar" implicitIdentifier="A3AC6577-4712-4628-813D-113498171A84" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="DTc-AP-Bah">
+ <allowedToolbarItems>
+ <toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="bld-8W-Wgg"/>
+ <toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="z4l-5x-MzK"/>
+ <toolbarItem implicitItemIdentifier="2CB58C0A-7B95-4233-8DD3-F94BFE7D3061" label="Share" paletteLabel="Share" image="NSShareTemplate" id="XJT-Ho-tuZ" customClass="ValidatedToolbarItem">
+ <nil key="toolTip"/>
+ <size key="minSize" width="40" height="32"/>
+ <size key="maxSize" width="48" height="32"/>
+ <button key="view" verticalHuggingPriority="750" id="y6e-ev-rVL">
+ <rect key="frame" x="0.0" y="14" width="48" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="NSShareTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="TBK-Ra-XzZ">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <connections>
+ <action selector="showShareMenu:" target="-1" id="fCB-HP-iou"/>
+ </connections>
+ </toolbarItem>
+ <toolbarItem implicitItemIdentifier="BA3542AF-D63A-4893-9CC7-8F67EF2E82B0" label="Style" paletteLabel="Style" id="u23-0z-Otl" customClass="ValidatedToolbarItem">
+ <nil key="toolTip"/>
+ <size key="minSize" width="100" height="26"/>
+ <size key="maxSize" width="120" height="26"/>
+ <popUpButton key="view" verticalHuggingPriority="750" id="Tzm-Cy-dQg">
+ <rect key="frame" x="0.0" y="14" width="120" height="26"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <popUpButtonCell key="cell" type="roundTextured" title="Streets" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="wvt-tP-O3a" id="3PJ-qK-Oh3">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="menu"/>
+ <menu key="menu" id="xf3-qk-IhF">
+ <items>
+ <menuItem title="Streets" state="on" tag="1" id="wvt-tP-O3a"/>
+ <menuItem title="Outdoors" tag="2" id="RkE-lp-fL9"/>
+ <menuItem title="Light" tag="3" id="R4X-kt-HHb"/>
+ <menuItem title="Dark" tag="4" id="jUC-5X-0Zx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Satellite" tag="5" id="CTe-e2-o42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Satellite Streets" tag="6" id="7ly-oA-0ND">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ </items>
+ </menu>
+ </popUpButtonCell>
+ </popUpButton>
+ <connections>
+ <action selector="setStyle:" target="-1" id="2Kw-9i-a3G"/>
+ </connections>
+ </toolbarItem>
+ </allowedToolbarItems>
+ <defaultToolbarItems>
+ <toolbarItem reference="XJT-Ho-tuZ"/>
+ <toolbarItem reference="z4l-5x-MzK"/>
+ <toolbarItem reference="u23-0z-Otl"/>
+ </defaultToolbarItems>
+ <connections>
+ <outlet property="delegate" destination="-2" id="V9D-gS-Tvu"/>
+ </connections>
+ </toolbar>
+ <connections>
+ <binding destination="-2" name="displayPatternTitle1" keyPath="mapView.centerCoordinate" id="wtz-AV-bG1">
+ <dictionary key="options">
+ <string key="NSDisplayPattern">%{title1}@</string>
+ <string key="NSValueTransformerName">LocationCoordinate2DTransformer</string>
+ </dictionary>
+ </binding>
+ <outlet property="delegate" destination="-2" id="HEo-Qf-o6o"/>
+ </connections>
+ </window>
+ <menu title="Map View" id="XbX-6a-Mgy">
+ <items>
+ <menuItem title="Drop Pin" id="qZJ-mM-bLj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="dropPin:" target="-1" id="hxx-eC-kqU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove Pin" id="Zhx-30-VmE">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="removePin:" target="-1" id="w0R-0B-7mG"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select Features" id="za5-bY-mdf">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="selectFeatures:" target="-1" id="ikt-CZ-yZT"/>
+ </connections>
+ </menuItem>
+ </items>
+ <connections>
+ <outlet property="delegate" destination="-2" id="oHe-ZP-lyc"/>
+ </connections>
+ <point key="canvasLocation" x="820" y="254.5"/>
+ </menu>
+ </objects>
+ <resources>
+ <image name="NSShareTemplate" width="11" height="16"/>
+ </resources>
+</document>
diff --git a/platform/osx/app/DroppedPinAnnotation.h b/platform/osx/app/DroppedPinAnnotation.h
index 69bb453f28..435a56738b 100644
--- a/platform/osx/app/DroppedPinAnnotation.h
+++ b/platform/osx/app/DroppedPinAnnotation.h
@@ -1,4 +1,4 @@
-#import "MGLPointAnnotation.h"
+#import <Mapbox/Mapbox.h>
@interface DroppedPinAnnotation : MGLPointAnnotation
diff --git a/platform/osx/app/DroppedPinAnnotation.m b/platform/osx/app/DroppedPinAnnotation.m
index a103ec923c..5b19fd7401 100644
--- a/platform/osx/app/DroppedPinAnnotation.m
+++ b/platform/osx/app/DroppedPinAnnotation.m
@@ -2,21 +2,27 @@
#import "LocationCoordinate2DTransformer.h"
#import "TimeIntervalTransformer.h"
-#import "NSValue+Additions.h"
+
+#import <Mapbox/Mapbox.h>
+
+static MGLCoordinateFormatter *DroppedPinCoordinateFormatter;
@implementation DroppedPinAnnotation {
NSTimer *_timer;
NSTimeInterval _priorShownTimeInterval;
NSDate *_dateShown;
- NSValueTransformer *_coordinateTransformer;
NSValueTransformer *_timeIntervalTransformer;
}
++ (void)initialize {
+ if (self == [DroppedPinAnnotation class]) {
+ DroppedPinCoordinateFormatter = [[MGLCoordinateFormatter alloc] init];
+ }
+}
+
- (instancetype)init {
if (self = [super init]) {
- _coordinateTransformer = [NSValueTransformer valueTransformerForName:
- NSStringFromClass([LocationCoordinate2DTransformer class])];
_timeIntervalTransformer = [NSValueTransformer valueTransformerForName:
NSStringFromClass([TimeIntervalTransformer class])];
[self update:nil];
@@ -54,8 +60,7 @@
}
- (void)update:(NSTimer *)timer {
- NSString *coordinate = [_coordinateTransformer transformedValue:
- [NSValue valueWithCLLocationCoordinate2D:self.coordinate]];
+ NSString *coordinate = [DroppedPinCoordinateFormatter stringFromCoordinate:self.coordinate];
NSString *elapsedTime = [_timeIntervalTransformer transformedValue:@(self.elapsedShownTime)];
self.subtitle = [NSString stringWithFormat:@"%@\nSelected for %@", coordinate, elapsedTime];
}
diff --git a/platform/osx/app/Icon.icns b/platform/osx/app/Icon.icns
deleted file mode 100644
index 64a5e57cf8..0000000000
--- a/platform/osx/app/Icon.icns
+++ /dev/null
Binary files differ
diff --git a/platform/osx/app/Info.plist b/platform/osx/app/Info.plist
index 0ae973842b..cc7037f589 100644
--- a/platform/osx/app/Info.plist
+++ b/platform/osx/app/Info.plist
@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
- <string>English</string>
+ <string>en</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
@@ -21,16 +21,10 @@
</array>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
- <key>CFBundleGetInfoString</key>
- <string>0.1.0</string>
- <key>CFBundleIconFile</key>
- <string>Icon.icns</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
- <key>CFBundleLongVersionString</key>
- <string>0.1.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
@@ -52,10 +46,8 @@
</array>
<key>CFBundleVersion</key>
<string>1</string>
- <key>CSResourcesFileMapped</key>
- <true/>
- <key>NSHighResolutionCapable</key>
- <true/>
+ <key>LSMinimumSystemVersion</key>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
diff --git a/platform/osx/app/LocationCoordinate2DTransformer.m b/platform/osx/app/LocationCoordinate2DTransformer.m
index d9c35b2a1f..59654f1676 100644
--- a/platform/osx/app/LocationCoordinate2DTransformer.m
+++ b/platform/osx/app/LocationCoordinate2DTransformer.m
@@ -1,26 +1,11 @@
#import "LocationCoordinate2DTransformer.h"
-#import "NSValue+Additions.h"
+#import <Mapbox/Mapbox.h>
-NSString *StringFromDegrees(CLLocationDegrees degrees, char positiveDirection, char negativeDirection) {
- double minutes = (degrees - floor(degrees)) * 60;
- double seconds = (minutes - floor(minutes)) * 60;
-
- NSMutableString *string = [NSMutableString stringWithFormat:@"%.0f°", fabs(degrees)];
- if (floor(minutes) || floor(seconds)) {
- [string appendFormat:@"%.0f′", minutes];
- }
- if (floor(seconds)) {
- [string appendFormat:@"%.0f″", seconds];
- }
- if (degrees) {
- [string appendFormat:@"%c", degrees > 0 ? positiveDirection : negativeDirection];
- }
- return string;
+@implementation LocationCoordinate2DTransformer {
+ MGLCoordinateFormatter *_coordinateFormatter;
}
-@implementation LocationCoordinate2DTransformer
-
+ (Class)transformedValueClass {
return [NSString class];
}
@@ -29,14 +14,18 @@ NSString *StringFromDegrees(CLLocationDegrees degrees, char positiveDirection, c
return NO;
}
+- (instancetype)init {
+ if (self = [super init]) {
+ _coordinateFormatter = [[MGLCoordinateFormatter alloc] init];
+ }
+ return self;
+}
+
- (id)transformedValue:(id)value {
if (![value isKindOfClass:[NSValue class]]) {
return nil;
}
- CLLocationCoordinate2D coordinate = [value CLLocationCoordinate2DValue];
- return [NSString stringWithFormat:@"%@, %@",
- StringFromDegrees(coordinate.latitude, 'N', 'S'),
- StringFromDegrees(coordinate.longitude, 'E', 'W')];
+ return [_coordinateFormatter stringForObjectValue:value].capitalizedString;
}
@end
diff --git a/platform/osx/app/MainMenu.xib b/platform/osx/app/MainMenu.xib
deleted file mode 100644
index b9934be5ee..0000000000
--- a/platform/osx/app/MainMenu.xib
+++ /dev/null
@@ -1,813 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
- <connections>
- <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application" customClass="NSObject">
- <connections>
- <outlet property="delegate" destination="Voe-Tx-rLC" id="z9N-Fm-MUP"/>
- </connections>
- </customObject>
- <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
- <connections>
- <outlet property="offlinePacksArrayController" destination="dWe-R6-sRz" id="Ar5-xu-ABm"/>
- <outlet property="offlinePacksPanel" destination="Jjv-gs-Tx6" id="0vK-rR-3ZX"/>
- <outlet property="preferencesWindow" destination="UWc-yQ-qda" id="Ota-aT-Mz2"/>
- </connections>
- </customObject>
- <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
- <items>
- <menuItem title="Mapbox GL" id="1Xt-HY-uBw">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Mapbox GL" systemMenu="apple" id="uQy-DD-JDr">
- <items>
- <menuItem title="About Mapbox GL" id="5kV-Vb-QxS">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
- <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
- <connections>
- <action selector="showPreferences:" target="-1" id="Llx-Uy-HTS"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
- <menuItem title="Services" id="NMo-om-nkz">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
- <menuItem title="Hide Mapbox GL" keyEquivalent="h" id="Olw-nP-bQN">
- <connections>
- <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
- </connections>
- </menuItem>
- <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- <connections>
- <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
- </connections>
- </menuItem>
- <menuItem title="Show All" id="Kd2-mp-pUS">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
- <menuItem title="Quit Mapbox GL" keyEquivalent="q" id="4sb-4s-VLi">
- <connections>
- <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="File" id="dMs-cI-mzQ">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="File" id="bib-Uj-vzu">
- <items>
- <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
- <connections>
- <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
- </connections>
- </menuItem>
- <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
- <connections>
- <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
- </connections>
- </menuItem>
- <menuItem title="Open Recent" id="tXI-mr-wws">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
- <items>
- <menuItem title="Clear Menu" id="vNY-rz-j42">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
- <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
- <connections>
- <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
- </connections>
- </menuItem>
- <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
- <connections>
- <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
- </connections>
- </menuItem>
- <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
- <connections>
- <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
- </connections>
- </menuItem>
- <menuItem title="Save Offline Pack…" id="UXB-sj-C7i">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="addOfflinePack:" target="-1" id="Usu-xO-QEx"/>
- </connections>
- </menuItem>
- <menuItem title="Revert to Saved" id="KaW-ft-85H">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
- <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
- <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
- <connections>
- <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
- </connections>
- </menuItem>
- <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
- <connections>
- <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Edit" id="5QF-Oa-p0T">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Edit" id="W48-6f-4Dl">
- <items>
- <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
- <connections>
- <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
- </connections>
- </menuItem>
- <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
- <connections>
- <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
- <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
- <connections>
- <action selector="cut:" target="-1" id="YJe-68-I9s"/>
- </connections>
- </menuItem>
- <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
- <connections>
- <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
- </connections>
- </menuItem>
- <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
- <connections>
- <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
- </connections>
- </menuItem>
- <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- <connections>
- <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
- </connections>
- </menuItem>
- <menuItem title="Delete" id="pa3-QI-u2k">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
- </connections>
- </menuItem>
- <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
- <connections>
- <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
- <menuItem title="Find" id="4EN-yA-p0u">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Find" id="1b7-l0-nxx">
- <items>
- <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
- <connections>
- <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
- </connections>
- </menuItem>
- <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- <connections>
- <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
- </connections>
- </menuItem>
- <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
- <connections>
- <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
- </connections>
- </menuItem>
- <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
- <connections>
- <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
- </connections>
- </menuItem>
- <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
- <connections>
- <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
- </connections>
- </menuItem>
- <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
- <connections>
- <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
- <items>
- <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
- <connections>
- <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
- </connections>
- </menuItem>
- <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
- <connections>
- <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
- <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
- </connections>
- </menuItem>
- <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
- </connections>
- </menuItem>
- <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Substitutions" id="9ic-FL-obx">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
- <items>
- <menuItem title="Show Substitutions" id="z6F-FW-3nz">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
- <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
- </connections>
- </menuItem>
- <menuItem title="Smart Quotes" id="hQb-2v-fYv">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
- </connections>
- </menuItem>
- <menuItem title="Smart Dashes" id="rgM-f4-ycn">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
- </connections>
- </menuItem>
- <menuItem title="Smart Links" id="cwL-P1-jid">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
- </connections>
- </menuItem>
- <menuItem title="Data Detectors" id="tRr-pd-1PS">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
- </connections>
- </menuItem>
- <menuItem title="Text Replacement" id="HFQ-gK-NFA">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Transformations" id="2oI-Rn-ZJC">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
- <items>
- <menuItem title="Make Upper Case" id="vmV-6d-7jI">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
- </connections>
- </menuItem>
- <menuItem title="Make Lower Case" id="d9M-CD-aMd">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
- </connections>
- </menuItem>
- <menuItem title="Capitalize" id="UEZ-Bs-lqG">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Speech" id="xrE-MZ-jX0">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
- <items>
- <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
- </connections>
- </menuItem>
- <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="View" id="H8h-7b-M4v">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="View" id="HyV-fh-RgO">
- <items>
- <menuItem title="Streets" state="on" tag="1" keyEquivalent="1" id="17N-yz-NNo">
- <connections>
- <action selector="setStyle:" target="-1" id="I4L-Wx-UXA"/>
- </connections>
- </menuItem>
- <menuItem title="Outdoors" tag="2" keyEquivalent="2" id="BBa-Qa-SQr">
- <connections>
- <action selector="setStyle:" target="-1" id="rM1-yG-t5u"/>
- </connections>
- </menuItem>
- <menuItem title="Light" tag="3" keyEquivalent="3" id="HWe-7u-UVJ">
- <connections>
- <action selector="setStyle:" target="-1" id="Q9V-O1-oRz"/>
- </connections>
- </menuItem>
- <menuItem title="Dark" tag="4" keyEquivalent="4" id="6HI-q6-AeV">
- <connections>
- <action selector="setStyle:" target="-1" id="YfH-1I-G50"/>
- </connections>
- </menuItem>
- <menuItem title="Satellite" tag="5" keyEquivalent="5" id="h0J-5X-kgF">
- <connections>
- <action selector="setStyle:" target="-1" id="GXt-oK-Hy1"/>
- </connections>
- </menuItem>
- <menuItem title="Satellite Streets" tag="6" keyEquivalent="6" id="9BL-00-HFt">
- <connections>
- <action selector="setStyle:" target="-1" id="oL4-AC-waq"/>
- </connections>
- </menuItem>
- <menuItem title="Custom Style…" id="L0h-86-2cU">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="chooseCustomStyle:" target="-1" id="QJF-fM-Ty3"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="BMF-ml-0Bd"/>
- <menuItem title="Zoom In" keyEquivalent="+" id="W82-WO-xvB">
- <connections>
- <action selector="zoomIn:" target="-1" id="g33-vK-zUu"/>
- </connections>
- </menuItem>
- <menuItem title="Zoom Out" keyEquivalent="-" id="j7h-PY-edM">
- <connections>
- <action selector="zoomOut:" target="-1" id="0pP-tO-9ex"/>
- </connections>
- </menuItem>
- <menuItem title="Snap to North" keyEquivalent="" id="Zss-3w-wkz">
- <connections>
- <action selector="snapToNorth:" target="-1" id="Ayq-GE-Lb5"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="mkP-YN-G0w"/>
- <menuItem title="Reload" keyEquivalent="r" id="JvI-nv-KaE">
- <connections>
- <action selector="reload:" target="-1" id="xkh-9F-mOe"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="CyM-Wv-Bnc"/>
- <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
- <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
- <connections>
- <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Debug" id="ZNC-5r-eBw">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Debug" id="McE-ka-r79">
- <items>
- <menuItem title="Show Tile Boundaries" id="rDE-dG-rTR">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleTileBoundaries:" target="-1" id="LAO-88-F7h"/>
- </connections>
- </menuItem>
- <menuItem title="Show Tile Info" id="LoH-qD-kb0">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleTileInfo:" target="-1" id="KCn-0G-V87"/>
- </connections>
- </menuItem>
- <menuItem title="Show Tile Timestamps" id="bY0-2E-LZ7">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleTileTimestamps:" target="-1" id="tBs-2N-KEG"/>
- </connections>
- </menuItem>
- <menuItem title="Show Collision Boxes" id="Y0b-3K-mJE">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleCollisionBoxes:" target="-1" id="EYa-7n-iWZ"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="dYw-bb-tr1"/>
- <menuItem title="Show Tooltips on Dropped Pins" id="uir-Rx-zmw">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleShowsToolTipsOnDroppedPins:" target="-1" id="1YC-Co-QQ6"/>
- </connections>
- </menuItem>
- <menuItem title="Use Random Cursors for Dropped Pins" id="ZTk-lc-Jgu">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="toggleRandomizesCursorsOnDroppedPins:" target="-1" id="Mpw-b8-oub"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="Sl5-nE-kHd"/>
- <menuItem title="Blanket Map With Pins" id="LMZ-oe-Ngh">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="dropManyPins:" target="-1" id="Rtv-8N-3Z8"/>
- </connections>
- </menuItem>
- <menuItem title="Add Polygon and Polyline" id="DVr-vT-lpe">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="drawPolygonAndPolyLineAnnotations:" target="-1" id="EhT-CB-gee"/>
- </connections>
- </menuItem>
- <menuItem title="Remove All Annotations" id="6rC-68-vk0">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="wQq-Mx-QY0"/>
- <menuItem title="Start World Tour" id="VFo-Jh-2sw">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="startWorldTour:" target="-1" id="66Y-Gm-Yn1"/>
- </connections>
- </menuItem>
- <menuItem title="Stop World Tour" id="Pa8-qU-xfr">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="stopWorldTour:" target="-1" id="aq0-7t-AGi"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Window" id="aUF-d1-5bR">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
- <items>
- <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
- <connections>
- <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
- </connections>
- </menuItem>
- <menuItem title="Zoom" keyEquivalent="z" id="R4o-n2-Eq4">
- <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
- <connections>
- <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="Uix-g7-fAt"/>
- <menuItem title="Offline Packs" id="YW3-jR-knj">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="showOfflinePacksPanel:" target="Voe-Tx-rLC" id="kj9-ht-KmF"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
- <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- <menuItem title="Help" id="wpr-3q-Mcd">
- <modifierMask key="keyEquivalentModifierMask"/>
- <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
- <items>
- <menuItem title="Mapbox GL Help" keyEquivalent="?" id="FKE-Sm-Kum">
- <connections>
- <action selector="showShortcuts:" target="-1" id="hNZ-sm-X2q"/>
- </connections>
- </menuItem>
- <menuItem isSeparatorItem="YES" id="EpY-wQ-SjH"/>
- <menuItem title="Improve This Map" id="xu5-WN-qYK">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="giveFeedback:" target="-1" id="cil-i9-r39"/>
- </connections>
- </menuItem>
- </items>
- </menu>
- </menuItem>
- </items>
- </menu>
- <window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="Preferences" animationBehavior="default" id="UWc-yQ-qda">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="109" y="131" width="350" height="62"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
- <view key="contentView" id="eA4-n3-qPe">
- <rect key="frame" x="0.0" y="0.0" width="350" height="62"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0IK-AW-Gg3">
- <rect key="frame" x="18" y="23" width="89" height="17"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Access token:" id="Ptd-FI-M5A">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <connections>
- <accessibilityConnection property="link" destination="7sb-sf-oJU" id="U0t-jC-oQ7"/>
- </connections>
- </textField>
- <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7sb-sf-oJU">
- <rect key="frame" x="113" y="20" width="197" height="22"/>
- <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="jlV-TC-NUv">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <connections>
- <binding destination="45S-yT-WUN" name="value" keyPath="values.MGLMapboxAccessToken" id="iJE-S2-ALY"/>
- </connections>
- </textField>
- <button translatesAutoresizingMaskIntoConstraints="NO" id="c3S-LC-PoX">
- <rect key="frame" x="318" y="25" width="12" height="12"/>
- <constraints>
- <constraint firstAttribute="width" constant="12" id="M3J-pU-gKn"/>
- </constraints>
- <buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="NSFollowLinkFreestandingTemplate" imagePosition="only" alignment="center" controlSize="small" imageScaling="proportionallyUpOrDown" inset="2" id="38x-37-Ay0">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="smallSystem"/>
- </buttonCell>
- <connections>
- <action selector="openAccessTokenManager:" target="-1" id="1LX-4G-roC"/>
- </connections>
- </button>
- </subviews>
- <constraints>
- <constraint firstAttribute="trailing" secondItem="c3S-LC-PoX" secondAttribute="trailing" constant="20" symbolic="YES" id="7QU-Jd-Rg6"/>
- <constraint firstItem="c3S-LC-PoX" firstAttribute="top" secondItem="eA4-n3-qPe" secondAttribute="top" constant="25" id="JOS-HU-27c"/>
- <constraint firstItem="7sb-sf-oJU" firstAttribute="leading" secondItem="0IK-AW-Gg3" secondAttribute="trailing" constant="8" symbolic="YES" id="SS6-VQ-sLK"/>
- <constraint firstItem="0IK-AW-Gg3" firstAttribute="leading" secondItem="eA4-n3-qPe" secondAttribute="leading" constant="20" symbolic="YES" id="TYG-io-qfV"/>
- <constraint firstItem="7sb-sf-oJU" firstAttribute="top" secondItem="eA4-n3-qPe" secondAttribute="top" constant="20" symbolic="YES" id="Vzb-q8-ecP"/>
- <constraint firstItem="c3S-LC-PoX" firstAttribute="leading" secondItem="7sb-sf-oJU" secondAttribute="trailing" constant="8" symbolic="YES" id="pjl-9u-IgM"/>
- <constraint firstItem="7sb-sf-oJU" firstAttribute="baseline" secondItem="0IK-AW-Gg3" secondAttribute="baseline" id="qIY-Jr-9Ws"/>
- <constraint firstItem="7sb-sf-oJU" firstAttribute="centerY" secondItem="c3S-LC-PoX" secondAttribute="centerY" id="zej-gw-fC0"/>
- </constraints>
- </view>
- <connections>
- <outlet property="initialFirstResponder" destination="7sb-sf-oJU" id="UZe-di-dnA"/>
- </connections>
- <point key="canvasLocation" x="754" y="210"/>
- </window>
- <userDefaultsController representsSharedInstance="YES" id="45S-yT-WUN"/>
- <window title="Offline Packs" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MBXOfflinePacksPanel" animationBehavior="default" id="Jjv-gs-Tx6" customClass="NSPanel">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="830" y="430" width="400" height="300"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
- <view key="contentView" id="8ha-hw-zOD">
- <rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
- <rect key="frame" x="-1" y="20" width="402" height="281"/>
- <clipView key="contentView" id="J9U-Yx-o2S">
- <rect key="frame" x="1" y="0.0" width="400" height="280"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
- <rect key="frame" x="0.0" y="0.0" width="400" height="257"/>
- <autoresizingMask key="autoresizingMask"/>
- <size key="intercellSpacing" width="3" height="2"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
- <tableColumns>
- <tableColumn identifier="" editable="NO" width="16" minWidth="10" maxWidth="3.4028234663852886e+38" id="xtw-hQ-8C5" userLabel="State">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
- </tableHeaderCell>
- <imageCell key="dataCell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="edU-Yw-20f"/>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <connections>
- <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.stateImage" id="2wd-1J-TZt"/>
- </connections>
- </tableColumn>
- <tableColumn editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
- </tableHeaderCell>
- <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="oys-QZ-34I">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <connections>
- <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.context" id="NtD-s5-ZUq">
- <dictionary key="options">
- <string key="NSValueTransformerName">OfflinePackNameValueTransformer</string>
- </dictionary>
- </binding>
- </connections>
- </tableColumn>
- <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
- </tableHeaderCell>
- <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WfC-qb-HsW">
- <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="sNm-Qn-ne6"/>
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <connections>
- <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesCompleted" id="mu6-Jg-GiU"/>
- </connections>
- </tableColumn>
- <tableColumn identifier="" editable="NO" width="50" minWidth="10" maxWidth="3.4028234663852886e+38" id="Rrd-A9-jqc">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
- </tableHeaderCell>
- <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="mHy-qJ-rOA">
- <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="kyx-ZP-OBH"/>
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <connections>
- <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
- </connections>
- </tableColumn>
- <tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="h7m-6l-KaS">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Size">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
- </tableHeaderCell>
- <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="701-bg-k6L">
- <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" id="IXV-J9-sP3"/>
- <font key="font" metaFont="system"/>
- <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
- <connections>
- <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfBytesCompleted" id="Zsa-Na-yFN"/>
- </connections>
- </tableColumn>
- </tableColumns>
- <connections>
- <action trigger="doubleAction" selector="chooseOfflinePack:" target="-1" id="pUN-eT-zRT"/>
- </connections>
- </tableView>
- </subviews>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
- </clipView>
- <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
- <rect key="frame" x="1" y="7" width="0.0" height="16"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
- <rect key="frame" x="224" y="17" width="15" height="102"/>
- <autoresizingMask key="autoresizingMask"/>
- </scroller>
- <tableHeaderView key="headerView" id="MAZ-Iq-hBi">
- <rect key="frame" x="0.0" y="0.0" width="400" height="23"/>
- <autoresizingMask key="autoresizingMask"/>
- </tableHeaderView>
- </scrollView>
- <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wzf-ce-Spm">
- <rect key="frame" x="0.0" y="-1" width="21" height="21"/>
- <constraints>
- <constraint firstAttribute="width" constant="21" id="5ST-tY-8Ph"/>
- </constraints>
- <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="sew-F7-i5T">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- <connections>
- <action selector="addOfflinePack:" target="-1" id="SN0-PM-HoU"/>
- </connections>
- </button>
- <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7L7-hr-zId">
- <rect key="frame" x="20" y="0.0" width="21" height="19"/>
- <constraints>
- <constraint firstAttribute="width" constant="21" id="JYb-AF-8gZ"/>
- </constraints>
- <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="oTF-3m-6qT">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- <string key="keyEquivalent" base64-UTF8="YES">
-CA
-</string>
- </buttonCell>
- <connections>
- <action selector="delete:" target="-1" id="EGL-bf-yUD"/>
- </connections>
- </button>
- </subviews>
- <constraints>
- <constraint firstItem="7L7-hr-zId" firstAttribute="centerY" secondItem="wzf-ce-Spm" secondAttribute="centerY" id="7TI-6w-bf1"/>
- <constraint firstAttribute="bottom" secondItem="Q8b-0e-dLv" secondAttribute="bottom" constant="20" symbolic="YES" id="DZa-ly-bhV"/>
- <constraint firstItem="wzf-ce-Spm" firstAttribute="top" secondItem="Q8b-0e-dLv" secondAttribute="bottom" id="LhK-5z-CQA"/>
- <constraint firstItem="Q8b-0e-dLv" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="-1" id="Oyo-ch-rZo"/>
- <constraint firstAttribute="bottom" secondItem="7L7-hr-zId" secondAttribute="bottom" id="TtY-j1-T5h"/>
- <constraint firstItem="Q8b-0e-dLv" firstAttribute="top" secondItem="8ha-hw-zOD" secondAttribute="top" constant="-1" id="WDk-Ig-Grr"/>
- <constraint firstAttribute="trailing" secondItem="Q8b-0e-dLv" secondAttribute="trailing" constant="-1" id="hHf-rd-Wcv"/>
- <constraint firstItem="7L7-hr-zId" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="20" symbolic="YES" id="iKJ-ph-ACS"/>
- <constraint firstAttribute="bottom" secondItem="wzf-ce-Spm" secondAttribute="bottom" constant="-1" id="jFV-Xi-fWr"/>
- <constraint firstItem="wzf-ce-Spm" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" id="kJt-oJ-72R"/>
- </constraints>
- </view>
- <point key="canvasLocation" x="720" y="317"/>
- </window>
- <arrayController objectClassName="MGLOfflinePack" editable="NO" avoidsEmptySelection="NO" id="dWe-R6-sRz" userLabel="Offline Packs Array Controller">
- <declaredKeys>
- <string>context</string>
- <string>countOfResourcesCompleted</string>
- <string>countOfResourcesExpected</string>
- <string>countOfBytesCompleted</string>
- <string>stateImage</string>
- </declaredKeys>
- </arrayController>
- </objects>
- <resources>
- <image name="NSAddTemplate" width="11" height="11"/>
- <image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
- <image name="NSRemoveTemplate" width="11" height="11"/>
- </resources>
-</document>
diff --git a/platform/osx/app/MapDocument.m b/platform/osx/app/MapDocument.m
index e7488cf00e..e9f3b99592 100644
--- a/platform/osx/app/MapDocument.m
+++ b/platform/osx/app/MapDocument.m
@@ -2,7 +2,6 @@
#import "AppDelegate.h"
#import "DroppedPinAnnotation.h"
-#import "NSValue+Additions.h"
#import <Mapbox/Mapbox.h>
@@ -15,6 +14,38 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
{ .latitude = -13.15589555, .longitude = -74.2178961777998 },
};
+NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotation>) *shapes) {
+ NSMutableArray *flattenedShapes = [NSMutableArray arrayWithCapacity:shapes.count];
+ for (id <MGLAnnotation> shape in shapes) {
+ NSArray *subshapes;
+ // Flatten multipoints but not polylines or polygons.
+ if ([shape isMemberOfClass:[MGLMultiPoint class]]) {
+ NSUInteger pointCount = [(MGLMultiPoint *)shape pointCount];
+ CLLocationCoordinate2D *coordinates = [(MGLMultiPoint *)shape coordinates];
+ NSMutableArray *pointAnnotations = [NSMutableArray arrayWithCapacity:pointCount];
+ for (NSUInteger i = 0; i < pointCount; i++) {
+ MGLPointAnnotation *pointAnnotation = [[MGLPointAnnotation alloc] init];
+ pointAnnotation.coordinate = coordinates[i];
+ [pointAnnotations addObject:pointAnnotation];
+ }
+ subshapes = pointAnnotations;
+ } else if ([shape isKindOfClass:[MGLMultiPolyline class]]) {
+ subshapes = [(MGLMultiPolyline *)shape polylines];
+ } else if ([shape isKindOfClass:[MGLMultiPolygon class]]) {
+ subshapes = [(MGLMultiPolygon *)shape polygons];
+ } else if ([shape isKindOfClass:[MGLShapeCollection class]]) {
+ subshapes = MBXFlattenedShapes([(MGLShapeCollection *)shape shapes]);
+ }
+
+ if (subshapes) {
+ [flattenedShapes addObjectsFromArray:subshapes];
+ } else {
+ [flattenedShapes addObject:shape];
+ }
+ }
+ return flattenedShapes;
+}
+
@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, MGLMapViewDelegate>
@property (weak) IBOutlet NSMenu *mapViewContextMenu;
@@ -211,10 +242,18 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
if (appDelegate.pendingDebugMask) {
self.mapView.debugMask = appDelegate.pendingDebugMask;
}
+ if (appDelegate.pendingMinimumZoomLevel >= 0) {
+ self.mapView.zoomLevel = MAX(appDelegate.pendingMinimumZoomLevel, self.mapView.zoomLevel);
+ appDelegate.pendingMaximumZoomLevel = -1;
+ }
+ if (appDelegate.pendingMaximumZoomLevel >= 0) {
+ self.mapView.zoomLevel = MIN(appDelegate.pendingMaximumZoomLevel, self.mapView.zoomLevel);
+ appDelegate.pendingMaximumZoomLevel = -1;
+ }
// Temporarily set the display name to the default center coordinate instead
// of “Untitled” until the binding kicks in.
- NSValue *coordinateValue = [NSValue valueWithCLLocationCoordinate2D:self.mapView.centerCoordinate];
+ NSValue *coordinateValue = [NSValue valueWithMGLCoordinate:self.mapView.centerCoordinate];
self.displayName = [[NSValueTransformer valueTransformerForName:@"LocationCoordinate2DTransformer"]
transformedValue:coordinateValue];
}
@@ -237,6 +276,18 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask;
}
+- (IBAction)toggleWireframes:(id)sender {
+ self.mapView.debugMask ^= MGLMapDebugWireframesMask;
+}
+
+- (IBAction)showColorBuffer:(id)sender {
+ self.mapView.debugMask &= ~MGLMapDebugStencilBufferMask;
+}
+
+- (IBAction)showStencilBuffer:(id)sender {
+ self.mapView.debugMask |= MGLMapDebugStencilBufferMask;
+}
+
- (IBAction)toggleShowsToolTipsOnDroppedPins:(id)sender {
_showsToolTipsOnDroppedPins = !_showsToolTipsOnDroppedPins;
}
@@ -398,7 +449,11 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
- (void)handlePressGesture:(NSPressGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state == NSGestureRecognizerStateBegan) {
NSPoint location = [gestureRecognizer locationInView:self.mapView];
- [self dropPinAtPoint:location];
+ if (!NSPointInRect([gestureRecognizer locationInView:self.mapView.compass], self.mapView.compass.bounds)
+ && !NSPointInRect([gestureRecognizer locationInView:self.mapView.zoomControls], self.mapView.zoomControls.bounds)
+ && !NSPointInRect([gestureRecognizer locationInView:self.mapView.attributionView], self.mapView.attributionView.bounds)) {
+ [self dropPinAtPoint:location];
+ }
}
}
@@ -413,9 +468,17 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
}
- (DroppedPinAnnotation *)pinAtPoint:(NSPoint)point {
+ NSArray *features = [self.mapView visibleFeaturesAtPoint:point];
+ NSString *title;
+ for (id <MGLFeature> feature in features) {
+ if (!title) {
+ title = [feature attributeForKey:@"name_en"] ?: [feature attributeForKey:@"name"];
+ }
+ }
+
DroppedPinAnnotation *annotation = [[DroppedPinAnnotation alloc] init];
annotation.coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
- annotation.title = @"Dropped Pin";
+ annotation.title = title ?: @"Dropped Pin";
_spellOutNumberFormatter.numberStyle = NSNumberFormatterSpellOutStyle;
if (_showsToolTipsOnDroppedPins) {
NSString *formattedNumber = [_spellOutNumberFormatter stringFromNumber:@(++_droppedPinCounter)];
@@ -432,6 +495,16 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[self.mapView removeAnnotation:[self.mapView annotationAtPoint:point]];
}
+- (IBAction)selectFeatures:(id)sender {
+ [self selectFeaturesAtPoint:_mouseLocationForMapViewContextMenu];
+}
+
+- (void)selectFeaturesAtPoint:(NSPoint)point {
+ NSArray *features = [self.mapView visibleFeaturesAtPoint:point];
+ NSArray *flattenedFeatures = MBXFlattenedShapes(features);
+ [self.mapView addAnnotations:flattenedFeatures];
+}
+
#pragma mark User interface validation
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
@@ -480,13 +553,16 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
return YES;
}
if (menuItem.action == @selector(dropPin:)) {
- BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
- menuItem.hidden = isOverAnnotation;
+ id <MGLAnnotation> annotationUnderCursor = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
+ menuItem.hidden = annotationUnderCursor != nil;
return YES;
}
if (menuItem.action == @selector(removePin:)) {
- BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
- menuItem.hidden = !isOverAnnotation;
+ id <MGLAnnotation> annotationUnderCursor = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
+ menuItem.hidden = annotationUnderCursor == nil;
+ return YES;
+ }
+ if (menuItem.action == @selector(selectFeatures:)) {
return YES;
}
if (menuItem.action == @selector(toggleTileBoundaries:)) {
@@ -509,6 +585,21 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
menuItem.title = isShown ? @"Hide Collision Boxes" : @"Show Collision Boxes";
return YES;
}
+ if (menuItem.action == @selector(toggleWireframes:)) {
+ BOOL isShown = self.mapView.debugMask & MGLMapDebugWireframesMask;
+ menuItem.title = isShown ? @"Hide Wireframes" : @"Show Wireframes";
+ return YES;
+ }
+ if (menuItem.action == @selector(showColorBuffer:)) {
+ BOOL enabled = self.mapView.debugMask & MGLMapDebugStencilBufferMask;
+ menuItem.state = enabled ? NSOffState : NSOnState;
+ return YES;
+ }
+ if (menuItem.action == @selector(showStencilBuffer:)) {
+ BOOL enabled = self.mapView.debugMask & MGLMapDebugStencilBufferMask;
+ menuItem.state = enabled ? NSOnState : NSOffState;
+ return YES;
+ }
if (menuItem.action == @selector(toggleShowsToolTipsOnDroppedPins:)) {
BOOL isShown = _showsToolTipsOnDroppedPins;
menuItem.title = isShown ? @"Hide Tooltips on Dropped Pins" : @"Show Tooltips on Dropped Pins";
@@ -647,7 +738,7 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[NSCursor dragCopyCursor],
[NSCursor contextualMenuCursor],
];
- annotationImage.cursor = cursors[arc4random_uniform(cursors.count)];
+ annotationImage.cursor = cursors[arc4random_uniform((uint32_t)cursors.count) % cursors.count];
} else {
annotationImage.cursor = nil;
}
@@ -668,6 +759,10 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
}
}
+- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation {
+ return 0.8;
+}
+
@end
@interface ValidatedToolbarItem : NSToolbarItem
diff --git a/platform/osx/app/MapDocument.xib b/platform/osx/app/MapDocument.xib
deleted file mode 100644
index e6891098d8..0000000000
--- a/platform/osx/app/MapDocument.xib
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="MapDocument">
- <connections>
- <outlet property="mapView" destination="q4d-kF-8Hi" id="7hI-dS-A5R"/>
- <outlet property="mapViewContextMenu" destination="XbX-6a-Mgy" id="YD0-1r-5N2"/>
- <outlet property="window" destination="cSv-fg-MAQ" id="TBu-Mu-79N"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application" customClass="NSObject"/>
- <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="MBXMapWindow" animationBehavior="default" id="cSv-fg-MAQ">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="388" y="211" width="512" height="480"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
- <view key="contentView" id="TuG-C5-zLS">
- <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <customView translatesAutoresizingMaskIntoConstraints="NO" id="q4d-kF-8Hi" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
- <connections>
- <outlet property="delegate" destination="-2" id="dh2-0H-jFZ"/>
- </connections>
- </customView>
- </subviews>
- <constraints>
- <constraint firstAttribute="bottom" secondItem="q4d-kF-8Hi" secondAttribute="bottom" id="L2t-Be-qWL"/>
- <constraint firstItem="q4d-kF-8Hi" firstAttribute="top" secondItem="TuG-C5-zLS" secondAttribute="top" id="T8A-o3-Bhq"/>
- <constraint firstItem="q4d-kF-8Hi" firstAttribute="leading" secondItem="TuG-C5-zLS" secondAttribute="leading" id="fGH-YW-Qd3"/>
- <constraint firstAttribute="trailing" secondItem="q4d-kF-8Hi" secondAttribute="trailing" id="yfG-iG-K4C"/>
- </constraints>
- </view>
- <toolbar key="toolbar" implicitIdentifier="A3AC6577-4712-4628-813D-113498171A84" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="DTc-AP-Bah">
- <allowedToolbarItems>
- <toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="bld-8W-Wgg"/>
- <toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="z4l-5x-MzK"/>
- <toolbarItem implicitItemIdentifier="2CB58C0A-7B95-4233-8DD3-F94BFE7D3061" label="Share" paletteLabel="Share" image="NSShareTemplate" id="XJT-Ho-tuZ" customClass="ValidatedToolbarItem">
- <nil key="toolTip"/>
- <size key="minSize" width="40" height="32"/>
- <size key="maxSize" width="48" height="32"/>
- <button key="view" verticalHuggingPriority="750" id="y6e-ev-rVL">
- <rect key="frame" x="0.0" y="14" width="48" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="NSShareTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="TBK-Ra-XzZ">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- <connections>
- <action selector="showShareMenu:" target="-1" id="fCB-HP-iou"/>
- </connections>
- </toolbarItem>
- <toolbarItem implicitItemIdentifier="BA3542AF-D63A-4893-9CC7-8F67EF2E82B0" label="Style" paletteLabel="Style" id="u23-0z-Otl" customClass="ValidatedToolbarItem">
- <nil key="toolTip"/>
- <size key="minSize" width="100" height="26"/>
- <size key="maxSize" width="120" height="26"/>
- <popUpButton key="view" verticalHuggingPriority="750" id="Tzm-Cy-dQg">
- <rect key="frame" x="0.0" y="14" width="120" height="26"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <popUpButtonCell key="cell" type="roundTextured" title="Streets" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="wvt-tP-O3a" id="3PJ-qK-Oh3">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="menu"/>
- <menu key="menu" id="xf3-qk-IhF">
- <items>
- <menuItem title="Streets" state="on" tag="1" id="wvt-tP-O3a"/>
- <menuItem title="Outdoors" tag="2" id="RkE-lp-fL9"/>
- <menuItem title="Light" tag="3" id="R4X-kt-HHb"/>
- <menuItem title="Dark" tag="4" id="jUC-5X-0Zx">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Satellite" tag="5" id="CTe-e2-o42">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Satellite Streets" tag="6" id="7ly-oA-0ND">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- </items>
- </menu>
- </popUpButtonCell>
- </popUpButton>
- <connections>
- <action selector="setStyle:" target="-1" id="2Kw-9i-a3G"/>
- </connections>
- </toolbarItem>
- </allowedToolbarItems>
- <defaultToolbarItems>
- <toolbarItem reference="XJT-Ho-tuZ"/>
- <toolbarItem reference="z4l-5x-MzK"/>
- <toolbarItem reference="u23-0z-Otl"/>
- </defaultToolbarItems>
- <connections>
- <outlet property="delegate" destination="-2" id="V9D-gS-Tvu"/>
- </connections>
- </toolbar>
- <connections>
- <binding destination="-2" name="displayPatternTitle1" keyPath="mapView.centerCoordinate" id="wtz-AV-bG1">
- <dictionary key="options">
- <string key="NSDisplayPattern">%{title1}@</string>
- <string key="NSValueTransformerName">LocationCoordinate2DTransformer</string>
- </dictionary>
- </binding>
- <outlet property="delegate" destination="-2" id="HEo-Qf-o6o"/>
- </connections>
- </window>
- <menu title="Map View" id="XbX-6a-Mgy">
- <items>
- <menuItem title="Drop Pin" id="qZJ-mM-bLj">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Remove Pin" id="Zhx-30-VmE">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- </items>
- <connections>
- <outlet property="delegate" destination="-2" id="oHe-ZP-lyc"/>
- </connections>
- <point key="canvasLocation" x="820" y="254.5"/>
- </menu>
- </objects>
- <resources>
- <image name="NSShareTemplate" width="11" height="16"/>
- </resources>
-</document>
diff --git a/platform/osx/app/NSValue+Additions.h b/platform/osx/app/NSValue+Additions.h
deleted file mode 100644
index 050dbc8132..0000000000
--- a/platform/osx/app/NSValue+Additions.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-@interface NSValue (Additions)
-
-+ (instancetype)valueWithCLLocationCoordinate2D:(CLLocationCoordinate2D)coordinate;
-
-@property (readonly) CLLocationCoordinate2D CLLocationCoordinate2DValue;
-
-@end
diff --git a/platform/osx/app/NSValue+Additions.m b/platform/osx/app/NSValue+Additions.m
deleted file mode 100644
index c060602f43..0000000000
--- a/platform/osx/app/NSValue+Additions.m
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "NSValue+Additions.h"
-
-@implementation NSValue (Additions)
-
-+ (instancetype)valueWithCLLocationCoordinate2D:(CLLocationCoordinate2D)coordinate {
- return [self valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)];
-}
-
-- (CLLocationCoordinate2D)CLLocationCoordinate2DValue {
- CLLocationCoordinate2D coordinate;
- [self getValue:&coordinate];
- return coordinate;
-}
-
-@end
diff --git a/platform/osx/app/TimeIntervalTransformer.m b/platform/osx/app/TimeIntervalTransformer.m
index e6d7333d25..39177dc5bc 100644
--- a/platform/osx/app/TimeIntervalTransformer.m
+++ b/platform/osx/app/TimeIntervalTransformer.m
@@ -1,7 +1,5 @@
#import "TimeIntervalTransformer.h"
-#import "NSValue+Additions.h"
-
@implementation TimeIntervalTransformer
+ (Class)transformedValueClass {
diff --git a/platform/osx/app/mapboxgl-app.gypi b/platform/osx/app/mapboxgl-app.gypi
deleted file mode 100644
index 8b8cc17276..0000000000
--- a/platform/osx/app/mapboxgl-app.gypi
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'osxapp',
- 'product_name': 'Mapbox GL',
- 'type': 'executable',
- 'product_extension': 'app',
- 'mac_bundle': 1,
- 'mac_bundle_resources': [
- 'Credits.rtf',
- 'Icon.icns',
- 'MainMenu.xib',
- 'MapDocument.xib',
- ],
-
- 'dependencies': [
- 'osxsdk',
- ],
-
- 'sources': [
- './AppDelegate.h',
- './AppDelegate.m',
- './DroppedPinAnnotation.h',
- './DroppedPinAnnotation.m',
- './LocationCoordinate2DTransformer.h',
- './LocationCoordinate2DTransformer.m',
- './MapDocument.h',
- './MapDocument.m',
- './OfflinePackNameValueTransformer.h',
- './OfflinePackNameValueTransformer.m',
- './TimeIntervalTransformer.h',
- './TimeIntervalTransformer.m',
- './NSValue+Additions.h',
- './NSValue+Additions.m',
- './main.m',
- ],
-
- 'xcode_settings': {
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'INFOPLIST_FILE': '../platform/osx/app/Info.plist',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '\${inherited}',
- '@executable_path/../Frameworks',
- ],
- 'PRODUCT_BUNDLE_IDENTIFIER': 'com.mapbox.MapboxGL',
- 'SDKROOT': 'macosx',
- 'SUPPORTED_PLATFORMS': 'macosx',
- },
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'COPY_PHASE_STRIP': 'NO',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'COPY_PHASE_STRIP': 'YES',
- },
- },
- },
-
- 'copies': [
- {
- 'destination': '<(PRODUCT_DIR)/${FRAMEWORKS_FOLDER_PATH}',
- 'files': [
- '<(PRODUCT_DIR)/Mapbox.framework',
- ],
- 'xcode_code_sign': 1,
- }
- ],
- },
- ]
-}
diff --git a/platform/osx/bitrise.yml b/platform/osx/bitrise.yml
index dc7c4b3028..fdf9ebb654 100644
--- a/platform/osx/bitrise.yml
+++ b/platform/osx/bitrise.yml
@@ -1,11 +1,6 @@
format_version: 1.1.0
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
-app:
- envs:
- - BITRISE_APP_TITLE: "Mapbox GL – OS X"
- - BITRISE_DEV_BRANCH: "master"
-
trigger_map:
- pattern: "*"
is_pull_request_allowed: true
@@ -27,24 +22,17 @@ workflows:
else
envman add --key SKIPCI --value false
fi
- - select-xcode-version:
- title: Select Xcode version
- run_if: '{{enveq "SKIPCI" "false"}}'
- - script:
- title: Install Homebrew dependencies
- run_if: '{{enveq "SKIPCI" "false"}}'
- inputs:
- - content: |-
- #!/bin/bash
- brew install pkgconfig
- brew install automake
- script:
- title: Run SDK unit tests
+ title: Run build script
run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- content: |-
#!/bin/bash
- make xctest BUILDTYPE=Debug
+ set -eu -o pipefail
+ gem install xcpretty --no-rdoc --no-ri
+ export BUILDTYPE=Debug
+ make osx
+ make test-osx
- is_debug: 'yes'
- slack:
title: Post to Slack
diff --git a/platform/osx/docs/doc-README.md b/platform/osx/docs/doc-README.md
new file mode 100644
index 0000000000..1e9619766f
--- /dev/null
+++ b/platform/osx/docs/doc-README.md
@@ -0,0 +1,9 @@
+# [Mapbox OS X SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/osx/)
+
+The Mapbox OS X SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on OS X 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+
+<img alt="" src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/osx/screenshot.png" width="645">
+
+For setup information, consult the README.md that comes with this documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox OS X SDK with few differences, mostly around unimplemented features like user location tracking. A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/osx/CHANGELOG.md) is also available.
+
+Mapbox does not officially support the OS X SDK to the same extent as the iOS SDK; however, [bug reports and pull requests](https://github.com/mapbox/mapbox-gl-native/issues/) are certainly welcome.
diff --git a/platform/osx/docs/pod-README.md b/platform/osx/docs/pod-README.md
new file mode 100644
index 0000000000..28c783cf56
--- /dev/null
+++ b/platform/osx/docs/pod-README.md
@@ -0,0 +1,43 @@
+# [Mapbox OS X SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/osx/)
+
+The Mapbox OS X SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on OS X 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+
+<img alt="" src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/osx/screenshot.png" width="645">
+
+## Installation
+
+1. Open the project editor, select your application target, then go to the General tab. Drag Mapbox.framework from the `dynamic` folder into the “Embedded Binaries” section. (Don’t drag it into the “Linked Frameworks and Libraries” section; Xcode will add it there automatically.) In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
+
+1. Mapbox vector tiles require a Mapbox account and API access token. In the project editor, select the application target, then go to the Info tab. Under the “Custom iOS Target Properties” section, set `MGLMapboxAccessToken` to your access token. You can obtain an access token from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/).
+
+## Usage
+
+In a storyboard or XIB, add a view to your view controller. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically:
+
+1. Switch to the Assistant Editor.
+1. Import the `Mapbox` module.
+1. Connect the map view to a new outlet in your view controller class. (Control-drag from the map view in Interface Builder to a valid location in your view controller implementation.) The resulting outlet declaration should look something like this:
+
+```objc
+// ViewController.m
+@import Mapbox;
+
+@interface ViewController : NSViewController
+
+@property (strong) IBOutlet MGLMapView *mapView;
+
+@end
+```
+
+```swift
+// ViewController.swift
+import Mapbox
+
+class ViewController: NSViewController {
+ @IBOutlet var mapView: MGLMapView!
+}
+```
+
+Full API documentation is included in this package, within the `documentation` folder. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox OS X SDK with few differences, mostly around unimplemented features like user location tracking.
+
+Mapbox does not officially support the OS X SDK to the same extent as the iOS SDK; however, [bug reports and pull requests](https://github.com/mapbox/mapbox-gl-native/issues/) are certainly welcome.
diff --git a/platform/osx/include/MGLAnnotationImage.h b/platform/osx/include/MGLAnnotationImage.h
deleted file mode 100644
index a33ea75a5e..0000000000
--- a/platform/osx/include/MGLAnnotationImage.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#import <AppKit/AppKit.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** The MGLAnnotationImage class is responsible for presenting point-based
- annotations visually on an MGLMapView instance. Annotation image objects
- pair NSImage objects with annotation-related metadata. They may be recycled
- later and put into a reuse queue that is maintained by the map view. */
-@interface MGLAnnotationImage : NSObject
-
-#pragma mark Initializing and preparing the image object
-/** @name Initializing and Preparing the Image Object */
-
-/** Initializes and returns a new annotation image object.
-
- @param image The image to display for the annotation.
- @param reuseIdentifier The string that identifies this annotation image in
- the reuse queue.
- @return The initialized annotation image object or `nil` if there was a
- problem initializing the object. */
-+ (instancetype)annotationImageWithImage:(NSImage *)image reuseIdentifier:(NSString *)reuseIdentifier;
-
-#pragma mark Getting and setting attributes
-/** @name Getting and Setting Attributes */
-
-/** The image to display for the annotation. */
-@property (nonatomic, readonly) NSImage *image;
-
-/** The string that identifies this annotation image in the reuse queue.
- (read-only)
-
- You specify the reuse identifier when you create the image object. You use
- this type later to retrieve an annotation image object that was created
- previously but which is currently unused because its annotation is not
- on-screen.
-
- If you define distinctly different types of annotations (with distinctly
- different annotation images to go with them), you can differentiate between
- the annotation types by specifying different reuse identifiers for each one.
- */
-@property (nonatomic, readonly) NSString *reuseIdentifier;
-
-/** A Boolean value indicating whether the annotation is selectable.
-
- The default value of this property is `YES`. If the value of this property
- is `NO`, the annotation image ignores click events and cannot be selected.
- */
-@property (nonatomic, getter=isSelectable) BOOL selectable;
-
-/** The cursor that appears above any annotation using this annotation image. By
- default, this property is set to `nil`, representing the current cursor. */
-@property (nonatomic, nullable) NSCursor *cursor;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/osx/include/MGLMapView.h b/platform/osx/include/MGLMapView.h
deleted file mode 100644
index f6ba86fb05..0000000000
--- a/platform/osx/include/MGLMapView.h
+++ /dev/null
@@ -1,640 +0,0 @@
-#import <Cocoa/Cocoa.h>
-#import <CoreLocation/CoreLocation.h>
-
-#import "MGLGeometry.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/** Options for enabling debugging features in an MGLMapView instance. */
-typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
- /** Edges of tile boundaries are shown as thick, red lines to help diagnose
- tile clipping issues. */
- MGLMapDebugTileBoundariesMask = 1 << 1,
-
- /** Each tile shows its tile coordinate (x/y/z) in the upper-left corner. */
- MGLMapDebugTileInfoMask = 1 << 2,
-
- /** Each tile shows a timestamp indicating when it was loaded. */
- MGLMapDebugTimestampsMask = 1 << 3,
-
- /** Edges of glyphs and symbols are shown as faint, green lines to help
- diagnose collision and label placement issues. */
- MGLMapDebugCollisionBoxesMask = 1 << 4,
-};
-
-@class MGLAnnotationImage;
-@class MGLMapCamera;
-
-@protocol MGLAnnotation;
-@protocol MGLMapViewDelegate;
-@protocol MGLOverlay;
-
-/** An interactive, customizable map view with an interface similar to the one
- provided by Apple’s MapKit.
-
- Using MGLMapView, you can embed the map inside a view, allow users to
- manipulate it with standard gestures, animate the map between different
- viewpoints, and present information in the form of annotations and overlays.
-
- The map view loads scalable vector tiles that conform to the
- <a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
- It styles them with a style that conforms to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
- Such styles can be designed in
- <a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
- mapbox.com.
-
- A collection of Mapbox-hosted styles is available through the MGLStyle
- class. These basic styles use
- <a href="https://www.mapbox.com/developers/vector-tiles/mapbox-streets">Mapbox Streets</a>
- or <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a> data
- sources, but you can specify a custom style that makes use of your own data.
-
- Mapbox-hosted vector tiles and styles require an API access token, which you
- can obtain from the
- <a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>.
- Access tokens associate requests to Mapbox’s vector tile and style APIs with
- your Mapbox account. They also deter other developers from using your styles
- without your permission.
-
- @note You are responsible for getting permission to use the map data and for
- ensuring that your use adheres to the relevant terms of use. */
-IB_DESIGNABLE
-@interface MGLMapView : NSView
-
-#pragma mark Creating instances
-/** @name Creating Instances */
-
-/** Initializes and returns a newly allocated map view with the specified frame
- and the default style.
-
- @param frame The frame for the view, measured in points.
- @return An initialized map view. */
-- (instancetype)initWithFrame:(NSRect)frame;
-
-/** Initializes and returns a newly allocated map view with the specified frame
- and style URL.
-
- @param frame The frame for the view, measured in points.
- @param styleURL URL of the map style to display. The URL may be a full HTTP
- or HTTPS URL, a Mapbox URL indicating the style’s map ID
- (`mapbox://styles/<user>/<style>`), or a path to a local file relative
- to the application’s resource path. Specify `nil` for the default style.
- @return An initialized map view. */
-- (instancetype)initWithFrame:(NSRect)frame styleURL:(nullable NSURL *)styleURL;
-
-#pragma mark Accessing the delegate
-/** @name Accessing the Delegate */
-
-/** The receiver’s delegate.
-
- A map view sends messages to its delegate to notify it of changes to its
- contents or the viewpoint. The delegate also provides information about
- annotations displayed on the map, such as the styles to apply to individual
- annotations. */
-@property (nonatomic, weak, nullable) IBOutlet id <MGLMapViewDelegate> delegate;
-
-#pragma mark Configuring the map’s appearance
-/** @name Configuring the Map’s Appearance */
-
-
-/** URL of the style currently displayed in the receiver.
-
- The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s
- map ID (`mapbox://styles/<user>/<style>`), or a path to a local file
- relative to the application’s resource path.
-
- If you set this property to `nil`, the receiver will use the default style
- and this property will automatically be set to that style’s URL. */
-@property (nonatomic, null_resettable) NSURL *styleURL;
-
-/** Reloads the style.
-
- You do not normally need to call this method. The map view automatically
- responds to changes in network connectivity by reloading the style. You may
- need to call this method if you change the access token after a style has
- loaded but before loading a style associated with a different Mapbox
- account. */
-- (IBAction)reloadStyle:(id)sender;
-
-/** A control for zooming in and out, positioned in the lower-right corner. */
-@property (nonatomic, readonly) NSSegmentedControl *zoomControls;
-
-/** A control indicating the map’s direction and allowing the user to manipulate
- the direction, positioned above the zoom controls in the lower-right corner.
- */
-@property (nonatomic, readonly) NSSlider *compass;
-
-/** The Mapbox logo, positioned in the lower-left corner.
-
- @note The Mapbox terms of service, which governs the use of Mapbox-hosted
- vector tiles and styles,
- <a href="https://www.mapbox.com/help/mapbox-logo/">requires</a> most
- Mapbox customers to display the Mapbox logo. If this applies to you, do
- not hide this view or change its contents. */
-@property (nonatomic, readonly) NSImageView *logoView;
-
-/** A view showing legally required copyright notices, positioned along the
- bottom of the map view, to the left of the Mapbox logo.
-
- @note The Mapbox terms of service, which governs the use of Mapbox-hosted
- vector tiles and styles,
- <a href="https://www.mapbox.com/help/attribution/">requires</a> these
- copyright notices to accompany any map that features Mapbox-designed
- styles, OpenStreetMap data, or other Mapbox data such as satellite or
- terrain data. If that applies to this map view, do not hide this view or
- remove any notices from it. */
-@property (nonatomic, readonly) NSView *attributionView;
-
-#pragma mark Manipulating the viewpoint
-/** @name Manipulating the Viewpoint */
-
-/** The geographic coordinate at the center of the map view.
-
- Changing the value of this property centers the map on the new coordinate
- without changing the current zoom level.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the -setCenterCoordinate:animated: method
- instead. */
-@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
-
-/** Changes the center coordinate of the map and optionally animates the change.
-
- Changing the center coordinate centers the map on the new coordinate without
- changing the current zoom level.
-
- @param coordinate The new center coordinate for the map.
- @param animated Specify `YES` if you want the map view to scroll to the new
- location or `NO` if you want the map to display the new location
- immediately. */
-- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
-
-/** The zoom level of the receiver.
-
- In addition to affecting the visual size and detail of features on the map,
- the zoom level affects the size of the vector tiles that are loaded. At zoom
- level 0, each tile covers the entire world map; at zoom level 1, it covers ¼
- of the world; at zoom level 2, <sup>1</sup>⁄<sub>16</sub> of the world, and
- so on.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the -setZoomLevel:animated: method instead.
- */
-@property (nonatomic) double zoomLevel;
-
-/**
- * The minimum zoom level at which the map can be shown.
- *
- * Depending on the map view’s aspect ratio, the map view may be prevented
- * from reaching the minimum zoom level, in order to keep the map from
- * repeating within the current viewport.
- *
- * If the value of this property is greater than that of the
- * maximumZoomLevel property, the behavior is undefined.
- *
- * The default minimumZoomLevel is 0.
- */
-@property (nonatomic) double minimumZoomLevel;
-
-/**
- * The maximum zoom level the map can be shown at.
- *
- * If the value of this property is smaller than that of the
- * minimumZoomLevel property, the behavior is undefined.
- *
- * The default maximumZoomLevel is 20.
- */
-@property (nonatomic) double maximumZoomLevel;
-
-/** Changes the zoom level of the map and optionally animates the change.
-
- Changing the zoom level scales the map without changing the current center
- coordinate.
-
- @param zoomLevel The new zoom level for the map.
- @param animated Specify `YES` if you want the map view to animate the change
- to the new zoom level or `NO` if you want the map to display the new
- zoom level immediately. */
-- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated;
-
-/** The heading of the map, measured in degrees clockwise from true north.
-
- The value `0` means that the top edge of the map view corresponds to true
- north. The value `90` means the top of the map is pointing due east. The
- value `180` means the top of the map points due south, and so on.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the -setDirection:animated: method instead.
- */
-@property (nonatomic) CLLocationDirection direction;
-
-/** Changes the heading of the map and optionally animates the change.
-
- @param direction The heading of the map, measured in degrees clockwise from
- true north.
- @param animated Specify `YES` if you want the map view to animate the change
- to the new heading or `NO` if you want the map to display the new
- heading immediately.
-
- Changing the heading rotates the map without changing the current center
- coordinate or zoom level. */
-- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated;
-
-/** A camera representing the current viewpoint of the map. */
-@property (nonatomic, copy) MGLMapCamera *camera;
-
-/** Moves the viewpoint to a different location with respect to the map with an
- optional transition animation.
-
- @param camera The new viewpoint.
- @param animated Specify `YES` if you want the map view to animate the change
- to the new viewpoint or `NO` if you want the map to display the new
- viewpoint immediately. */
-- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
-
-/** Moves the viewpoint to a different location with respect to the map with an
- optional transition duration and timing function.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously.
- @param function A timing function used for the animation. Set this parameter
- to `nil` for a transition that matches most system animations. If the
- duration is `0`, this parameter is ignored.
- @param completion The block to execute after the animation finishes. */
-- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
-
-/** Moves the viewpoint to a different location using a transition animation
- that evokes powered flight and a default duration based on the length of the
- flight path.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param completion The block to execute after the animation finishes. */
-- (void)flyToCamera:(MGLMapCamera *)camera completionHandler:(nullable void (^)(void))completion;
-
-/** Moves the viewpoint to a different location using a transition animation
- that evokes powered flight and an optional transition duration.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously. Specify a negative value to use the default duration,
- which is based on the length of the flight path.
- @param completion The block to execute after the animation finishes. */
-- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration completionHandler:(nullable void (^)(void))completion;
-
-/** Moves the viewpoint to a different location using a transition animation
- that evokes powered flight and an optional transition duration and peak
- altitude.
-
- The transition animation seamlessly incorporates zooming and panning to help
- the user find his or her bearings even after traversing a great distance.
-
- @param camera The new viewpoint.
- @param duration The amount of time, measured in seconds, that the transition
- animation should take. Specify `0` to jump to the new viewpoint
- instantaneously. Specify a negative value to use the default duration,
- which is based on the length of the flight path.
- @param peakAltitude The altitude, measured in meters, at the midpoint of the
- animation. The value of this parameter is ignored if it is negative or
- if the animation transition resulting from a similar call to
- `-setCamera:animated:` would have a midpoint at a higher altitude.
- @param completion The block to execute after the animation finishes. */
-- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;
-
-/** The geographic coordinate bounds visible in the receiver’s viewport.
-
- Changing the value of this property updates the receiver immediately. If you
- want to animate the change, use the -setVisibleCoordinateBounds:animated:
- method instead. */
-@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
-
-/** Changes the receiver’s viewport to fit the given coordinate bounds,
- optionally animating the change.
-
- @param bounds The bounds that the viewport will show in its entirety.
- @param animated Specify `YES` to animate the change by smoothly scrolling
- and zooming or `NO` to immediately display the given bounds. */
-- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
-
-/** A Boolean value indicating whether the receiver automatically adjusts its
- content insets.
-
- When the value of this property is `YES`, the map view automatically updates
- its `contentInsets` property to account for any overlapping title bar or
- toolbar. To overlap with the title bar or toolbar, the containing window’s
- style mask must have `NSFullSizeContentViewWindowMask` set, and the title
- bar must not be transparent.
-
- The default value of this property is `YES`. */
-@property (nonatomic, assign) BOOL automaticallyAdjustsContentInsets;
-
-/** 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 `NSEdgeInsetsZero`, 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.
-
- When the value of the `automaticallyAdjustsContentInsets` property is `YES`,
- the value of this property may be overridden at any time.
-
- Changing the value of this property updates the map view immediately. If you
- want to animate the change, use the `-setContentInsets:animated:` method
- instead. */
-@property (nonatomic, assign) NSEdgeInsets contentInsets;
-
-/** Sets the distance from the edges of the map view’s frame to the edges of the
- map view’s logical viewport, with an optional transition animation.
-
- When the value of this property is equal to `NSEdgeInsetsZero`, 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.
-
- When the value of the `automaticallyAdjustsContentInsets` property is `YES`,
- the value of this property may be overridden at any time.
-
- @param contentInsets The new values to inset the content by.
- @param animated Specify `YES` if you want the map view to animate the change
- to the content insets or `NO` if you want the map to inset the content
- immediately. */
-- (void)setContentInsets:(NSEdgeInsets)contentInsets animated:(BOOL)animated;
-
-#pragma mark Configuring gesture recognition
-/** @name Configuring How the User Interacts with the Map */
-
-/** A Boolean value that determines whether the user may zoom the map in and
- out, changing the zoom level.
-
- When this property is set to `YES`, the default, the user may zoom the map
- in and out by pinching two fingers, by using a scroll wheel on a
- traditional mouse, or by dragging the mouse cursor up and down while holding
- down the Shift key. When the receiver has focus, the user may also zoom by
- pressing the up and down arrow keys while holding down the Option key.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the map zoom
- programmatically. */
-@property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
-
-/** A Boolean value that determines whether the user may scroll around the map,
- changing the center coordinate.
-
- When this property is set to `YES`, the default, the user may scroll the map
- by swiping with two fingers or dragging the mouse cursor. When the receiver
- has focus, the user may also scroll around the map by pressing the arrow
- keys.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the map location
- programmatically. */
-@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
-
-/** A Boolean value that determines whether the user may rotate the map,
- changing the direction.
-
- When this property is set to `YES`, the default, the user may rotate the map
- by moving two fingers in a circular motion or by dragging the mouse cursor
- left and right while holding down the Option key. When the receiver has
- focus, the user may also zoom by pressing the left and right arrow keys
- while holding down the Option key.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still rotate the map
- programmatically. */
-@property (nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
-
-/** A Boolean value that determines whether the user may tilt of the map,
- changing the pitch.
-
- When this property is set to `YES`, the default, the user may rotate the map
- by dragging the mouse cursor up and down while holding down the Option key.
-
- This property controls only user interactions with the map. If you set the
- value of this property to `NO`, you may still change the pitch of the map
- programmatically. */
-@property (nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
-
-#pragma mark Annotating the map
-/** @name Annotating the Map */
-
-/** The complete list of annotations associated with the receiver. (read-only)
-
- The objects in this array must adopt the MGLAnnotation protocol. If no
- annotations are associated with the map view, the value of this property is
- `nil`. */
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
-
-/** Adds an annotation to the map view.
-
- @param annotation The annotation object to add to the receiver. This object
- must conform to the MGLAnnotation protocol. The map view retains the
- annotation object. */
-- (void)addAnnotation:(id <MGLAnnotation>)annotation;
-
-/** Adds an array of annotations to the map view.
-
- @param annotations An array of annotation objects. Each object in the array
- must conform to the MGLAnnotation protocol. The map view retains each
- individual annotation object. */
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
-
-/** Removes an annotation from the map view, deselecting it if it is selected.
-
- Removing an annotation object dissociates it from the map view entirely,
- preventing it from being displayed on the map. Thus you would typically call
- this method only when you want to hide or delete a given annotation.
-
- @param annotation The annotation object to remove. This object must conform
- to the MGLAnnotation protocol. */
-- (void)removeAnnotation:(id <MGLAnnotation>)annotation;
-
-/** Removes an array of annotations from the map view, deselecting any selected
- annotations in the array.
-
- Removing annotation objects dissociates them from the map view entirely,
- preventing them from being displayed on the map. Thus you would typically
- call this method only when you want to hide or delete the given annotations.
-
- @param annotations The array of annotation objects to remove. Objects in the
- array must conform to the MGLAnnotation protocol. */
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
-
-/** Returns a reusable annotation image object associated with its identifier.
-
- For performance reasons, you should generally reuse MGLAnnotationImage
- objects for identical-looking annotations in your map views. Dequeueing
- saves time and memory during performance-critical operations such as
- scrolling.
-
- @param identifier A string identifying the annotation image to be reused.
- This string is the same one you specify when initially returning the
- annotation image object using the -mapView:imageForAnnotation: method.
- @return An annotation image object with the given identifier, or `nil` if no
- such object exists in the reuse queue. */
-- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier;
-
-#pragma mark Managing annotation selections
-/** @name Managing Annotation Selections */
-
-/** The currently selected annotations.
-
- Assigning a new array to this property selects only the first annotation in
- the array. */
-@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
-
-/** Selects an annotation and displays a callout popover for it.
-
- If the given annotation is not visible within the current viewport, this
- method has no effect.
-
- @param annotation The annotation object to select. */
-- (void)selectAnnotation:(id <MGLAnnotation>)annotation;
-
-/** Deselects an annotation and hides its callout popover.
-
- @param annotation The annotation object to deselect. */
-- (void)deselectAnnotation:(nullable id <MGLAnnotation>)annotation;
-
-/** A common view controller for managing a callout popover’s content view.
-
- Like any instance of NSPopover, an annotation callout manages its contents
- with a view controller. The annotation object is the view controller’s
- represented object. This means that you can bind controls in the view
- controller’s content view to KVO-compliant properties of the annotation
- object, such as -title and -subtitle.
-
- This property defines a common view controller that is used for every
- annotation’s callout view. If you set this property to `nil`, a default
- view controller will be used that manages a simple title label and subtitle
- label. If you need distinct view controllers for different annotations, the
- map view’s delegate should implement
- -mapView:calloutViewControllerForAnnotation: instead. */
-@property (nonatomic, strong, null_resettable) IBOutlet NSViewController *calloutViewController;
-
-#pragma mark Finding annotations
-/** @name Finding Annotations */
-
-/** Returns a point annotation located at the given point.
-
- @param point A point in the view’s coordinate system.
- @return A point annotation whose annotation image coincides with the point.
- If multiple point annotations coincide with the point, the return value
- is the annotation that would be selected if the user clicks at this
- point.
- */
-- (id <MGLAnnotation>)annotationAtPoint:(NSPoint)point;
-
-#pragma mark Overlaying the map
-/** @name Overlaying the Map */
-
-/** Adds a single overlay to the map.
-
- To remove an overlay from a map, use the -removeOverlay: method.
-
- @param overlay The overlay object to add. This object must conform to the
- MGLOverlay protocol. */
-- (void)addOverlay:(id <MGLOverlay>)overlay;
-
-/** Adds an array of overlays to the map.
-
- To remove multiple overlays from a map, use the -removeOverlays: method.
-
- @param overlays An array of objects, each of which must conform to the
- MGLOverlay protocol. */
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
-
-/** Removes a single overlay from the map.
-
- If the specified overlay is not currently associated with the map view, this
- method does nothing.
-
- @param overlay The overlay object to remove. */
-- (void)removeOverlay:(id <MGLOverlay>)overlay;
-
-/** Removes an array of overlays from the map.
-
- If a given overlay object is not associated with the map view, it is
- ignored.
-
- @param overlays An array of objects, each of which conforms to the
- MGLOverlay protocol. */
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
-
-#pragma mark Converting geographic coordinates
-/** @name Converting Geographic Coordinates */
-
-/** Converts a geographic coordinate to a point in the given view’s coordinate
- system.
-
- @param coordinate The geographic coordinate to convert.
- @param view The view in whose coordinate system the returned point should be
- expressed. If this parameter is `nil`, the returned point is expressed
- in the window’s coordinate system. If `view` is not `nil`, it must
- belong to the same window as the map view.
- @return The point (in the appropriate view or window coordinate system)
- corresponding to the given geographic coordinate. */
-- (NSPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable NSView *)view;
-
-/** Converts a point in the given view’s coordinate system to a geographic
- coordinate.
-
- @param point The point to convert.
- @param view The view in whose coordinate system the point is expressed.
- @return The geographic coordinate at the given point. */
-- (CLLocationCoordinate2D)convertPoint:(NSPoint)point toCoordinateFromView:(nullable NSView *)view;
-
-/** Converts a geographic bounding box to a rectangle in the given view’s
- coordinate system.
-
- @param bounds The geographic bounding box to convert.
- @param view The view in whose coordinate system the returned rectangle
- should be expressed. If this parameter is `nil`, the returned rectangle
- is expressed in the window’s coordinate system. If `view` is not `nil`,
- it must belong to the same window as the map view. */
-- (NSRect)convertCoordinateBounds:(MGLCoordinateBounds)bounds toRectToView:(nullable NSView *)view;
-
-/** Converts a rectangle in the given view’s coordinate system to a geographic
- bounding box.
-
- @param rect The rectangle to convert.
- @param view The view in whose coordinate system the rectangle is expressed.
- @return The geographic bounding box coextensive with the given rectangle. */
-- (MGLCoordinateBounds)convertRect:(NSRect)rect toCoordinateBoundsFromView:(nullable NSView *)view;
-
-/** Returns the distance spanned by one point in the map view’s coordinate
- system at the given latitude and current zoom level.
-
- The distance between points decreases as the latitude approaches the poles.
- This relationship parallels the relationship between longitudinal
- coordinates at different latitudes.
-
- @param latitude The latitude of the geographic coordinate represented by the
- point.
- @return The distance in meters spanned by a single point. */
-- (CLLocationDistance)metersPerPointAtLatitude:(CLLocationDegrees)latitude;
-
-#pragma mark Debugging the map
-/** @name Debugging the Map */
-
-/** The options that determine which debugging aids are shown on the map.
-
- These options are all disabled by default and should remain disabled in
- released software for performance and aesthetic reasons. */
-@property (nonatomic) MGLMapDebugMaskOptions debugMask;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/osx/include/MGLMapViewDelegate.h b/platform/osx/include/MGLMapViewDelegate.h
deleted file mode 100644
index fcd013284d..0000000000
--- a/platform/osx/include/MGLMapViewDelegate.h
+++ /dev/null
@@ -1,199 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@class MGLMapView;
-@class MGLAnnotationImage;
-@class MGLPolygon;
-@class MGLPolyline;
-@class MGLShape;
-
-/** The MGLMapViewDelegate protocol defines a set of optional methods that you
- can use to receive messages from an MGLMapView instance. Because many map
- operations require the MGLMapView class to load data asynchronously, the map
- view calls these methods to notify your application when specific operations
- complete. The map view also uses these methods to request information about
- annotations displayed on the map, such as the styles and interaction modes
- to apply to individual annotations. */
-@protocol MGLMapViewDelegate <NSObject>
-
-@optional
-
-#pragma mark Responding to map viewpoint changes
-/** @name Responding to Map Viewpoint Changes */
-
-/** Tells the delegate that the viewpoint depicted by the map view is about to
- change.
-
- This method is called whenever the currently displayed map camera will start
- changing for any reason.
-
- @param mapView The map view whose viewpoint will change.
- @param animated Whether the change will cause an animated effect on the map.
- */
-- (void)mapView:(MGLMapView *)mapView cameraWillChangeAnimated:(BOOL)animated;
-
-/** Tells the delegate that the viewpoint depicted by the map view is changing.
-
- This method is called as the currently displayed map camera changes due to
- animation. During movement, this method may be called many times to report
- updates to the viewpoint. Therefore, your implementation of this method
- should be as lightweight as possible to avoid affecting performance.
-
- @param mapView The map view whose viewpoint is changing. */
-- (void)mapViewCameraIsChanging:(MGLMapView *)mapView;
-
-/** Tells the delegate that the viewpoint depicted by the map view has finished
- changing.
-
- This method is called whenever the currently displayed map camera has
- finished changing.
-
- @param mapView The map view whose viewpoint has changed.
- @param animated Whether the change caused an animated effect on the map. */
-- (void)mapView:(MGLMapView *)mapView cameraDidChangeAnimated:(BOOL)animated;
-
-#pragma mark Loading the map
-/** @name Loading the Map */
-
-/** Tells the delegate that the map view will begin to load.
-
- This method is called whenever the map view starts loading, including when a
- new style has been set and the map must reload.
-
- @param mapView The map view that is starting to load. */
-- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView;
-
-/** Tells the delegate that the map view has finished loading.
-
- This method is called whenever the map view finishes loading, either after
- the initial load or after a style change has forced a reload.
-
- @param mapView The map view that has finished loading. */
-- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView;
-
-- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
-- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
-- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-
-#pragma mark Managing the display of annotations
-/** @name Managing the Display of Annotations */
-
-/** Returns an annotation image object to mark the given point annotation object
- on the map.
-
- @param mapView The map view that requested the annotation image.
- @param annotation The object representing the annotation that is about to be
- displayed.
- @return The image object to display for the given annotation or `nil` if you
- want to display the default marker image. */
-- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
-
-/** Returns the alpha value to use when rendering a shape annotation.
-
- A value of 0.0 results in a completely transparent shape. A value of 1.0,
- the default, results in a completely opaque shape.
-
- @param mapView The map view rendering the shape annotation.
- @param annotation The annotation being rendered.
- @return An alpha value between 0 and 1.0. */
-- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
-
-/** Returns the color to use when rendering the outline of a shape annotation.
-
- The default stroke color is the selected menu item color. If a pattern
- color is specified, the result is undefined.
-
- @param mapView The map view rendering the shape annotation.
- @param annotation The annotation being rendered.
- @return A color to use for the shape outline. */
-- (NSColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
-
-/** Returns the color to use when rendering the fill of a polygon annotation.
-
- The default fill color is selected menu item color. If a pattern color
- is specified, the result is undefined.
-
- @param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return The polygon’s interior fill color. */
-- (NSColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
-
-/** Returns the line width in points to use when rendering the outline of a
- polyline annotation.
-
- By default, the polyline is outlined with a line 3.0 points wide.
-
- @param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return A line width for the polyline, measured in points. */
-- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation;
-
-#pragma mark Selecting annotations
-/** @name Selecting annotations */
-
-/** Tells the delegate that one of its annotations has been selected.
-
- You can use this method to track changes to the selection state of
- annotations.
-
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was selected. */
-- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
-
-/** Tells the delegate that one of its annotations has been deselected.
-
- You can use this method to track changes in the selection state of
- annotations.
-
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was deselected. */
-- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
-
-#pragma mark Displaying information about annotations
-/** @name Displaying Information About Annotations */
-
-/** Returns a Boolean value indicating whether the annotation is able to display
- extra information in a callout popover.
-
- This method is called after an annotation is selected, before any callout is
- displayed for the annotation.
-
- If the return value is `YES`, a callout popover is shown when the user
- clicks on a selected annotation. The default callout displays the
- annotation’s title and subtitle. You can customize the popover’s contents by
- implementing the -mapView:calloutViewControllerForAnnotation: method.
-
- If the return value is `NO`, or if this method is unimplemented, or if the
- annotation lacks a title, the annotation will not show a callout even when
- selected.
-
- @param mapView The map view that has selected the annotation.
- @param annotation The object representing the annotation.
- @return A Boolean value indicating whether the annotation should show a
- callout.
- */
-- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
-
-/** Returns a view controller to manage the callout popover’s content view.
-
- Like any instance of NSPopover, an annotation callout manages its contents
- with a view controller. The annotation object is the view controller’s
- represented object. This means that you can bind controls in the view
- controller’s content view to KVO-compliant properties of the annotation
- object, such as -title and -subtitle.
-
- If each annotation should have an identical callout, you can set the
- MGLMapView instance’s -setCalloutViewController: method instead.
-
- @param mapView The map view that is requesting a callout view controller.
- @param annotation The object representing the annotation.
- @return A view controller for the given annotation. */
-- (nullable NSViewController *)mapView:(MGLMapView *)mapView calloutViewControllerForAnnotation:(id <MGLAnnotation>)annotation;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/osx/jazzy.yml b/platform/osx/jazzy.yml
new file mode 100644
index 0000000000..9e160d050f
--- /dev/null
+++ b/platform/osx/jazzy.yml
@@ -0,0 +1,84 @@
+module: Mapbox
+author: Mapbox
+author_url: https://www.mapbox.com/
+github_url: https://github.com/mapbox/mapbox-gl-native
+copyright: '© 2014–2016 [Mapbox](https://www.mapbox.com/). See [license](https://github.com/mapbox/mapbox-gl-native/blob/master/LICENSE.md) for more details.'
+
+head: |
+ <link rel='shortcut icon' href='https://www.mapbox.com/img/favicon.ico' type='image/x-icon' />
+
+objc: Yes
+skip_undocumented: Yes
+hide_documentation_coverage: Yes
+umbrella_header: src/Mapbox.h
+framework_root: ../darwin/src
+
+custom_categories:
+ - name: Maps
+ children:
+ - MGLAccountManager
+ - MGLMapCamera
+ - MGLMapDebugMaskOptions
+ - MGLMapView
+ - MGLMapViewDelegate
+ - MGLStyle
+ - MGLStyleDefaultVersion
+ - MGLUserTrackingMode
+ - name: Annotations
+ children:
+ - MGLAnnotation
+ - MGLAnnotationImage
+ - MGLMultiPoint
+ - MGLMultiPolygon
+ - MGLMultiPolyline
+ - MGLPointAnnotation
+ - MGLPolygon
+ - MGLPolyline
+ - MGLOverlay
+ - MGLShape
+ - MGLShapeCollection
+ - name: Map Data
+ children:
+ - MGLFeature
+ - MGLMultiPointFeature
+ - MGLMultiPolygonFeature
+ - MGLMultiPolylineFeature
+ - MGLPointFeature
+ - MGLPolygonFeature
+ - MGLPolylineFeature
+ - MGLShapeCollectionFeature
+ - name: Offline Maps
+ children:
+ - MGLOfflineRegion
+ - MGLOfflineStorage
+ - MGLOfflinePack
+ - MGLOfflinePackAdditionCompletionHandler
+ - MGLOfflinePackErrorNotification
+ - MGLOfflinePackErrorUserInfoKey
+ - MGLOfflinePackMaximumCountUserInfoKey
+ - MGLOfflinePackMaximumMapboxTilesReachedNotification
+ - MGLOfflinePackProgress
+ - MGLOfflinePackProgressChangedNotification
+ - MGLOfflinePackProgressUserInfoKey
+ - MGLOfflinePackRemovalCompletionHandler
+ - MGLOfflinePackState
+ - MGLOfflinePackStateUserInfoKey
+ - MGLTilePyramidOfflineRegion
+ - name: Geometry
+ children:
+ - MGLClockDirectionFormatter
+ - MGLCompassDirectionFormatter
+ - MGLCoordinateBounds
+ - MGLCoordinateBoundsEqualToCoordinateBounds
+ - MGLCoordinateBoundsGetCoordinateSpan
+ - MGLCoordinateBoundsIsEmpty
+ - MGLCoordinateBoundsMake
+ - MGLCoordinateBoundsOffset
+ - MGLCoordinateFormatter
+ - MGLCoordinateSpan
+ - MGLCoordinateSpanEqualToCoordinateSpan
+ - MGLCoordinateSpanMake
+ - MGLCoordinateSpanZero
+ - MGLDegreesFromRadians
+ - MGLRadiansFromDegrees
+ - MGLStringFromCoordinateBounds
diff --git a/platform/osx/osx.xcodeproj/project.pbxproj b/platform/osx/osx.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..51d9f0671e
--- /dev/null
+++ b/platform/osx/osx.xcodeproj/project.pbxproj
@@ -0,0 +1,1178 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 52BECB0A1CC5A26F009CD791 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52BECB091CC5A26F009CD791 /* SystemConfiguration.framework */; };
+ DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */; };
+ DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2A61CC9EB2700E826B2 /* MGLCoordinateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */; };
+ DA35A2A81CC9F41600E826B2 /* MGLCoordinateFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */; };
+ DA35A2AD1CCA091800E826B2 /* MGLCompassDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2AB1CCA091800E826B2 /* MGLCompassDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2AC1CCA091800E826B2 /* MGLCompassDirectionFormatter.m */; };
+ DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2B51CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m */; };
+ DA35A2BF1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2BD1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2C01CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2BE1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m */; };
+ DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */; };
+ DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */; };
+ DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E961CC2E3400062CAFB /* AppDelegate.m */; };
+ DA839E9A1CC2E3400062CAFB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E991CC2E3400062CAFB /* main.m */; };
+ DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E9C1CC2E3400062CAFB /* MapDocument.m */; };
+ DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA839E9E1CC2E3400062CAFB /* MapDocument.xib */; };
+ DA839EA21CC2E3400062CAFB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA839EA11CC2E3400062CAFB /* Assets.xcassets */; };
+ DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA839EA31CC2E3400062CAFB /* MainMenu.xib */; };
+ DA8933A51CCD287300E68420 /* MGLAnnotationCallout.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA8933A71CCD287300E68420 /* MGLAnnotationCallout.xib */; };
+ DA8933AE1CCD290700E68420 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933AB1CCD290700E68420 /* Localizable.strings */; };
+ DA8933B51CCD2C2500E68420 /* Foundation.strings in Resources */ = {isa = PBXBuildFile; fileRef = DA8933B31CCD2C2500E68420 /* Foundation.strings */; };
+ DA8933B81CCD2C2D00E68420 /* Foundation.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = DA8933B61CCD2C2D00E68420 /* Foundation.stringsdict */; };
+ DAB6924A1CC75A31005AAB54 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3451CC31D1200DB3429 /* libmbgl-core.a */; };
+ DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; };
+ DACC22141CF3D3E200D220D9 /* MGLFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22121CF3D3E200D220D9 /* MGLFeature.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DACC22151CF3D3E200D220D9 /* MGLFeature.mm in Sources */ = {isa = PBXBuildFile; fileRef = DACC22131CF3D3E200D220D9 /* MGLFeature.mm */; };
+ DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */; };
+ DAD165741CF4CD7A001FF4B9 /* MGLShapeCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAD165751CF4CD7A001FF4B9 /* MGLShapeCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.m */; };
+ DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = DAE6C2E11CC304F900DB3429 /* Credits.rtf */; };
+ DAE6C2ED1CC3050F00DB3429 /* DroppedPinAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */; };
+ DAE6C2EE1CC3050F00DB3429 /* LocationCoordinate2DTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C2E61CC3050F00DB3429 /* LocationCoordinate2DTransformer.m */; };
+ DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */; };
+ DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C2EC1CC3050F00DB3429 /* TimeIntervalTransformer.m */; };
+ DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3281CC30DB200DB3429 /* Mapbox.framework */; };
+ DAE6C33D1CC30DB200DB3429 /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3281CC30DB200DB3429 /* Mapbox.framework */; };
+ DAE6C33E1CC30DB200DB3429 /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3281CC30DB200DB3429 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ DAE6C3471CC31D1200DB3429 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3451CC31D1200DB3429 /* libmbgl-core.a */; };
+ DAE6C3481CC31D1200DB3429 /* libmbgl-platform-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3461CC31D1200DB3429 /* libmbgl-platform-osx.a */; };
+ DAE6C35A1CC31E0400DB3429 /* MGLAccountManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34A1CC31E0400DB3429 /* MGLAccountManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C35B1CC31E0400DB3429 /* MGLAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34B1CC31E0400DB3429 /* MGLAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C35C1CC31E0400DB3429 /* MGLGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34C1CC31E0400DB3429 /* MGLGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C35D1CC31E0400DB3429 /* MGLMapCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C35E1CC31E0400DB3429 /* MGLMultiPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34E1CC31E0400DB3429 /* MGLMultiPoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C35F1CC31E0400DB3429 /* MGLOfflinePack.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C34F1CC31E0400DB3429 /* MGLOfflinePack.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3501CC31E0400DB3429 /* MGLOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3611CC31E0400DB3429 /* MGLOfflineStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3511CC31E0400DB3429 /* MGLOfflineStorage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3521CC31E0400DB3429 /* MGLOverlay.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3631CC31E0400DB3429 /* MGLPointAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3531CC31E0400DB3429 /* MGLPointAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3641CC31E0400DB3429 /* MGLPolygon.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3541CC31E0400DB3429 /* MGLPolygon.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3551CC31E0400DB3429 /* MGLPolyline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3661CC31E0400DB3429 /* MGLShape.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3561CC31E0400DB3429 /* MGLShape.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3671CC31E0400DB3429 /* MGLStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3571CC31E0400DB3429 /* MGLStyle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3581CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3691CC31E0400DB3429 /* MGLTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3591CC31E0400DB3429 /* MGLTypes.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3841CC31E2A00DB3429 /* MGLAccountManager_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C36A1CC31E2A00DB3429 /* MGLAccountManager_Private.h */; };
+ DAE6C3851CC31E2A00DB3429 /* MGLAccountManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C36B1CC31E2A00DB3429 /* MGLAccountManager.m */; };
+ DAE6C3861CC31E2A00DB3429 /* MGLGeometry_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C36C1CC31E2A00DB3429 /* MGLGeometry_Private.h */; };
+ DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C36D1CC31E2A00DB3429 /* MGLGeometry.mm */; };
+ DAE6C3881CC31E2A00DB3429 /* MGLMapCamera.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */; };
+ DAE6C3891CC31E2A00DB3429 /* MGLMultiPoint_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C36F1CC31E2A00DB3429 /* MGLMultiPoint_Private.h */; };
+ DAE6C38A1CC31E2A00DB3429 /* MGLMultiPoint.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3701CC31E2A00DB3429 /* MGLMultiPoint.mm */; };
+ DAE6C38B1CC31E2A00DB3429 /* MGLOfflinePack_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3711CC31E2A00DB3429 /* MGLOfflinePack_Private.h */; };
+ DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3721CC31E2A00DB3429 /* MGLOfflinePack.mm */; };
+ DAE6C38D1CC31E2A00DB3429 /* MGLOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3731CC31E2A00DB3429 /* MGLOfflineRegion_Private.h */; };
+ DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3741CC31E2A00DB3429 /* MGLOfflineStorage_Private.h */; };
+ DAE6C38F1CC31E2A00DB3429 /* MGLOfflineStorage.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3751CC31E2A00DB3429 /* MGLOfflineStorage.mm */; };
+ DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3761CC31E2A00DB3429 /* MGLPointAnnotation.m */; };
+ DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3771CC31E2A00DB3429 /* MGLPolygon.mm */; };
+ DAE6C3921CC31E2A00DB3429 /* MGLPolyline.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3781CC31E2A00DB3429 /* MGLPolyline.mm */; };
+ DAE6C3931CC31E2A00DB3429 /* MGLShape.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3791CC31E2A00DB3429 /* MGLShape.m */; };
+ DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */; };
+ DAE6C3951CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C37B1CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm */; };
+ DAE6C3961CC31E2A00DB3429 /* MGLTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C37C1CC31E2A00DB3429 /* MGLTypes.m */; };
+ DAE6C3971CC31E2A00DB3429 /* NSBundle+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C37D1CC31E2A00DB3429 /* NSBundle+MGLAdditions.h */; };
+ DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C37E1CC31E2A00DB3429 /* NSBundle+MGLAdditions.m */; };
+ DAE6C3991CC31E2A00DB3429 /* NSException+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C37F1CC31E2A00DB3429 /* NSException+MGLAdditions.h */; };
+ DAE6C39A1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3801CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h */; };
+ DAE6C39B1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3811CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m */; };
+ DAE6C39C1CC31E2A00DB3429 /* NSString+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3821CC31E2A00DB3429 /* NSString+MGLAdditions.h */; };
+ DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3831CC31E2A00DB3429 /* NSString+MGLAdditions.m */; };
+ DAE6C3A31CC31E9400DB3429 /* MGLAnnotationImage.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C39F1CC31E9400DB3429 /* MGLAnnotationImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3A41CC31E9400DB3429 /* MGLMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3A01CC31E9400DB3429 /* MGLMapView.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3A51CC31E9400DB3429 /* MGLMapView+IBAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3A11CC31E9400DB3429 /* MGLMapView+IBAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3A61CC31E9400DB3429 /* MGLMapViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3A21CC31E9400DB3429 /* MGLMapViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3A71CC31EF300DB3429 /* MGLAnnotationImage.m */; };
+ DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3A81CC31EF300DB3429 /* MGLAttributionButton.h */; };
+ DAE6C3B31CC31EF300DB3429 /* MGLAttributionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3A91CC31EF300DB3429 /* MGLAttributionButton.m */; };
+ DAE6C3B41CC31EF300DB3429 /* MGLCompassCell.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3AA1CC31EF300DB3429 /* MGLCompassCell.h */; };
+ DAE6C3B51CC31EF300DB3429 /* MGLCompassCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3AB1CC31EF300DB3429 /* MGLCompassCell.m */; };
+ DAE6C3B61CC31EF300DB3429 /* MGLMapView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3AC1CC31EF300DB3429 /* MGLMapView_Private.h */; };
+ DAE6C3B71CC31EF300DB3429 /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3AD1CC31EF300DB3429 /* MGLMapView.mm */; };
+ DAE6C3B81CC31EF300DB3429 /* MGLMapView+IBAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3AE1CC31EF300DB3429 /* MGLMapView+IBAdditions.m */; };
+ DAE6C3B91CC31EF300DB3429 /* MGLOpenGLLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3AF1CC31EF300DB3429 /* MGLOpenGLLayer.h */; };
+ DAE6C3BA1CC31EF300DB3429 /* MGLOpenGLLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3B01CC31EF300DB3429 /* MGLOpenGLLayer.mm */; };
+ DAE6C3BE1CC31F2E00DB3429 /* default_marker.pdf in Resources */ = {isa = PBXBuildFile; fileRef = DAE6C3BB1CC31F2E00DB3429 /* default_marker.pdf */; };
+ DAE6C3BF1CC31F2E00DB3429 /* mapbox.pdf in Resources */ = {isa = PBXBuildFile; fileRef = DAE6C3BC1CC31F2E00DB3429 /* mapbox.pdf */; };
+ DAE6C3C21CC31F4500DB3429 /* Mapbox.h in Headers */ = {isa = PBXBuildFile; fileRef = DAE6C3C11CC31F4500DB3429 /* Mapbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAE6C3C71CC3499100DB3429 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DAE6C3C61CC3499100DB3429 /* libsqlite3.tbd */; };
+ DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3C81CC34BD800DB3429 /* MGLGeometryTests.mm */; };
+ DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3C91CC34BD800DB3429 /* MGLOfflinePackTests.m */; };
+ DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */; };
+ DAE6C3D51CC34C9900DB3429 /* MGLOfflineStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */; };
+ DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ DAE6C3331CC30DB200DB3429 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA839E8A1CC2E3400062CAFB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DAE6C3271CC30DB200DB3429;
+ remoteInfo = dynamic;
+ };
+ DAE6C33B1CC30DB200DB3429 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA839E8A1CC2E3400062CAFB /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DAE6C3271CC30DB200DB3429;
+ remoteInfo = dynamic;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ DAE6C3221CC30B3C00DB3429 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ DAE6C33E1CC30DB200DB3429 /* Mapbox.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 52BECB091CC5A26F009CD791 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+ DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
+ DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; };
+ DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; };
+ DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2AB1CCA091800E826B2 /* MGLCompassDirectionFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompassDirectionFormatter.h; sourceTree = "<group>"; };
+ DA35A2AC1CCA091800E826B2 /* MGLCompassDirectionFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompassDirectionFormatter.m; sourceTree = "<group>"; };
+ DA35A2B51CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCompassDirectionFormatterTests.m; path = ../../darwin/test/MGLCompassDirectionFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2BD1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLClockDirectionFormatter.h; sourceTree = "<group>"; };
+ DA35A2BE1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLClockDirectionFormatter.m; sourceTree = "<group>"; };
+ DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLClockDirectionFormatterTests.m; path = ../../darwin/test/MGLClockDirectionFormatterTests.m; sourceTree = "<group>"; };
+ DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; };
+ DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA839E921CC2E3400062CAFB /* Mapbox GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DA839E951CC2E3400062CAFB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ DA839E961CC2E3400062CAFB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ DA839E991CC2E3400062CAFB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ DA839E9B1CC2E3400062CAFB /* MapDocument.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MapDocument.h; sourceTree = "<group>"; };
+ DA839E9C1CC2E3400062CAFB /* MapDocument.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MapDocument.m; sourceTree = "<group>"; };
+ DA839E9F1CC2E3400062CAFB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MapDocument.xib; sourceTree = "<group>"; };
+ DA839EA11CC2E3400062CAFB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ DA839EA41CC2E3400062CAFB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ DA839EA61CC2E3400062CAFB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DA8933A61CCD287300E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MGLAnnotationCallout.xib; sourceTree = "<group>"; };
+ DA8933AC1CCD290700E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA8933B41CCD2C2500E68420 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA8933B71CCD2C2D00E68420 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; };
+ DACC22121CF3D3E200D220D9 /* MGLFeature.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature.h; sourceTree = "<group>"; };
+ DACC22131CF3D3E200D220D9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
+ DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
+ DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = "<group>"; };
+ DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLShapeCollection.m; sourceTree = "<group>"; };
+ DAE6C2E11CC304F900DB3429 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
+ DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DroppedPinAnnotation.h; sourceTree = "<group>"; };
+ DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DroppedPinAnnotation.m; sourceTree = "<group>"; };
+ DAE6C2E51CC3050F00DB3429 /* LocationCoordinate2DTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocationCoordinate2DTransformer.h; sourceTree = "<group>"; };
+ DAE6C2E61CC3050F00DB3429 /* LocationCoordinate2DTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocationCoordinate2DTransformer.m; sourceTree = "<group>"; };
+ DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OfflinePackNameValueTransformer.h; sourceTree = "<group>"; };
+ DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OfflinePackNameValueTransformer.m; sourceTree = "<group>"; };
+ DAE6C2EB1CC3050F00DB3429 /* TimeIntervalTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TimeIntervalTransformer.h; sourceTree = "<group>"; };
+ DAE6C2EC1CC3050F00DB3429 /* TimeIntervalTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TimeIntervalTransformer.m; sourceTree = "<group>"; };
+ DAE6C3281CC30DB200DB3429 /* Mapbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAE6C32C1CC30DB200DB3429 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DAE6C3311CC30DB200DB3429 /* test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = test.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAE6C33A1CC30DB200DB3429 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ DAE6C3451CC31D1200DB3429 /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libmbgl-core.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAE6C3461CC31D1200DB3429 /* libmbgl-platform-osx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libmbgl-platform-osx.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ DAE6C34A1CC31E0400DB3429 /* MGLAccountManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAccountManager.h; sourceTree = "<group>"; };
+ DAE6C34B1CC31E0400DB3429 /* MGLAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotation.h; sourceTree = "<group>"; };
+ DAE6C34C1CC31E0400DB3429 /* MGLGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLGeometry.h; sourceTree = "<group>"; };
+ DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapCamera.h; sourceTree = "<group>"; };
+ DAE6C34E1CC31E0400DB3429 /* MGLMultiPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMultiPoint.h; sourceTree = "<group>"; };
+ DAE6C34F1CC31E0400DB3429 /* MGLOfflinePack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflinePack.h; sourceTree = "<group>"; };
+ DAE6C3501CC31E0400DB3429 /* MGLOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineRegion.h; sourceTree = "<group>"; };
+ DAE6C3511CC31E0400DB3429 /* MGLOfflineStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineStorage.h; sourceTree = "<group>"; };
+ DAE6C3521CC31E0400DB3429 /* MGLOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOverlay.h; sourceTree = "<group>"; };
+ DAE6C3531CC31E0400DB3429 /* MGLPointAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointAnnotation.h; sourceTree = "<group>"; };
+ DAE6C3541CC31E0400DB3429 /* MGLPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon.h; sourceTree = "<group>"; };
+ DAE6C3551CC31E0400DB3429 /* MGLPolyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline.h; sourceTree = "<group>"; };
+ DAE6C3561CC31E0400DB3429 /* MGLShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShape.h; sourceTree = "<group>"; };
+ DAE6C3571CC31E0400DB3429 /* MGLStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyle.h; sourceTree = "<group>"; };
+ DAE6C3581CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTilePyramidOfflineRegion.h; sourceTree = "<group>"; };
+ DAE6C3591CC31E0400DB3429 /* MGLTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTypes.h; sourceTree = "<group>"; };
+ DAE6C36A1CC31E2A00DB3429 /* MGLAccountManager_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAccountManager_Private.h; sourceTree = "<group>"; };
+ DAE6C36B1CC31E2A00DB3429 /* MGLAccountManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAccountManager.m; sourceTree = "<group>"; };
+ DAE6C36C1CC31E2A00DB3429 /* MGLGeometry_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLGeometry_Private.h; sourceTree = "<group>"; };
+ DAE6C36D1CC31E2A00DB3429 /* MGLGeometry.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLGeometry.mm; sourceTree = "<group>"; };
+ DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapCamera.mm; sourceTree = "<group>"; };
+ DAE6C36F1CC31E2A00DB3429 /* MGLMultiPoint_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMultiPoint_Private.h; sourceTree = "<group>"; };
+ DAE6C3701CC31E2A00DB3429 /* MGLMultiPoint.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMultiPoint.mm; sourceTree = "<group>"; };
+ DAE6C3711CC31E2A00DB3429 /* MGLOfflinePack_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflinePack_Private.h; sourceTree = "<group>"; };
+ DAE6C3721CC31E2A00DB3429 /* MGLOfflinePack.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOfflinePack.mm; sourceTree = "<group>"; };
+ DAE6C3731CC31E2A00DB3429 /* MGLOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineRegion_Private.h; sourceTree = "<group>"; };
+ DAE6C3741CC31E2A00DB3429 /* MGLOfflineStorage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOfflineStorage_Private.h; sourceTree = "<group>"; };
+ DAE6C3751CC31E2A00DB3429 /* MGLOfflineStorage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOfflineStorage.mm; sourceTree = "<group>"; };
+ DAE6C3761CC31E2A00DB3429 /* MGLPointAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLPointAnnotation.m; sourceTree = "<group>"; };
+ DAE6C3771CC31E2A00DB3429 /* MGLPolygon.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPolygon.mm; sourceTree = "<group>"; };
+ DAE6C3781CC31E2A00DB3429 /* MGLPolyline.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLPolyline.mm; sourceTree = "<group>"; };
+ DAE6C3791CC31E2A00DB3429 /* MGLShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLShape.m; sourceTree = "<group>"; };
+ DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLStyle.mm; sourceTree = "<group>"; };
+ DAE6C37B1CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTilePyramidOfflineRegion.mm; sourceTree = "<group>"; };
+ DAE6C37C1CC31E2A00DB3429 /* MGLTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLTypes.m; sourceTree = "<group>"; };
+ DAE6C37D1CC31E2A00DB3429 /* NSBundle+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+MGLAdditions.h"; sourceTree = "<group>"; };
+ DAE6C37E1CC31E2A00DB3429 /* NSBundle+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+MGLAdditions.m"; sourceTree = "<group>"; };
+ DAE6C37F1CC31E2A00DB3429 /* NSException+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSException+MGLAdditions.h"; sourceTree = "<group>"; };
+ DAE6C3801CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSProcessInfo+MGLAdditions.h"; sourceTree = "<group>"; };
+ DAE6C3811CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSProcessInfo+MGLAdditions.m"; sourceTree = "<group>"; };
+ DAE6C3821CC31E2A00DB3429 /* NSString+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MGLAdditions.h"; sourceTree = "<group>"; };
+ DAE6C3831CC31E2A00DB3429 /* NSString+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MGLAdditions.m"; sourceTree = "<group>"; };
+ DAE6C39F1CC31E9400DB3429 /* MGLAnnotationImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage.h; sourceTree = "<group>"; };
+ DAE6C3A01CC31E9400DB3429 /* MGLMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView.h; sourceTree = "<group>"; };
+ DAE6C3A11CC31E9400DB3429 /* MGLMapView+IBAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+IBAdditions.h"; sourceTree = "<group>"; };
+ DAE6C3A21CC31E9400DB3429 /* MGLMapViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapViewDelegate.h; sourceTree = "<group>"; };
+ DAE6C3A71CC31EF300DB3429 /* MGLAnnotationImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationImage.m; sourceTree = "<group>"; };
+ DAE6C3A81CC31EF300DB3429 /* MGLAttributionButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionButton.h; sourceTree = "<group>"; };
+ DAE6C3A91CC31EF300DB3429 /* MGLAttributionButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAttributionButton.m; sourceTree = "<group>"; };
+ DAE6C3AA1CC31EF300DB3429 /* MGLCompassCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompassCell.h; sourceTree = "<group>"; };
+ DAE6C3AB1CC31EF300DB3429 /* MGLCompassCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompassCell.m; sourceTree = "<group>"; };
+ DAE6C3AC1CC31EF300DB3429 /* MGLMapView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView_Private.h; sourceTree = "<group>"; };
+ DAE6C3AD1CC31EF300DB3429 /* MGLMapView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapView.mm; sourceTree = "<group>"; };
+ DAE6C3AE1CC31EF300DB3429 /* MGLMapView+IBAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLMapView+IBAdditions.m"; sourceTree = "<group>"; };
+ DAE6C3AF1CC31EF300DB3429 /* MGLOpenGLLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLLayer.h; sourceTree = "<group>"; };
+ DAE6C3B01CC31EF300DB3429 /* MGLOpenGLLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLLayer.mm; sourceTree = "<group>"; };
+ DAE6C3BB1CC31F2E00DB3429 /* default_marker.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = default_marker.pdf; sourceTree = "<group>"; };
+ DAE6C3BC1CC31F2E00DB3429 /* mapbox.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = mapbox.pdf; sourceTree = "<group>"; };
+ DAE6C3C11CC31F4500DB3429 /* Mapbox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mapbox.h; path = src/Mapbox.h; sourceTree = SOURCE_ROOT; };
+ DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = mbgl.xcconfig; path = ../../build/osx/mbgl.xcconfig; sourceTree = "<group>"; };
+ DAE6C3C61CC3499100DB3429 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
+ DAE6C3C81CC34BD800DB3429 /* MGLGeometryTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLGeometryTests.mm; path = ../../darwin/test/MGLGeometryTests.mm; sourceTree = "<group>"; };
+ DAE6C3C91CC34BD800DB3429 /* MGLOfflinePackTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflinePackTests.m; path = ../../darwin/test/MGLOfflinePackTests.m; sourceTree = "<group>"; };
+ DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = "<group>"; };
+ DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineStorageTests.m; path = ../../darwin/test/MGLOfflineStorageTests.m; sourceTree = "<group>"; };
+ DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ DA839E8F1CC2E3400062CAFB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAE6C33D1CC30DB200DB3429 /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C3241CC30DB200DB3429 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 52BECB0A1CC5A26F009CD791 /* SystemConfiguration.framework in Frameworks */,
+ DAE6C3471CC31D1200DB3429 /* libmbgl-core.a in Frameworks */,
+ DAE6C3481CC31D1200DB3429 /* libmbgl-platform-osx.a in Frameworks */,
+ DAE6C3C71CC3499100DB3429 /* libsqlite3.tbd in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C32E1CC30DB200DB3429 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAB6924A1CC75A31005AAB54 /* libmbgl-core.a in Frameworks */,
+ DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ DA839E891CC2E3400062CAFB = {
+ isa = PBXGroup;
+ children = (
+ DA839E941CC2E3400062CAFB /* Demo App */,
+ DAE6C3291CC30DB200DB3429 /* SDK */,
+ DAE6C3371CC30DB200DB3429 /* SDK Tests */,
+ DAE6C31E1CC308BC00DB3429 /* Frameworks */,
+ DAE6C3C41CC31F7800DB3429 /* Configuration */,
+ DA839E931CC2E3400062CAFB /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ DA839E931CC2E3400062CAFB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DA839E921CC2E3400062CAFB /* Mapbox GL.app */,
+ DAE6C3281CC30DB200DB3429 /* Mapbox.framework */,
+ DAE6C3311CC30DB200DB3429 /* test.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ DA839E941CC2E3400062CAFB /* Demo App */ = {
+ isa = PBXGroup;
+ children = (
+ DA839E951CC2E3400062CAFB /* AppDelegate.h */,
+ DA839E961CC2E3400062CAFB /* AppDelegate.m */,
+ DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */,
+ DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */,
+ DAE6C2E51CC3050F00DB3429 /* LocationCoordinate2DTransformer.h */,
+ DAE6C2E61CC3050F00DB3429 /* LocationCoordinate2DTransformer.m */,
+ DA839E9B1CC2E3400062CAFB /* MapDocument.h */,
+ DA839E9C1CC2E3400062CAFB /* MapDocument.m */,
+ DA839E9E1CC2E3400062CAFB /* MapDocument.xib */,
+ DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */,
+ DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */,
+ DAE6C2EB1CC3050F00DB3429 /* TimeIntervalTransformer.h */,
+ DAE6C2EC1CC3050F00DB3429 /* TimeIntervalTransformer.m */,
+ DA839EA11CC2E3400062CAFB /* Assets.xcassets */,
+ DA839EA31CC2E3400062CAFB /* MainMenu.xib */,
+ DAE6C2E11CC304F900DB3429 /* Credits.rtf */,
+ DA839EA61CC2E3400062CAFB /* Info.plist */,
+ DA839E981CC2E3400062CAFB /* Supporting Files */,
+ );
+ name = "Demo App";
+ path = app;
+ sourceTree = "<group>";
+ };
+ DA839E981CC2E3400062CAFB /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ DA839E991CC2E3400062CAFB /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ DA8933A81CCD28D100E68420 /* Kit Resources */ = {
+ isa = PBXGroup;
+ children = (
+ DA8933AB1CCD290700E68420 /* Localizable.strings */,
+ DAE6C3BB1CC31F2E00DB3429 /* default_marker.pdf */,
+ DAE6C3BC1CC31F2E00DB3429 /* mapbox.pdf */,
+ DA8933A71CCD287300E68420 /* MGLAnnotationCallout.xib */,
+ );
+ name = "Kit Resources";
+ sourceTree = "<group>";
+ };
+ DA8933B21CCD2C0700E68420 /* Foundation Resources */ = {
+ isa = PBXGroup;
+ children = (
+ DA8933B31CCD2C2500E68420 /* Foundation.strings */,
+ DA8933B61CCD2C2D00E68420 /* Foundation.stringsdict */,
+ );
+ name = "Foundation Resources";
+ path = ../../darwin/resources;
+ sourceTree = "<group>";
+ };
+ DAD1657C1CF4CE6B001FF4B9 /* Formatters */ = {
+ isa = PBXGroup;
+ children = (
+ DA35A2BD1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.h */,
+ DA35A2BE1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m */,
+ DA35A2AB1CCA091800E826B2 /* MGLCompassDirectionFormatter.h */,
+ DA35A2AC1CCA091800E826B2 /* MGLCompassDirectionFormatter.m */,
+ DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */,
+ DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */,
+ );
+ name = Formatters;
+ sourceTree = "<group>";
+ };
+ DAD1657D1CF4CECB001FF4B9 /* Geometry */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C34B1CC31E0400DB3429 /* MGLAnnotation.h */,
+ DACC22121CF3D3E200D220D9 /* MGLFeature.h */,
+ DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */,
+ DACC22131CF3D3E200D220D9 /* MGLFeature.mm */,
+ DAE6C34C1CC31E0400DB3429 /* MGLGeometry.h */,
+ DAE6C36C1CC31E2A00DB3429 /* MGLGeometry_Private.h */,
+ DAE6C36D1CC31E2A00DB3429 /* MGLGeometry.mm */,
+ DAE6C34E1CC31E0400DB3429 /* MGLMultiPoint.h */,
+ DAE6C36F1CC31E2A00DB3429 /* MGLMultiPoint_Private.h */,
+ DAE6C3701CC31E2A00DB3429 /* MGLMultiPoint.mm */,
+ DAE6C3521CC31E0400DB3429 /* MGLOverlay.h */,
+ DAE6C3531CC31E0400DB3429 /* MGLPointAnnotation.h */,
+ DAE6C3761CC31E2A00DB3429 /* MGLPointAnnotation.m */,
+ DAE6C3541CC31E0400DB3429 /* MGLPolygon.h */,
+ DAE6C3771CC31E2A00DB3429 /* MGLPolygon.mm */,
+ DAE6C3551CC31E0400DB3429 /* MGLPolyline.h */,
+ DAE6C3781CC31E2A00DB3429 /* MGLPolyline.mm */,
+ DAE6C3561CC31E0400DB3429 /* MGLShape.h */,
+ DAE6C3791CC31E2A00DB3429 /* MGLShape.m */,
+ DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */,
+ DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.m */,
+ );
+ name = Geometry;
+ sourceTree = "<group>";
+ };
+ DAD1657E1CF4CF04001FF4B9 /* Offline Maps */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C34F1CC31E0400DB3429 /* MGLOfflinePack.h */,
+ DAE6C3711CC31E2A00DB3429 /* MGLOfflinePack_Private.h */,
+ DAE6C3721CC31E2A00DB3429 /* MGLOfflinePack.mm */,
+ DAE6C3501CC31E0400DB3429 /* MGLOfflineRegion.h */,
+ DAE6C3731CC31E2A00DB3429 /* MGLOfflineRegion_Private.h */,
+ DAE6C3511CC31E0400DB3429 /* MGLOfflineStorage.h */,
+ DAE6C3741CC31E2A00DB3429 /* MGLOfflineStorage_Private.h */,
+ DAE6C3751CC31E2A00DB3429 /* MGLOfflineStorage.mm */,
+ DAE6C3581CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h */,
+ DAE6C37B1CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm */,
+ );
+ name = "Offline Maps";
+ sourceTree = "<group>";
+ };
+ DAD1657F1CF4CF50001FF4B9 /* Categories */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C37D1CC31E2A00DB3429 /* NSBundle+MGLAdditions.h */,
+ DAE6C37E1CC31E2A00DB3429 /* NSBundle+MGLAdditions.m */,
+ DAE6C37F1CC31E2A00DB3429 /* NSException+MGLAdditions.h */,
+ DAE6C3801CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h */,
+ DAE6C3811CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m */,
+ DAE6C3821CC31E2A00DB3429 /* NSString+MGLAdditions.h */,
+ DAE6C3831CC31E2A00DB3429 /* NSString+MGLAdditions.m */,
+ DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */,
+ DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */,
+ );
+ name = Categories;
+ sourceTree = "<group>";
+ };
+ DAE6C31E1CC308BC00DB3429 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 52BECB091CC5A26F009CD791 /* SystemConfiguration.framework */,
+ DAE6C3451CC31D1200DB3429 /* libmbgl-core.a */,
+ DAE6C3461CC31D1200DB3429 /* libmbgl-platform-osx.a */,
+ DAE6C3C61CC3499100DB3429 /* libsqlite3.tbd */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ DAE6C3291CC30DB200DB3429 /* SDK */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C3C11CC31F4500DB3429 /* Mapbox.h */,
+ DAE6C3491CC31DF500DB3429 /* Foundation */,
+ DA8933B21CCD2C0700E68420 /* Foundation Resources */,
+ DAE6C39E1CC31E7C00DB3429 /* Kit */,
+ DA8933A81CCD28D100E68420 /* Kit Resources */,
+ DAE6C32C1CC30DB200DB3429 /* Info.plist */,
+ );
+ name = SDK;
+ path = sdk;
+ sourceTree = "<group>";
+ };
+ DAE6C3371CC30DB200DB3429 /* SDK Tests */ = {
+ isa = PBXGroup;
+ children = (
+ DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */,
+ DA35A2B51CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m */,
+ DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */,
+ DA0CD58D1CF56F5800A5F5A5 /* MGLFeatureTests.mm */,
+ DAE6C3C81CC34BD800DB3429 /* MGLGeometryTests.mm */,
+ DAE6C3C91CC34BD800DB3429 /* MGLOfflinePackTests.m */,
+ DAE6C3CA1CC34BD800DB3429 /* MGLOfflineRegionTests.m */,
+ DAE6C3CB1CC34BD800DB3429 /* MGLOfflineStorageTests.m */,
+ DAE6C3CC1CC34BD800DB3429 /* MGLStyleTests.mm */,
+ DAE6C33A1CC30DB200DB3429 /* Info.plist */,
+ );
+ name = "SDK Tests";
+ path = test;
+ sourceTree = "<group>";
+ };
+ DAE6C3491CC31DF500DB3429 /* Foundation */ = {
+ isa = PBXGroup;
+ children = (
+ DAD1657F1CF4CF50001FF4B9 /* Categories */,
+ DAD1657C1CF4CE6B001FF4B9 /* Formatters */,
+ DAD1657D1CF4CECB001FF4B9 /* Geometry */,
+ DAD1657E1CF4CF04001FF4B9 /* Offline Maps */,
+ DAE6C34A1CC31E0400DB3429 /* MGLAccountManager.h */,
+ DAE6C36A1CC31E2A00DB3429 /* MGLAccountManager_Private.h */,
+ DAE6C36B1CC31E2A00DB3429 /* MGLAccountManager.m */,
+ DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */,
+ DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */,
+ DAE6C3571CC31E0400DB3429 /* MGLStyle.h */,
+ DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */,
+ DAE6C3591CC31E0400DB3429 /* MGLTypes.h */,
+ DAE6C37C1CC31E2A00DB3429 /* MGLTypes.m */,
+ );
+ name = Foundation;
+ path = ../darwin/src;
+ sourceTree = SOURCE_ROOT;
+ };
+ DAE6C39E1CC31E7C00DB3429 /* Kit */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C39F1CC31E9400DB3429 /* MGLAnnotationImage.h */,
+ DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */,
+ DAE6C3A71CC31EF300DB3429 /* MGLAnnotationImage.m */,
+ DAE6C3A81CC31EF300DB3429 /* MGLAttributionButton.h */,
+ DAE6C3A91CC31EF300DB3429 /* MGLAttributionButton.m */,
+ DAE6C3AA1CC31EF300DB3429 /* MGLCompassCell.h */,
+ DAE6C3AB1CC31EF300DB3429 /* MGLCompassCell.m */,
+ DAE6C3A01CC31E9400DB3429 /* MGLMapView.h */,
+ DAE6C3AC1CC31EF300DB3429 /* MGLMapView_Private.h */,
+ DAE6C3AD1CC31EF300DB3429 /* MGLMapView.mm */,
+ DAE6C3A11CC31E9400DB3429 /* MGLMapView+IBAdditions.h */,
+ DAE6C3AE1CC31EF300DB3429 /* MGLMapView+IBAdditions.m */,
+ DAE6C3A21CC31E9400DB3429 /* MGLMapViewDelegate.h */,
+ DAE6C3AF1CC31EF300DB3429 /* MGLOpenGLLayer.h */,
+ DAE6C3B01CC31EF300DB3429 /* MGLOpenGLLayer.mm */,
+ );
+ name = Kit;
+ path = src;
+ sourceTree = SOURCE_ROOT;
+ };
+ DAE6C3C41CC31F7800DB3429 /* Configuration */ = {
+ isa = PBXGroup;
+ children = (
+ DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */,
+ );
+ name = Configuration;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ DAE6C3251CC30DB200DB3429 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAE6C38D1CC31E2A00DB3429 /* MGLOfflineRegion_Private.h in Headers */,
+ DAE6C35B1CC31E0400DB3429 /* MGLAnnotation.h in Headers */,
+ DAE6C3B61CC31EF300DB3429 /* MGLMapView_Private.h in Headers */,
+ DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */,
+ DAE6C3A31CC31E9400DB3429 /* MGLAnnotationImage.h in Headers */,
+ DAE6C3A41CC31E9400DB3429 /* MGLMapView.h in Headers */,
+ DAE6C3611CC31E0400DB3429 /* MGLOfflineStorage.h in Headers */,
+ DAE6C35E1CC31E0400DB3429 /* MGLMultiPoint.h in Headers */,
+ DAE6C3971CC31E2A00DB3429 /* NSBundle+MGLAdditions.h in Headers */,
+ DAD165741CF4CD7A001FF4B9 /* MGLShapeCollection.h in Headers */,
+ DAE6C3631CC31E0400DB3429 /* MGLPointAnnotation.h in Headers */,
+ DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */,
+ DAE6C35F1CC31E0400DB3429 /* MGLOfflinePack.h in Headers */,
+ DAE6C39C1CC31E2A00DB3429 /* NSString+MGLAdditions.h in Headers */,
+ DAE6C3861CC31E2A00DB3429 /* MGLGeometry_Private.h in Headers */,
+ DAE6C3841CC31E2A00DB3429 /* MGLAccountManager_Private.h in Headers */,
+ DAE6C3691CC31E0400DB3429 /* MGLTypes.h in Headers */,
+ DAE6C3991CC31E2A00DB3429 /* NSException+MGLAdditions.h in Headers */,
+ DAE6C3661CC31E0400DB3429 /* MGLShape.h in Headers */,
+ DAE6C3C21CC31F4500DB3429 /* Mapbox.h in Headers */,
+ DAE6C3641CC31E0400DB3429 /* MGLPolygon.h in Headers */,
+ DA35A2BF1CCA9B1A00E826B2 /* MGLClockDirectionFormatter.h in Headers */,
+ DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */,
+ DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */,
+ DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */,
+ DAE6C39A1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h in Headers */,
+ DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */,
+ DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */,
+ DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */,
+ DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */,
+ DAE6C3A61CC31E9400DB3429 /* MGLMapViewDelegate.h in Headers */,
+ DAE6C38B1CC31E2A00DB3429 /* MGLOfflinePack_Private.h in Headers */,
+ DACC22141CF3D3E200D220D9 /* MGLFeature.h in Headers */,
+ DAE6C35C1CC31E0400DB3429 /* MGLGeometry.h in Headers */,
+ DAE6C35A1CC31E0400DB3429 /* MGLAccountManager.h in Headers */,
+ DAE6C35D1CC31E0400DB3429 /* MGLMapCamera.h in Headers */,
+ DAE6C3B41CC31EF300DB3429 /* MGLCompassCell.h in Headers */,
+ DAE6C3B91CC31EF300DB3429 /* MGLOpenGLLayer.h in Headers */,
+ DAE6C3891CC31E2A00DB3429 /* MGLMultiPoint_Private.h in Headers */,
+ DAE6C3A51CC31E9400DB3429 /* MGLMapView+IBAdditions.h in Headers */,
+ DA35A2AD1CCA091800E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
+ DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */,
+ DAE6C3671CC31E0400DB3429 /* MGLStyle.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXLegacyTarget section */
+ DAAA17961CE13BAE00731EFE /* docs */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "xdocument OUTPUT=build/osx/pkg/documentation";
+ buildConfigurationList = DAAA17991CE13BAE00731EFE /* Build configuration list for PBXLegacyTarget "docs" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = ../../;
+ dependencies = (
+ );
+ name = docs;
+ passBuildSettingsInEnvironment = 1;
+ productName = docs;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXNativeTarget section */
+ DA839E911CC2E3400062CAFB /* osxapp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DA839EA91CC2E3400062CAFB /* Build configuration list for PBXNativeTarget "osxapp" */;
+ buildPhases = (
+ DA839E8E1CC2E3400062CAFB /* Sources */,
+ DA839E8F1CC2E3400062CAFB /* Frameworks */,
+ DA839E901CC2E3400062CAFB /* Resources */,
+ DAE6C3221CC30B3C00DB3429 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DAE6C33C1CC30DB200DB3429 /* PBXTargetDependency */,
+ );
+ name = osxapp;
+ productName = osxapp;
+ productReference = DA839E921CC2E3400062CAFB /* Mapbox GL.app */;
+ productType = "com.apple.product-type.application";
+ };
+ DAE6C3271CC30DB200DB3429 /* dynamic */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DAE6C3431CC30DB200DB3429 /* Build configuration list for PBXNativeTarget "dynamic" */;
+ buildPhases = (
+ DAE6C3231CC30DB200DB3429 /* Sources */,
+ DAE6C3241CC30DB200DB3429 /* Frameworks */,
+ DAE6C3251CC30DB200DB3429 /* Headers */,
+ DAE6C3261CC30DB200DB3429 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dynamic;
+ productName = dynamic;
+ productReference = DAE6C3281CC30DB200DB3429 /* Mapbox.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ DAE6C3301CC30DB200DB3429 /* test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = DAE6C3441CC30DB200DB3429 /* Build configuration list for PBXNativeTarget "test" */;
+ buildPhases = (
+ DAE6C32D1CC30DB200DB3429 /* Sources */,
+ DAE6C32E1CC30DB200DB3429 /* Frameworks */,
+ DAE6C32F1CC30DB200DB3429 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ DAE6C3341CC30DB200DB3429 /* PBXTargetDependency */,
+ );
+ name = test;
+ productName = dynamicTests;
+ productReference = DAE6C3311CC30DB200DB3429 /* test.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ DA839E8A1CC2E3400062CAFB /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = MBX;
+ LastUpgradeCheck = 0730;
+ ORGANIZATIONNAME = Mapbox;
+ TargetAttributes = {
+ DA839E911CC2E3400062CAFB = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DAAA17961CE13BAE00731EFE = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ DAE6C3271CC30DB200DB3429 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ DAE6C3301CC30DB200DB3429 = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ };
+ };
+ buildConfigurationList = DA839E8D1CC2E3400062CAFB /* Build configuration list for PBXProject "osx" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = DA839E891CC2E3400062CAFB;
+ productRefGroup = DA839E931CC2E3400062CAFB /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ DA839E911CC2E3400062CAFB /* osxapp */,
+ DAE6C3271CC30DB200DB3429 /* dynamic */,
+ DAE6C3301CC30DB200DB3429 /* test */,
+ DAAA17961CE13BAE00731EFE /* docs */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ DA839E901CC2E3400062CAFB /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA839EA21CC2E3400062CAFB /* Assets.xcassets in Resources */,
+ DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */,
+ DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */,
+ DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C3261CC30DB200DB3429 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA8933AE1CCD290700E68420 /* Localizable.strings in Resources */,
+ DAE6C3BE1CC31F2E00DB3429 /* default_marker.pdf in Resources */,
+ DAE6C3BF1CC31F2E00DB3429 /* mapbox.pdf in Resources */,
+ DA8933A51CCD287300E68420 /* MGLAnnotationCallout.xib in Resources */,
+ DA8933B51CCD2C2500E68420 /* Foundation.strings in Resources */,
+ DA8933B81CCD2C2D00E68420 /* Foundation.stringsdict in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C32F1CC30DB200DB3429 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ DA839E8E1CC2E3400062CAFB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */,
+ DAE6C2ED1CC3050F00DB3429 /* DroppedPinAnnotation.m in Sources */,
+ DAE6C2EE1CC3050F00DB3429 /* LocationCoordinate2DTransformer.m in Sources */,
+ DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.m in Sources */,
+ DA839E9A1CC2E3400062CAFB /* main.m in Sources */,
+ DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */,
+ DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C3231CC30DB200DB3429 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.m in Sources */,
+ DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */,
+ DAE6C3B71CC31EF300DB3429 /* MGLMapView.mm in Sources */,
+ DAE6C38C1CC31E2A00DB3429 /* MGLOfflinePack.mm in Sources */,
+ DAE6C3B11CC31EF300DB3429 /* MGLAnnotationImage.m in Sources */,
+ DACC22151CF3D3E200D220D9 /* MGLFeature.mm in Sources */,
+ DAE6C3B31CC31EF300DB3429 /* MGLAttributionButton.m in Sources */,
+ DAE6C3931CC31E2A00DB3429 /* MGLShape.m in Sources */,
+ DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */,
+ DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */,
+ DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */,
+ DAE6C3B81CC31EF300DB3429 /* MGLMapView+IBAdditions.m in Sources */,
+ DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */,
+ DA35A2C01CCA9B1A00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
+ DAE6C3BA1CC31EF300DB3429 /* MGLOpenGLLayer.mm in Sources */,
+ DAE6C38A1CC31E2A00DB3429 /* MGLMultiPoint.mm in Sources */,
+ DAE6C3961CC31E2A00DB3429 /* MGLTypes.m in Sources */,
+ DA35A2A61CC9EB2700E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ DAE6C3881CC31E2A00DB3429 /* MGLMapCamera.mm in Sources */,
+ DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */,
+ DAE6C39B1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m in Sources */,
+ DAE6C38F1CC31E2A00DB3429 /* MGLOfflineStorage.mm in Sources */,
+ DAE6C3951CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm in Sources */,
+ DAE6C3851CC31E2A00DB3429 /* MGLAccountManager.m in Sources */,
+ DAE6C3921CC31E2A00DB3429 /* MGLPolyline.mm in Sources */,
+ DAE6C3B51CC31EF300DB3429 /* MGLCompassCell.m in Sources */,
+ DAD165751CF4CD7A001FF4B9 /* MGLShapeCollection.m in Sources */,
+ DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ DAE6C32D1CC30DB200DB3429 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */,
+ DAE6C3D41CC34C9900DB3429 /* MGLOfflineRegionTests.m in Sources */,
+ DAE6C3D61CC34C9900DB3429 /* MGLStyleTests.mm in Sources */,
+ DA35A2B61CCA14D700E826B2 /* MGLCompassDirectionFormatterTests.m in Sources */,
+ DAE6C3D21CC34C9900DB3429 /* MGLGeometryTests.mm in Sources */,
+ DAE6C3D51CC34C9900DB3429 /* MGLOfflineStorageTests.m in Sources */,
+ DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */,
+ DA35A2A81CC9F41600E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
+ DA0CD58E1CF56F5800A5F5A5 /* MGLFeatureTests.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ DAE6C3341CC30DB200DB3429 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DAE6C3271CC30DB200DB3429 /* dynamic */;
+ targetProxy = DAE6C3331CC30DB200DB3429 /* PBXContainerItemProxy */;
+ };
+ DAE6C33C1CC30DB200DB3429 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DAE6C3271CC30DB200DB3429 /* dynamic */;
+ targetProxy = DAE6C33B1CC30DB200DB3429 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ DA839E9E1CC2E3400062CAFB /* MapDocument.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA839E9F1CC2E3400062CAFB /* Base */,
+ );
+ name = MapDocument.xib;
+ sourceTree = "<group>";
+ };
+ DA839EA31CC2E3400062CAFB /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA839EA41CC2E3400062CAFB /* Base */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+ DA8933A71CCD287300E68420 /* MGLAnnotationCallout.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933A61CCD287300E68420 /* Base */,
+ );
+ name = MGLAnnotationCallout.xib;
+ sourceTree = "<group>";
+ };
+ DA8933AB1CCD290700E68420 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933AC1CCD290700E68420 /* Base */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ DA8933B31CCD2C2500E68420 /* Foundation.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933B41CCD2C2500E68420 /* Base */,
+ );
+ name = Foundation.strings;
+ sourceTree = "<group>";
+ };
+ DA8933B61CCD2C2D00E68420 /* Foundation.stringsdict */ = {
+ isa = PBXVariantGroup;
+ children = (
+ DA8933B71CCD2C2D00E68420 /* en */,
+ );
+ name = Foundation.stringsdict;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ DA839EA71CC2E3400062CAFB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ DA839EA81CC2E3400062CAFB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ DA839EAA1CC2E3400062CAFB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = "Mapbox GL";
+ };
+ name = Debug;
+ };
+ DA839EAB1CC2E3400062CAFB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = "Mapbox GL";
+ };
+ name = Release;
+ };
+ DAAA17971CE13BAE00731EFE /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ DEBUGGING_SYMBOLS = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ DAAA17981CE13BAE00731EFE /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ DAE6C33F1CC30DB200DB3429 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ FRAMEWORK_VERSION = A;
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ DAE6C3401CC30DB200DB3429 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ FRAMEWORK_VERSION = A;
+ HEADER_SEARCH_PATHS = (
+ ../default,
+ ../../include,
+ ../../src,
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(zlib_cflags)",
+ "$(rapidjson_cflags)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ OTHER_LDFLAGS = (
+ "$(zlib_ldflags)",
+ "$(opengl_ldflags)",
+ "$(geojsonvt_static_libs)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
+ PRODUCT_NAME = Mapbox;
+ SKIP_INSTALL = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ DAE6C3411CC30DB200DB3429 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ HEADER_SEARCH_PATHS = ../../include;
+ INFOPLIST_FILE = test/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ DAE6C3421CC30DB200DB3429 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = DAE6C3C51CC31F9100DB3429 /* mbgl.xcconfig */;
+ buildSettings = {
+ COMBINE_HIDPI_IMAGES = YES;
+ HEADER_SEARCH_PATHS = ../../include;
+ INFOPLIST_FILE = test/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "$(variant_cflags)",
+ "$(geometry_cflags)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ DA839E8D1CC2E3400062CAFB /* Build configuration list for PBXProject "osx" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA839EA71CC2E3400062CAFB /* Debug */,
+ DA839EA81CC2E3400062CAFB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DA839EA91CC2E3400062CAFB /* Build configuration list for PBXNativeTarget "osxapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DA839EAA1CC2E3400062CAFB /* Debug */,
+ DA839EAB1CC2E3400062CAFB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DAAA17991CE13BAE00731EFE /* Build configuration list for PBXLegacyTarget "docs" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DAAA17971CE13BAE00731EFE /* Debug */,
+ DAAA17981CE13BAE00731EFE /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DAE6C3431CC30DB200DB3429 /* Build configuration list for PBXNativeTarget "dynamic" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DAE6C33F1CC30DB200DB3429 /* Debug */,
+ DAE6C3401CC30DB200DB3429 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ DAE6C3441CC30DB200DB3429 /* Build configuration list for PBXNativeTarget "test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ DAE6C3411CC30DB200DB3429 /* Debug */,
+ DAE6C3421CC30DB200DB3429 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = DA839E8A1CC2E3400062CAFB /* Project object */;
+}
diff --git a/platform/osx/osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/platform/osx/osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..6451d59cab
--- /dev/null
+++ b/platform/osx/osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:/Users/mxn/hub/mapbox-gl-native/platform/osx/osx.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
new file mode 100644
index 0000000000..2c9e4a3c74
--- /dev/null
+++ b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "4E8A9455A3A23B7FD2A8FC52"
+ BuildableName = "All"
+ BlueprintName = "All"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3301CC30DB200DB3429"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3301CC30DB200DB3429"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
new file mode 100644
index 0000000000..b3659411ed
--- /dev/null
+++ b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "NO"
+ buildForProfiling = "NO"
+ buildForArchiving = "NO"
+ buildForAnalyzing = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3301CC30DB200DB3429"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3301CC30DB200DB3429"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3271CC30DB200DB3429"
+ BuildableName = "Mapbox.framework"
+ BlueprintName = "dynamic"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/osxapp.xcscheme b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/osxapp.xcscheme
new file mode 100644
index 0000000000..528ce0e755
--- /dev/null
+++ b/platform/osx/osx.xcodeproj/xcshareddata/xcschemes/osxapp.xcscheme
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0730"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DAE6C3301CC30DB200DB3429"
+ BuildableName = "test.xctest"
+ BlueprintName = "test"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DA839E911CC2E3400062CAFB"
+ BuildableName = "Mapbox GL.app"
+ BlueprintName = "osxapp"
+ ReferencedContainer = "container:osx.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/osx.xcworkspace/contents.xcworkspacedata b/platform/osx/osx.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..3d745d1790
--- /dev/null
+++ b/platform/osx/osx.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "container:osx.xcodeproj">
+ </FileRef>
+ <FileRef
+ location = "group:../../build/osx/platform/osx/platform.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/platform/osx/osx.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist b/platform/osx/osx.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000000..cb6ecad738
--- /dev/null
+++ b/platform/osx/osx.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+ type = "3"
+ version = "2.0">
+ <Breakpoints>
+ <BreakpointProxy
+ BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
+ <BreakpointContent
+ shouldBeEnabled = "No"
+ ignoreCount = "0"
+ continueAfterRunningActions = "No"
+ scope = "1"
+ stopOnStyle = "0">
+ <Actions>
+ <BreakpointActionProxy
+ ActionExtensionID = "Xcode.BreakpointAction.Sound">
+ <ActionContent
+ soundName = "Basso">
+ </ActionContent>
+ </BreakpointActionProxy>
+ </Actions>
+ </BreakpointContent>
+ </BreakpointProxy>
+ </Breakpoints>
+</Bucket>
diff --git a/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-offline.xcscheme b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-offline.xcscheme
new file mode 100644
index 0000000000..c88fefb747
--- /dev/null
+++ b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-offline.xcscheme
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "960D5BDFBD73C605ACCF58C2"
+ BuildableName = "mbgl-offline"
+ BlueprintName = "mbgl-offline"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "960D5BDFBD73C605ACCF58C2"
+ BuildableName = "mbgl-offline"
+ BlueprintName = "mbgl-offline"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "YES"
+ customWorkingDirectory = "$(PROJECT_DIR)/../.."
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "960D5BDFBD73C605ACCF58C2"
+ BuildableName = "mbgl-offline"
+ BlueprintName = "mbgl-offline"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "960D5BDFBD73C605ACCF58C2"
+ BuildableName = "mbgl-offline"
+ BlueprintName = "mbgl-offline"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-render.xcscheme b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-render.xcscheme
new file mode 100644
index 0000000000..522998bd3c
--- /dev/null
+++ b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/mbgl-render.xcscheme
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D27AE71B9B193AD277AD4CFE"
+ BuildableName = "mbgl-render"
+ BlueprintName = "mbgl-render"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D27AE71B9B193AD277AD4CFE"
+ BuildableName = "mbgl-render"
+ BlueprintName = "mbgl-render"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "YES"
+ customWorkingDirectory = "$(PROJECT_DIR)/../.."
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D27AE71B9B193AD277AD4CFE"
+ BuildableName = "mbgl-render"
+ BlueprintName = "mbgl-render"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "D27AE71B9B193AD277AD4CFE"
+ BuildableName = "mbgl-render"
+ BlueprintName = "mbgl-render"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/test.xcscheme b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/test.xcscheme
new file mode 100644
index 0000000000..f995e74964
--- /dev/null
+++ b/platform/osx/osx.xcworkspace/xcshareddata/xcschemes/test.xcscheme
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DC37A7F85AC5E5958672B4F8"
+ BuildableName = "test"
+ BlueprintName = "test"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DC37A7F85AC5E5958672B4F8"
+ BuildableName = "test"
+ BlueprintName = "test"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "YES"
+ customWorkingDirectory = "$(PROJECT_DIR)/../.."
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DC37A7F85AC5E5958672B4F8"
+ BuildableName = "test"
+ BlueprintName = "test"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "DC37A7F85AC5E5958672B4F8"
+ BuildableName = "test"
+ BlueprintName = "test"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/platform.gyp b/platform/osx/platform.gyp
new file mode 100644
index 0000000000..bbda61e7d9
--- /dev/null
+++ b/platform/osx/platform.gyp
@@ -0,0 +1,104 @@
+{
+ 'variables': {
+ 'loop_lib': 'darwin',
+ 'headless_lib': 'cgl',
+ 'coverage': 0,
+ },
+ 'includes': [
+ '../../build/osx/config.gypi',
+ '../../mbgl.gypi',
+ '../../test/test.gypi',
+ '../../benchmark/benchmark.gypi',
+ '../../bin/glfw.gypi',
+ '../../bin/render.gypi',
+ '../../bin/offline.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'test',
+ 'type': 'executable',
+
+ 'dependencies': [
+ 'test-lib',
+ 'platform-lib',
+ ],
+
+ 'sources': [
+ '../../test/src/main.cpp',
+ ],
+ },
+ {
+ 'target_name': 'benchmark',
+ 'type': 'executable',
+
+ 'dependencies': [
+ 'benchmark-lib',
+ 'platform-lib',
+ ],
+
+ 'sources': [
+ '../../benchmark/src/main.cpp',
+ ],
+ },
+ {
+ 'target_name': 'platform-lib',
+ 'product_name': 'mbgl-platform-osx',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'core',
+ ],
+
+ 'include_dirs': [
+ 'include',
+ '../darwin/include',
+ '../default',
+ '../../include',
+ '../../src', # TODO: eliminate
+ '<(SHARED_INTERMEDIATE_DIR)/include',
+ ],
+
+ 'sources': [
+ '../default/asset_file_source.cpp',
+ '../default/default_file_source.cpp',
+ '../default/online_file_source.cpp',
+ '../default/mbgl/storage/offline.hpp',
+ '../default/mbgl/storage/offline.cpp',
+ '../default/mbgl/storage/offline_database.hpp',
+ '../default/mbgl/storage/offline_database.cpp',
+ '../default/mbgl/storage/offline_download.hpp',
+ '../default/mbgl/storage/offline_download.cpp',
+ '../default/sqlite3.hpp',
+ '../default/sqlite3.cpp',
+ '../darwin/src/http_file_source.mm',
+ '../darwin/src/log_nslog.mm',
+ '../darwin/src/string_nsstring.mm',
+ '../darwin/src/image.mm',
+ '../darwin/src/nsthread.mm',
+ '../darwin/src/reachability.m',
+ ],
+
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [
+ '<@(sqlite_cflags)',
+ '<@(zlib_cflags)',
+ '<@(rapidjson_cflags)',
+ ],
+ 'CLANG_ENABLE_OBJC_ARC': 'YES',
+ 'CLANG_ENABLE_MODULES': 'YES',
+ },
+
+ 'link_settings': {
+ 'libraries': [
+ '<@(sqlite_static_libs)',
+ '<@(zlib_static_libs)',
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '<@(zlib_ldflags)' ],
+ },
+ },
+ },
+ ],
+}
diff --git a/platform/osx/screenshot.png b/platform/osx/screenshot.png
new file mode 100644
index 0000000000..3bf0c46ab0
--- /dev/null
+++ b/platform/osx/screenshot.png
Binary files differ
diff --git a/platform/osx/scripts/configure.sh b/platform/osx/scripts/configure.sh
index 77fe9226dd..2952ec2535 100644
--- a/platform/osx/scripts/configure.sh
+++ b/platform/osx/scripts/configure.sh
@@ -1,15 +1,18 @@
#!/usr/bin/env bash
-BOOST_VERSION=1.59.0
-BOOST_LIBPROGRAM_OPTIONS_VERSION=1.59.0
-LIBCURL_VERSION=system
+UNIQUE_RESOURCE_VERSION=dev
+PROTOZERO_VERSION=1.3.0
+BOOST_VERSION=1.60.0
+BOOST_LIBPROGRAM_OPTIONS_VERSION=1.60.0
GLFW_VERSION=3.1.2
SQLITE_VERSION=3.9.1
-LIBUV_VERSION=1.7.5
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
-GEOJSONVT_VERSION=3.1.0
-VARIANT_VERSION=1.0
+GEOMETRY_VERSION=0.5.0
+GEOJSONVT_VERSION=4.1.2
+VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
GTEST_VERSION=1.7.0
PIXELMATCH_VERSION=0.9.0
+EARCUT_VERSION=0.11
+BENCHMARK_VERSION=1.0.0
diff --git a/platform/osx/scripts/defaults.mk b/platform/osx/scripts/defaults.mk
deleted file mode 100644
index 94dda854fa..0000000000
--- a/platform/osx/scripts/defaults.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-HEADLESS ?= cgl
-PLATFORM ?= osx
-ASSET ?= fs
-HTTP ?= nsurl
diff --git a/platform/osx/scripts/document.sh b/platform/osx/scripts/document.sh
new file mode 100755
index 0000000000..5559d858c7
--- /dev/null
+++ b/platform/osx/scripts/document.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+if [ -z `which jazzy` ]; then
+ echo "Installing jazzy…"
+ gem install jazzy
+ if [ -z `which jazzy` ]; then
+ echo "Unable to install jazzy. See https://github.com/mapbox/mapbox-gl-native/blob/master/platform/osx/INSTALL.md"
+ exit 1
+ fi
+fi
+
+OUTPUT=${OUTPUT:-documentation}
+
+BRANCH=$( git describe --tags --match=osx-v*.*.* --abbrev=0 )
+SHORT_VERSION=$( echo ${BRANCH} | sed 's/^osx-v//' )
+RELEASE_VERSION=$( echo ${SHORT_VERSION} | sed -e 's/^osx-v//' -e 's/-.*//' )
+
+SWIFT_VERSION=$(xcrun swift -version | head -n 1 | sed -e 's/^Apple Swift version //' -e 's/ .*$//')
+
+rm -rf /tmp/mbgl
+mkdir -p /tmp/mbgl/
+README=/tmp/mbgl/README.md
+cp platform/osx/docs/doc-README.md "${README}"
+# http://stackoverflow.com/a/4858011/4585461
+echo "## Changes in version ${RELEASE_VERSION}" >> "${README}"
+sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^##/q' -e 'p' -e 'ba' -e '}' platform/osx/CHANGELOG.md >> "${README}"
+
+rm -rf ${OUTPUT}
+mkdir -p ${OUTPUT}
+
+jazzy \
+ --config platform/osx/jazzy.yml \
+ --sdk macosx \
+ --swift-version $SWIFT_VERSION \
+ --github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \
+ --module-version ${SHORT_VERSION} \
+ --readme ${README} \
+ --output ${OUTPUT}
+# https://github.com/realm/jazzy/issues/411
+find ${OUTPUT} -name *.html -exec \
+ perl -pi -e 's/Mapbox\s+(Docs|Reference)/Mapbox OS X SDK $1/' {} \;
diff --git a/platform/osx/scripts/install.sh b/platform/osx/scripts/install.sh
deleted file mode 100755
index 0f9b5a2752..0000000000
--- a/platform/osx/scripts/install.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-mapbox_time "checkout_mason" \
-git submodule update --init .mason
diff --git a/platform/osx/scripts/osxtest.xcscheme b/platform/osx/scripts/osxtest.xcscheme
new file mode 100644
index 0000000000..5060703614
--- /dev/null
+++ b/platform/osx/scripts/osxtest.xcscheme
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6EE19CDFBCE7BD04FE561812"
+ BuildableName = "osxtest.xctest"
+ BlueprintName = "osxtest"
+ ReferencedContainer = "container:../../build/osx/platform/osx/platform.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/platform/osx/scripts/package.sh b/platform/osx/scripts/package.sh
index f39d730674..1304430835 100755
--- a/platform/osx/scripts/package.sh
+++ b/platform/osx/scripts/package.sh
@@ -6,52 +6,52 @@ set -u
NAME=Mapbox
OUTPUT=build/osx/pkg
-OSX_SDK_VERSION=`xcrun --sdk macosx --show-sdk-version`
-LIBUV_VERSION=1.7.5
-
-if [[ ${#} -eq 0 ]]; then # e.g. "make xpackage"
- BUILDTYPE="Release"
- GCC_GENERATE_DEBUGGING_SYMBOLS="YES"
-else # e.g. "make xpackage-strip"
- BUILDTYPE="Release"
- GCC_GENERATE_DEBUGGING_SYMBOLS="NO"
-fi
+DERIVED_DATA=build/osx
+PRODUCTS=${DERIVED_DATA}/Build/Products
+
+BUILDTYPE=${BUILDTYPE:-Release}
+GCC_GENERATE_DEBUGGING_SYMBOLS=${SYMBOLS:-YES}
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
function finish { >&2 echo -en "\033[0m"; }
trap finish EXIT
-step "Creating build files..."
-export MASON_PLATFORM=osx
-export BUILDTYPE=${BUILDTYPE:-Release}
-export HOST=osx
-make Xcode/osx
+rm -rf ${OUTPUT}
-VERSION=${TRAVIS_JOB_NUMBER:-${BITRISE_BUILD_NUMBER:-0}}
+HASH=`git log | head -1 | awk '{ print $2 }' | cut -c 1-10` && true
+PROJ_VERSION=$(git rev-list --count HEAD)
+SEM_VERSION=$( git describe --tags --match=osx-v*.*.* --abbrev=0 | sed 's/^osx-v//' )
+SHORT_VERSION=${SEM_VERSION%-*}
-step "Building OS X framework (build ${VERSION})..."
-xcodebuild -sdk macosx${OSX_SDK_VERSION} \
- ARCHS="x86_64" \
- ONLY_ACTIVE_ARCH=NO \
+step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})…"
+xcodebuild \
GCC_GENERATE_DEBUGGING_SYMBOLS=${GCC_GENERATE_DEBUGGING_SYMBOLS} \
- CURRENT_PROJECT_VERSION=${VERSION} \
- -project ./build/osx-x86_64/gyp/osx.xcodeproj \
+ CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
+ CURRENT_SHORT_VERSION=${SHORT_VERSION} \
+ CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \
+ CURRENT_COMMIT_HASH=${HASH} \
+ -derivedDataPath ${DERIVED_DATA} \
+ -workspace ./platform/osx/osx.xcworkspace \
+ -scheme dynamic \
-configuration ${BUILDTYPE} \
- -target osxsdk \
- -jobs ${JOBS}
-
-TARGET_BUILD_DIR=gyp/build/${BUILDTYPE}
-INFOPLIST_PATH=Mapbox.framework/Versions/Current/Resources/Info.plist
-
-# Uncomment when we're ready to release an official version.
-#VERSION=$( git tag | grep ^osx | sed 's/^osx-//' | sort -r | grep -v '\-rc.' | grep -v '\-pre.' | sed -n '1p' | sed 's/^v//' )
-#if [ "$VERSION" ]; then
-# plutil \
-# -replace CFBundleShortVersionString -string ${VERSION} \
-# $TARGET_BUILD_DIR/$INFOPLIST_PATH
-# plutil \
-# -replace CFBundleVersion -string ${VERSION} \
-# $TARGET_BUILD_DIR/$INFOPLIST_PATH
-#fi
-
-echo $TARGET_BUILD_DIR/Mapbox.framework
+ -jobs ${JOBS} | xcpretty
+
+step "Copying dynamic framework into place"
+mkdir -p "${OUTPUT}/${NAME}.framework"
+cp -r ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework/* "${OUTPUT}/${NAME}.framework"
+if [[ -e ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework.dSYM ]]; then
+ cp -r ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework.dSYM "${OUTPUT}"
+fi
+
+if [[ "${GCC_GENERATE_DEBUGGING_SYMBOLS}" == false ]]; then
+ step "Stripping binaries…"
+ strip -Sx "${OUTPUT}/${NAME}.framework/${NAME}"
+fi
+
+step "Copying library resources…"
+cp -pv LICENSE.md "${OUTPUT}"
+cp -pv platform/osx/docs/pod-README.md "${OUTPUT}/README.md"
+sed -n -e '/^## /,$p' platform/osx/CHANGELOG.md > "${OUTPUT}/CHANGELOG.md"
+
+step "Generating API documentation…"
+make xdocument OUTPUT="${OUTPUT}/documentation"
diff --git a/platform/osx/scripts/run.sh b/platform/osx/scripts/run.sh
deleted file mode 100755
index 6d00d1b977..0000000000
--- a/platform/osx/scripts/run.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-source ./platform/osx/scripts/setup.sh
-
-BUILDTYPE=${BUILDTYPE:-Release}
-
-################################################################################
-# Build
-################################################################################
-
-mapbox_time "compile_render_binary" \
-make render -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "compile_offline_binary" \
-make offline -j${JOBS} BUILDTYPE=${BUILDTYPE}
-
-mapbox_time "compile_tests" \
-make xtest -j${JOBS} BUILDTYPE=${BUILDTYPE}
diff --git a/platform/osx/scripts/setup.sh b/platform/osx/scripts/setup.sh
deleted file mode 100755
index 502d61d44d..0000000000
--- a/platform/osx/scripts/setup.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-# This script is sourced; do not set -e or -o pipefail here.
-
-# Ensure mason is on the PATH
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-
-# Set the core file limit to unlimited so a core file is generated upon crash
-ulimit -c unlimited -S
diff --git a/platform/osx/scripts/test.sh b/platform/osx/scripts/test.sh
deleted file mode 100755
index 98b22a9b98..0000000000
--- a/platform/osx/scripts/test.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-set -u
-
-OSX_SDK_VERSION=`xcrun --sdk macosx --show-sdk-version`
-OSX_PROJ_PATH=./build/osx-x86_64/gyp/osx.xcodeproj
-
-export BUILDTYPE=${BUILDTYPE:-Release}
-
-if [[ ! -e "${OSX_PROJ_PATH}/xcshareddata/xcschemes/osxsdk.xcscheme" ]]; then
- # Generate schemes
- open -g "${OSX_PROJ_PATH}"
- sleep 20
-
- # Share osxsdk scheme
- mkdir -pv "${OSX_PROJ_PATH}/xcshareddata/xcschemes/"
- mv -v \
- "${OSX_PROJ_PATH}/xcuserdata/${USER}.xcuserdatad/xcschemes/osxsdk.xcscheme" \
- "${OSX_PROJ_PATH}/xcshareddata/xcschemes/"
-fi
-
-xcodebuild -verbose \
- -sdk macosx${OSX_SDK_VERSION} \
- -project "${OSX_PROJ_PATH}" \
- -scheme osxsdk \
- test
diff --git a/platform/osx/sdk/Base.lproj/Localizable.strings b/platform/osx/sdk/Base.lproj/Localizable.strings
new file mode 100644
index 0000000000..818c82b2ec
--- /dev/null
+++ b/platform/osx/sdk/Base.lproj/Localizable.strings
@@ -0,0 +1,30 @@
+/* Linked part of copyright notice */
+"COPYRIGHT_MAPBOX" = "Mapbox";
+
+/* Copyright notice link */
+"COPYRIGHT_MAPBOX_LINK" = "https://www.mapbox.com/about/maps/";
+
+/* Linked part of copyright notice */
+"COPYRIGHT_OSM" = "OpenStreetMap";
+
+/* Copyright notice link */
+"COPYRIGHT_OSM_LINK" = "http://www.openstreetmap.org/about/";
+
+/* Copyright notice prefix */
+"COPYRIGHT_PREFIX" = "© ";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Zoom In";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Zoom Out";
+
diff --git a/platform/osx/sdk/Base.lproj/MGLAnnotationCallout.xib b/platform/osx/sdk/Base.lproj/MGLAnnotationCallout.xib
new file mode 100644
index 0000000000..c8e29bc29e
--- /dev/null
+++ b/platform/osx/sdk/Base.lproj/MGLAnnotationCallout.xib
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSViewController">
+ <connections>
+ <outlet property="view" destination="c22-O7-iKe" id="QAM-0O-WIj"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="c22-O7-iKe">
+ <rect key="frame" x="0.0" y="0.0" width="270" height="50"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k5x-ao-Pz3">
+ <rect key="frame" x="18" y="25" width="234" height="17"/>
+ <textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" title="Title" id="nVE-Zi-KcG">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <attributedString key="userComments">
+ <fragment content="Placeholder for the annotation’s title">
+ <attributes>
+ <font key="NSFont" metaFont="smallSystem"/>
+ <paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
+ </attributes>
+ </fragment>
+ </attributedString>
+ <connections>
+ <binding destination="-2" name="value" keyPath="representedObject.title" id="3nD-YS-gzq"/>
+ </connections>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e9C-Ve-ccw">
+ <rect key="frame" x="18" y="8" width="234" height="14"/>
+ <textFieldCell key="cell" controlSize="small" selectable="YES" sendsActionOnEndEditing="YES" title="Subtitle" id="eKw-tQ-dw8">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <attributedString key="userComments">
+ <fragment content="Placeholder for the annotation’s subtitle">
+ <attributes>
+ <font key="NSFont" metaFont="smallSystem"/>
+ <paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
+ </attributes>
+ </fragment>
+ </attributedString>
+ <connections>
+ <binding destination="-2" name="value" keyPath="representedObject.subtitle" id="RQf-48-DyH"/>
+ </connections>
+ </textField>
+ </subviews>
+ <constraints>
+ <constraint firstItem="e9C-Ve-ccw" firstAttribute="leading" secondItem="k5x-ao-Pz3" secondAttribute="leading" id="ApT-ew-CYb"/>
+ <constraint firstAttribute="bottom" secondItem="e9C-Ve-ccw" secondAttribute="bottom" constant="8" id="CWV-Dd-8oi"/>
+ <constraint firstItem="k5x-ao-Pz3" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="20" id="UUL-GB-Jtv"/>
+ <constraint firstItem="e9C-Ve-ccw" firstAttribute="top" secondItem="k5x-ao-Pz3" secondAttribute="bottom" constant="3" id="Urc-wn-m8X"/>
+ <constraint firstItem="e9C-Ve-ccw" firstAttribute="trailing" secondItem="k5x-ao-Pz3" secondAttribute="trailing" id="gss-6G-9GF"/>
+ <constraint firstAttribute="trailing" secondItem="k5x-ao-Pz3" secondAttribute="trailing" constant="20" id="xCZ-s9-HaP"/>
+ <constraint firstItem="k5x-ao-Pz3" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="8" id="xcm-oY-jjy"/>
+ </constraints>
+ <point key="canvasLocation" x="257" y="355"/>
+ </customView>
+ </objects>
+</document>
diff --git a/platform/osx/sdk/Info.plist b/platform/osx/sdk/Info.plist
index aeebbb5072..3b2b38a58a 100644
--- a/platform/osx/sdk/Info.plist
+++ b/platform/osx/sdk/Info.plist
@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
@@ -12,11 +16,9 @@
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
- <key>CFBundleShortVersionString</key>
- <string>0.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>0.0.0</string>
+ <string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
diff --git a/platform/osx/sdk/Mapbox.h b/platform/osx/sdk/Mapbox.h
deleted file mode 100644
index 4a2cd32504..0000000000
--- a/platform/osx/sdk/Mapbox.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#import <Cocoa/Cocoa.h>
-
-/// Project version number for Mapbox.
-FOUNDATION_EXPORT double MapboxVersionNumber;
-
-/// Project version string for Mapbox.
-FOUNDATION_EXPORT const unsigned char MapboxVersionString[];
-
-#import <Mapbox/MGLAccountManager.h>
-#import <Mapbox/MGLAnnotation.h>
-#import <Mapbox/MGLAnnotationImage.h>
-#import <Mapbox/MGLGeometry.h>
-#import <Mapbox/MGLMapCamera.h>
-#import <Mapbox/MGLMapView.h>
-#import <Mapbox/MGLMapView+IBAdditions.h>
-#import <Mapbox/MGLMapViewDelegate.h>
-#import <Mapbox/MGLMultiPoint.h>
-#import <Mapbox/MGLOfflinePack.h>
-#import <Mapbox/MGLOfflineRegion.h>
-#import <Mapbox/MGLOfflineStorage.h>
-#import <Mapbox/MGLOverlay.h>
-#import <Mapbox/MGLPointAnnotation.h>
-#import <Mapbox/MGLPolygon.h>
-#import <Mapbox/MGLPolyline.h>
-#import <Mapbox/MGLShape.h>
-#import <Mapbox/MGLStyle.h>
-#import <Mapbox/MGLTilePyramidOfflineRegion.h>
-#import <Mapbox/MGLTypes.h>
diff --git a/platform/osx/sdk/Mapbox.m b/platform/osx/sdk/Mapbox.m
deleted file mode 100644
index dc935cd265..0000000000
--- a/platform/osx/sdk/Mapbox.m
+++ /dev/null
@@ -1,33 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-#import "../src/MGLMapView_Private.h"
-#import "../../darwin/src/NSBundle+MGLAdditions.h"
-#import "../../darwin/src/NSProcessInfo+MGLAdditions.h"
-#import "../../darwin/src/NSString+MGLAdditions.h"
-
-__attribute__((constructor))
-static void InitializeMapbox() {
- static int initialized = 0;
- if (initialized) {
- return;
- }
-
- mgl_linkBundleCategory();
- mgl_linkStringCategory();
- mgl_linkProcessInfoCategory();
- mgl_linkMapViewIBCategory();
-
- [MGLAccountManager class];
- [MGLAnnotationImage class];
- [MGLMapCamera class];
- [MGLMapView class];
- [MGLMultiPoint class];
- [MGLOfflinePack class];
- [MGLOfflineStorage class];
- [MGLPointAnnotation class];
- [MGLPolygon class];
- [MGLPolyline class];
- [MGLShape class];
- [MGLStyle class];
- [MGLTilePyramidOfflineRegion class];
-}
diff --git a/platform/osx/src/resources/default_marker.pdf b/platform/osx/sdk/default_marker.pdf
index 4e2e332301..4e2e332301 100644
--- a/platform/osx/src/resources/default_marker.pdf
+++ b/platform/osx/sdk/default_marker.pdf
Binary files differ
diff --git a/platform/osx/sdk/framework-osx.gypi b/platform/osx/sdk/framework-osx.gypi
deleted file mode 100644
index 259066f702..0000000000
--- a/platform/osx/sdk/framework-osx.gypi
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'osxsdk',
- 'product_name': 'Mapbox',
- 'type': 'shared_library',
- 'mac_bundle': 1,
-
- 'dependencies': [
- 'mbgl.gyp:core',
- 'mbgl.gyp:platform-<(platform_lib)',
- 'mbgl.gyp:http-<(http_lib)',
- 'mbgl.gyp:asset-<(asset_lib)',
- ],
-
- 'xcode_settings': {
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'CURRENT_PROJECT_VERSION': '0',
- 'DEFINES_MODULE': 'YES',
- 'DYLIB_INSTALL_NAME_BASE': '@rpath',
- 'INFOPLIST_FILE': '../platform/osx/sdk/Info.plist',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '${inherited}',
- '@executable_path/../Frameworks',
- '@loader_path/Frameworks',
- ],
- 'PRODUCT_BUNDLE_IDENTIFIER': 'com.mapbox.MapboxGL',
- 'OTHER_LDFLAGS': [ '-stdlib=libc++', '-lstdc++' ],
- 'SDKROOT': 'macosx',
- 'SKIP_INSTALL': 'YES',
- 'SUPPORTED_PLATFORMS':'macosx',
- 'VERSIONING_SYSTEM': 'apple-generic',
- },
-
- 'mac_framework_headers': [
- './Mapbox.h',
- '<!@(find ../platform/{darwin,osx}/include -type f \! -name \'.*\')',
- ],
-
- 'sources': [
- './Mapbox.m',
- ],
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'GCC_OPTIMIZATION_LEVEL': '0',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'GCC_OPTIMIZATION_LEVEL': 's',
- },
- },
- },
-
- 'direct_dependent_settings': {
- 'libraries': [
- '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
- '$(SDKROOT)/System/Library/Frameworks/CoreLocation.framework',
- ],
- },
- },
- ]
-}
diff --git a/platform/osx/src/resources/mapbox.pdf b/platform/osx/sdk/mapbox.pdf
index c08a0e3135..c08a0e3135 100644
--- a/platform/osx/src/resources/mapbox.pdf
+++ b/platform/osx/sdk/mapbox.pdf
Binary files differ
diff --git a/platform/osx/src/MGLAnnotationImage.h b/platform/osx/src/MGLAnnotationImage.h
new file mode 100644
index 0000000000..ad44993ee1
--- /dev/null
+++ b/platform/osx/src/MGLAnnotationImage.h
@@ -0,0 +1,64 @@
+#import <AppKit/AppKit.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The `MGLAnnotationImage` class is responsible for presenting point-based
+ annotations visually on an `MGLMapView` instance. Annotation image objects pair
+ `NSImage` objects with annotation-related metadata. They may be recycled later
+ and put into a reuse queue that is maintained by the map view.
+ */
+@interface MGLAnnotationImage : NSObject
+
+#pragma mark Initializing and Preparing the Image Object
+
+/**
+ Initializes and returns a new annotation image object.
+
+ @param image The image to display for the annotation.
+ @param reuseIdentifier The string that identifies this annotation image in the
+ reuse queue.
+ @return The initialized annotation image object or `nil` if there was a problem
+ initializing the object.
+ */
++ (instancetype)annotationImageWithImage:(NSImage *)image reuseIdentifier:(NSString *)reuseIdentifier;
+
+#pragma mark Getting and Setting Attributes
+
+/** The image to display for the annotation. */
+@property (nonatomic, readonly) NSImage *image;
+
+/**
+ The string that identifies this annotation image in the reuse queue.
+ (read-only)
+
+ You specify the reuse identifier when you create the image object. You use this
+ type later to retrieve an annotation image object that was created previously
+ but which is currently unused because its annotation is not on-screen.
+
+ If you define distinctly different types of annotations (with distinctly
+ different annotation images to go with them), you can differentiate between the
+ annotation types by specifying different reuse identifiers for each one.
+ */
+@property (nonatomic, readonly) NSString *reuseIdentifier;
+
+/**
+ A Boolean value indicating whether the annotation is selectable.
+
+ The default value of this property is `YES`. If the value of this property is
+ `NO`, the annotation image ignores click events and cannot be selected.
+ */
+@property (nonatomic, getter=isSelectable) BOOL selectable;
+
+/**
+ The cursor that appears above any annotation using this annotation image.
+
+ By default, this property is set to `nil`, representing the current cursor.
+ */
+@property (nonatomic, nullable) NSCursor *cursor;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/osx/src/MGLAnnotationImage.m b/platform/osx/src/MGLAnnotationImage.m
index 49346b073b..1b545651d2 100644
--- a/platform/osx/src/MGLAnnotationImage.m
+++ b/platform/osx/src/MGLAnnotationImage.m
@@ -1,9 +1,10 @@
-#import "MGLAnnotationImage.h"
+#import "MGLAnnotationImage_Private.h"
@interface MGLAnnotationImage ()
@property (nonatomic) NSImage *image;
@property (nonatomic) NSString *reuseIdentifier;
+@property (nonatomic, strong, nullable) NSString *styleIconIdentifier;
@end
diff --git a/platform/osx/src/MGLAnnotationImage_Private.h b/platform/osx/src/MGLAnnotationImage_Private.h
new file mode 100644
index 0000000000..21963a86a0
--- /dev/null
+++ b/platform/osx/src/MGLAnnotationImage_Private.h
@@ -0,0 +1,8 @@
+#import <Mapbox/Mapbox.h>
+
+@interface MGLAnnotationImage (Private)
+
+/// Unique identifier of the sprite image used by the style to represent the receiver’s `image`.
+@property (nonatomic, strong, nullable) NSString *styleIconIdentifier;
+
+@end
diff --git a/platform/osx/src/MGLAttributionButton.m b/platform/osx/src/MGLAttributionButton.m
index b14d36cffd..e21b860794 100644
--- a/platform/osx/src/MGLAttributionButton.m
+++ b/platform/osx/src/MGLAttributionButton.m
@@ -1,5 +1,7 @@
#import "MGLAttributionButton.h"
+#import "NSBundle+MGLAdditions.h"
+
@implementation MGLAttributionButton {
NSTrackingRectTag _trackingAreaTag;
}
@@ -10,8 +12,7 @@
self.bezelStyle = NSRegularSquareBezelStyle;
// Start with a copyright symbol. The whole string will be mini.
- NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:@"© "
- attributes:@{
+ NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COPYRIGHT_PREFIX", nil, nil, @"© ", @"Copyright notice prefix") attributes:@{
NSFontAttributeName: [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]],
}];
// Append the specified title, underlining it like a hyperlink.
diff --git a/platform/osx/include/MGLMapView+IBAdditions.h b/platform/osx/src/MGLMapView+IBAdditions.h
index 81f4506a57..81f4506a57 100644
--- a/platform/osx/include/MGLMapView+IBAdditions.h
+++ b/platform/osx/src/MGLMapView+IBAdditions.h
diff --git a/platform/osx/src/MGLMapView+IBAdditions.m b/platform/osx/src/MGLMapView+IBAdditions.m
index da18760645..eada47ef90 100644
--- a/platform/osx/src/MGLMapView+IBAdditions.m
+++ b/platform/osx/src/MGLMapView+IBAdditions.m
@@ -2,8 +2,6 @@
#import "MGLMapView_Private.h"
-void mgl_linkMapViewIBCategory() {}
-
@implementation MGLMapView (IBAdditions)
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ {
diff --git a/platform/osx/src/MGLMapView.h b/platform/osx/src/MGLMapView.h
new file mode 100644
index 0000000000..7b3efd293b
--- /dev/null
+++ b/platform/osx/src/MGLMapView.h
@@ -0,0 +1,903 @@
+#import <Cocoa/Cocoa.h>
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLGeometry.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Options for enabling debugging features in an MGLMapView instance. */
+typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
+ /** Edges of tile boundaries are shown as thick, red lines to help diagnose
+ tile clipping issues. */
+ MGLMapDebugTileBoundariesMask = 1 << 1,
+
+ /** Each tile shows its tile coordinate (x/y/z) in the upper-left corner. */
+ MGLMapDebugTileInfoMask = 1 << 2,
+
+ /** Each tile shows a timestamp indicating when it was loaded. */
+ MGLMapDebugTimestampsMask = 1 << 3,
+
+ /** Edges of glyphs and symbols are shown as faint, green lines to help
+ diagnose collision and label placement issues. */
+ MGLMapDebugCollisionBoxesMask = 1 << 4,
+
+ /** Line widths, backgrounds, and fill colors are ignored to create a
+ wireframe effect. */
+ MGLMapDebugWireframesMask = 1 << 5,
+
+ /** The stencil buffer is shown instead of the color buffer. */
+ MGLMapDebugStencilBufferMask = 1 << 6,
+};
+
+@class MGLAnnotationImage;
+@class MGLMapCamera;
+
+@protocol MGLAnnotation;
+@protocol MGLMapViewDelegate;
+@protocol MGLOverlay;
+@protocol MGLFeature;
+
+/**
+ An interactive, customizable map view with an interface similar to the one
+ provided by Apple’s MapKit.
+
+ Using `MGLMapView`, you can embed the map inside a view, allow users to
+ manipulate it with standard gestures, animate the map between different
+ viewpoints, and present information in the form of annotations and overlays.
+
+ The map view loads scalable vector tiles that conform to the
+ <a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
+ It styles them with a style that conforms to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
+ Such styles can be designed in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
+ mapbox.com.
+
+ A collection of Mapbox-hosted styles is available through the `MGLStyle` class.
+ These basic styles use
+ <a href="https://www.mapbox.com/developers/vector-tiles/mapbox-streets">Mapbox Streets</a>
+ or <a href="https://www.mapbox.com/satellite/">Mapbox Satellite</a> data
+ sources, but you can specify a custom style that makes use of your own data.
+
+ Mapbox-hosted vector tiles and styles require an API access token, which you
+ can obtain from the
+ <a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>.
+ Access tokens associate requests to Mapbox’s vector tile and style APIs with
+ your Mapbox account. They also deter other developers from using your styles
+ without your permission.
+
+ @note You are responsible for getting permission to use the map data and for
+ ensuring that your use adheres to the relevant terms of use.
+ */
+IB_DESIGNABLE
+@interface MGLMapView : NSView
+
+#pragma mark Creating Instances
+
+/**
+ Initializes and returns a newly allocated map view with the specified frame and
+ the default style.
+
+ @param frame The frame for the view, measured in points.
+ @return An initialized map view.
+ */
+- (instancetype)initWithFrame:(NSRect)frame;
+
+/**
+ Initializes and returns a newly allocated map view with the specified frame and
+ style URL.
+
+ @param frame The frame for the view, measured in points.
+ @param styleURL URL of the map style to display. The URL may be a full HTTP or
+ HTTPS URL, a Mapbox URL indicating the style’s map ID
+ (`mapbox://styles/<user>/<style>`), or a path to a local file relative to
+ the application’s resource path. Specify `nil` for the default style.
+ @return An initialized map view.
+ */
+- (instancetype)initWithFrame:(NSRect)frame styleURL:(nullable NSURL *)styleURL;
+
+#pragma mark Accessing the Delegate
+
+/**
+ The receiver’s delegate.
+
+ A map view sends messages to its delegate to notify it of changes to its
+ contents or the viewpoint. The delegate also provides information about
+ annotations displayed on the map, such as the styles to apply to individual
+ annotations.
+ */
+@property (nonatomic, weak, nullable) IBOutlet id <MGLMapViewDelegate> delegate;
+
+#pragma mark Configuring the Map’s Appearance
+
+/**
+ URL of the style currently displayed in the receiver.
+
+ The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s
+ map ID (`mapbox://styles/<user>/<style>`), or a path to a local file relative
+ to the application’s resource path.
+
+ If you set this property to `nil`, the receiver will use the default style and
+ this property will automatically be set to that style’s URL.
+ */
+@property (nonatomic, null_resettable) NSURL *styleURL;
+
+/**
+ Reloads the style.
+
+ You do not normally need to call this method. The map view automatically
+ responds to changes in network connectivity by reloading the style. You may
+ need to call this method if you change the access token after a style has
+ loaded but before loading a style associated with a different Mapbox account.
+ */
+- (IBAction)reloadStyle:(id)sender;
+
+/**
+ A control for zooming in and out, positioned in the lower-right corner.
+ */
+@property (nonatomic, readonly) NSSegmentedControl *zoomControls;
+
+/**
+ A control indicating the map’s direction and allowing the user to manipulate
+ the direction, positioned above the zoom controls in the lower-right corner.
+ */
+@property (nonatomic, readonly) NSSlider *compass;
+
+/**
+ The Mapbox logo, positioned in the lower-left corner.
+
+ @note The Mapbox terms of service, which governs the use of Mapbox-hosted
+ vector tiles and styles,
+ <a href="https://www.mapbox.com/help/mapbox-logo/">requires</a> most Mapbox
+ customers to display the Mapbox logo. If this applies to you, do not hide
+ this view or change its contents.
+ */
+@property (nonatomic, readonly) NSImageView *logoView;
+
+/**
+ A view showing legally required copyright notices, positioned along the bottom
+ of the map view, to the left of the Mapbox logo.
+
+ @note The Mapbox terms of service, which governs the use of Mapbox-hosted
+ vector tiles and styles,
+ <a href="https://www.mapbox.com/help/attribution/">requires</a> these
+ copyright notices to accompany any map that features Mapbox-designed styles,
+ OpenStreetMap data, or other Mapbox data such as satellite or terrain data.
+ If that applies to this map view, do not hide this view or remove any
+ notices from it.
+ */
+@property (nonatomic, readonly) NSView *attributionView;
+
+#pragma mark Manipulating the Viewpoint
+
+/**
+ The geographic coordinate at the center of the map view.
+
+ Changing the value of this property centers the map on the new coordinate
+ without changing the current zoom level.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setCenterCoordinate:animated:` method
+ instead.
+ */
+@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
+
+/**
+ Changes the center coordinate of the map and optionally animates the change.
+
+ Changing the center coordinate centers the map on the new coordinate without
+ changing the current zoom level.
+
+ @param coordinate The new center coordinate for the map.
+ @param animated Specify `YES` if you want the map view to scroll to the new
+ location or `NO` if you want the map to display the new location
+ immediately.
+ */
+- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;
+
+/**
+ The zoom level of the receiver.
+
+ In addition to affecting the visual size and detail of features on the map, the
+ zoom level affects the size of the vector tiles that are loaded. At zoom level
+ 0, each tile covers the entire world map; at zoom level 1, it covers ¼ of the
+ world; at zoom level 2, <sup>1</sup>⁄<sub>16</sub> of the world, and so on.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setZoomLevel:animated:` method instead.
+ */
+@property (nonatomic) double zoomLevel;
+
+/**
+ The minimum zoom level at which the map can be shown.
+
+ Depending on the map view’s aspect ratio, the map view may be prevented from
+ reaching the minimum zoom level, in order to keep the map from repeating within
+ the current viewport.
+
+ If the value of this property is greater than that of the `maximumZoomLevel`
+ property, the behavior is undefined.
+
+ The default value of this property is 0.
+ */
+@property (nonatomic) double minimumZoomLevel;
+
+/**
+ The maximum zoom level the map can be shown at.
+
+ If the value of this property is smaller than that of the `minimumZoomLevel`
+ property, the behavior is undefined.
+
+ The default value of this property is 20.
+ */
+@property (nonatomic) double maximumZoomLevel;
+
+/**
+ Changes the zoom level of the map and optionally animates the change.
+
+ Changing the zoom level scales the map without changing the current center
+ coordinate.
+
+ @param zoomLevel The new zoom level for the map.
+ @param animated Specify `YES` if you want the map view to animate the change
+ to the new zoom level or `NO` if you want the map to display the new zoom
+ level immediately.
+ */
+- (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated;
+
+/**
+ The heading of the map, measured in degrees clockwise from true north.
+
+ The value `0` means that the top edge of the map view corresponds to true
+ north. The value `90` means the top of the map is pointing due east. The value
+ `180` means the top of the map points due south, and so on.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setDirection:animated:` method instead.
+ */
+@property (nonatomic) CLLocationDirection direction;
+
+/**
+ Changes the heading of the map and optionally animates the change.
+
+ Changing the heading rotates the map without changing the current center
+ coordinate or zoom level.
+
+ @param direction The heading of the map, measured in degrees clockwise from
+ true north.
+ @param animated Specify `YES` if you want the map view to animate the change
+ to the new heading or `NO` if you want the map to display the new heading
+ immediately.
+ */
+- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated;
+
+/**
+ A camera representing the current viewpoint of the map.
+ */
+@property (nonatomic, copy) MGLMapCamera *camera;
+
+/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition animation.
+
+ @param camera The new viewpoint.
+ @param animated Specify `YES` if you want the map view to animate the change to
+ the new viewpoint or `NO` if you want the map to display the new viewpoint
+ immediately.
+ */
+- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
+
+/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and a default duration based on the length of the flight
+ path.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and an optional transition duration.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously. Specify a negative value to use the default duration, which
+ is based on the length of the flight path.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration completionHandler:(nullable void (^)(void))completion;
+
+/**
+ Moves the viewpoint to a different location using a transition animation that
+ evokes powered flight and an optional transition duration and peak altitude.
+
+ The transition animation seamlessly incorporates zooming and panning to help
+ the user find his or her bearings even after traversing a great distance.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously. Specify a negative value to use the default duration, which
+ is based on the length of the flight path.
+ @param peakAltitude The altitude, measured in meters, at the midpoint of the
+ animation. The value of this parameter is ignored if it is negative or if
+ the animation transition resulting from a similar call to
+ `-setCamera:animated:` would have a midpoint at a higher altitude.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion;
+
+/**
+ The geographic coordinate bounds visible in the receiver’s viewport.
+
+ Changing the value of this property updates the receiver immediately. If you
+ want to animate the change, use the `-setVisibleCoordinateBounds:animated:`
+ method instead.
+ */
+@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
+
+/**
+ Changes the receiver’s viewport to fit the given coordinate bounds, optionally
+ animating the change.
+
+ @param bounds The bounds that the viewport will show in its entirety.
+ @param animated Specify `YES` to animate the change by smoothly scrolling and
+ zooming or `NO` to immediately display the given bounds.
+ */
+- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
+
+/**
+ Changes the receiver’s viewport to fit the given coordinate bounds and
+ optionally some additional padding on each side.
+
+ @param bounds The bounds that the viewport will show in its entirety.
+ @param insets The minimum padding (in screen points) that will be visible
+ around the given coordinate bounds.
+ @param animated Specify `YES` to animate the change by smoothly scrolling and
+ zooming or `NO` to immediately display the given bounds.
+ */
+- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated;
+
+/**
+ Returns the camera that best fits the given coordinate bounds.
+
+ @param bounds The coordinate bounds to fit to the receiver’s viewport.
+ @return A camera object centered on the same location as the coordinate bounds
+ with zoom level as high (close to the ground) as possible while still
+ including the entire coordinate bounds. The camera object uses the current
+ direction and pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds;
+
+/**
+ Returns the camera that best fits the given coordinate bounds, optionally with
+ some additional padding on each side.
+
+ @param bounds The coordinate bounds to fit to the receiver’s viewport.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the same location as the coordinate bounds
+ with zoom level as high (close to the ground) as possible while still
+ including the entire coordinate bounds. The camera object uses the current
+ direction and pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets;
+
+/**
+ A Boolean value indicating whether the receiver automatically adjusts its
+ content insets.
+
+ When the value of this property is `YES`, the map view automatically updates
+ its `contentInsets` property to account for any overlapping title bar or
+ toolbar. To overlap with the title bar or toolbar, the containing window’s
+ style mask must have `NSFullSizeContentViewWindowMask` set, and the title bar
+ must not be transparent.
+
+ The default value of this property is `YES`.
+ */
+@property (nonatomic, assign) BOOL automaticallyAdjustsContentInsets;
+
+/**
+ 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 `NSEdgeInsetsZero`, 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.
+
+ When the value of the `automaticallyAdjustsContentInsets` property is `YES`,
+ the value of this property may be overridden at any time.
+
+ Changing the value of this property updates the map view immediately. If you
+ want to animate the change, use the `-setContentInsets:animated:` method
+ instead.
+ */
+@property (nonatomic, assign) NSEdgeInsets contentInsets;
+
+/**
+ Sets the distance from the edges of the map view’s frame to the edges of the
+ map view’s logical viewport, with an optional transition animation.
+
+ When the value of this property is equal to `NSEdgeInsetsZero`, 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.
+
+ When the value of the `automaticallyAdjustsContentInsets` property is `YES`,
+ the value of this property may be overridden at any time.
+
+ @param contentInsets The new values to inset the content by.
+ @param animated Specify `YES` if you want the map view to animate the change to
+ the content insets or `NO` if you want the map to inset the content
+ immediately.
+ */
+- (void)setContentInsets:(NSEdgeInsets)contentInsets animated:(BOOL)animated;
+
+#pragma mark Configuring How the User Interacts with the Map
+
+/**
+ A Boolean value that determines whether the user may zoom the map in and out,
+ changing the zoom level.
+
+ When this property is set to `YES`, the default, the user may zoom the map in
+ and out by pinching two fingers, by using a scroll wheel on a traditional
+ mouse, or by dragging the mouse cursor up and down while holding down the Shift
+ key. When the receiver has focus, the user may also zoom by pressing the up and
+ down arrow keys while holding down the Option key.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the map zoom
+ programmatically.
+ */
+@property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
+
+/**
+ A Boolean value that determines whether the user may scroll around the map,
+ changing the center coordinate.
+
+ When this property is set to `YES`, the default, the user may scroll the map by
+ swiping with two fingers or dragging the mouse cursor. When the receiver has
+ focus, the user may also scroll around the map by pressing the arrow keys.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the map location
+ programmatically.
+ */
+@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
+
+/**
+ A Boolean value that determines whether the user may rotate the map, changing
+ the direction.
+
+ When this property is set to `YES`, the default, the user may rotate the map by
+ moving two fingers in a circular motion or by dragging the mouse cursor left
+ and right while holding down the Option key. When the receiver has focus, the
+ user may also zoom by pressing the left and right arrow keys while holding down
+ the Option key.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still rotate the map programmatically.
+ */
+@property (nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
+
+/**
+ A Boolean value that determines whether the user may tilt of the map, changing
+ the pitch.
+
+ When this property is set to `YES`, the default, the user may rotate the map by
+ dragging the mouse cursor up and down while holding down the Option key.
+
+ This property controls only user interactions with the map. If you set the
+ value of this property to `NO`, you may still change the pitch of the map
+ programmatically.
+ */
+@property (nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
+
+#pragma mark Annotating the Map
+
+/**
+ The complete list of annotations associated with the receiver. (read-only)
+
+ The objects in this array must adopt the `MGLAnnotation` protocol. If no
+ annotations are associated with the map view, the value of this property is
+ `nil`.
+ */
+@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
+
+/**
+ Adds an annotation to the map view.
+
+ @note `MGLMultiPolyline`, `MGLMultiPolygon`, and `MGLShapeCollection` objects
+ cannot be added to the map view at this time. Nor can `MGLMultiPoint`
+ objects that are not instances of `MGLPolyline` or `MGLPolygon`. Any
+ multipoint, multipolyline, multipolygon, or shape collection object that is
+ specified is silently ignored.
+
+ @param annotation The annotation object to add to the receiver. This object
+ must conform to the `MGLAnnotation` protocol. The map view retains the
+ annotation object.
+ */
+- (void)addAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Adds an array of annotations to the map view.
+
+ @note `MGLMultiPolyline`, `MGLMultiPolygon`, and `MGLShapeCollection` objects
+ cannot be added to the map view at this time. Nor can `MGLMultiPoint`
+ objects that are not instances of `MGLPolyline` or `MGLPolygon`. Any
+ multipoint, multipolyline, multipolygon, or shape collection objects that
+ are specified are silently ignored.
+
+ @param annotations An array of annotation objects. Each object in the array
+ must conform to the `MGLAnnotation` protocol. The map view retains each
+ individual annotation object.
+ */
+- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+
+/**
+ Removes an annotation from the map view, deselecting it if it is selected.
+
+ Removing an annotation object dissociates it from the map view entirely,
+ preventing it from being displayed on the map. Thus you would typically call
+ this method only when you want to hide or delete a given annotation.
+
+ @param annotation The annotation object to remove. This object must conform to
+ the `MGLAnnotation` protocol.
+ */
+- (void)removeAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Removes an array of annotations from the map view, deselecting any selected
+ annotations in the array.
+
+ Removing annotation objects dissociates them from the map view entirely,
+ preventing them from being displayed on the map. Thus you would typically call
+ this method only when you want to hide or delete the given annotations.
+
+ @param annotations The array of annotation objects to remove. Objects in the
+ array must conform to the `MGLAnnotation` protocol.
+ */
+- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+
+/**
+ Returns a reusable annotation image object associated with its identifier.
+
+ For performance reasons, you should generally reuse `MGLAnnotationImage`
+ objects for identical-looking annotations in your map views. Dequeueing saves
+ time and memory during performance-critical operations such as scrolling.
+
+ @param identifier A string identifying the annotation image to be reused. This
+ string is the same one you specify when initially returning the annotation
+ image object using the `-mapView:imageForAnnotation:` method.
+ @return An annotation image object with the given identifier, or `nil` if no
+ such object exists in the reuse queue.
+ */
+- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier;
+
+#pragma mark Managing Annotation Selections
+
+/**
+ The currently selected annotations.
+
+ Assigning a new array to this property selects only the first annotation in the
+ array.
+ */
+@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
+
+/**
+ Selects an annotation and displays a callout popover for it.
+
+ If the given annotation is not visible within the current viewport, this method
+ has no effect.
+
+ @param annotation The annotation object to select.
+ */
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Deselects an annotation and hides its callout popover.
+
+ @param annotation The annotation object to deselect.
+ */
+- (void)deselectAnnotation:(nullable id <MGLAnnotation>)annotation;
+
+/**
+ A common view controller for managing a callout popover’s content view.
+
+ Like any instance of `NSPopover`, an annotation callout manages its contents
+ with a view controller. The annotation object is the view controller’s
+ represented object. This means that you can bind controls in the view
+ controller’s content view to KVO-compliant properties of the annotation object,
+ such as `title` and `subtitle`.
+
+ This property defines a common view controller that is used for every
+ annotation’s callout view. If you set this property to `nil`, a default view
+ controller will be used that manages a simple title label and subtitle label.
+ If you need distinct view controllers for different annotations, the map view’s
+ delegate should implement `-mapView:calloutViewControllerForAnnotation:`
+ instead.
+ */
+@property (nonatomic, strong, null_resettable) IBOutlet NSViewController *calloutViewController;
+
+#pragma mark Finding Annotations
+
+/**
+ Returns a point annotation located at the given point.
+
+ @param point A point in the view’s coordinate system.
+ @return A point annotation whose annotation image coincides with the point. If
+ multiple point annotations coincide with the point, the return value is the
+ annotation that would be selected if the user clicks at this point.
+ */
+- (id <MGLAnnotation>)annotationAtPoint:(NSPoint)point;
+
+#pragma mark Overlaying the Map
+
+/**
+ Adds a single overlay to the map.
+
+ To remove an overlay from a map, use the `-removeOverlay:` method.
+
+ @param overlay The overlay object to add. This object must conform to the
+ `MGLOverlay` protocol.
+ */
+- (void)addOverlay:(id <MGLOverlay>)overlay;
+
+/**
+ Adds an array of overlays to the map.
+
+ To remove multiple overlays from a map, use the `-removeOverlays:` method.
+
+ @param overlays An array of objects, each of which must conform to the
+ `MGLOverlay` protocol.
+ */
+- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+
+/**
+ Removes a single overlay from the map.
+
+ If the specified overlay is not currently associated with the map view, this
+ method does nothing.
+
+ @param overlay The overlay object to remove.
+ */
+- (void)removeOverlay:(id <MGLOverlay>)overlay;
+
+/**
+ Removes an array of overlays from the map.
+
+ If a given overlay object is not associated with the map view, it is ignored.
+
+ @param overlays An array of objects, each of which conforms to the `MGLOverlay`
+ protocol.
+ */
+- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+
+#pragma mark Accessing the Underlying Map Data
+
+/**
+ Returns an array of rendered map features that intersect with a given point.
+
+ This method may return features from any of the map’s style layers. To restrict
+ the search to a particular layer or layers, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:` method. For more
+ information about searching for map features, see that method’s documentation.
+
+ @param point A point expressed in the map view’s coordinate system.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(_:));
+
+/**
+ Returns an array of rendered map features that intersect with a given point,
+ restricted to the given style layers.
+
+ Each object in the returned array represents a feature rendered by the
+ current style and provides access to attributes specified by the relevant
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
+ The returned array includes features specified in vector and GeoJSON tile
+ sources but does not include anything from raster, image, or video sources.
+
+ Only visible features are returned. For example, suppose the current style uses
+ the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
+ but none of the specified style layers includes features that have the `maki`
+ property set to `bus`. If you pass a point corresponding to the location of a
+ bus stop into this method, the bus stop feature does not appear in the
+ resulting array. On the other hand, if the style does include bus stops, an
+ `MGLFeature` object representing that bus stop is returned and its
+ `attributes` dictionary has the `maki` key set to `bus` (along with other
+ attributes). The dictionary contains only the attributes provided by the
+ tile source; it does not include computed attribute values or rules about how
+ the feature is rendered by the current style.
+
+ The returned array is sorted by z-order, starting with the topmost rendered
+ feature and ending with the bottommost rendered feature. A feature that is
+ rendered multiple times due to wrapping across the antimeridian at low zoom
+ levels is included only once, subject to the caveat that follows.
+
+ Features come from tiled vector data or GeoJSON data that is converted to tiles
+ internally, so feature geometries are clipped at tile boundaries and features
+ may appear duplicated across tiles. For example, suppose the specified point
+ lies along a road that spans the screen. The resulting array includes those
+ parts of the road that lie within the map tile that contain the specified
+ point, even if the road extends into other tiles.
+
+ To find out the layer names in a particular style, view the style in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+
+ @param point A point expressed in the map view’s coordinate system.
+ @param styleLayerIdentifiers A set of strings that correspond to the names of
+ layers defined in the current style. Only the features contained in these
+ layers are included in the returned array.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(_:styleLayerIdentifiers:));
+
+/**
+ Returns an array of rendered map features that intersect with the given
+ rectangle.
+
+ This method may return features from any of the map’s style layers. To restrict
+ the search to a particular layer or layers, use the
+ `-visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:` method. For more
+ information about searching for map features, see that method’s documentation.
+
+ @param rect A rectangle expressed in the map view’s coordinate system.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(_:));
+
+/**
+ Returns an array of rendered map features that intersect with the given
+ rectangle, restricted to the given style layers.
+
+ Each object in the returned array represents a feature rendered by the
+ current style and provides access to attributes specified by the relevant
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile sources</a>.
+ The returned array includes features specified in vector and GeoJSON tile
+ sources but does not include anything from raster, image, or video sources.
+
+ Only visible features are returned. For example, suppose the current style uses
+ the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets source</a>,
+ but none of the specified style layers includes features that have the `maki`
+ property set to `bus`. If you pass a rectangle containing the location of a bus
+ stop into this method, the bus stop feature does not appear in the resulting
+ array. On the other hand, if the style does include bus stops, an `MGLFeature`
+ object representing that bus stop is returned and its `attributes` dictionary
+ has the `maki` key set to `bus` (along with other attributes). The dictionary
+ contains only the attributes provided by the tile source; it does not include
+ computed attribute values or rules about how the feature is rendered by the
+ current style.
+
+ The returned array is sorted by z-order, starting with the topmost rendered
+ feature and ending with the bottommost rendered feature. A feature that is
+ rendered multiple times due to wrapping across the antimeridian at low zoom
+ levels is included only once, subject to the caveat that follows.
+
+ Features come from tiled vector data or GeoJSON data that is converted to tiles
+ internally, so feature geometries are clipped at tile boundaries and features
+ may appear duplicated across tiles. For example, suppose the specified
+ rectangle intersects with a road that spans the screen. The resulting array
+ includes those parts of the road that lie within the map tiles covering the
+ specified rectangle, even if the road extends into other tiles. The portion of
+ the road within each map tile is included individually.
+
+ To find out the layer names in a particular style, view the style in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>.
+
+ @param rect A rectangle expressed in the map view’s coordinate system.
+ @param styleLayerIdentifiers A set of strings that correspond to the names of
+ layers defined in the current style. Only the features contained in these
+ layers are included in the returned array.
+ @return An array of objects conforming to the `MGLFeature` protocol that
+ represent features in the sources used by the current style.
+ */
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(_:styleLayerIdentifiers:));
+
+#pragma mark Converting Geographic Coordinates
+
+/**
+ Converts a geographic coordinate to a point in the given view’s coordinate
+ system.
+
+ @param coordinate The geographic coordinate to convert.
+ @param view The view in whose coordinate system the returned point should be
+ expressed. If this parameter is `nil`, the returned point is expressed in
+ the window’s coordinate system. If `view` is not `nil`, it must belong to
+ the same window as the map view.
+ @return The point (in the appropriate view or window coordinate system)
+ corresponding to the given geographic coordinate.
+ */
+- (NSPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable NSView *)view;
+
+/**
+ Converts a point in the given view’s coordinate system to a geographic
+ coordinate.
+
+ @param point The point to convert.
+ @param view The view in whose coordinate system the point is expressed.
+ @return The geographic coordinate at the given point.
+ */
+- (CLLocationCoordinate2D)convertPoint:(NSPoint)point toCoordinateFromView:(nullable NSView *)view;
+
+/**
+ Converts a geographic bounding box to a rectangle in the given view’s
+ coordinate system.
+
+ @param bounds The geographic bounding box to convert.
+ @param view The view in whose coordinate system the returned rectangle should
+ be expressed. If this parameter is `nil`, the returned rectangle is
+ expressed in the window’s coordinate system. If `view` is not `nil`, it must
+ belong to the same window as the map view.
+ */
+- (NSRect)convertCoordinateBounds:(MGLCoordinateBounds)bounds toRectToView:(nullable NSView *)view;
+
+/**
+ Converts a rectangle in the given view’s coordinate system to a geographic
+ bounding box.
+
+ @param rect The rectangle to convert.
+ @param view The view in whose coordinate system the rectangle is expressed.
+ @return The geographic bounding box coextensive with the given rectangle.
+ */
+- (MGLCoordinateBounds)convertRect:(NSRect)rect toCoordinateBoundsFromView:(nullable NSView *)view;
+
+/**
+ Returns the distance spanned by one point in the map view’s coordinate system
+ at the given latitude and current zoom level.
+
+ The distance between points decreases as the latitude approaches the poles.
+ This relationship parallels the relationship between longitudinal coordinates
+ at different latitudes.
+
+ @param latitude The latitude of the geographic coordinate represented by the
+ point.
+ @return The distance in meters spanned by a single point.
+ */
+- (CLLocationDistance)metersPerPointAtLatitude:(CLLocationDegrees)latitude;
+
+#pragma mark Debugging the Map
+
+/**
+ The options that determine which debugging aids are shown on the map.
+
+ These options are all disabled by default and should remain disabled in
+ released software for performance and aesthetic reasons.
+ */
+@property (nonatomic) MGLMapDebugMaskOptions debugMask;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/osx/src/MGLMapView.mm b/platform/osx/src/MGLMapView.mm
index a247832fee..ac4eae9d34 100644
--- a/platform/osx/src/MGLMapView.mm
+++ b/platform/osx/src/MGLMapView.mm
@@ -1,12 +1,14 @@
#import "MGLMapView_Private.h"
+#import "MGLAnnotationImage_Private.h"
#import "MGLAttributionButton.h"
#import "MGLCompassCell.h"
#import "MGLOpenGLLayer.h"
#import "MGLStyle.h"
-#import "../../darwin/src/MGLGeometry_Private.h"
-#import "../../darwin/src/MGLMultiPoint_Private.h"
-#import "../../darwin/src/MGLOfflineStorage_Private.h"
+#import "MGLFeature_Private.h"
+#import "MGLGeometry_Private.h"
+#import "MGLMultiPoint_Private.h"
+#import "MGLOfflineStorage_Private.h"
#import "MGLAccountManager.h"
#import "MGLMapCamera.h"
@@ -16,25 +18,24 @@
#import "MGLMapViewDelegate.h"
#import <mbgl/mbgl.hpp>
-#import <mbgl/annotation/point_annotation.hpp>
+#import <mbgl/annotation/annotation.hpp>
#import <mbgl/map/camera.hpp>
#import <mbgl/platform/darwin/reachability.h>
#import <mbgl/gl/gl.hpp>
#import <mbgl/sprite/sprite_image.hpp>
#import <mbgl/storage/default_file_source.hpp>
#import <mbgl/storage/network_status.hpp>
+#import <mbgl/math/wrap.hpp>
#import <mbgl/util/constants.hpp>
-#import <mbgl/util/math.hpp>
-#import <mbgl/util/std.hpp>
#import <mbgl/util/chrono.hpp>
#import <map>
#import <unordered_set>
-#import "../../darwin/src/NSBundle+MGLAdditions.h"
-#import "../../darwin/src/NSProcessInfo+MGLAdditions.h"
-#import "../../darwin/src/NSException+MGLAdditions.h"
-#import "../../darwin/src/NSString+MGLAdditions.h"
+#import "NSBundle+MGLAdditions.h"
+#import "NSProcessInfo+MGLAdditions.h"
+#import "NSException+MGLAdditions.h"
+#import "NSString+MGLAdditions.h"
#import <QuartzCore/QuartzCore.h>
@@ -56,6 +57,9 @@ const CGFloat MGLKeyPanningIncrement = 150;
/// Degrees that a single press of the rotation keyboard shortcut rotates the map by.
const CLLocationDegrees MGLKeyRotationIncrement = 25;
+/// Key for the user default that, when true, causes the map view to zoom in and out on scroll wheel events.
+NSString * const MGLScrollWheelZoomsMapViewDefaultKey = @"MGLScrollWheelZoomsMapView";
+
/// Reuse identifier and file name of the default point annotation image.
static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker";
@@ -76,8 +80,14 @@ struct MGLAttribution {
/// URL to open when the attribution button is clicked.
NSString *urlString;
} MGLAttributions[] = {
- { @"Mapbox", @"https://www.mapbox.com/about/maps/" },
- { @"OpenStreetMap", @"http://www.openstreetmap.org/about/" },
+ {
+ .title = NSLocalizedStringWithDefaultValue(@"COPYRIGHT_MAPBOX", nil, nil, @"Mapbox", @"Linked part of copyright notice"),
+ .urlString = NSLocalizedStringWithDefaultValue(@"COPYRIGHT_MAPBOX_LINK", nil, nil, @"https://www.mapbox.com/about/maps/", @"Copyright notice link"),
+ },
+ {
+ .title = NSLocalizedStringWithDefaultValue(@"COPYRIGHT_OSM", nil, nil, @"OpenStreetMap", @"Linked part of copyright notice"),
+ .urlString = NSLocalizedStringWithDefaultValue(@"COPYRIGHT_OSM_LINK", nil, nil, @"http://www.openstreetmap.org/about/", @"Copyright notice link"),
+ },
};
/// Unique identifier representing a single annotation in mbgl.
@@ -127,9 +137,8 @@ mbgl::Color MGLColorObjectFromNSColor(NSColor *color) {
class MGLAnnotationContext {
public:
id <MGLAnnotation> annotation;
- /// mbgl-given identifier for the annotation image used by this annotation.
- /// Based on the annotation image’s reusable identifier.
- NSString *symbolIdentifier;
+ /// The annotation’s image’s reuse identifier.
+ NSString *imageReuseIdentifier;
};
@interface MGLMapView () <NSPopoverDelegate, MGLMultiPointDelegate>
@@ -193,6 +202,14 @@ public:
#pragma mark Lifecycle
++ (void)initialize {
+ if (self == [MGLMapView class]) {
+ [[NSUserDefaults standardUserDefaults] registerDefaults:@{
+ MGLScrollWheelZoomsMapViewDefaultKey: @NO,
+ }];
+ }
+}
+
- (instancetype)initWithFrame:(NSRect)frameRect {
if (self = [super initWithFrame:frameRect]) {
[self commonInit];
@@ -245,7 +262,8 @@ public:
[[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:NULL];
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
- _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);
+ _mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
+ [self validateTileCacheSize];
// Install the OpenGL layer. Interface Builder’s synchronous drawing means
// we can’t display a map, so don’t even bother to have a map layer.
@@ -291,12 +309,12 @@ public:
[(NSSegmentedCell *)_zoomControls.cell setTrackingMode:NSSegmentSwitchTrackingMomentary];
_zoomControls.continuous = YES;
_zoomControls.segmentCount = 2;
- [_zoomControls setLabel:@"−" forSegment:0]; // U+2212 MINUS SIGN
+ [_zoomControls setLabel:NSLocalizedStringWithDefaultValue(@"ZOOM_OUT_LABEL", nil, nil, @"−", @"Label of Zoom Out button; U+2212 MINUS SIGN") forSegment:0];
[(NSSegmentedCell *)_zoomControls.cell setTag:0 forSegment:0];
- [(NSSegmentedCell *)_zoomControls.cell setToolTip:@"Zoom Out" forSegment:0];
- [_zoomControls setLabel:@"+" forSegment:1];
+ [(NSSegmentedCell *)_zoomControls.cell setToolTip:NSLocalizedStringWithDefaultValue(@"ZOOM_OUT_TOOLTIP", nil, nil, @"Zoom Out", @"Tooltip of Zoom Out button") forSegment:0];
+ [_zoomControls setLabel:NSLocalizedStringWithDefaultValue(@"ZOOM_IN_LABEL", nil, nil, @"+", @"Label of Zoom In button") forSegment:1];
[(NSSegmentedCell *)_zoomControls.cell setTag:1 forSegment:1];
- [(NSSegmentedCell *)_zoomControls.cell setToolTip:@"Zoom In" forSegment:1];
+ [(NSSegmentedCell *)_zoomControls.cell setToolTip:NSLocalizedStringWithDefaultValue(@"ZOOM_IN_TOOLTIP", nil, nil, @"Zoom In", @"Tooltip of Zoom In button") forSegment:1];
_zoomControls.target = self;
_zoomControls.action = @selector(zoomInOrOut:);
_zoomControls.controlSize = NSRegularControlSize;
@@ -329,7 +347,7 @@ public:
logoImage.alignmentRect = NSInsetRect(logoImage.alignmentRect, 3, 3);
_logoView.image = logoImage;
_logoView.translatesAutoresizingMaskIntoConstraints = NO;
- _logoView.accessibilityTitle = @"Mapbox";
+ _logoView.accessibilityTitle = NSLocalizedStringWithDefaultValue(@"MAP_A11Y_TITLE", nil, nil, @"Mapbox", @"Accessibility title");
[self addSubview:_logoView];
}
@@ -364,7 +382,6 @@ public:
/// Adds gesture recognizers for manipulating the viewport and selecting annotations.
- (void)installGestureRecognizers {
- self.acceptsTouchEvents = YES;
_scrollEnabled = YES;
_zoomEnabled = YES;
_rotateEnabled = YES;
@@ -378,10 +395,6 @@ public:
clickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO;
[self addGestureRecognizer:clickGestureRecognizer];
- NSClickGestureRecognizer *secondaryClickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleSecondaryClickGesture:)];
- secondaryClickGestureRecognizer.buttonMask = 0x2;
- [self addGestureRecognizer:secondaryClickGestureRecognizer];
-
NSClickGestureRecognizer *doubleClickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleClickGesture:)];
doubleClickGestureRecognizer.numberOfClicksRequired = 2;
doubleClickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO;
@@ -439,6 +452,9 @@ public:
[self.calloutForSelectedAnnotation close];
self.calloutForSelectedAnnotation = nil;
+ // Removing the annotations unregisters any outstanding KVO observers.
+ [self removeAnnotations:self.annotations];
+
if (_mbglMap) {
delete _mbglMap;
_mbglMap = nullptr;
@@ -449,10 +465,29 @@ public:
}
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(__unused id)object change:(__unused NSDictionary *)change context:(__unused void *)context {
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(__unused NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentLayoutRect"] ||
[keyPath isEqualToString:@"titlebarAppearsTransparent"]) {
[self adjustContentInsets];
+ } else if ([keyPath isEqualToString:@"coordinate"] &&
+ [object conformsToProtocol:@protocol(MGLAnnotation)] &&
+ ![object isKindOfClass:[MGLMultiPoint class]]) {
+ id <MGLAnnotation> annotation = object;
+ MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
+ // We can get here because a subclass registered itself as an observer
+ // of the coordinate key path of a non-multipoint annotation but failed
+ // to handle the change. This check deters us from treating the
+ // subclass’s context as an annotation tag. If the context happens to
+ // match a valid annotation tag, the annotation will be unnecessarily
+ // but safely updated.
+ if (annotation == [self annotationWithTag:annotationTag]) {
+ const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(annotation.coordinate);
+ MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
+ _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, annotationImage.styleIconIdentifier.UTF8String ?: "" });
+ if (annotationTag == _selectedAnnotationTag) {
+ [self deselectAnnotation:annotation];
+ }
+ }
}
}
@@ -470,6 +505,8 @@ public:
_delegateHasFillColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)];
_delegateHasLineWidthsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:lineWidthForPolylineAnnotation:)];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
if ([self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) {
NSLog(@"-mapView:regionWillChangeAnimated: is not supported by the OS X SDK, but %@ implements it anyways. "
@"Please implement -[%@ mapView:cameraWillChangeAnimated:] instead.",
@@ -485,6 +522,7 @@ public:
@"Please implement -[%@ mapView:cameraDidChangeAnimated:] instead.",
NSStringFromClass([delegate class]), NSStringFromClass([delegate class]));
}
+#pragma clang diagnostic pop
}
#pragma mark Style
@@ -529,7 +567,6 @@ public:
[self deselectAnnotation:self.selectedAnnotation];
if (!self.dormant && !newWindow) {
self.dormant = YES;
- _mbglMap->pause();
}
[self.window removeObserver:self forKeyPath:@"contentLayoutRect"];
@@ -539,7 +576,6 @@ public:
- (void)viewDidMoveToWindow {
NSWindow *window = self.window;
if (self.dormant && window) {
- _mbglMap->resume();
self.dormant = NO;
}
@@ -569,6 +605,9 @@ public:
- (void)setFrame:(NSRect)frame {
super.frame = frame;
+ if (!NSEqualRects(frame, self.frame)) {
+ [self validateTileCacheSize];
+ }
if (!_isTargetingInterfaceBuilder) {
_mbglMap->update(mbgl::Update::Dimensions);
}
@@ -667,20 +706,49 @@ public:
- (void)renderSync {
if (!self.dormant) {
- CGFloat zoomFactor = _mbglMap->getMaxZoom() - _mbglMap->getMinZoom() + 1;
- CGFloat cpuFactor = (CGFloat)[NSProcessInfo processInfo].processorCount;
- CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000;
- CGFloat sizeFactor = ((CGFloat)_mbglMap->getWidth() / mbgl::util::tileSize) * ((CGFloat)_mbglMap->getHeight() / mbgl::util::tileSize);
-
- NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
-
- _mbglMap->setSourceTileCacheSize(cacheSize);
- _mbglMap->renderSync();
-
+ // Enable vertex buffer objects.
+ mbgl::gl::InitializeExtensions([](const char *name) {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void *symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<mbgl::gl::glProc>(symbol);
+ });
+
+ _mbglMap->render();
+
+ if (_isPrinting) {
+ _isPrinting = NO;
+ std::string png = encodePNG(_mbglView->readStillImage());
+ NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()];
+ NSImage *image = [[NSImage alloc] initWithData:data];
+ [self performSelector:@selector(printWithImage:) withObject:image afterDelay:0];
+ }
+
// [self updateUserLocationAnnotationView];
}
}
+- (void)validateTileCacheSize {
+ if (!_mbglMap) {
+ return;
+ }
+
+ CGFloat zoomFactor = self.maximumZoomLevel - self.minimumZoomLevel + 1;
+ CGFloat cpuFactor = [NSProcessInfo processInfo].processorCount;
+ CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000;
+ CGFloat sizeFactor = (NSWidth(self.bounds) / mbgl::util::tileSize) * (NSHeight(self.bounds) / mbgl::util::tileSize);
+
+ NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
+
+ _mbglMap->setSourceTileCacheSize(cacheSize);
+}
+
- (void)invalidate {
MGLAssertIsMainThread();
@@ -886,11 +954,13 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
_mbglMap->setMinZoom(minimumZoomLevel);
+ [self validateTileCacheSize];
}
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
+ [self validateTileCacheSize];
}
- (double)maximumZoomLevel {
@@ -946,14 +1016,8 @@ public:
}
- (MGLMapCamera *)camera {
- CGFloat pitch = _mbglMap->getPitch();
- CLLocationDistance altitude = MGLAltitudeForZoomLevel(self.zoomLevel, pitch,
- self.centerCoordinate.latitude,
- self.frame.size);
- return [MGLMapCamera cameraLookingAtCenterCoordinate:self.centerCoordinate
- fromDistance:altitude
- pitch:pitch
- heading:self.direction];
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+ return [self cameraForCameraOptions:_mbglMap->getCameraOptions(padding)];
}
- (void)setCamera:(MGLMapCamera *)camera {
@@ -1085,6 +1149,31 @@ public:
_mbglMap->easeTo(cameraOptions, animationOptions);
}
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds {
+ return [self cameraThatFitsCoordinateBounds:bounds edgePadding:NSEdgeInsetsZero];
+}
+
+- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets {
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), padding);
+ return [self cameraForCameraOptions:cameraOptions];
+}
+
+- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions {
+ CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
+ double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel;
+ CLLocationDirection direction = cameraOptions.angle ? -MGLDegreesFromRadians(*cameraOptions.angle) : self.direction;
+ CGFloat pitch = cameraOptions.pitch ? MGLDegreesFromRadians(*cameraOptions.pitch) : _mbglMap->getPitch();
+ CLLocationDistance altitude = MGLAltitudeForZoomLevel(zoomLevel, pitch,
+ centerCoordinate.latitude,
+ self.frame.size);
+ return [MGLMapCamera cameraLookingAtCenterCoordinate:centerCoordinate
+ fromDistance:altitude
+ pitch:pitch
+ heading:direction];
+}
+
- (void)setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets {
_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets;
[self adjustContentInsets];
@@ -1210,7 +1299,7 @@ public:
if (gestureRecognizer.state == NSGestureRecognizerStateChanged) {
delta.y *= -1;
[self offsetCenterCoordinateBy:delta animated:NO];
- [gestureRecognizer setTranslation:NSZeroPoint inView:self];
+ [gestureRecognizer setTranslation:NSZeroPoint inView:nil];
}
}
}
@@ -1252,7 +1341,8 @@ public:
/// Click or tap to select an annotation.
- (void)handleClickGesture:(NSClickGestureRecognizer *)gestureRecognizer {
- if (gestureRecognizer.state != NSGestureRecognizerStateEnded) {
+ if (gestureRecognizer.state != NSGestureRecognizerStateEnded
+ || [self subviewContainingGesture:gestureRecognizer]) {
return;
}
@@ -1269,21 +1359,10 @@ public:
}
}
-/// Tap with two fingers (“right-click”) to zoom out.
-- (void)handleSecondaryClickGesture:(NSClickGestureRecognizer *)gestureRecognizer {
- if (!self.zoomEnabled || gestureRecognizer.state != NSGestureRecognizerStateEnded) {
- return;
- }
-
- _mbglMap->cancelTransitions();
-
- NSPoint gesturePoint = [gestureRecognizer locationInView:self];
- [self scaleBy:0.5 atPoint:gesturePoint animated:YES];
-}
-
/// Double-click or double-tap to zoom in.
- (void)handleDoubleClickGesture:(NSClickGestureRecognizer *)gestureRecognizer {
- if (!self.zoomEnabled || gestureRecognizer.state != NSGestureRecognizerStateEnded) {
+ if (!self.zoomEnabled || gestureRecognizer.state != NSGestureRecognizerStateEnded
+ || [self subviewContainingGesture:gestureRecognizer]) {
return;
}
@@ -1300,6 +1379,7 @@ public:
_mbglMap->cancelTransitions();
+ // Tap with two fingers (“right-click”) to zoom out on mice but not trackpads.
NSPoint gesturePoint = [self convertPoint:event.locationInWindow fromView:nil];
[self scaleBy:0.5 atPoint:gesturePoint animated:YES];
}
@@ -1332,14 +1412,23 @@ public:
- (void)scrollWheel:(NSEvent *)event {
// https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#10_7Dragging
- if (event.phase == NSEventPhaseNone && event.momentumPhase == NSEventPhaseNone && !event.hasPreciseScrollingDeltas) {
+ BOOL isScrollWheel = event.phase == NSEventPhaseNone && event.momentumPhase == NSEventPhaseNone && !event.hasPreciseScrollingDeltas;
+ if (isScrollWheel || [[NSUserDefaults standardUserDefaults] boolForKey:MGLScrollWheelZoomsMapViewDefaultKey]) {
// A traditional, vertical scroll wheel zooms instead of panning.
- if (self.zoomEnabled && std::abs(event.scrollingDeltaX) < std::abs(event.scrollingDeltaY)) {
- _mbglMap->cancelTransitions();
-
- NSPoint gesturePoint = [self convertPoint:event.locationInWindow fromView:nil];
- double zoomDelta = event.scrollingDeltaY / 4;
- [self scaleBy:exp2(zoomDelta) atPoint:gesturePoint animated:YES];
+ if (self.zoomEnabled) {
+ const double delta =
+ event.scrollingDeltaY / ([event hasPreciseScrollingDeltas] ? 100 : 10);
+ if (delta != 0) {
+ double scale = 2.0 / (1.0 + std::exp(-std::abs(delta)));
+
+ // Zooming out.
+ if (delta < 0) {
+ scale = 1.0 / scale;
+ }
+
+ NSPoint gesturePoint = [self convertPoint:event.locationInWindow fromView:nil];
+ [self scaleBy:scale atPoint:gesturePoint animated:NO];
+ }
}
} else if (self.scrollEnabled
&& _magnificationGestureRecognizer.state == NSGestureRecognizerStatePossible
@@ -1360,6 +1449,20 @@ public:
}
}
+/// Returns the subview that the gesture is located in.
+- (NSView *)subviewContainingGesture:(NSGestureRecognizer *)gestureRecognizer {
+ if (NSPointInRect([gestureRecognizer locationInView:self.compass], self.compass.bounds)) {
+ return self.compass;
+ }
+ if (NSPointInRect([gestureRecognizer locationInView:self.zoomControls], self.zoomControls.bounds)) {
+ return self.zoomControls;
+ }
+ if (NSPointInRect([gestureRecognizer locationInView:self.attributionView], self.attributionView.bounds)) {
+ return self.attributionView;
+ }
+ return nil;
+}
+
#pragma mark Keyboard events
- (void)keyDown:(NSEvent *)event {
@@ -1501,16 +1604,29 @@ public:
BOOL delegateHasImagesForAnnotations = [self.delegate respondsToSelector:@selector(mapView:imageForAnnotation:)];
- std::vector<mbgl::PointAnnotation> points;
- std::vector<mbgl::ShapeAnnotation> shapes;
-
for (id <MGLAnnotation> annotation in annotations) {
NSAssert([annotation conformsToProtocol:@protocol(MGLAnnotation)], @"Annotation does not conform to MGLAnnotation");
if ([annotation isKindOfClass:[MGLMultiPoint class]]) {
+ // Actual multipoints aren’t supported as annotations.
+ if ([annotation isMemberOfClass:[MGLMultiPoint class]]
+ || [annotation isMemberOfClass:[MGLMultiPointFeature class]]) {
+ continue;
+ }
+
// The multipoint knows how to style itself (with the map view’s help).
- [(MGLMultiPoint *)annotation addShapeAnnotationObjectToCollection:shapes withDelegate:self];
- } else {
+ MGLMultiPoint *multiPoint = (MGLMultiPoint *)annotation;
+ if (!multiPoint.pointCount) {
+ continue;
+ }
+
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation([multiPoint annotationObjectWithDelegate:self]);
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ _annotationContextsByAnnotationTag[annotationTag] = context;
+ } else if (![annotation isKindOfClass:[MGLMultiPolyline class]]
+ || ![annotation isKindOfClass:[MGLMultiPolygon class]]
+ || ![annotation isKindOfClass:[MGLShapeCollection class]]) {
MGLAnnotationImage *annotationImage = nil;
if (delegateHasImagesForAnnotations) {
annotationImage = [self.delegate mapView:self imageForAnnotation:annotation];
@@ -1519,64 +1635,66 @@ public:
annotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName];
}
if (!annotationImage) {
- // Create a default annotation image that depicts a round pin
- // rising from the center, with a shadow slightly below center.
- // The alignment rect therefore excludes the bottom half.
- NSImage *image = MGLDefaultMarkerImage();
- NSRect alignmentRect = image.alignmentRect;
- alignmentRect.origin.y = NSMidY(alignmentRect);
- alignmentRect.size.height /= 2;
- image.alignmentRect = alignmentRect;
- annotationImage = [MGLAnnotationImage annotationImageWithImage:image
- reuseIdentifier:MGLDefaultStyleMarkerSymbolName];
+ annotationImage = self.defaultAnnotationImage;
+ }
+
+ NSString *symbolName = annotationImage.styleIconIdentifier;
+ if (!symbolName) {
+ symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
+ annotationImage.styleIconIdentifier = symbolName;
}
if (!self.annotationImagesByIdentifier[annotationImage.reuseIdentifier]) {
self.annotationImagesByIdentifier[annotationImage.reuseIdentifier] = annotationImage;
[self installAnnotationImage:annotationImage];
}
+
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation(mbgl::SymbolAnnotation {
+ MGLPointFromLocationCoordinate2D(annotation.coordinate),
+ symbolName.UTF8String ?: ""
+ });
+
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ context.imageReuseIdentifier = annotationImage.reuseIdentifier;
+ _annotationContextsByAnnotationTag[annotationTag] = context;
- NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
- points.emplace_back(MGLLatLngFromLocationCoordinate2D(annotation.coordinate), symbolName ? [symbolName UTF8String] : "");
-
+ if ([annotation isKindOfClass:[NSObject class]]) {
+ NSAssert(![annotation isKindOfClass:[MGLMultiPoint class]], @"Point annotation should not be MGLMultiPoint.");
+ [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:(void *)(NSUInteger)annotationTag];
+ }
+
// Opt into potentially expensive tooltip tracking areas.
if (annotation.toolTip.length) {
_wantsToolTipRects = YES;
}
}
}
-
- // Add any point annotations to mbgl and our own index.
- if (points.size()) {
- std::vector<MGLAnnotationTag> pointAnnotationTags = _mbglMap->addPointAnnotations(points);
-
- for (size_t i = 0; i < pointAnnotationTags.size(); ++i) {
- MGLAnnotationContext context;
- context.annotation = annotations[i];
- context.symbolIdentifier = @(points[i].icon.c_str());
- _annotationContextsByAnnotationTag[pointAnnotationTags[i]] = context;
- }
- }
-
- // Add any shape annotations to mbgl and our own index.
- if (shapes.size()) {
- std::vector<MGLAnnotationTag> shapeAnnotationTags = _mbglMap->addShapeAnnotations(shapes);
-
- for (size_t i = 0; i < shapeAnnotationTags.size(); ++i) {
- MGLAnnotationContext context;
- context.annotation = annotations[i];
- _annotationContextsByAnnotationTag[shapeAnnotationTags[i]] = context;
- }
- }
-
+
[self didChangeValueForKey:@"annotations"];
[self updateAnnotationTrackingAreas];
}
+/// Initializes and returns a default annotation image that depicts a round pin
+/// rising from the center, with a shadow slightly below center. The alignment
+/// rect therefore excludes the bottom half.
+- (MGLAnnotationImage *)defaultAnnotationImage {
+ NSImage *image = MGLDefaultMarkerImage();
+ NSRect alignmentRect = image.alignmentRect;
+ alignmentRect.origin.y = NSMidY(alignmentRect);
+ alignmentRect.size.height /= 2;
+ image.alignmentRect = alignmentRect;
+ return [MGLAnnotationImage annotationImageWithImage:image
+ reuseIdentifier:MGLDefaultStyleMarkerSymbolName];
+}
+
/// Sends the raw pixel data of the annotation image’s image to mbgl and
/// calculates state needed for hit testing later.
- (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage {
+ NSString *iconIdentifier = annotationImage.styleIconIdentifier;
+ self.annotationImagesByIdentifier[annotationImage.reuseIdentifier] = annotationImage;
+
NSImage *image = annotationImage.image;
NSSize size = image.size;
if (size.width == 0 || size.height == 0 || !image.valid) {
@@ -1598,8 +1716,7 @@ public:
std::copy(rep.bitmapData, rep.bitmapData + cPremultipliedImage.size(), cPremultipliedImage.data.get());
auto cSpriteImage = std::make_shared<mbgl::SpriteImage>(std::move(cPremultipliedImage),
(float)(rep.pixelsWide / size.width));
- NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier];
- _mbglMap->addAnnotationIcon(symbolName.UTF8String, cSpriteImage);
+ _mbglMap->addAnnotationIcon(iconIdentifier.UTF8String, cSpriteImage);
// Create a slop area with a “radius” equal to the annotation image’s entire
// size, allowing the eventual click to be on any point within this image.
@@ -1624,16 +1741,14 @@ public:
return;
}
- std::vector<MGLAnnotationTag> annotationTagsToRemove;
- annotationTagsToRemove.reserve(annotations.count);
-
+ [self willChangeValueForKey:@"annotations"];
+
for (id <MGLAnnotation> annotation in annotations) {
NSAssert([annotation conformsToProtocol:@protocol(MGLAnnotation)], @"Annotation does not conform to MGLAnnotation");
MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation];
NSAssert(annotationTag != MGLAnnotationTagNotFound, @"No ID for annotation %@", annotation);
- annotationTagsToRemove.push_back(annotationTag);
-
+
if (annotationTag == _selectedAnnotationTag) {
[self deselectAnnotation:annotation];
}
@@ -1642,21 +1757,21 @@ public:
}
_annotationContextsByAnnotationTag.erase(annotationTag);
+
+ if ([annotation isKindOfClass:[NSObject class]] &&
+ ![annotation isKindOfClass:[MGLMultiPoint class]]) {
+ [(NSObject *)annotation removeObserver:self forKeyPath:@"coordinate" context:(void *)(NSUInteger)annotationTag];
+ }
+
+ _mbglMap->removeAnnotation(annotationTag);
}
- [self willChangeValueForKey:@"annotations"];
- _mbglMap->removeAnnotations(annotationTagsToRemove);
[self didChangeValueForKey:@"annotations"];
[self updateAnnotationTrackingAreas];
}
- (nullable MGLAnnotationImage *)dequeueReusableAnnotationImageWithIdentifier:(NSString *)identifier {
- // This prefix is used to avoid collisions with style-defined sprites in
- // mbgl, but reusable identifiers are never prefixed.
- if ([identifier hasPrefix:MGLAnnotationSpritePrefix]) {
- identifier = [identifier substringFromIndex:MGLAnnotationSpritePrefix.length];
- }
return self.annotationImagesByIdentifier[identifier];
}
@@ -1767,8 +1882,8 @@ public:
}
// Choose the first nearby annotation.
- if (_annotationsNearbyLastClick.size()) {
- hitAnnotationTag = _annotationsNearbyLastClick.front();
+ if (nearbyAnnotations.size()) {
+ hitAnnotationTag = nearbyAnnotations.front();
}
}
}
@@ -1915,6 +2030,9 @@ public:
}
NSImage *image = [self imageOfAnnotationWithTag:annotationTag].image;
if (!image) {
+ image = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName].image;
+ }
+ if (!image) {
return NSZeroRect;
}
@@ -1938,7 +2056,7 @@ public:
return nil;
}
- NSString *customSymbol = _annotationContextsByAnnotationTag.at(annotationTag).symbolIdentifier;
+ NSString *customSymbol = _annotationContextsByAnnotationTag.at(annotationTag).imageReuseIdentifier;
NSString *symbolName = customSymbol.length ? customSymbol : MGLDefaultStyleMarkerSymbolName;
return [self dequeueReusableAnnotationImageWithIdentifier:symbolName];
@@ -2076,10 +2194,8 @@ public:
}
- (NSString *)view:(__unused NSView *)view stringForToolTip:(__unused NSToolTipTag)tag point:(__unused NSPoint)point userData:(void *)data {
- if ((NSUInteger)data >= MGLAnnotationTagNotFound) {
- return nil;
- }
- MGLAnnotationTag annotationTag = (NSUInteger)data;
+ NSAssert((NSUInteger)data < MGLAnnotationTagNotFound, @"Invalid annotation tag in tooltip rect user data.");
+ MGLAnnotationTag annotationTag = (MGLAnnotationTag)MIN((NSUInteger)data, MGLAnnotationTagNotFound);
id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag];
return annotation.toolTip;
}
@@ -2113,6 +2229,55 @@ public:
}
}
+#pragma mark Data
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point {
+ return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil];
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+ // Cocoa origin is at the lower-left corner.
+ mbgl::ScreenCoordinate screenCoordinate = { point.x, NSHeight(self.bounds) - point.y };
+
+ mbgl::optional<std::vector<std::string>> optionalLayerIDs;
+ if (styleLayerIdentifiers) {
+ __block std::vector<std::string> layerIDs;
+ layerIDs.reserve(styleLayerIdentifiers.count);
+ [styleLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
+ layerIDs.push_back(identifier.UTF8String);
+ }];
+ optionalLayerIDs = layerIDs;
+ }
+
+ std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenCoordinate, optionalLayerIDs);
+ return MGLFeaturesFromMBGLFeatures(features);
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect {
+ return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil];
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+ // Cocoa origin is at the lower-left corner.
+ mbgl::ScreenBox screenBox = {
+ { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) },
+ { NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) },
+ };
+
+ mbgl::optional<std::vector<std::string>> optionalLayerIDs;
+ if (styleLayerIdentifiers) {
+ __block std::vector<std::string> layerIDs;
+ layerIDs.reserve(styleLayerIdentifiers.count);
+ [styleLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
+ layerIDs.push_back(identifier.UTF8String);
+ }];
+ optionalLayerIDs = layerIDs;
+ }
+
+ std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenBox, optionalLayerIDs);
+ return MGLFeaturesFromMBGLFeatures(features);
+}
+
#pragma mark Interface Builder methods
- (void)prepareForInterfaceBuilder {
@@ -2233,6 +2398,12 @@ public:
if (options & mbgl::MapDebugOptions::Collision) {
mask |= MGLMapDebugCollisionBoxesMask;
}
+ if (options & mbgl::MapDebugOptions::Wireframe) {
+ mask |= MGLMapDebugWireframesMask;
+ }
+ if (options & mbgl::MapDebugOptions::StencilClip) {
+ mask |= MGLMapDebugStencilBufferMask;
+ }
return mask;
}
@@ -2250,6 +2421,12 @@ public:
if (debugMask & MGLMapDebugCollisionBoxesMask) {
options |= mbgl::MapDebugOptions::Collision;
}
+ if (debugMask & MGLMapDebugWireframesMask) {
+ options |= mbgl::MapDebugOptions::Wireframe;
+ }
+ if (debugMask & MGLMapDebugStencilBufferMask) {
+ options |= mbgl::MapDebugOptions::StencilClip;
+ }
_mbglMap->setDebug(options);
}
@@ -2258,85 +2435,39 @@ class MGLMapViewImpl : public mbgl::View {
public:
MGLMapViewImpl(MGLMapView *nativeView_, const float scaleFactor_)
: nativeView(nativeView_), scaleFactor(scaleFactor_) {}
- virtual ~MGLMapViewImpl() {}
-
-
+
float getPixelRatio() const override {
return scaleFactor;
}
-
+
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>(nativeView.bounds.size.width),
- static_cast<uint16_t>(nativeView.bounds.size.height) }};
+ static_cast<uint16_t>(nativeView.bounds.size.height) }};
}
-
+
std::array<uint16_t, 2> getFramebufferSize() const override {
NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds];
return {{ static_cast<uint16_t>(bounds.size.width),
- static_cast<uint16_t>(bounds.size.height) }};
+ static_cast<uint16_t>(bounds.size.height) }};
}
-
- void notify() override {}
-
+
void notifyMapChange(mbgl::MapChange change) override {
- assert([[NSThread currentThread] isMainThread]);
[nativeView notifyMapChange:change];
}
-
+
+ void invalidate() override {
+ [nativeView invalidate];
+ }
+
void activate() override {
MGLOpenGLLayer *layer = (MGLOpenGLLayer *)nativeView.layer;
- if ([NSOpenGLContext currentContext] != layer.openGLContext) {
- // Enable our OpenGL context on the Map thread.
- [layer.openGLContext makeCurrentContext];
-
- // Enable vertex buffer objects.
- mbgl::gl::InitializeExtensions([](const char *name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void *symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<mbgl::gl::glProc>(symbol);
- });
- }
+ [layer.openGLContext makeCurrentContext];
}
-
+
void deactivate() override {
[NSOpenGLContext clearCurrentContext];
}
-
- void invalidate() override {
- [nativeView performSelectorOnMainThread:@selector(invalidate)
- withObject:nil
- waitUntilDone:NO];
- }
-
- void beforeRender() override {
- // This normally gets called right away by mbgl::Map, but only on the
- // main thread. OpenGL contexts and extensions are thread-local, so this
- // has to happen on the Map thread too.
- activate();
-
-// auto size = getFramebufferSize();
-// MBGL_CHECK_ERROR(glViewport(0, 0, size[0], size[1]));
- }
-
- void afterRender() override {
- if (nativeView->_isPrinting) {
- nativeView->_isPrinting = NO;
- std::string png = encodePNG(readStillImage());
- NSData *data = [[NSData alloc] initWithBytes:png.data() length:png.size()];
- NSImage *image = [[NSImage alloc] initWithData:data];
- [nativeView performSelectorOnMainThread:@selector(printWithImage:)
- withObject:image
- waitUntilDone:NO];
- }
- }
-
+
mbgl::PremultipliedImage readStillImage() override {
auto size = getFramebufferSize();
const unsigned int w = size[0];
@@ -2345,7 +2476,7 @@ public:
mbgl::PremultipliedImage image { w, h };
MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
- const int stride = image.stride();
+ const size_t stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t *rgba = image.data.get();
for (int i = 0, j = h - 1; i < j; i++, j--) {
diff --git a/platform/osx/src/MGLMapViewDelegate.h b/platform/osx/src/MGLMapViewDelegate.h
new file mode 100644
index 0000000000..0b7eec84ac
--- /dev/null
+++ b/platform/osx/src/MGLMapViewDelegate.h
@@ -0,0 +1,220 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class MGLMapView;
+@class MGLAnnotationImage;
+@class MGLPolygon;
+@class MGLPolyline;
+@class MGLShape;
+
+/**
+ The `MGLMapViewDelegate` protocol defines a set of optional methods that you
+ can use to receive messages from an `MGLMapView` instance. Because many map
+ operations require the `MGLMapView` class to load data asynchronously, the map
+ view calls these methods to notify your application when specific operations
+ complete. The map view also uses these methods to request information about
+ annotations displayed on the map, such as the styles and interaction modes to
+ apply to individual annotations.
+ */
+@protocol MGLMapViewDelegate <NSObject>
+
+@optional
+
+#pragma mark Responding to Map Viewpoint Changes
+
+/**
+ Tells the delegate that the viewpoint depicted by the map view is about to
+ change.
+
+ This method is called whenever the currently displayed map camera will start
+ changing for any reason.
+
+ @param mapView The map view whose viewpoint will change.
+ @param animated Whether the change will cause an animated effect on the map.
+ */
+- (void)mapView:(MGLMapView *)mapView cameraWillChangeAnimated:(BOOL)animated;
+
+/**
+ Tells the delegate that the viewpoint depicted by the map view is changing.
+
+ This method is called as the currently displayed map camera changes due to
+ animation. During movement, this method may be called many times to report
+ updates to the viewpoint. Therefore, your implementation of this method should
+ be as lightweight as possible to avoid affecting performance.
+
+ @param mapView The map view whose viewpoint is changing.
+ */
+- (void)mapViewCameraIsChanging:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the viewpoint depicted by the map view has finished
+ changing.
+
+ This method is called whenever the currently displayed map camera has finished
+ changing.
+
+ @param mapView The map view whose viewpoint has changed.
+ @param animated Whether the change caused an animated effect on the map.
+ */
+- (void)mapView:(MGLMapView *)mapView cameraDidChangeAnimated:(BOOL)animated;
+
+#pragma mark Loading the Map
+
+/**
+ Tells the delegate that the map view will begin to load.
+
+ This method is called whenever the map view starts loading, including when a
+ new style has been set and the map must reload.
+
+ @param mapView The map view that is starting to load.
+ */
+- (void)mapViewWillStartLoadingMap:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the map view has finished loading.
+
+ This method is called whenever the map view finishes loading, either after the
+ initial load or after a style change has forced a reload.
+
+ @param mapView The map view that has finished loading.
+ */
+- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView;
+
+- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
+- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
+- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+
+#pragma mark Managing the Display of Annotations
+
+/**
+ Returns an annotation image object to mark the given point annotation object on
+ the map.
+
+ @param mapView The map view that requested the annotation image.
+ @param annotation The object representing the annotation that is about to be
+ displayed.
+ @return The image object to display for the given annotation or `nil` if you
+ want to display the default marker image.
+ */
+- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns the alpha value to use when rendering a shape annotation.
+
+ A value of 0.0 results in a completely transparent shape. A value of 1.0, the
+ default, results in a completely opaque shape.
+
+ @param mapView The map view rendering the shape annotation.
+ @param annotation The annotation being rendered.
+ @return An alpha value between 0 and 1.0.
+ */
+- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
+
+/**
+ Returns the color to use when rendering the outline of a shape annotation.
+
+ The default stroke color is the selected menu item color. If a pattern color is
+ specified, the result is undefined.
+
+ @param mapView The map view rendering the shape annotation.
+ @param annotation The annotation being rendered.
+ @return A color to use for the shape outline.
+ */
+- (NSColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
+
+/**
+ Returns the color to use when rendering the fill of a polygon annotation.
+
+ The default fill color is selected menu item color. If a pattern color is
+ specified, the result is undefined.
+
+ @param mapView The map view rendering the polygon annotation.
+ @param annotation The annotation being rendered.
+ @return The polygon’s interior fill color.
+ */
+- (NSColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
+
+/**
+ Returns the line width in points to use when rendering the outline of a
+ polyline annotation.
+
+ By default, the polyline is outlined with a line 3.0 points wide.
+
+ @param mapView The map view rendering the polygon annotation.
+ @param annotation The annotation being rendered.
+ @return A line width for the polyline, measured in points.
+ */
+- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation;
+
+#pragma mark Selecting Annotations
+
+/**
+ Tells the delegate that one of its annotations has been selected.
+
+ You can use this method to track changes to the selection state of annotations.
+
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was selected.
+ */
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
+
+/**
+ Tells the delegate that one of its annotations has been deselected.
+
+ You can use this method to track changes in the selection state of annotations.
+
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was deselected.
+ */
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
+
+#pragma mark Displaying Information About Annotations
+
+/**
+ Returns a Boolean value indicating whether the annotation is able to display
+ extra information in a callout popover.
+
+ This method is called after an annotation is selected, before any callout is
+ displayed for the annotation.
+
+ If the return value is `YES`, a callout popover is shown when the user clicks
+ on a selected annotation. The default callout displays the annotation’s title
+ and subtitle. You can customize the popover’s contents by implementing the
+ `-mapView:calloutViewControllerForAnnotation:` method.
+
+ If the return value is `NO`, or if this method is unimplemented, or if the
+ annotation lacks a title, the annotation will not show a callout even when
+ selected.
+
+ @param mapView The map view that has selected the annotation.
+ @param annotation The object representing the annotation.
+ @return A Boolean value indicating whether the annotation should show a
+ callout.
+ */
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
+
+/**
+ Returns a view controller to manage the callout popover’s content view.
+
+ Like any instance of `NSPopover`, an annotation callout manages its contents
+ with a view controller. The annotation object is the view controller’s
+ represented object. This means that you can bind controls in the view
+ controller’s content view to KVO-compliant properties of the annotation object,
+ such as `title` and `subtitle`.
+
+ If each annotation should have an identical callout, you can set the
+ `MGLMapView` instance’s `-setCalloutViewController:` method instead.
+
+ @param mapView The map view that is requesting a callout view controller.
+ @param annotation The object representing the annotation.
+ @return A view controller for the given annotation.
+ */
+- (nullable NSViewController *)mapView:(MGLMapView *)mapView calloutViewControllerForAnnotation:(id <MGLAnnotation>)annotation;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/osx/src/MGLMapView_Private.h b/platform/osx/src/MGLMapView_Private.h
index b9a112718e..76b1727925 100644
--- a/platform/osx/src/MGLMapView_Private.h
+++ b/platform/osx/src/MGLMapView_Private.h
@@ -1,7 +1,5 @@
#import "MGLMapView.h"
-void mgl_linkMapViewIBCategory();
-
@interface MGLMapView (Private)
/// True if the view or application is in a state where it is not expected to be
diff --git a/platform/osx/src/Mapbox.h b/platform/osx/src/Mapbox.h
new file mode 100644
index 0000000000..e4545e04bc
--- /dev/null
+++ b/platform/osx/src/Mapbox.h
@@ -0,0 +1,34 @@
+#import <Cocoa/Cocoa.h>
+
+/// Project version number for Mapbox.
+FOUNDATION_EXPORT double MapboxVersionNumber;
+
+/// Project version string for Mapbox.
+FOUNDATION_EXPORT const unsigned char MapboxVersionString[];
+
+#import "MGLAccountManager.h"
+#import "MGLAnnotation.h"
+#import "MGLAnnotationImage.h"
+#import "MGLClockDirectionFormatter.h"
+#import "MGLCompassDirectionFormatter.h"
+#import "MGLCoordinateFormatter.h"
+#import "MGLFeature.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+#import "MGLMapView.h"
+#import "MGLMapView+IBAdditions.h"
+#import "MGLMapViewDelegate.h"
+#import "MGLMultiPoint.h"
+#import "MGLOfflinePack.h"
+#import "MGLOfflineRegion.h"
+#import "MGLOfflineStorage.h"
+#import "MGLOverlay.h"
+#import "MGLPointAnnotation.h"
+#import "MGLPolygon.h"
+#import "MGLPolyline.h"
+#import "MGLShape.h"
+#import "MGLShapeCollection.h"
+#import "MGLStyle.h"
+#import "MGLTilePyramidOfflineRegion.h"
+#import "MGLTypes.h"
+#import "NSValue+MGLAdditions.h"
diff --git a/platform/osx/src/resources/MGLAnnotationCallout.xib b/platform/osx/src/resources/MGLAnnotationCallout.xib
deleted file mode 100644
index edf84a26a7..0000000000
--- a/platform/osx/src/resources/MGLAnnotationCallout.xib
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
- <dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="NSViewController">
- <connections>
- <outlet property="view" destination="c22-O7-iKe" id="QAM-0O-WIj"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application" customClass="NSObject"/>
- <customView id="c22-O7-iKe">
- <rect key="frame" x="0.0" y="0.0" width="270" height="50"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <subviews>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k5x-ao-Pz3">
- <rect key="frame" x="18" y="25" width="234" height="17"/>
- <textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" title="Title" id="nVE-Zi-KcG">
- <font key="font" metaFont="system"/>
- <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <connections>
- <binding destination="-2" name="value" keyPath="representedObject.title" id="3nD-YS-gzq"/>
- </connections>
- </textField>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e9C-Ve-ccw">
- <rect key="frame" x="18" y="8" width="234" height="14"/>
- <textFieldCell key="cell" controlSize="small" selectable="YES" sendsActionOnEndEditing="YES" title="Subtitle" id="eKw-tQ-dw8">
- <font key="font" metaFont="smallSystem"/>
- <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
- <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
- </textFieldCell>
- <connections>
- <binding destination="-2" name="value" keyPath="representedObject.subtitle" id="RQf-48-DyH"/>
- </connections>
- </textField>
- </subviews>
- <constraints>
- <constraint firstItem="e9C-Ve-ccw" firstAttribute="leading" secondItem="k5x-ao-Pz3" secondAttribute="leading" id="ApT-ew-CYb"/>
- <constraint firstAttribute="bottom" secondItem="e9C-Ve-ccw" secondAttribute="bottom" constant="8" id="CWV-Dd-8oi"/>
- <constraint firstItem="k5x-ao-Pz3" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="20" id="UUL-GB-Jtv"/>
- <constraint firstItem="e9C-Ve-ccw" firstAttribute="top" secondItem="k5x-ao-Pz3" secondAttribute="bottom" constant="3" id="Urc-wn-m8X"/>
- <constraint firstItem="e9C-Ve-ccw" firstAttribute="trailing" secondItem="k5x-ao-Pz3" secondAttribute="trailing" id="gss-6G-9GF"/>
- <constraint firstAttribute="trailing" secondItem="k5x-ao-Pz3" secondAttribute="trailing" constant="20" id="xCZ-s9-HaP"/>
- <constraint firstItem="k5x-ao-Pz3" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="8" id="xcm-oY-jjy"/>
- </constraints>
- <point key="canvasLocation" x="257" y="355"/>
- </customView>
- </objects>
-</document>
diff --git a/platform/osx/test/MGLGeometryTests.mm b/platform/osx/test/MGLGeometryTests.mm
deleted file mode 100644
index e6e7c4b45c..0000000000
--- a/platform/osx/test/MGLGeometryTests.mm
+++ /dev/null
@@ -1,58 +0,0 @@
-#import "../../darwin/src/MGLGeometry_Private.h"
-
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#import <XCTest/XCTest.h>
-
-@interface MGLGeometryTests : XCTestCase
-@end
-
-@implementation MGLGeometryTests
-
-- (void)testCoordinateBoundsIsEmpty {
- MGLCoordinateBounds emptyBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(10, 0));
- XCTAssertTrue(MGLCoordinateBoundsIsEmpty(emptyBounds));
- XCTAssertFalse(MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpanZero, MGLCoordinateBoundsGetCoordinateSpan(emptyBounds)));
-}
-
-- (void)testAngleConversions {
- XCTAssertEqual(-180, MGLDegreesFromRadians(-M_PI));
- XCTAssertEqual(0, MGLDegreesFromRadians(0));
- XCTAssertEqual(45, MGLDegreesFromRadians(M_PI_4));
- XCTAssertEqual(90, MGLDegreesFromRadians(M_PI_2));
- XCTAssertEqual(180, MGLDegreesFromRadians(M_PI));
- XCTAssertEqual(360, MGLDegreesFromRadians(2 * M_PI));
- XCTAssertEqual(720, MGLDegreesFromRadians(4 * M_PI));
-
- XCTAssertEqual(-360, MGLDegreesFromRadians(MGLRadiansFromDegrees(-360)));
- XCTAssertEqual(-180, MGLDegreesFromRadians(MGLRadiansFromDegrees(-180)));
- XCTAssertEqual(-90, MGLDegreesFromRadians(MGLRadiansFromDegrees(-90)));
- XCTAssertEqual(-45, MGLDegreesFromRadians(MGLRadiansFromDegrees(-45)));
- XCTAssertEqual(0, MGLDegreesFromRadians(MGLRadiansFromDegrees(0)));
- XCTAssertEqual(45, MGLDegreesFromRadians(MGLRadiansFromDegrees(45)));
- XCTAssertEqual(90, MGLDegreesFromRadians(MGLRadiansFromDegrees(90)));
- XCTAssertEqual(180, MGLDegreesFromRadians(MGLRadiansFromDegrees(180)));
- XCTAssertEqual(360, MGLDegreesFromRadians(MGLRadiansFromDegrees(360)));
-}
-
-- (void)testAltitudeConversions {
- CGSize tallSize = CGSizeMake(600, 1200);
- CGSize midSize = CGSizeMake(600, 800);
- CGSize shortSize = CGSizeMake(600, 400);
-
- XCTAssertEqualWithAccuracy(1800, MGLAltitudeForZoomLevel(MGLZoomLevelForAltitude(1800, 0, 0, midSize), 0, 0, midSize), 1);
- XCTAssertLessThan(MGLZoomLevelForAltitude(1800, 0, 0, midSize), MGLZoomLevelForAltitude(1800, 0, 0, tallSize));
- XCTAssertGreaterThan(MGLZoomLevelForAltitude(1800, 0, 0, midSize), MGLZoomLevelForAltitude(1800, 0, 0, shortSize));
-
- XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 0, 0, midSize), 0, 0, midSize), 3);
- XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 0, 0, midSize), 0, 0, midSize), 3);
-
- XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 0, 40, midSize), 0, 40, midSize), 3);
- XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 0, 40, midSize), 0, 40, midSize), 3);
-
- XCTAssertEqualWithAccuracy(0, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(0, 60, 40, midSize), 60, 40, midSize), 3);
- XCTAssertEqualWithAccuracy(18, MGLZoomLevelForAltitude(MGLAltitudeForZoomLevel(18, 60, 40, midSize), 60, 40, midSize), 3);
-}
-
-@end
diff --git a/platform/osx/test/MGLOfflinePackTests.m b/platform/osx/test/MGLOfflinePackTests.m
deleted file mode 100644
index 41262d16c7..0000000000
--- a/platform/osx/test/MGLOfflinePackTests.m
+++ /dev/null
@@ -1,40 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#import <XCTest/XCTest.h>
-
-@interface MGLOfflinePackTests : XCTestCase
-
-@end
-
-@implementation MGLOfflinePackTests
-
-- (void)testInvalidation {
- MGLOfflinePack *invalidPack = [[MGLOfflinePack alloc] init];
-
- XCTAssertEqual(invalidPack.state, MGLOfflinePackStateInvalid, @"Offline pack should be invalid when initialized independently of MGLOfflineStorage.");
-
- XCTAssertThrowsSpecificNamed(invalidPack.region, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its region.");
- XCTAssertThrowsSpecificNamed(invalidPack.context, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its context.");
- XCTAssertThrowsSpecificNamed([invalidPack resume], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being resumed.");
- XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being suspended.");
-}
-
-- (void)testProgressBoxing {
- MGLOfflinePackProgress progress = {
- .countOfResourcesCompleted = 1,
- .countOfResourcesExpected = 2,
- .countOfBytesCompleted = 7,
- .maximumResourcesExpected = UINT64_MAX,
- };
- MGLOfflinePackProgress roundTrippedProgress = [NSValue valueWithMGLOfflinePackProgress:progress].MGLOfflinePackProgressValue;
-
- XCTAssertEqual(progress.countOfResourcesCompleted, roundTrippedProgress.countOfResourcesCompleted, @"Completed resources should round-trip.");
- XCTAssertEqual(progress.countOfResourcesExpected, roundTrippedProgress.countOfResourcesExpected, @"Expected resources should round-trip.");
- XCTAssertEqual(progress.countOfBytesCompleted, roundTrippedProgress.countOfBytesCompleted, @"Completed bytes should round-trip.");
- XCTAssertEqual(progress.maximumResourcesExpected, roundTrippedProgress.maximumResourcesExpected, @"Maximum expected resources should round-trip.");
-}
-
-@end
diff --git a/platform/osx/test/MGLOfflineRegionTests.m b/platform/osx/test/MGLOfflineRegionTests.m
deleted file mode 100644
index 321485feba..0000000000
--- a/platform/osx/test/MGLOfflineRegionTests.m
+++ /dev/null
@@ -1,35 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#import <XCTest/XCTest.h>
-
-@interface MGLOfflineRegionTests : XCTestCase
-
-@end
-
-@implementation MGLOfflineRegionTests
-
-- (void)testStyleURLs {
- MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(kCLLocationCoordinate2DInvalid, kCLLocationCoordinate2DInvalid);
- MGLTilePyramidOfflineRegion *region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:nil bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX];
- XCTAssertEqualObjects(region.styleURL, [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion], @"Streets isn’t the default style.");
-
- NSURL *localURL = [NSURL URLWithString:@"beautiful.style"];
- XCTAssertThrowsSpecificNamed([[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:localURL bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX], NSException, @"Invalid style URL", @"No exception raised when initializing region with a local file URL as the style URL.");
-}
-
-- (void)testEquality {
- MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(kCLLocationCoordinate2DInvalid, kCLLocationCoordinate2DInvalid);
- MGLTilePyramidOfflineRegion *original = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:[MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion] bounds:bounds fromZoomLevel:5 toZoomLevel:10];
- MGLTilePyramidOfflineRegion *copy = [original copy];
- XCTAssertEqualObjects(original, copy, @"Tile pyramid region should be equal to its copy.");
-
- XCTAssertEqualObjects(original.styleURL, copy.styleURL, @"Style URL has changed.");
- XCTAssert(MGLCoordinateBoundsEqualToCoordinateBounds(original.bounds, copy.bounds), @"Bounds have changed.");
- XCTAssertEqual(original.minimumZoomLevel, original.minimumZoomLevel, @"Minimum zoom level has changed.");
- XCTAssertEqual(original.maximumZoomLevel, original.maximumZoomLevel, @"Maximum zoom level has changed.");
-}
-
-@end
diff --git a/platform/osx/test/MGLOfflineStorageTests.m b/platform/osx/test/MGLOfflineStorageTests.m
deleted file mode 100644
index 53dc7f4a59..0000000000
--- a/platform/osx/test/MGLOfflineStorageTests.m
+++ /dev/null
@@ -1,126 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#import <XCTest/XCTest.h>
-
-@interface MGLOfflineStorageTests : XCTestCase
-
-@end
-
-@implementation MGLOfflineStorageTests
-
-- (void)testSharedObject {
- XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage], [MGLOfflineStorage sharedOfflineStorage], @"There should only be one shared offline storage object.");
-}
-
-// This test needs to come first so it can test the initial loading of packs.
-- (void)testAAALoadPacks {
- XCTestExpectation *kvoExpectation = [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
- NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
- return changeKind = NSKeyValueChangeSetting;
- }];
-
- [self waitForExpectationsWithTimeout:1 handler:nil];
-
- XCTAssertNotNil([MGLOfflineStorage sharedOfflineStorage].packs, @"Shared offline storage object should have a non-nil collection of packs by this point.");
-}
-
-- (void)testAddPack {
- NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count;
-
- NSURL *styleURL = [MGLStyle lightStyleURLWithVersion:8];
- /// Somewhere near Grape Grove, Ohio, United States.
- MGLCoordinateBounds bounds = {
- { 39.70358155855172, -83.69506472545841 },
- { 39.703818870225376, -83.69420641857361 },
- };
- double zoomLevel = 20;
- MGLTilePyramidOfflineRegion *region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:styleURL bounds:bounds fromZoomLevel:zoomLevel toZoomLevel:zoomLevel];
-
- NSString *nameKey = @"Name";
- NSString *name = @"🍇 Grape Grove";
-
- NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{
- nameKey: name,
- }];
-
- __block MGLOfflinePack *pack;
- [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
- NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
- NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
- return changeKind == NSKeyValueChangeInsertion && indices.count == 1;
- }];
- XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"];
- [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) {
- XCTAssertNotNil(completionHandlerPack, @"Added pack should exist.");
- XCTAssertEqual(completionHandlerPack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state.");
- pack = completionHandlerPack;
- [additionCompletionHandlerExpectation fulfill];
- }];
- [self waitForExpectationsWithTimeout:1 handler:nil];
-
- XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks.");
-
- XCTAssertEqual(pack, [MGLOfflineStorage sharedOfflineStorage].packs.lastObject, @"Pack should be appended to end of packs array.");
-
- XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed.");
-
- NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
- XCTAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
- XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
- XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed.");
-
- XCTAssertEqual(pack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state.");
-
- [self keyValueObservingExpectationForObject:pack keyPath:@"state" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
- NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
- MGLOfflinePackState state = [change[NSKeyValueChangeNewKey] integerValue];
- return changeKind == NSKeyValueChangeSetting && state == MGLOfflinePackStateInactive;
- }];
- [self expectationForNotification:MGLOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) {
- MGLOfflinePack *notificationPack = notification.object;
- XCTAssert([notificationPack isKindOfClass:[MGLOfflinePack class]], @"Object of notification should be an MGLOfflinePack.");
-
- NSDictionary *userInfo = notification.userInfo;
- XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary.");
-
- NSNumber *stateNumber = userInfo[MGLOfflinePackStateUserInfoKey];
- XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber.");
- XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state.");
-
- NSValue *progressValue = userInfo[MGLOfflinePackProgressUserInfoKey];
- XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue.");
- XCTAssertEqualObjects(progressValue, [NSValue valueWithMGLOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress.");
-
- return notificationPack == pack && pack.state == MGLOfflinePackStateInactive;
- }];
- [pack requestProgress];
- [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testRemovePack {
- NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count;
-
- MGLOfflinePack *pack = [MGLOfflineStorage sharedOfflineStorage].packs.lastObject;
- XCTAssertNotNil(pack, @"Added pack should still exist.");
-
- XCTestExpectation *kvoExpectation = [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) {
- NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
- NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
- return changeKind = NSKeyValueChangeRemoval && indices.count == 1;
- }];
- XCTestExpectation *completionHandlerExpectation = [self expectationWithDescription:@"remove pack completion handler"];
- [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
- XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should be invalid in the completion handler.");
- [completionHandlerExpectation fulfill];
- }];
- [self waitForExpectationsWithTimeout:1 handler:nil];
-
- XCTAssertEqual(pack.state, MGLOfflinePackStateInvalid, @"Removed pack should have been invalidated synchronously.");
-
- XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks - 1, @"Removed pack should have been removed from the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks or -testAddPack.");
-}
-
-@end
diff --git a/platform/osx/test/MGLStyleTests.mm b/platform/osx/test/MGLStyleTests.mm
deleted file mode 100644
index d08376bb59..0000000000
--- a/platform/osx/test/MGLStyleTests.mm
+++ /dev/null
@@ -1,102 +0,0 @@
-#import "MGLStyle.h"
-
-#import "../../darwin/src/NSBundle+MGLAdditions.h"
-
-#import <mbgl/util/default_styles.hpp>
-
-#pragma clang diagnostic ignored "-Wgnu-statement-expression"
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-
-#import <XCTest/XCTest.h>
-#import <objc/runtime.h>
-
-@interface MGLStyleTests : XCTestCase
-@end
-
-@implementation MGLStyleTests
-
-- (void)testUnversionedStyleURLs {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqualObjects([MGLStyle streetsStyleURL].absoluteString, @"mapbox://styles/mapbox/streets-v8");
- XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8");
- XCTAssertEqualObjects([MGLStyle lightStyleURL].absoluteString, @"mapbox://styles/mapbox/light-v8");
- XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @"mapbox://styles/mapbox/dark-v8");
- XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-v8");
- XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8");
-#pragma clang diagnostic pop
-}
-
-- (void)testVersionedStyleURLs {
- // Test that all the default styles have publicly-declared MGLStyle class
- // methods and that the URLs all have the right values.
- XCTAssertEqualObjects([MGLStyle streetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/streets-v99");
- XCTAssertEqualObjects([MGLStyle outdoorsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/outdoors-v99");
- XCTAssertEqualObjects([MGLStyle lightStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/light-v99");
- XCTAssertEqualObjects([MGLStyle darkStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/dark-v99");
- XCTAssertEqualObjects([MGLStyle satelliteStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-v99");
- XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString, @"mapbox://styles/mapbox/satellite-streets-v99");
-
- static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
- "MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
-}
-
-- (void)testStyleURLDeclarations {
- // Make sure this test is comprehensive.
- const unsigned numImplicitArgs = 2 /* _cmd, self */;
- unsigned numMethods = 0;
- Method *methods = class_copyMethodList(object_getClass([MGLStyle class]), &numMethods);
- unsigned numVersionedMethods = 0;
- for (NSUInteger i = 0; i < numMethods; i++) {
- Method method = methods[i];
- SEL selector = method_getName(method);
- NSString *name = @(sel_getName(selector));
- unsigned numArgs = method_getNumberOfArguments(method);
- if ([name hasSuffix:@"StyleURL"]) {
- XCTAssertEqual(numArgs, numImplicitArgs, @"Unversioned style URL method should have no parameters, but it has %u.", numArgs - numImplicitArgs);
- } else if ([name hasSuffix:@"StyleURLWithVersion:"]) {
- XCTAssertEqual(numArgs, numImplicitArgs + 1, @"Versioned style URL method should have one parameter, but it has %u.", numArgs - numImplicitArgs);
- numVersionedMethods++;
- } else {
- XCTAssertEqual([name rangeOfString:@"URL"].location, NSNotFound, @"MGLStyle style URL method %@ is malformed.", name);
- }
- }
- XCTAssertEqual(mbgl::util::default_styles::numOrderedStyles, numVersionedMethods,
- @"There are %lu default styles but MGLStyleTests only provides versioned style URL methods for %u of them.",
- mbgl::util::default_styles::numOrderedStyles, numVersionedMethods);
-
- // Test that all the versioned style methods are in the public header.
- NSString *styleHeader = self.stringWithContentsOfStyleHeader;
-
- NSError *versionedMethodError;
- NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*;)RE");
- NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError];
- XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods.");
- NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
- XCTAssertEqual(numVersionedMethodDeclarations, numVersionedMethods);
-
- // Test that “current version is” statements are present and current for all versioned style methods.
- NSError *versionError;
- NSString *versionExpressionString = @(R"RE(the current version is `(\d+)`)RE");
- NSRegularExpression *versionExpression = [NSRegularExpression regularExpressionWithPattern:versionExpressionString options:0 error:&versionError];
- XCTAssertNil(versionError, @"Error compiling regular expression to search for current version statements.");
- NSUInteger numVersionDeclarations = [versionExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
- XCTAssertEqual(numVersionDeclarations, numVersionedMethods);
- [versionExpression enumerateMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length) usingBlock:^(NSTextCheckingResult * _Nullable result, __unused NSMatchingFlags flags, __unused BOOL * _Nonnull stop) {
- XCTAssertEqual(result.numberOfRanges, 2U, @"Regular expression should have one capture group.");
- NSString *version = [styleHeader substringWithRange:[result rangeAtIndex:1]];
- XCTAssertEqual([version integerValue], MGLStyleDefaultVersion, @"Versioned style URL method should document current version as %ld, not %ld.", MGLStyleDefaultVersion, version.integerValue);
- }];
-}
-
-- (NSString *)stringWithContentsOfStyleHeader {
- NSURL *styleHeaderURL = [[[NSBundle mgl_frameworkBundle].bundleURL
- URLByAppendingPathComponent:@"Headers" isDirectory:YES]
- URLByAppendingPathComponent:@"MGLStyle.h"];
- NSError *styleHeaderError;
- NSString *styleHeader = [NSString stringWithContentsOfURL:styleHeaderURL usedEncoding:nil error:&styleHeaderError];
- XCTAssertNil(styleHeaderError, @"Error getting contents of MGLStyle.h.");
- return styleHeader;
-}
-
-@end
diff --git a/platform/osx/test/osxtest.gypi b/platform/osx/test/osxtest.gypi
deleted file mode 100644
index 6165b6fa88..0000000000
--- a/platform/osx/test/osxtest.gypi
+++ /dev/null
@@ -1,84 +0,0 @@
-{
- 'includes': [
- '../../../gyp/common.gypi',
- ],
- 'targets': [
- {
- 'target_name': 'osxtest',
- 'product_name': 'osxtest',
- 'type': 'loadable_module',
- 'mac_xctest_bundle': 1,
-
- 'dependencies': [
- 'osxsdk',
- ],
-
- 'variables': {
- 'cflags_cc': [
- ],
- 'ldflags': [
- '-stdlib=libc++',
- '-lstdc++',
- ],
- },
-
- 'xcode_settings': {
- 'CLANG_ENABLE_MODULES': 'YES',
- 'CLANG_ENABLE_OBJC_ARC': 'YES',
- 'ENABLE_STRICT_OBJC_MSGSEND': 'YES',
- 'GCC_DYNAMIC_NO_PIC': 'NO',
- 'GCC_NO_COMMON_BLOCKS': 'YES',
- 'INFOPLIST_FILE': '../platform/osx/test/Info.plist',
- 'LD_RUNPATH_SEARCH_PATHS': [
- '${inherited}',
- '@executable_path/../Frameworks',
- '@loader_path/../Frameworks',
- ],
- 'PRODUCT_BUNDLE_IDENTIFIER': 'com.mapbox.osxtest',
- 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
- 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
- 'SDKROOT': 'macosx',
- 'SKIP_INSTALL': 'YES',
- 'SUPPORTED_PLATFORMS':'macosx',
- },
-
- 'sources': [
- './MGLGeometryTests.mm',
- './MGLOfflinePackTests.m',
- './MGLOfflineRegionTests.m',
- './MGLOfflineStorageTests.m',
- './MGLStyleTests.mm',
- ],
-
- 'include_dirs': [
- '../../../platform/osx/include',
- '../../../platform/darwin/include',
- '../../../include',
- '../../../src',
- ],
-
- 'configurations': {
- 'Debug': {
- 'xcode_settings': {
- 'COPY_PHASE_STRIP': 'NO',
- 'DEBUG_INFORMATION_FORMAT': 'dwarf',
- 'ENABLE_TESTABILITY': 'YES',
- 'GCC_OPTIMIZATION_LEVEL': '0',
- 'GCC_PREPROCESSOR_DEFINITIONS': [
- 'DEBUG=1',
- '${inherited}',
- ],
- 'ONLY_ACTIVE_ARCH': 'YES',
- },
- },
- 'Release': {
- 'xcode_settings': {
- 'COPY_PHASE_STRIP': 'YES',
- 'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
- 'ENABLE_NS_ASSERTIONS': 'NO',
- },
- },
- },
- },
- ]
-}
diff --git a/platform/qt/README.md b/platform/qt/README.md
new file mode 100644
index 0000000000..d763682717
--- /dev/null
+++ b/platform/qt/README.md
@@ -0,0 +1,73 @@
+# Mapbox Qt SDK
+
+[![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds)
+[![Bitrise](https://www.bitrise.io/app/96cfbc97e0245c22.svg?token=GxsqIOGPXhn0F23sSVSsYA&branch=master)](https://www.bitrise.io/app/96cfbc97e0245c22)
+
+Provides [Qt](http://www.qt.io/) example applications and APIs via `QMapboxGL`
+and `QQuickMapboxGL`:
+
+**QMapboxGL** - implements a C++03x-conformant API that has been tested from Qt
+4.7 onwards (Travis CI currently builds it using both Qt 4 and Qt 5).
+
+**QQuickMapboxGL** - implements a Qt Quick (QML) item that can be added to a
+scene. Because `QQuickFramebufferObject` has been added in Qt version 5.2, we
+support this API from this version onwards.
+
+![Mapbox Qt QML
+example](https://c2.staticflickr.com/8/7689/26247088394_01541b34de_o.png)
+
+### Developing
+
+[Qt supports many platforms](https://www.qt.io/qt-framework/#section-4) - so far
+we have been testing our code on Linux and OS X desktops, as well as embedded
+devices such as Raspberry Pi 3.
+
+If you need to maintain backwards compatibility with prior releases of
+Qt, chose `QMapboxGL`. Otherwise, go for `QQuickMapboxGL`.
+
+Both `QMapboxGL` and `QQuickMapboxGL` requires a [Mapbox access
+token](https://www.mapbox.com/help/define-access-token/) at runtime - you need
+to provide it by setting the environment variable `MAPBOX_ACCESS_TOKEN`:
+
+ export MAPBOX_ACCESS_TOKEN=MYTOKEN
+
+#### Using QMapboxGL
+
+`QMapboxGL` is a [QObject](http://doc.qt.io/qt-5/qobject.html) - [MapWindow](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/qt/app/mapwindow.hpp) provides an example [QGLWidget](http://doc.qt.io/qt-5/qglwidget.html) that contains a `QMapboxGL` object. If you use `QMapboxGL` in non-standard Qt widgets, make sure to initialize the GL extensions required by Mapbox whenever possible:
+
+ QMapbox::initializeGLExtensions();
+
+#### Linux
+
+For Linux (Ubuntu) desktop, together with these [build
+instructions](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/linux#build),
+you also need:
+
+ sudo apt-get install qt5-default qtlocation5-dev qtpositioning5-dev \
+ qml-module-qtquick2 qml-module-qtpositioning qml-module-qtgraphicaleffects \
+ qml-module-qtquick-layouts qml-module-qtquick-controls
+
+#### OS X
+
+For OS X desktop, you can install Qt 5 via [Homebrew](http://brew.sh):
+
+ brew install qt5
+
+We provide the following build targets for our Qt SDK:
+
+### Build instructions
+
+#### Qt library
+
+```make qt-lib``` to build the `qmapboxgl` shared library. Public API headers
+can be found in [platform/qt/include](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/qt/include) folder.
+
+#### QMapboxGL example application
+
+```make qt-app``` or ```make run-qt-app``` to run the application at the end of
+build.
+
+#### QQuickMapboxGL example application:
+
+```make qt-qml-app``` or ```make run-qt-qml-app``` to run the application at the
+end of build.
diff --git a/platform/qt/app/main.cpp b/platform/qt/app/main.cpp
new file mode 100644
index 0000000000..ecb4191e19
--- /dev/null
+++ b/platform/qt/app/main.cpp
@@ -0,0 +1,24 @@
+#include "mapwindow.hpp"
+
+#include <QApplication>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ QMapboxGLSettings settings;
+ settings.setAccessToken(qgetenv("MAPBOX_ACCESS_TOKEN"));
+ settings.setCacheDatabasePath("/tmp/mbgl-cache.db");
+ settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024);
+
+ MapWindow window(settings);
+
+ window.resize(800, 600);
+ window.show();
+
+ if (argc == 2 && QString("--test") == argv[1]) {
+ window.selfTest();
+ }
+
+ return app.exec();
+}
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
new file mode 100644
index 0000000000..35f56a0bef
--- /dev/null
+++ b/platform/qt/app/mapwindow.cpp
@@ -0,0 +1,167 @@
+#include "mapwindow.hpp"
+
+#include <QApplication>
+#include <QDebug>
+#include <QIcon>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QString>
+
+int kAnimationDuration = 10000;
+
+MapWindow::MapWindow(const QMapboxGLSettings &settings)
+ : m_map(nullptr, settings)
+ , m_bearingAnimation(&m_map, "bearing")
+ , m_zoomAnimation(&m_map, "zoom")
+{
+ connect(&m_map, SIGNAL(needsRendering()), this, SLOT(updateGL()));
+
+ // Set default location to Helsinki.
+ m_map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
+
+ changeStyle();
+
+ connect(&m_zoomAnimation, SIGNAL(finished()), this, SLOT(animationFinished()));
+ connect(&m_zoomAnimation, SIGNAL(valueChanged(const QVariant&)), this, SLOT(animationValueChanged()));
+
+ setWindowIcon(QIcon(":icon.png"));
+}
+
+void MapWindow::selfTest()
+{
+ m_bearingAnimation.setDuration(kAnimationDuration);
+ m_bearingAnimation.setEndValue(m_map.bearing() + 360 * 4);
+ m_bearingAnimation.start();
+
+ m_zoomAnimation.setDuration(kAnimationDuration);
+ m_zoomAnimation.setEndValue(m_map.zoom() + 3);
+ m_zoomAnimation.start();
+}
+
+void MapWindow::animationFinished()
+{
+ qDebug() << "Animation ticks/s: " << m_animationTicks / static_cast<float>(kAnimationDuration) * 1000.;
+ qDebug() << "Frame draws/s: " << m_frameDraws / static_cast<float>(kAnimationDuration) * 1000.;
+
+ qApp->quit();
+}
+
+void MapWindow::animationValueChanged()
+{
+ m_animationTicks++;
+}
+
+void MapWindow::changeStyle()
+{
+ static uint8_t currentStyleIndex;
+
+ auto& styles = QMapbox::defaultStyles();
+
+ m_map.setStyleURL(styles[currentStyleIndex].first);
+ setWindowTitle(QString("Mapbox GL: ") + styles[currentStyleIndex].second);
+
+ if (++currentStyleIndex == styles.size()) {
+ currentStyleIndex = 0;
+ }
+}
+
+void MapWindow::keyPressEvent(QKeyEvent *ev)
+{
+ switch (ev->key()) {
+ case Qt::Key_S:
+ changeStyle();
+ break;
+ case Qt::Key_Tab:
+ m_map.cycleDebugOptions();
+ break;
+ default:
+ break;
+ }
+
+ ev->accept();
+}
+
+void MapWindow::mousePressEvent(QMouseEvent *ev)
+{
+#if QT_VERSION < 0x050000
+ m_lastPos = ev->posF();
+#else
+ m_lastPos = ev->localPos();
+#endif
+
+ if (ev->type() == QEvent::MouseButtonPress) {
+ if (ev->buttons() == (Qt::LeftButton | Qt::RightButton)) {
+ changeStyle();
+ }
+ }
+
+ if (ev->type() == QEvent::MouseButtonDblClick) {
+ if (ev->buttons() == Qt::LeftButton) {
+ m_map.scaleBy(2.0, m_lastPos);
+ } else if (ev->buttons() == Qt::RightButton) {
+ m_map.scaleBy(0.5, m_lastPos);
+ }
+ }
+
+ ev->accept();
+}
+
+void MapWindow::mouseMoveEvent(QMouseEvent *ev)
+{
+#if QT_VERSION < 0x050000
+ QPointF delta = ev->posF() - m_lastPos;
+#else
+ QPointF delta = ev->localPos() - m_lastPos;
+#endif
+
+ if (!delta.isNull()) {
+ if (ev->buttons() == Qt::LeftButton) {
+ m_map.moveBy(delta);
+ } else if (ev->buttons() == Qt::RightButton) {
+#if QT_VERSION < 0x050000
+ m_map.rotateBy(m_lastPos, ev->posF());
+#else
+ m_map.rotateBy(m_lastPos, ev->localPos());
+#endif
+ }
+ }
+
+#if QT_VERSION < 0x050000
+ m_lastPos = ev->posF();
+#else
+ m_lastPos = ev->localPos();
+#endif
+ ev->accept();
+}
+
+void MapWindow::wheelEvent(QWheelEvent *ev)
+{
+ if (ev->orientation() == Qt::Horizontal) {
+ return;
+ }
+
+ float factor = ev->delta() / 1200.;
+ if (ev->delta() < 0) {
+ factor = factor > -1 ? factor : 1 / factor;
+ }
+
+ m_map.scaleBy(1 + factor, ev->pos());
+ ev->accept();
+}
+
+void MapWindow::initializeGL()
+{
+ QMapbox::initializeGLExtensions();
+}
+
+void MapWindow::resizeGL(int w, int h)
+{
+ QSize size(w, h);
+ m_map.resize(size);
+}
+
+void MapWindow::paintGL()
+{
+ m_frameDraws++;
+ m_map.render();
+}
diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp
new file mode 100644
index 0000000000..452d302eb3
--- /dev/null
+++ b/platform/qt/app/mapwindow.hpp
@@ -0,0 +1,49 @@
+#ifndef MAPWINDOW_H
+#define MAPWINDOW_H
+
+#include <QGLWidget>
+#include <QMapboxGL>
+#include <QPropertyAnimation>
+
+class QKeyEvent;
+class QMouseEvent;
+class QWheelEvent;
+
+class MapWindow : public QGLWidget
+{
+ Q_OBJECT
+
+public:
+ MapWindow(const QMapboxGLSettings &);
+
+ void selfTest();
+
+protected slots:
+ void animationValueChanged();
+ void animationFinished();
+
+private:
+ void changeStyle();
+
+ // QGLWidget implementation.
+ void keyPressEvent(QKeyEvent *ev) final;
+ void mousePressEvent(QMouseEvent *ev) final;
+ void mouseMoveEvent(QMouseEvent *ev) final;
+ void wheelEvent(QWheelEvent *ev) final;
+
+ void initializeGL() final;
+ void resizeGL(int w, int h) final;
+ void paintGL() final;
+
+ QPointF m_lastPos;
+
+ QMapboxGL m_map;
+
+ QPropertyAnimation m_bearingAnimation;
+ QPropertyAnimation m_zoomAnimation;
+
+ unsigned m_animationTicks = 0;
+ unsigned m_frameDraws = 0;
+};
+
+#endif
diff --git a/platform/qt/app/qmapboxgl.gypi b/platform/qt/app/qmapboxgl.gypi
new file mode 100644
index 0000000000..62a2939ad2
--- /dev/null
+++ b/platform/qt/app/qmapboxgl.gypi
@@ -0,0 +1,55 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'qt-app',
+ 'product_name': 'qmapboxgl',
+ 'type': 'executable',
+
+ 'includes': [
+ '../qt.gypi',
+ ],
+
+ 'dependencies': [
+ 'qt-lib',
+ ],
+
+ 'sources': [
+ 'main.cpp',
+ 'mapwindow.cpp',
+ 'mapwindow.hpp',
+ ],
+
+ 'include_dirs': [
+ '../include',
+ ],
+
+ 'variables': {
+ 'cflags': [
+ '<@(opengl_cflags)',
+ '<@(qt_core_cflags)',
+ '<@(qt_gui_cflags)',
+ '<@(qt_opengl_cflags)',
+ '-fPIC',
+ ],
+ 'ldflags': [
+ '<@(opengl_ldflags)',
+ '<@(qt_core_ldflags)',
+ '<@(qt_gui_ldflags)',
+ '<@(qt_opengl_ldflags)',
+ ],
+ },
+
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags)' ],
+ 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ },
+ }, {
+ 'cflags_cc': [ '<@(cflags)' ],
+ 'libraries': [ '<@(ldflags)' ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/platform/qt/bitrise-qt4.yml b/platform/qt/bitrise-qt4.yml
new file mode 100644
index 0000000000..83c222a9db
--- /dev/null
+++ b/platform/qt/bitrise-qt4.yml
@@ -0,0 +1,56 @@
+---
+format_version: 1.0.0
+default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
+trigger_map:
+- pattern: "*"
+ is_pull_request_allowed: true
+ workflow: primary
+workflows:
+ primary:
+ steps:
+ - script:
+ title: Check for skipping CI
+ inputs:
+ - content: |-
+ #!/bin/bash
+
+ if [[ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[skip ci\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[ci skip\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[skip ci\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[ci skip\]/p')" ]]; then
+ envman add --key SKIPCI --value true
+ else
+ envman add --key SKIPCI --value false
+ fi
+ - script:
+ title: Run build
+ run_if: '{{enveq "SKIPCI" "false"}}'
+ inputs:
+ - content: |-
+ #!/bin/bash
+ set -eu -o pipefail
+ brew install qt
+ brew link qt
+ brew linkapps qt
+ export BUILDTYPE=Debug
+ make qt-app
+ make test-qt
+ - is_debug: 'yes'
+ - slack:
+ title: Post to Slack
+ run_if: '{{enveq "SKIPCI" "false"}}'
+ inputs:
+ - webhook_url: "$SLACK_HOOK_URL"
+ - channel: "#gl-bots"
+ - from_username: 'Bitrise Qt4 OS X'
+ - from_username_on_error: 'Bitrise Qt4 OS X'
+ - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ by ${GIT_CLONE_COMMIT_COMMITER_NAME}
+ passed'
+ - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ by ${GIT_CLONE_COMMIT_COMMITER_NAME}
+ failed'
+ - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
+ - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
diff --git a/platform/qt/bitrise-qt5.yml b/platform/qt/bitrise-qt5.yml
new file mode 100644
index 0000000000..1e767b3169
--- /dev/null
+++ b/platform/qt/bitrise-qt5.yml
@@ -0,0 +1,57 @@
+---
+format_version: 1.0.0
+default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
+trigger_map:
+- pattern: "*"
+ is_pull_request_allowed: true
+ workflow: primary
+workflows:
+ primary:
+ steps:
+ - script:
+ title: Check for skipping CI
+ inputs:
+ - content: |-
+ #!/bin/bash
+
+ if [[ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[skip ci\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[ci skip\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[skip ci\]/p')" ||
+ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[ci skip\]/p')" ]]; then
+ envman add --key SKIPCI --value true
+ else
+ envman add --key SKIPCI --value false
+ fi
+ - script:
+ title: Run build
+ run_if: '{{enveq "SKIPCI" "false"}}'
+ inputs:
+ - content: |-
+ #!/bin/bash
+ set -eu -o pipefail
+ brew install qt5
+ brew link qt5 --force
+ brew linkapps qt5
+ export BUILDTYPE=Debug
+ make qt-app
+ make qt-qml-app
+ make test-qt
+ - is_debug: 'yes'
+ - slack:
+ title: Post to Slack
+ run_if: '{{enveq "SKIPCI" "false"}}'
+ inputs:
+ - webhook_url: "$SLACK_HOOK_URL"
+ - channel: "#gl-bots"
+ - from_username: 'Bitrise Qt5 OS X'
+ - from_username_on_error: 'Bitrise Qt5 OS X'
+ - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ by ${GIT_CLONE_COMMIT_COMMITER_NAME}
+ passed'
+ - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ by ${GIT_CLONE_COMMIT_COMMITER_NAME}
+ failed'
+ - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
+ - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
diff --git a/platform/qt/include/QMapbox b/platform/qt/include/QMapbox
new file mode 100644
index 0000000000..a8479c09aa
--- /dev/null
+++ b/platform/qt/include/QMapbox
@@ -0,0 +1 @@
+#include "qmapbox.hpp"
diff --git a/platform/qt/include/QMapboxGL b/platform/qt/include/QMapboxGL
new file mode 100644
index 0000000000..15b55a9abe
--- /dev/null
+++ b/platform/qt/include/QMapboxGL
@@ -0,0 +1 @@
+#include "qmapboxgl.hpp"
diff --git a/platform/qt/include/QQuickMapboxGL b/platform/qt/include/QQuickMapboxGL
new file mode 100644
index 0000000000..db109a1d3a
--- /dev/null
+++ b/platform/qt/include/QQuickMapboxGL
@@ -0,0 +1 @@
+#include "qquickmapboxgl.hpp"
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
new file mode 100644
index 0000000000..3200da2729
--- /dev/null
+++ b/platform/qt/include/qmapbox.hpp
@@ -0,0 +1,67 @@
+#ifndef QMAPBOX_H
+#define QMAPBOX_H
+
+#include <QList>
+#include <QPair>
+#include <QVariant>
+#include <QString>
+
+// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
+
+namespace QMapbox {
+
+typedef QPair<double, double> Coordinate;
+typedef QList<Coordinate> Coordinates;
+typedef QList<Coordinates> CoordinateSegments;
+
+typedef QPair<Coordinate, double> CoordinateZoom;
+
+typedef quint32 AnnotationID;
+typedef QList<AnnotationID> AnnotationIDs;
+
+typedef QPair<Coordinate, QString> PointAnnotation;
+
+// FIXME: We need to add support for custom style properties
+typedef QPair<CoordinateSegments, QString> ShapeAnnotation;
+
+enum NetworkMode {
+ Online, // Default
+ Offline,
+};
+
+struct Q_DECL_EXPORT CameraOptions {
+ QVariant center; // Coordinate
+ QVariant anchor; // QPointF
+ QVariant zoom; // double
+ QVariant angle; // double
+ QVariant pitch; // double
+};
+
+Q_DECL_EXPORT QList<QPair<QString, QString> >& defaultStyles();
+
+Q_DECL_EXPORT NetworkMode networkMode();
+Q_DECL_EXPORT void setNetworkMode(NetworkMode);
+
+// This struct is a 1:1 copy of mbgl::CustomLayerRenderParameters.
+struct Q_DECL_EXPORT CustomLayerRenderParameters {
+ double width;
+ double height;
+ double latitude;
+ double longitude;
+ double zoom;
+ double bearing;
+ double pitch;
+ double altitude;
+};
+
+typedef void (*CustomLayerInitializeFunction)(void* context) ;
+typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRenderParameters&);
+typedef void (*CustomLayerDeinitializeFunction)(void* context);
+
+Q_DECL_EXPORT void initializeGLExtensions();
+
+}
+
+Q_DECLARE_METATYPE(QMapbox::Coordinate);
+
+#endif // QMAPBOX_H
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
new file mode 100644
index 0000000000..8ce6b02f47
--- /dev/null
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -0,0 +1,220 @@
+#ifndef QMAPBOXGL_H
+#define QMAPBOXGL_H
+
+#include <QMapbox>
+#include <QObject>
+#include <QPointF>
+
+class QImage;
+class QMargins;
+class QSize;
+class QString;
+class QStringList;
+
+class QMapboxGLPrivate;
+
+// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
+
+class Q_DECL_EXPORT QMapboxGLSettings
+{
+public:
+ QMapboxGLSettings();
+
+ enum MapMode {
+ ContinuousMap = 0,
+ StillMap
+ };
+
+ enum GLContextMode {
+ UniqueGLContext = 0,
+ SharedGLContext
+ };
+
+ enum ConstrainMode {
+ NoConstrain = 0,
+ ConstrainHeightOnly,
+ ConstrainWidthAndHeight
+ };
+
+ enum ViewportMode {
+ DefaultViewport = 0,
+ FlippedYViewport
+ };
+
+ MapMode mapMode() const;
+ void setMapMode(MapMode);
+
+ GLContextMode contextMode() const;
+ void setContextMode(GLContextMode);
+
+ ConstrainMode constrainMode() const;
+ void setConstrainMode(ConstrainMode);
+
+ ViewportMode viewportMode() const;
+ void setViewportMode(ViewportMode);
+
+ unsigned cacheDatabaseMaximumSize() const;
+ void setCacheDatabaseMaximumSize(unsigned);
+
+ QString cacheDatabasePath() const;
+ void setCacheDatabasePath(const QString &);
+
+ QString assetPath() const;
+ void setAssetPath(const QString &);
+
+ QString accessToken() const;
+ void setAccessToken(const QString &);
+
+private:
+ MapMode m_mapMode;
+ GLContextMode m_contextMode;
+ ConstrainMode m_constrainMode;
+ ViewportMode m_viewportMode;
+
+ unsigned m_cacheMaximumSize;
+ QString m_cacheDatabasePath;
+ QString m_assetPath;
+ QString m_accessToken;
+};
+
+class Q_DECL_EXPORT QMapboxGL : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(double latitude READ latitude WRITE setLatitude)
+ Q_PROPERTY(double longitude READ longitude WRITE setLongitude)
+ Q_PROPERTY(double zoom READ zoom WRITE setZoom)
+ Q_PROPERTY(double bearing READ bearing WRITE setBearing)
+ Q_PROPERTY(double pitch READ pitch WRITE setPitch)
+ Q_ENUMS(MapChange)
+
+public:
+ // Determines the orientation of the map.
+ enum NorthOrientation {
+ NorthUpwards, // Default
+ NorthRightwards,
+ NorthDownwards,
+ NorthLeftwards,
+ };
+
+ // Reflects mbgl::MapChange.
+ enum MapChange {
+ MapChangeRegionWillChange = 0,
+ MapChangeRegionWillChangeAnimated,
+ MapChangeRegionIsChanging,
+ MapChangeRegionDidChange,
+ MapChangeRegionDidChangeAnimated,
+ MapChangeWillStartLoadingMap,
+ MapChangeDidFinishLoadingMap,
+ MapChangeDidFailLoadingMap,
+ MapChangeWillStartRenderingFrame,
+ MapChangeDidFinishRenderingFrame,
+ MapChangeDidFinishRenderingFrameFullyRendered,
+ MapChangeWillStartRenderingMap,
+ MapChangeDidFinishRenderingMap,
+ MapChangeDidFinishRenderingMapFullyRendered
+ };
+
+ QMapboxGL(QObject *parent = 0, const QMapboxGLSettings& = QMapboxGLSettings());
+ virtual ~QMapboxGL();
+
+ void cycleDebugOptions();
+
+ QString styleJSON() const;
+ QString styleURL() const;
+
+ void setStyleJSON(const QString &);
+ void setStyleURL(const QString &);
+
+ double latitude() const;
+ void setLatitude(double latitude);
+
+ double longitude() const;
+ void setLongitude(double longitude);
+
+ double scale() const;
+ void setScale(double scale, const QPointF &center = QPointF());
+
+ double zoom() const;
+ void setZoom(double zoom);
+
+ double minimumZoom() const;
+ double maximumZoom() const;
+
+ double bearing() const;
+ void setBearing(double degrees);
+ void setBearing(double degrees, const QPointF &center);
+
+ double pitch() const;
+ void setPitch(double pitch);
+
+ NorthOrientation northOrientation() const;
+ void setNorthOrientation(NorthOrientation);
+
+ QMapbox::Coordinate coordinate() const;
+ void setCoordinate(const QMapbox::Coordinate &);
+ void setCoordinateZoom(const QMapbox::Coordinate &, double zoom);
+
+ void jumpTo(const QMapbox::CameraOptions&);
+
+ void setGestureInProgress(bool inProgress);
+
+ void addClass(const QString &);
+ void removeClass(const QString &);
+ bool hasClass(const QString &) const;
+ void setClasses(const QStringList &);
+ QStringList getClasses() const;
+
+ QMapbox::AnnotationID addPointAnnotation(const QMapbox::PointAnnotation &);
+ QMapbox::AnnotationID addShapeAnnotation(const QMapbox::ShapeAnnotation &);
+
+ void updatePointAnnotation(QMapbox::AnnotationID, const QMapbox::PointAnnotation &);
+
+ void removeAnnotation(QMapbox::AnnotationID);
+
+ bool isRotating() const;
+ bool isScaling() const;
+ bool isPanning() const;
+ bool isFullyLoaded() const;
+
+ void moveBy(const QPointF &offset);
+ void scaleBy(double scale, const QPointF &center = QPointF());
+ void rotateBy(const QPointF &first, const QPointF &second);
+
+ void resize(const QSize &size);
+
+ void addAnnotationIcon(const QString &name, const QImage &sprite);
+
+ QPointF pixelForCoordinate(const QMapbox::Coordinate &) const;
+ QMapbox::Coordinate coordinateForPixel(const QPointF &) const;
+
+ QMapbox::CoordinateZoom coordinateZoomForBounds(const QMapbox::Coordinate &sw, QMapbox::Coordinate &ne) const;
+ QMapbox::CoordinateZoom coordinateZoomForBounds(const QMapbox::Coordinate &sw, QMapbox::Coordinate &ne, double bearing, double pitch);
+
+ void setMargins(const QMargins &margins);
+ QMargins margins() const;
+
+ void addCustomLayer(const QString &id,
+ QMapbox::CustomLayerInitializeFunction,
+ QMapbox::CustomLayerRenderFunction,
+ QMapbox::CustomLayerDeinitializeFunction,
+ void* context,
+ char* before = NULL);
+ void removeCustomLayer(const QString& id);
+
+public slots:
+ void render();
+ void connectionEstablished();
+
+signals:
+ void needsRendering();
+ void mapChanged(QMapboxGL::MapChange);
+
+private:
+ Q_DISABLE_COPY(QMapboxGL)
+
+ QMapboxGLPrivate *d_ptr;
+};
+
+Q_DECLARE_METATYPE(QMapboxGL::MapChange);
+
+#endif // QMAPBOXGL_H
diff --git a/platform/qt/include/qquickmapboxgl.hpp b/platform/qt/include/qquickmapboxgl.hpp
new file mode 100644
index 0000000000..6c2847fe59
--- /dev/null
+++ b/platform/qt/include/qquickmapboxgl.hpp
@@ -0,0 +1,133 @@
+#ifndef QQUICKMAPBOXGL_H
+#define QQUICKMAPBOXGL_H
+
+#include <QColor>
+#include <QGeoCoordinate>
+#include <QGeoServiceProvider>
+#include <QGeoShape>
+#include <QPointF>
+#include <QQuickFramebufferObject>
+
+class QDeclarativeGeoServiceProvider;
+class QQuickItem;
+
+class Q_DECL_EXPORT QQuickMapboxGL : public QQuickFramebufferObject
+{
+ Q_OBJECT
+
+ // Map QML Type interface implementation.
+ Q_ENUMS(QGeoServiceProvider::Error)
+ Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged)
+ Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged)
+ Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged)
+ Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged)
+ Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged)
+ Q_PROPERTY(QGeoServiceProvider::Error error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
+ Q_PROPERTY(QGeoShape visibleRegion READ visibleRegion WRITE setVisibleRegion)
+ Q_PROPERTY(bool copyrightsVisible READ copyrightsVisible WRITE setCopyrightsVisible NOTIFY copyrightsVisibleChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+
+ // MapboxGL QML Type interface.
+ Q_PROPERTY(QString style READ style WRITE setStyle NOTIFY styleChanged)
+ Q_PROPERTY(qreal bearing READ bearing WRITE setBearing NOTIFY bearingChanged)
+ Q_PROPERTY(qreal pitch READ pitch WRITE setPitch NOTIFY pitchChanged)
+
+public:
+ QQuickMapboxGL(QQuickItem *parent = 0);
+ virtual ~QQuickMapboxGL();
+
+ // QQuickFramebufferObject implementation.
+ virtual Renderer *createRenderer() const Q_DECL_FINAL;
+
+ // Map QML Type interface implementation.
+ void setPlugin(QDeclarativeGeoServiceProvider *plugin);
+ QDeclarativeGeoServiceProvider *plugin() const;
+
+ void setMinimumZoomLevel(qreal minimumZoomLevel);
+ qreal minimumZoomLevel() const;
+
+ void setMaximumZoomLevel(qreal maximumZoomLevel);
+ qreal maximumZoomLevel() const;
+
+ void setZoomLevel(qreal zoomLevel);
+ qreal zoomLevel() const;
+
+ QGeoCoordinate center() const;
+
+ QGeoServiceProvider::Error error() const;
+ QString errorString() const;
+
+ void setVisibleRegion(const QGeoShape &shape);
+ QGeoShape visibleRegion() const;
+
+ void setCopyrightsVisible(bool visible);
+ bool copyrightsVisible() const;
+
+ void setColor(const QColor &color);
+ QColor color() const;
+
+ Q_INVOKABLE void pan(int dx, int dy);
+
+ // MapboxGL QML Type interface.
+ void setStyle(const QString &style);
+ QString style() const;
+
+ void setBearing(qreal bearing);
+ qreal bearing() const;
+
+ void setPitch(qreal pitch);
+ qreal pitch() const;
+
+ QPointF swapPan();
+
+ enum SyncState {
+ NothingNeedsSync = 0,
+ ZoomNeedsSync = 1 << 0,
+ CenterNeedsSync = 1 << 1,
+ StyleNeedsSync = 1 << 2,
+ PanNeedsSync = 1 << 3,
+ BearingNeedsSync = 1 << 4,
+ PitchNeedsSync = 1 << 5,
+ };
+
+ int swapSyncState();
+
+signals:
+ void minimumZoomLevelChanged();
+ void maximumZoomLevelChanged();
+ void zoomLevelChanged(qreal zoomLevel);
+ void centerChanged(const QGeoCoordinate &coordinate);
+
+ // Compatibility with Map QML Type, but no-op.
+ void pluginChanged(QDeclarativeGeoServiceProvider *plugin);
+ void errorChanged();
+ void copyrightLinkActivated(const QString &link);
+ void copyrightsVisibleChanged(bool visible);
+ void colorChanged(const QColor &color);
+
+ void styleChanged();
+ void bearingChanged(qreal angle);
+ void pitchChanged(qreal angle);
+
+public slots:
+ void setCenter(const QGeoCoordinate &center);
+
+private:
+ qreal m_minimumZoomLevel = 0;
+ qreal m_maximumZoomLevel = 20;
+ qreal m_zoomLevel = 20;
+
+ QPointF m_pan = QPointF(0, 0);
+
+ QGeoCoordinate m_center;
+ QGeoShape m_visibleRegion;
+
+ QString m_style;
+ qreal m_bearing = 0;
+ qreal m_pitch = 0;
+
+ int m_syncState = NothingNeedsSync;
+};
+
+#endif // QQUICKMAPBOXGL_H
diff --git a/platform/qt/platform.gyp b/platform/qt/platform.gyp
new file mode 100644
index 0000000000..2a0e1c57bc
--- /dev/null
+++ b/platform/qt/platform.gyp
@@ -0,0 +1,217 @@
+{
+ 'variables': {
+ 'loop_lib': 'qt',
+ 'conditions': [
+ ['OS == "mac"', {
+ 'headless_lib': 'cgl',
+ }, {
+ 'headless_lib': 'glx',
+ }]
+ ],
+ 'qtlibversion': '1.0.0',
+ 'coverage': 0,
+ },
+ 'conditions': [
+ ['<(qt_version_major) == 5', {
+ 'includes': [ 'qmlapp/qquickmapboxgl.gypi' ],
+ }],
+ ],
+ 'includes': [
+ 'app/qmapboxgl.gypi',
+ '../../mbgl.gypi',
+ '../../test/test.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'platform-lib',
+ 'product_name': 'mbgl-platform-qt',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'hard_dependency': 1,
+
+ 'dependencies': [
+ 'core',
+ ],
+
+ 'includes': [
+ 'qt.gypi',
+ ],
+
+ 'sources': [
+ '../default/asset_file_source.cpp',
+ '../default/default_file_source.cpp',
+ '../default/log_stderr.cpp',
+ '../default/mbgl/storage/offline.cpp',
+ '../default/mbgl/storage/offline_database.cpp',
+ '../default/mbgl/storage/offline_download.cpp',
+ '../default/online_file_source.cpp',
+ '../default/sqlite3.cpp',
+ '../default/string_stdlib.cpp',
+ '../default/thread.cpp',
+ 'include/qmapbox.hpp',
+ 'include/qmapboxgl.hpp',
+ 'include/qquickmapboxgl.hpp',
+ 'qmapbox.qrc',
+ 'src/async_task.cpp',
+ 'src/async_task_impl.hpp',
+ 'src/http_file_source.cpp',
+ 'src/http_file_source.hpp',
+ 'src/http_request.cpp',
+ 'src/http_request.hpp',
+ 'src/image.cpp',
+ 'src/qmapbox.cpp',
+ 'src/qmapboxgl.cpp',
+ 'src/qmapboxgl_p.hpp',
+ 'src/qquickmapboxgl.cpp',
+ 'src/qquickmapboxglrenderer.cpp',
+ 'src/qquickmapboxglrenderer.hpp',
+ 'src/run_loop.cpp',
+ 'src/run_loop_impl.hpp',
+ 'src/timer.cpp',
+ 'src/timer_impl.hpp',
+ ],
+
+ 'variables': {
+ 'cflags': [
+ '<@(boost_cflags)',
+ '<@(nunicode_cflags)',
+ '<@(opengl_cflags)',
+ '<@(qt_core_cflags)',
+ '<@(qt_gui_cflags)',
+ '<@(qt_network_cflags)',
+ '<@(rapidjson_cflags)',
+ '<@(sqlite_cflags)',
+ '-fPIC',
+ ],
+ 'ldflags': [
+ '<@(nunicode_ldflags)',
+ '<@(opengl_ldflags)',
+ '<@(qt_core_ldflags)',
+ '<@(qt_gui_ldflags)',
+ '<@(qt_network_ldflags)',
+ '<@(sqlite_ldflags)',
+ '<@(zlib_ldflags)',
+ ],
+ 'libraries': [
+ '<@(nunicode_static_libs)',
+ '<@(sqlite_static_libs)',
+ '<@(zlib_static_libs)',
+ ],
+ },
+
+ 'include_dirs': [
+ 'include',
+ '../default',
+ '../../include',
+ '../../src', # TODO: eliminate
+ ],
+
+ 'conditions': [
+ ['<(qt_image_decoders) == 0', {
+ 'sources': [
+ '../default/jpeg_reader.cpp',
+ '../default/webp_reader.cpp',
+ ],
+ 'variables': {
+ 'cflags': [
+ '<@(libjpeg-turbo_cflags)',
+ '<@(webp_cflags)',
+ ],
+ 'ldflags': [
+ '<@(libjpeg-turbo_ldflags)',
+ '<@(webp_ldflags)',
+ ],
+ 'libraries': [
+ '<@(libjpeg-turbo_static_libs)',
+ '<@(webp_static_libs)',
+ ],
+ },
+ }],
+ ['<(qt_version_major) == 4', {
+ 'variables': {
+ 'cflags': [
+ '<@(qt_opengl_cflags)',
+ # Qt4 generates code with unused variables.
+ '-Wno-unused-variable',
+ ],
+ 'ldflags': [
+ '<@(qt_opengl_ldflags)',
+ ],
+ },
+ }],
+ ['<(qt_version_major) == 5', {
+ 'variables': {
+ 'cflags': [
+ '<@(qt_location_cflags)',
+ '<@(qt_positioning_cflags)',
+ '<@(qt_quick_cflags)',
+ ],
+ 'ldflags': [
+ '<@(qt_location_ldflags)',
+ '<@(qt_positioning_ldflags)',
+ '<@(qt_quick_ldflags)',
+ ],
+ },
+ }, {
+ 'sources/': [ [ 'exclude', 'qquick*' ] ],
+ }],
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags)' ],
+ }
+ }, {
+ 'cflags_cc': [ '<@(cflags)' ],
+ }]
+ ],
+
+ 'link_settings': {
+ 'conditions': [
+ ['OS == "mac"', {
+ 'libraries': [ '<@(libraries)' ],
+ 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
+ }, {
+ 'libraries': [ '<@(libraries)', '<@(ldflags)' ],
+ }]
+ ],
+ },
+ },
+ {
+ 'target_name': 'qt-lib',
+ 'product_name': 'qmapboxgl',
+ 'type': 'shared_library',
+ 'product_extension': 'so.<(qtlibversion)',
+
+ 'includes': [
+ 'qt.gypi',
+ ],
+
+ 'dependencies': [
+ 'platform-lib',
+ ],
+
+ 'link_settings': {
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '-all_load' ],
+ 'LD_DYLIB_INSTALL_NAME': '@executable_path/libqmapboxgl.so.<(qtlibversion)',
+ }
+ }]
+ ],
+ },
+ },
+ {
+ 'target_name': 'test',
+ 'type': 'executable',
+
+ 'dependencies': [
+ 'test-lib',
+ 'platform-lib',
+ ],
+
+ 'sources': [
+ '../../test/src/main.cpp',
+ ],
+ },
+ ],
+}
diff --git a/platform/qt/qmapbox.qrc b/platform/qt/qmapbox.qrc
new file mode 100644
index 0000000000..de0c6cdced
--- /dev/null
+++ b/platform/qt/qmapbox.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file alias="ca-bundle.crt">../../common/ca-bundle.crt</file>
+ <file alias="icon.png">../../common/icon.png</file>
+</qresource>
+</RCC>
diff --git a/platform/qt/qmlapp/main.cpp b/platform/qt/qmlapp/main.cpp
new file mode 100644
index 0000000000..1b07c699cc
--- /dev/null
+++ b/platform/qt/qmlapp/main.cpp
@@ -0,0 +1,22 @@
+#include <QGuiApplication>
+#include <QIcon>
+#include <QQmlApplicationEngine>
+#include <qqml.h>
+
+#include <QQuickMapboxGL>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+#if QT_VERSION >= 0x050300
+ app.setWindowIcon(QIcon(":icon.png"));
+#endif
+
+ qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "QQuickMapboxGL");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/platform/qt/qmlapp/main.qml b/platform/qt/qmlapp/main.qml
new file mode 100644
index 0000000000..a8629e94f6
--- /dev/null
+++ b/platform/qt/qmlapp/main.qml
@@ -0,0 +1,220 @@
+import QtGraphicalEffects 1.0
+import QtPositioning 5.0
+import QtQuick 2.0
+import QtQuick.Controls 1.0
+import QtQuick.Layouts 1.0
+
+import QQuickMapboxGL 1.0
+
+ApplicationWindow {
+ title: "Mapbox GL QML example"
+ width: 1024
+ height: 768
+ visible: true
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: 50
+ spacing: anchors.margins
+
+ Flipable {
+ id: flipable
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ transform: Rotation {
+ origin.x: flipable.width / 2
+ origin.y: flipable.height / 2
+
+ axis.x: 0; axis.y: 1; axis.z: 0
+
+ angle: flipSlider.value
+ }
+
+ front: Rectangle {
+ anchors.fill: parent
+
+ QQuickMapboxGL {
+ id: mapStreets
+
+ anchors.fill: parent
+ visible: false
+
+ style: "mapbox://styles/mapbox/streets-v9"
+
+ center: QtPositioning.coordinate(60.170448, 24.942046) // Helsinki
+ zoomLevel: 14
+ minimumZoomLevel: 0
+ maximumZoomLevel: 20
+
+ bearing: bearingSlider.value
+ pitch: pitchSlider.value
+
+ color: "red"
+ copyrightsVisible: true
+
+ Image {
+ id: logo
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+
+ opacity: .75
+
+ sourceSize.width: 80
+ sourceSize.height: 80
+
+ source: "icon.png"
+ }
+ }
+
+ Rectangle {
+ id: maskStreets
+
+ anchors.fill: parent
+ anchors.margins: 20
+
+ radius: 30
+ clip: true
+ visible: false
+ }
+
+ OpacityMask {
+ anchors.fill: maskStreets
+
+ source: mapStreets
+ maskSource: maskStreets
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ property var lastX: 0
+ property var lastY: 0
+
+ onWheel: mapStreets.zoomLevel += 0.2 * wheel.angleDelta.y / 120
+
+ onPressed: {
+ lastX = mouse.x
+ lastY = mouse.y
+ }
+
+ onPositionChanged: {
+ mapStreets.pan(mouse.x - lastX, mouse.y - lastY)
+
+ lastX = mouse.x
+ lastY = mouse.y
+ }
+ }
+ }
+
+ back: Rectangle {
+ anchors.fill: parent
+
+ QQuickMapboxGL {
+ id: mapSatellite
+
+ anchors.fill: parent
+ visible: false
+
+ style: "mapbox://styles/mapbox/satellite-streets-v9"
+
+ center: mapStreets.center
+ zoomLevel: mapStreets.zoomLevel
+ minimumZoomLevel: mapStreets.minimumZoomLevel
+ maximumZoomLevel: mapStreets.maximumZoomLevel
+
+ bearing: mapStreets.bearing
+ pitch: mapStreets.pitch
+
+ Image {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: logo.anchors.margins
+
+ opacity: logo.opacity
+
+ sourceSize.width: logo.sourceSize.width
+ sourceSize.height: logo.sourceSize.height
+
+ source: logo.source
+ }
+ }
+
+ Rectangle {
+ id: maskSatellite
+
+ anchors.fill: parent
+ anchors.margins: maskStreets.anchors.margins
+
+ radius: maskStreets.radius
+ clip: true
+ visible: false
+ }
+
+ OpacityMask {
+ anchors.fill: maskSatellite
+
+ source: mapSatellite
+ maskSource: maskSatellite
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ property var lastX: 0
+ property var lastY: 0
+
+ onWheel: mapStreets.zoomLevel += 0.2 * wheel.angleDelta.y / 120
+
+ onPressed: {
+ lastX = mouse.x
+ lastY = mouse.y
+ }
+
+ onPositionChanged: {
+ mapStreets.pan(mouse.x - lastX, mouse.y - lastY)
+
+ lastX = mouse.x
+ lastY = mouse.y
+ }
+ }
+ }
+ }
+
+ Slider {
+ id: bearingSlider
+
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
+
+ value: 0
+ minimumValue: 0
+ maximumValue: 180
+ }
+
+ Slider {
+ id: pitchSlider
+
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
+
+ value: 0
+ minimumValue: 0
+ maximumValue: 60
+ }
+
+ Slider {
+ id: flipSlider
+
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
+
+ value: 0
+ minimumValue: 0
+ maximumValue: 180
+ }
+ }
+}
diff --git a/platform/qt/qmlapp/qml.qrc b/platform/qt/qmlapp/qml.qrc
new file mode 100644
index 0000000000..5f6483ac33
--- /dev/null
+++ b/platform/qt/qmlapp/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/platform/qt/qmlapp/qquickmapboxgl.gypi b/platform/qt/qmlapp/qquickmapboxgl.gypi
new file mode 100644
index 0000000000..7b539a52f2
--- /dev/null
+++ b/platform/qt/qmlapp/qquickmapboxgl.gypi
@@ -0,0 +1,60 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'qt-qml-app',
+ 'product_name': 'qquickmapboxgl',
+ 'type': 'executable',
+
+ 'includes': [
+ '../qt.gypi',
+ ],
+
+ 'dependencies': [
+ 'qt-lib',
+ ],
+
+ 'sources': [
+ 'main.cpp',
+ 'qml.qrc',
+ ],
+
+ 'include_dirs': [
+ '../include',
+ ],
+
+ 'variables': {
+ 'cflags': [
+ '<@(opengl_cflags)',
+ '<@(qt_core_cflags)',
+ '<@(qt_gui_cflags)',
+ '<@(qt_location_cflags)',
+ '<@(qt_positioning_cflags)',
+ '<@(qt_qml_cflags)',
+ '<@(qt_quick_cflags)',
+ '-fPIC',
+ ],
+ 'ldflags': [
+ '<@(opengl_ldflags)',
+ '<@(qt_core_ldflags)',
+ '<@(qt_gui_ldflags)',
+ '<@(qt_location_ldflags)',
+ '<@(qt_positioning_ldflags)',
+ '<@(qt_qml_ldflags)',
+ '<@(qt_quick_ldflags)',
+ ],
+ },
+
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags)' ],
+ 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ },
+ }, {
+ 'cflags_cc': [ '<@(cflags)' ],
+ 'libraries': [ '<@(ldflags)' ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/platform/qt/qt.gypi b/platform/qt/qt.gypi
new file mode 100644
index 0000000000..e98b3254b0
--- /dev/null
+++ b/platform/qt/qt.gypi
@@ -0,0 +1,20 @@
+{
+ 'rules': [
+ {
+ 'rule_name': 'MOC files',
+ 'extension': 'hpp',
+ 'process_outputs_as_sources': 1,
+ 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_DIRNAME)/moc_<(RULE_INPUT_ROOT).cpp' ],
+ 'action': [ '<(qt_moc)', '<(RULE_INPUT_PATH)', '-o', '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_DIRNAME)/moc_<(RULE_INPUT_ROOT).cpp' ],
+ 'message': 'Generating MOC <(RULE_INPUT_ROOT).cpp',
+ },
+ {
+ 'rule_name': 'QRC files',
+ 'extension': 'qrc',
+ 'process_outputs_as_sources': 1,
+ 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_DIRNAME)/qrc_<(RULE_INPUT_ROOT).cpp' ],
+ 'action': [ '<(qt_rcc)', '<(RULE_INPUT_PATH)', '-name', '<(RULE_INPUT_ROOT)', '-o', '<(SHARED_INTERMEDIATE_DIR)/<(RULE_INPUT_DIRNAME)/qrc_<(RULE_INPUT_ROOT).cpp' ],
+ 'message': 'Generating QRC <(RULE_INPUT_ROOT).cpp',
+ },
+ ],
+}
diff --git a/platform/qt/scripts/configure.sh b/platform/qt/scripts/configure.sh
new file mode 100644
index 0000000000..4b855f7c58
--- /dev/null
+++ b/platform/qt/scripts/configure.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+
+CXX11ABI=$(scripts/check-cxx11abi.sh)
+
+UNIQUE_RESOURCE_VERSION=dev
+PROTOZERO_VERSION=1.3.0
+BOOST_VERSION=1.60.0
+GEOMETRY_VERSION=0.5.0
+GEOJSONVT_VERSION=4.1.2${CXX11ABI:-}
+GTEST_VERSION=1.7.0${CXX11ABI:-}
+LIBJPEG_TURBO_VERSION=1.4.2
+NUNICODE_VERSION=1.6
+PIXELMATCH_VERSION=0.9.0
+RAPIDJSON_VERSION=1.0.2
+SQLITE_VERSION=3.9.1
+VARIANT_VERSION=1.1.0
+ZLIB_VERSION=system
+WEBP_VERSION=0.5.0
+EARCUT_VERSION=0.11
+
+function print_default_flags {
+ CONFIG+=" 'cflags': $(quote_flags -fvisibility=hidden),"$LN
+}
+
+if [ "$MASON_PLATFORM" == "osx" ]; then
+ function print_opengl_flags {
+ CONFIG+=" 'opengl_cflags%': [],"$LN
+ CONFIG+=" 'opengl_ldflags%': ['-framework OpenGL', '-framework CoreFoundation'],"$LN
+ }
+else
+ function print_opengl_flags {
+ CONFIG+=" 'opengl_cflags%': $(quote_flags $(pkg-config gl x11 --cflags)),"$LN
+ CONFIG+=" 'opengl_ldflags%': $(quote_flags $(pkg-config gl x11 --libs)),"$LN
+ }
+fi
+
+function print_qt_flags {
+ mason install Qt system
+
+ QT_VERSION_MAJOR=$(qmake -query QT_VERSION | cut -d. -f1)
+ CONFIG+=" 'qt_version_major%': ['${QT_VERSION_MAJOR}'],"$LN
+ CONFIG+=" 'qt_image_decoders%': [0],"$LN
+
+ CONFIG+=" 'qt_core_cflags%': $(quote_flags $(mason cflags Qt system "QtCore")),"$LN
+ CONFIG+=" 'qt_gui_cflags%': $(quote_flags $(mason cflags Qt system "QtGui")),"$LN
+ CONFIG+=" 'qt_opengl_cflags%': $(quote_flags $(mason cflags Qt system "QtOpenGL")),"$LN
+ CONFIG+=" 'qt_network_cflags%': $(quote_flags $(mason cflags Qt system "QtNetwork")),"$LN
+
+ CONFIG+=" 'qt_core_ldflags%': $(quote_flags $(mason ldflags Qt system "QtCore")),"$LN
+ CONFIG+=" 'qt_gui_ldflags%': $(quote_flags $(mason ldflags Qt system "QtGui")),"$LN
+ CONFIG+=" 'qt_opengl_ldflags%': $(quote_flags $(mason ldflags Qt system "QtOpenGL")),"$LN
+ CONFIG+=" 'qt_network_ldflags%': $(quote_flags $(mason ldflags Qt system "QtNetwork")),"$LN
+
+ if [ ${QT_VERSION_MAJOR} -gt 4 ]; then
+ CONFIG+=" 'qt_location_cflags%': $(quote_flags $(mason cflags Qt system "QtLocation")),"$LN
+ CONFIG+=" 'qt_qml_cflags%': $(quote_flags $(mason cflags Qt system "QtQml")), "$LN
+ CONFIG+=" 'qt_quick_cflags%': $(quote_flags $(mason cflags Qt system "QtQuick")), "$LN
+ CONFIG+=" 'qt_positioning_cflags%': $(quote_flags $(mason cflags Qt system "QtPositioning")),"$LN
+
+ CONFIG+=" 'qt_location_ldflags%': $(quote_flags $(mason ldflags Qt system "QtLocation")),"$LN
+ CONFIG+=" 'qt_qml_ldflags%': $(quote_flags $(mason ldflags Qt system "QtQml")), "$LN
+ CONFIG+=" 'qt_quick_ldflags%': $(quote_flags $(mason ldflags Qt system "QtQuick")), "$LN
+ CONFIG+=" 'qt_positioning_ldflags%': $(quote_flags $(mason ldflags Qt system "QtPositioning")),"$LN
+ fi
+
+ if hash moc 2>/dev/null && hash rcc 2>/dev/null; then
+ CONFIG+=" 'qt_moc%': '$(which moc)',"$LN
+ CONFIG+=" 'qt_rcc%': '$(which rcc)',"$LN
+ elif [ ${QT_VERSION_MAJOR} -gt 4 ] ; then
+ CONFIG+=" 'qt_moc%': '$(pkg-config Qt${QT_VERSION_MAJOR}Core --variable=host_bins)/moc',"$LN
+ CONFIG+=" 'qt_rcc%': '$(pkg-config Qt${QT_VERSION_MAJOR}Core --variable=host_bins)/rcc',"$LN
+ else
+ CONFIG+=" 'qt_moc%': '$(pkg-config QtCore --variable=moc_location)',"$LN
+ CONFIG+=" 'qt_rcc%': '$(pkg-config QtCore --variable=rcc_location)',"$LN
+ fi
+}
+
+CONFIGURE_SUPLATFORM=platform/qt/scripts/configure-${MASON_PLATFORM_VERSION}.sh
+
+if [ -f $CONFIGURE_SUPLATFORM ]; then
+ source $CONFIGURE_SUPLATFORM
+fi
diff --git a/platform/qt/scripts/toolchain.sh b/platform/qt/scripts/toolchain.sh
new file mode 100755
index 0000000000..bb08e0b010
--- /dev/null
+++ b/platform/qt/scripts/toolchain.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+if [ -z ${MASON_SYSROOT:-} ]; then
+ exit 0
+fi
+
+export PATH="`pwd`/.mason:${PATH}"
+export MASON_DIR="`pwd`/.mason"
+
+export PATH=`mason env PATH`
+
+echo CXX=\"`which $(mason env CXX)`\"
+echo CC=\"`which $(mason env CC)`\"
+echo LD=\"`which $(mason env LD)`\"
+echo LINK=\"`which $(mason env CXX)`\"
+echo AR=\"`which $(mason env AR)`\"
+echo RANLIB=\"`which $(mason env RANLIB)`\"
+echo STRIP=\"`which $(mason env STRIP)`\"
diff --git a/platform/qt/src/async_task.cpp b/platform/qt/src/async_task.cpp
new file mode 100644
index 0000000000..c376c1c370
--- /dev/null
+++ b/platform/qt/src/async_task.cpp
@@ -0,0 +1,43 @@
+#include <mbgl/util/async_task.hpp>
+
+#include "async_task_impl.hpp"
+
+#include <mbgl/util/run_loop.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+namespace util {
+
+AsyncTask::Impl::Impl(std::function<void()>&& fn)
+ : runLoop(RunLoop::Get()),
+ task(std::move(fn)) {
+ connect(this, SIGNAL(send(void)), this, SLOT(runTask(void)), Qt::QueuedConnection);
+}
+
+void AsyncTask::Impl::maySend() {
+ if (!queued.test_and_set()) {
+ emit send();
+ }
+}
+
+void AsyncTask::Impl::runTask() {
+ assert(runLoop == RunLoop::Get());
+
+ queued.clear();
+ task();
+}
+
+AsyncTask::AsyncTask(std::function<void()>&& fn)
+ : impl(std::make_unique<Impl>(std::move(fn))) {
+}
+
+AsyncTask::~AsyncTask() {
+}
+
+void AsyncTask::send() {
+ impl->maySend();
+}
+
+}
+}
diff --git a/platform/qt/src/async_task_impl.hpp b/platform/qt/src/async_task_impl.hpp
new file mode 100644
index 0000000000..171990b15c
--- /dev/null
+++ b/platform/qt/src/async_task_impl.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/util/async_task.hpp>
+
+#include <QObject>
+
+#include <functional>
+#include <atomic>
+
+namespace mbgl {
+namespace util {
+
+class RunLoop;
+
+class AsyncTask::Impl : public QObject {
+ Q_OBJECT
+
+public:
+ Impl(std::function<void()>&& fn);
+
+ void maySend();
+
+public slots:
+ void runTask();
+
+signals:
+ void send();
+
+private:
+ RunLoop* runLoop;
+
+ std::function<void()> task;
+ std::atomic_flag queued = ATOMIC_FLAG_INIT;
+};
+
+
+}
+}
diff --git a/platform/qt/src/http_file_source.cpp b/platform/qt/src/http_file_source.cpp
new file mode 100644
index 0000000000..87948609df
--- /dev/null
+++ b/platform/qt/src/http_file_source.cpp
@@ -0,0 +1,131 @@
+#include "http_file_source.hpp"
+#include "http_request.hpp"
+
+#include <mbgl/platform/log.hpp>
+
+#include <QByteArray>
+#include <QDir>
+#include <QNetworkProxyFactory>
+#include <QNetworkReply>
+#include <QSslConfiguration>
+
+// Needs to be on the global namespace
+// for linking purposes.
+void initResources() {
+ Q_INIT_RESOURCE(qmapbox);
+}
+
+namespace mbgl {
+
+HTTPFileSource::Impl::Impl() : m_manager(new QNetworkAccessManager(this))
+{
+ initResources();
+
+ QNetworkProxyFactory::setUseSystemConfiguration(true);
+
+#if QT_VERSION >= 0x050000
+ m_ssl.setProtocol(QSsl::SecureProtocols);
+#else
+ // Qt 4 defines SecureProtocols as TLS1 or SSL3, but we don't want SSL3.
+ m_ssl.setProtocol(QSsl::TlsV1);
+#endif
+
+ m_ssl.setCaCertificates(QSslCertificate::fromPath(":ca-bundle.crt"));
+ if (m_ssl.caCertificates().isEmpty()) {
+ mbgl::Log::Warning(mbgl::Event::HttpRequest, "Could not load list of certificate authorities");
+ }
+
+ connect(m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinish(QNetworkReply*)));
+}
+
+void HTTPFileSource::Impl::request(HTTPRequest* req)
+{
+ QUrl url = req->requestUrl();
+
+ QPair<QNetworkReply*, QVector<HTTPRequest*>>& data = m_pending[url];
+ QVector<HTTPRequest*>& requestsVector = data.second;
+ requestsVector.append(req);
+
+ if (requestsVector.size() > 1) {
+ return;
+ }
+
+ QNetworkRequest networkRequest = req->networkRequest();
+ networkRequest.setSslConfiguration(m_ssl);
+
+ data.first = m_manager->get(networkRequest);
+}
+
+void HTTPFileSource::Impl::cancel(HTTPRequest* req)
+{
+ QUrl url = req->requestUrl();
+
+ auto it = m_pending.find(url);
+ if (it == m_pending.end()) {
+ return;
+ }
+
+ QPair<QNetworkReply*, QVector<HTTPRequest*>>& data = it.value();
+ QNetworkReply* reply = data.first;
+ QVector<HTTPRequest*>& requestsVector = data.second;
+
+ for (int i = 0; i < requestsVector.size(); ++i) {
+ if (req == requestsVector.at(i)) {
+ requestsVector.remove(i);
+ break;
+ }
+ }
+
+ if (requestsVector.empty()) {
+ m_pending.erase(it);
+#if QT_VERSION >= 0x050000
+ reply->abort();
+#else
+ // XXX: We should be aborting the reply here
+ // but a bug on Qt4 causes the connection of
+ // other ongoing requests to drop if we call
+ // abort() too often (and we do).
+ Q_UNUSED(reply);
+#endif
+ }
+}
+
+void HTTPFileSource::Impl::replyFinish(QNetworkReply* reply)
+{
+ const QUrl& url = reply->request().url();
+
+ auto it = m_pending.find(url);
+ if (it == m_pending.end()) {
+ reply->deleteLater();
+ return;
+ }
+
+ QVector<HTTPRequest*>& requestsVector = it.value().second;
+ for (auto req : requestsVector) {
+ req->handleNetworkReply(reply);
+ }
+
+ m_pending.erase(it);
+ reply->deleteLater();
+}
+
+HTTPFileSource::HTTPFileSource()
+ : impl(std::make_unique<Impl>()) {
+}
+
+HTTPFileSource::~HTTPFileSource() = default;
+
+std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback)
+{
+ return std::make_unique<HTTPRequest>(impl.get(), resource, callback);
+}
+
+uint32_t HTTPFileSource::maximumConcurrentRequests() {
+#if QT_VERSION >= 0x050000
+ return 20;
+#else
+ return 10;
+#endif
+}
+
+} // mbgl
diff --git a/platform/qt/src/http_file_source.hpp b/platform/qt/src/http_file_source.hpp
new file mode 100644
index 0000000000..274b464026
--- /dev/null
+++ b/platform/qt/src/http_file_source.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+
+#include <QMap>
+#include <QNetworkAccessManager>
+#include <QObject>
+#include <QPair>
+#include <QQueue>
+#include <QSslConfiguration>
+#include <QUrl>
+#include <QVector>
+
+namespace mbgl {
+
+class HTTPRequest;
+
+class HTTPFileSource::Impl : public QObject
+{
+ Q_OBJECT
+
+public:
+ Impl();
+ virtual ~Impl() = default;
+
+ void request(HTTPRequest*);
+ void cancel(HTTPRequest*);
+
+public slots:
+ void replyFinish(QNetworkReply* reply);
+
+private:
+ QMap<QUrl, QPair<QNetworkReply*, QVector<HTTPRequest*>>> m_pending;
+ QNetworkAccessManager *m_manager;
+ QSslConfiguration m_ssl;
+};
+
+} // namespace mbgl
diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp
new file mode 100644
index 0000000000..7290f5a974
--- /dev/null
+++ b/platform/qt/src/http_request.cpp
@@ -0,0 +1,122 @@
+#include "http_request.hpp"
+#include "http_file_source.hpp"
+
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/http_header.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <QByteArray>
+#include <QNetworkReply>
+#include <QPair>
+
+namespace mbgl {
+
+HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context, const Resource& resource, FileSource::Callback callback)
+ : m_context(context)
+ , m_resource(resource)
+ , m_callback(callback)
+{
+ m_context->request(this);
+}
+
+HTTPRequest::~HTTPRequest()
+{
+ if (!m_handled) {
+ m_context->cancel(this);
+ }
+}
+
+QUrl HTTPRequest::requestUrl() const
+{
+ return QUrl::fromPercentEncoding(QByteArray(m_resource.url.data(), m_resource.url.size()));
+}
+
+QNetworkRequest HTTPRequest::networkRequest() const
+{
+ QNetworkRequest req = QNetworkRequest(requestUrl());
+ req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
+ req.setRawHeader("User-Agent", "MapboxGL/1.0 [Qt]");
+
+ if (m_resource.priorEtag) {
+ req.setRawHeader("If-None-Match", QByteArray(m_resource.priorEtag->data(), m_resource.priorEtag->size()));
+ } else if (m_resource.priorModified) {
+ req.setRawHeader("If-Modified-Since", util::rfc1123(*m_resource.priorModified).c_str());
+ }
+
+ return req;
+}
+
+void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
+{
+ m_handled = true;
+
+ // Calling `callback` may result in deleting `this`.
+ // Copy data to temporaries first.
+ auto callback = m_callback;
+ mbgl::Response response;
+
+ using Error = Response::Error;
+
+ // Handle non-HTTP errors (i.e. like connection).
+ if (reply->error() && reply->error() < 100) {
+ response.error = std::make_unique<Error>(
+ Error::Reason::Connection, reply->errorString().toStdString());
+ callback(response);
+ return;
+ }
+
+ QPair<QByteArray, QByteArray> line;
+ foreach(line, reply->rawHeaderPairs()) {
+ QString header = QString(line.first).toLower();
+
+ if (header == "last-modified") {
+ response.modified = util::parseTimestamp(line.second.constData());
+ } else if (header == "etag") {
+ response.etag = std::string(line.second.constData(), line.second.size());
+ } else if (header == "cache-control") {
+ response.expires = http::CacheControl::parse(line.second.constData()).toTimePoint();
+ } else if (header == "expires") {
+ response.expires = util::parseTimestamp(line.second.constData());
+ }
+ }
+
+ int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ switch(responseCode) {
+ case 200: {
+ QByteArray bytes = reply->readAll();
+ if (bytes.isEmpty()) {
+ response.data = std::make_shared<std::string>();
+ } else {
+ response.data = std::make_shared<std::string>(bytes.data(), bytes.size());
+ }
+ break;
+ }
+ case 204:
+ response.noContent = true;
+ break;
+ case 304:
+ response.notModified = true;
+ break;
+ case 404: {
+ if (m_resource.kind == Resource::Kind::Tile) {
+ response.noContent = true;
+ } else {
+ response.error = std::make_unique<Error>(
+ Error::Reason::NotFound, "HTTP status code 404");
+ }
+ break;
+ }
+ default:
+ Response::Error::Reason reason = (responseCode >= 500 && responseCode < 600) ?
+ Error::Reason::Server : Error::Reason::Other;
+
+ response.error = std::make_unique<Error>(
+ reason, "HTTP status code " + util::toString(responseCode));
+ }
+
+ callback(response);
+}
+
+} // namespace mbgl
diff --git a/platform/qt/src/http_request.hpp b/platform/qt/src/http_request.hpp
new file mode 100644
index 0000000000..9af219f3c9
--- /dev/null
+++ b/platform/qt/src/http_request.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <mbgl/storage/http_file_source.hpp>
+#include <mbgl/util/async_request.hpp>
+
+#include <QNetworkRequest>
+#include <QUrl>
+
+class QNetworkReply;
+
+namespace mbgl {
+
+class Response;
+
+class HTTPRequest : public AsyncRequest
+{
+public:
+ HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
+ virtual ~HTTPRequest();
+
+ QUrl requestUrl() const;
+ QNetworkRequest networkRequest() const;
+
+ void handleNetworkReply(QNetworkReply *reply);
+
+private:
+ HTTPFileSource::Impl* m_context;
+ Resource m_resource;
+ FileSource::Callback m_callback;
+
+ bool m_handled = false;
+};
+
+} // namespace mbgl
diff --git a/platform/qt/src/image.cpp b/platform/qt/src/image.cpp
new file mode 100644
index 0000000000..f51f993d48
--- /dev/null
+++ b/platform/qt/src/image.cpp
@@ -0,0 +1,63 @@
+#include <mbgl/util/image.hpp>
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QImage>
+
+namespace mbgl {
+
+std::string encodePNG(const PremultipliedImage& pre) {
+ QImage image(pre.data.get(), pre.width, pre.height,
+ QImage::Format_ARGB32_Premultiplied);
+
+ QByteArray array;
+ QBuffer buffer(&array);
+
+ buffer.open(QIODevice::WriteOnly);
+ image.rgbSwapped().save(&buffer, "PNG");
+
+ return std::string(array.constData(), array.size());
+}
+
+#if !defined(QT_IMAGE_DECODERS)
+PremultipliedImage decodeJPEG(const uint8_t*, size_t);
+PremultipliedImage decodeWebP(const uint8_t*, size_t);
+#endif
+
+PremultipliedImage decodeImage(const std::string& string) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(string.data());
+ const size_t size = string.size();
+
+#if !defined(QT_IMAGE_DECODERS)
+ if (size >= 12) {
+ uint32_t riff_magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+ uint32_t webp_magic = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11];
+ if (riff_magic == 0x52494646 && webp_magic == 0x57454250) {
+ return decodeWebP(data, size);
+ }
+ }
+
+ if (size >= 2) {
+ uint16_t magic = ((data[0] << 8) | data[1]) & 0xffff;
+ if (magic == 0xFFD8) {
+ return decodeJPEG(data, size);
+ }
+ }
+#endif
+
+ QImage image =
+ QImage::fromData(data, size)
+ .rgbSwapped()
+ .convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ if (image.isNull()) {
+ throw std::runtime_error("Unsupported image type");
+ }
+
+ auto img = std::make_unique<uint8_t[]>(image.byteCount());
+ memcpy(img.get(), image.constBits(), image.byteCount());
+
+ return { size_t(image.width()), size_t(image.height()), std::move(img) };
+}
+
+}
diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp
new file mode 100644
index 0000000000..17ddbe4ca5
--- /dev/null
+++ b/platform/qt/src/qmapbox.cpp
@@ -0,0 +1,57 @@
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/storage/network_status.hpp>
+#include <mbgl/util/default_styles.hpp>
+#include <mbgl/util/traits.hpp>
+
+#include <QMapbox>
+
+#if QT_VERSION >= 0x050000
+#include <QOpenGLContext>
+#else
+#include <QGLContext>
+#endif
+
+// mbgl::MapMode
+static_assert(mbgl::underlying_type(QMapbox::Online) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Online), "error");
+static_assert(mbgl::underlying_type(QMapbox::Offline) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Offline), "error");
+
+namespace QMapbox {
+
+Q_DECL_EXPORT NetworkMode networkMode()
+{
+ return static_cast<NetworkMode>(mbgl::NetworkStatus::Get());
+}
+
+Q_DECL_EXPORT void setNetworkMode(NetworkMode mode)
+{
+ mbgl::NetworkStatus::Set(static_cast<mbgl::NetworkStatus::Status>(mode));
+}
+
+Q_DECL_EXPORT QList<QPair<QString, QString>>& defaultStyles()
+{
+ static QList<QPair<QString, QString>> styles;
+
+ if (styles.isEmpty()) {
+ for (auto style : mbgl::util::default_styles::orderedStyles) {
+ styles.append(QPair<QString, QString>(
+ QString::fromStdString(style.url), QString::fromStdString(style.name)));
+ }
+ }
+
+ return styles;
+}
+
+Q_DECL_EXPORT void initializeGLExtensions()
+{
+ mbgl::gl::InitializeExtensions([](const char* name) {
+#if QT_VERSION >= 0x050000
+ QOpenGLContext* thisContext = QOpenGLContext::currentContext();
+ return thisContext->getProcAddress(name);
+#else
+ const QGLContext* thisContext = QGLContext::currentContext();
+ return reinterpret_cast<mbgl::gl::glProc>(thisContext->getProcAddress(name));
+#endif
+ });
+}
+
+}
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
new file mode 100644
index 0000000000..144d545d3c
--- /dev/null
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -0,0 +1,655 @@
+#include "qmapboxgl_p.hpp"
+
+#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/sprite/sprite_image.hpp>
+#include <mbgl/storage/network_status.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/traits.hpp>
+
+#if QT_VERSION >= 0x050000
+#include <QGuiApplication>
+#include <QWindow>
+#else
+#include <QCoreApplication>
+#endif
+
+#include <QImage>
+#include <QMapboxGL>
+#include <QMargins>
+#include <QString>
+#include <QStringList>
+#include <QThreadStorage>
+
+#include <memory>
+
+using namespace QMapbox;
+
+// mbgl::MapMode
+static_assert(mbgl::underlying_type(QMapboxGLSettings::ContinuousMap) == mbgl::underlying_type(mbgl::MapMode::Continuous), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::StillMap) == mbgl::underlying_type(mbgl::MapMode::Still), "error");
+
+// mbgl::GLContextMode
+static_assert(mbgl::underlying_type(QMapboxGLSettings::UniqueGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Unique), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::SharedGLContext) == mbgl::underlying_type(mbgl::GLContextMode::Shared), "error");
+
+// mbgl::ConstrainMode
+static_assert(mbgl::underlying_type(QMapboxGLSettings::NoConstrain) == mbgl::underlying_type(mbgl::ConstrainMode::None), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainHeightOnly) == mbgl::underlying_type(mbgl::ConstrainMode::HeightOnly), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainWidthAndHeight) == mbgl::underlying_type(mbgl::ConstrainMode::WidthAndHeight), "error");
+
+// mbgl::ViewportMode
+static_assert(mbgl::underlying_type(QMapboxGLSettings::DefaultViewport) == mbgl::underlying_type(mbgl::ViewportMode::Default), "error");
+static_assert(mbgl::underlying_type(QMapboxGLSettings::FlippedYViewport) == mbgl::underlying_type(mbgl::ViewportMode::FlippedY), "error");
+
+// mbgl::NorthOrientation
+static_assert(mbgl::underlying_type(QMapboxGL::NorthUpwards) == mbgl::underlying_type(mbgl::NorthOrientation::Upwards), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::NorthRightwards) == mbgl::underlying_type(mbgl::NorthOrientation::Rightwards), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::NorthDownwards) == mbgl::underlying_type(mbgl::NorthOrientation::Downwards), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::NorthLeftwards) == mbgl::underlying_type(mbgl::NorthOrientation::Leftwards), "error");
+
+// mbgl::MapChange
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeRegionWillChange) == mbgl::underlying_type(mbgl::MapChangeRegionWillChange), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeRegionWillChangeAnimated) == mbgl::underlying_type(mbgl::MapChangeRegionWillChangeAnimated), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeRegionIsChanging) == mbgl::underlying_type(mbgl::MapChangeRegionIsChanging), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeRegionDidChange) == mbgl::underlying_type(mbgl::MapChangeRegionDidChange), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeRegionDidChangeAnimated) == mbgl::underlying_type(mbgl::MapChangeRegionDidChangeAnimated), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeWillStartLoadingMap) == mbgl::underlying_type(mbgl::MapChangeWillStartLoadingMap), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishLoadingMap) == mbgl::underlying_type(mbgl::MapChangeDidFinishLoadingMap), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFailLoadingMap) == mbgl::underlying_type(mbgl::MapChangeDidFailLoadingMap), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeWillStartRenderingFrame) == mbgl::underlying_type(mbgl::MapChangeWillStartRenderingFrame), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishRenderingFrame) == mbgl::underlying_type(mbgl::MapChangeDidFinishRenderingFrame), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered) == mbgl::underlying_type(mbgl::MapChangeDidFinishRenderingFrameFullyRendered), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeWillStartRenderingMap) == mbgl::underlying_type(mbgl::MapChangeWillStartRenderingMap), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishRenderingMap) == mbgl::underlying_type(mbgl::MapChangeDidFinishRenderingMap), "error");
+static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered) == mbgl::underlying_type(mbgl::MapChangeDidFinishRenderingMapFullyRendered), "error");
+
+namespace {
+
+QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop;
+
+}
+
+QMapboxGLSettings::QMapboxGLSettings()
+ : m_mapMode(QMapboxGLSettings::ContinuousMap)
+ , m_contextMode(QMapboxGLSettings::SharedGLContext)
+ , m_constrainMode(QMapboxGLSettings::ConstrainHeightOnly)
+ , m_viewportMode(QMapboxGLSettings::DefaultViewport)
+ , m_cacheMaximumSize(mbgl::util::DEFAULT_MAX_CACHE_SIZE)
+ , m_cacheDatabasePath(":memory:")
+ , m_assetPath(QCoreApplication::applicationDirPath())
+{
+}
+
+QMapboxGLSettings::MapMode QMapboxGLSettings::mapMode() const
+{
+ return m_mapMode;
+}
+
+void QMapboxGLSettings::setMapMode(MapMode mode)
+{
+ m_mapMode = mode;
+}
+
+QMapboxGLSettings::GLContextMode QMapboxGLSettings::contextMode() const
+{
+ return m_contextMode;
+}
+
+void QMapboxGLSettings::setContextMode(GLContextMode mode)
+{
+ m_contextMode = mode;
+}
+
+QMapboxGLSettings::ConstrainMode QMapboxGLSettings::constrainMode() const
+{
+ return m_constrainMode;
+}
+
+void QMapboxGLSettings::setConstrainMode(ConstrainMode mode)
+{
+ m_constrainMode = mode;
+}
+
+QMapboxGLSettings::ViewportMode QMapboxGLSettings::viewportMode() const
+{
+ return m_viewportMode;
+}
+
+void QMapboxGLSettings::setViewportMode(ViewportMode mode)
+{
+ m_viewportMode = mode;
+}
+
+unsigned QMapboxGLSettings::cacheDatabaseMaximumSize() const
+{
+ return m_cacheMaximumSize;
+}
+
+void QMapboxGLSettings::setCacheDatabaseMaximumSize(unsigned size)
+{
+ m_cacheMaximumSize = size;
+}
+
+QString QMapboxGLSettings::cacheDatabasePath() const
+{
+ return m_cacheDatabasePath;
+}
+
+void QMapboxGLSettings::setCacheDatabasePath(const QString &path)
+{
+ m_cacheDatabasePath = path;
+}
+
+QString QMapboxGLSettings::assetPath() const
+{
+ return m_assetPath;
+}
+
+void QMapboxGLSettings::setAssetPath(const QString &path)
+{
+ m_assetPath = path;
+}
+
+QString QMapboxGLSettings::accessToken() const {
+ return m_accessToken;
+}
+
+void QMapboxGLSettings::setAccessToken(const QString &token)
+{
+ m_accessToken = token;
+}
+
+QMapboxGL::QMapboxGL(QObject *parent_, const QMapboxGLSettings &settings)
+ : QObject(parent_)
+{
+ // Multiple QMapboxGL running on the same thread
+ // will share the same mbgl::util::RunLoop
+ if (!loop.hasLocalData()) {
+ loop.setLocalData(std::make_shared<mbgl::util::RunLoop>());
+ }
+
+ d_ptr = new QMapboxGLPrivate(this, settings);
+}
+
+QMapboxGL::~QMapboxGL()
+{
+ delete d_ptr;
+}
+
+void QMapboxGL::cycleDebugOptions()
+{
+ d_ptr->mapObj->cycleDebugOptions();
+}
+
+QString QMapboxGL::styleJSON() const
+{
+ return QString::fromStdString(d_ptr->mapObj->getStyleJSON());
+}
+
+QString QMapboxGL::styleURL() const
+{
+ return QString::fromStdString(d_ptr->mapObj->getStyleURL());
+}
+
+void QMapboxGL::setStyleJSON(const QString &style)
+{
+ d_ptr->mapObj->setStyleJSON(style.toStdString());
+}
+
+void QMapboxGL::setStyleURL(const QString &url)
+{
+ d_ptr->mapObj->setStyleURL(url.toStdString());
+}
+
+double QMapboxGL::latitude() const
+{
+ return d_ptr->mapObj->getLatLng(d_ptr->margins).latitude;
+}
+
+void QMapboxGL::setLatitude(double latitude_)
+{
+ d_ptr->mapObj->setLatLng(mbgl::LatLng { latitude_, longitude() }, d_ptr->margins);
+}
+
+double QMapboxGL::longitude() const
+{
+ return d_ptr->mapObj->getLatLng(d_ptr->margins).longitude;
+}
+
+void QMapboxGL::setLongitude(double longitude_)
+{
+ d_ptr->mapObj->setLatLng(mbgl::LatLng { latitude(), longitude_ }, d_ptr->margins);
+}
+
+double QMapboxGL::scale() const
+{
+ return d_ptr->mapObj->getScale();
+}
+
+void QMapboxGL::setScale(double scale_, const QPointF &center)
+{
+ d_ptr->mapObj->setScale(scale_, mbgl::ScreenCoordinate { center.x(), center.y() });
+}
+
+double QMapboxGL::zoom() const
+{
+ return d_ptr->mapObj->getZoom();
+}
+
+void QMapboxGL::setZoom(double zoom_)
+{
+ d_ptr->mapObj->setZoom(zoom_, d_ptr->margins);
+}
+
+double QMapboxGL::minimumZoom() const
+{
+ return d_ptr->mapObj->getMinZoom();
+}
+
+double QMapboxGL::maximumZoom() const
+{
+ return d_ptr->mapObj->getMaxZoom();
+}
+
+Coordinate QMapboxGL::coordinate() const
+{
+ const mbgl::LatLng& latLng = d_ptr->mapObj->getLatLng(d_ptr->margins);
+ return Coordinate(latLng.latitude, latLng.longitude);
+}
+
+void QMapboxGL::setCoordinate(const Coordinate &coordinate_)
+{
+ d_ptr->mapObj->setLatLng(mbgl::LatLng { coordinate_.first, coordinate_.second }, d_ptr->margins);
+}
+
+void QMapboxGL::setCoordinateZoom(const Coordinate &coordinate_, double zoom_)
+{
+ d_ptr->mapObj->setLatLngZoom(
+ mbgl::LatLng { coordinate_.first, coordinate_.second }, zoom_, d_ptr->margins);
+}
+
+void QMapboxGL::jumpTo(const CameraOptions& camera)
+{
+ mbgl::CameraOptions mbglCamera;
+ if (camera.center.isValid()) {
+ const Coordinate center = camera.center.value<Coordinate>();
+ mbglCamera.center = mbgl::LatLng { center.first, center.second };
+ }
+ if (camera.anchor.isValid()) {
+ const QPointF anchor = camera.anchor.value<QPointF>();
+ mbglCamera.anchor = mbgl::ScreenCoordinate { anchor.x(), anchor.y() };
+ }
+ if (camera.zoom.isValid()) {
+ mbglCamera.zoom = camera.zoom.value<double>();
+ }
+ if (camera.angle.isValid()) {
+ mbglCamera.angle = -camera.angle.value<double>() * mbgl::util::DEG2RAD;
+ }
+ if (camera.pitch.isValid()) {
+ mbglCamera.pitch = camera.pitch.value<double>() * mbgl::util::DEG2RAD;
+ }
+
+ mbglCamera.padding = d_ptr->margins;
+
+ d_ptr->mapObj->jumpTo(mbglCamera);
+}
+
+double QMapboxGL::bearing() const
+{
+ return d_ptr->mapObj->getBearing();
+}
+
+void QMapboxGL::setBearing(double degrees)
+{
+ d_ptr->mapObj->setBearing(degrees, d_ptr->margins);
+}
+
+void QMapboxGL::setBearing(double degrees, const QPointF &center)
+{
+ d_ptr->mapObj->setBearing(degrees, mbgl::ScreenCoordinate { center.x(), center.y() });
+}
+
+double QMapboxGL::pitch() const
+{
+ return d_ptr->mapObj->getPitch();
+}
+
+void QMapboxGL::setPitch(double pitch_)
+{
+ d_ptr->mapObj->setPitch(pitch_);
+}
+
+QMapboxGL::NorthOrientation QMapboxGL::northOrientation() const
+{
+ return static_cast<QMapboxGL::NorthOrientation>(d_ptr->mapObj->getNorthOrientation());
+}
+
+void QMapboxGL::setNorthOrientation(NorthOrientation orientation)
+{
+ d_ptr->mapObj->setNorthOrientation(static_cast<mbgl::NorthOrientation>(orientation));
+}
+
+void QMapboxGL::setGestureInProgress(bool inProgress)
+{
+ d_ptr->mapObj->setGestureInProgress(inProgress);
+}
+
+void QMapboxGL::addClass(const QString &className)
+{
+ d_ptr->mapObj->addClass(className.toStdString());
+}
+
+void QMapboxGL::removeClass(const QString &className)
+{
+ d_ptr->mapObj->removeClass(className.toStdString());
+}
+
+bool QMapboxGL::hasClass(const QString &className) const
+{
+ return d_ptr->mapObj->hasClass(className.toStdString());
+}
+
+void QMapboxGL::setClasses(const QStringList &classNames)
+{
+ std::vector<std::string> mbglClassNames;
+ mbglClassNames.reserve(classNames.size());
+
+ for (const QString &className : classNames) {
+ mbglClassNames.emplace_back(className.toStdString());
+ }
+
+ d_ptr->mapObj->setClasses(mbglClassNames);
+}
+
+QStringList QMapboxGL::getClasses() const
+{
+ QStringList classNames;
+ for (const std::string &mbglClass : d_ptr->mapObj->getClasses()) {
+ classNames << QString::fromStdString(mbglClass);
+ }
+ return classNames;
+}
+
+mbgl::Annotation fromPointAnnotation(const PointAnnotation &pointAnnotation) {
+ const Coordinate &coordinate = pointAnnotation.first;
+ const QString &icon = pointAnnotation.second;
+ return mbgl::SymbolAnnotation { mbgl::Point<double> { coordinate.second, coordinate.first }, icon.toStdString() };
+}
+
+AnnotationID QMapboxGL::addPointAnnotation(const PointAnnotation &pointAnnotation)
+{
+ return d_ptr->mapObj->addAnnotation(fromPointAnnotation(pointAnnotation));
+}
+
+void QMapboxGL::updatePointAnnotation(AnnotationID id, const PointAnnotation &pointAnnotation)
+{
+ d_ptr->mapObj->updateAnnotation(id, fromPointAnnotation(pointAnnotation));
+}
+
+mbgl::Annotation fromQMapboxGLShapeAnnotation(const ShapeAnnotation &shapeAnnotation) {
+ const CoordinateSegments &segments = shapeAnnotation.first;
+ const QString &styleLayer = shapeAnnotation.second;
+
+ mbgl::Polygon<double> polygon;
+ polygon.reserve(segments.size());
+
+ for (const Coordinates &coordinates : segments) {
+ mbgl::LinearRing<double> linearRing;
+ linearRing.reserve(coordinates.size());
+
+ for (const Coordinate &coordinate : coordinates) {
+ linearRing.emplace_back(mbgl::Point<double>(coordinate.first, coordinate.second));
+ }
+
+ polygon.emplace_back(linearRing);
+ }
+
+ return mbgl::StyleSourcedAnnotation { polygon, styleLayer.toStdString() };
+}
+
+AnnotationID QMapboxGL::addShapeAnnotation(const ShapeAnnotation &shapeAnnotation)
+{
+ return d_ptr->mapObj->addAnnotation(fromQMapboxGLShapeAnnotation(shapeAnnotation));
+}
+
+void QMapboxGL::removeAnnotation(AnnotationID annotationID)
+{
+ d_ptr->mapObj->removeAnnotation(annotationID);
+}
+
+bool QMapboxGL::isRotating() const
+{
+ return d_ptr->mapObj->isRotating();
+}
+
+bool QMapboxGL::isScaling() const
+{
+ return d_ptr->mapObj->isScaling();
+}
+
+bool QMapboxGL::isPanning() const
+{
+ return d_ptr->mapObj->isPanning();
+}
+
+bool QMapboxGL::isFullyLoaded() const
+{
+ return d_ptr->mapObj->isFullyLoaded();
+}
+
+void QMapboxGL::moveBy(const QPointF &offset)
+{
+ d_ptr->mapObj->moveBy(mbgl::ScreenCoordinate { offset.x(), offset.y() });
+}
+
+void QMapboxGL::scaleBy(double scale_, const QPointF &center) {
+ d_ptr->mapObj->scaleBy(scale_, mbgl::ScreenCoordinate { center.x(), center.y() });
+}
+
+void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second)
+{
+ d_ptr->mapObj->rotateBy(
+ mbgl::ScreenCoordinate { first.x(), first.y() },
+ mbgl::ScreenCoordinate { second.x(), second.y() });
+}
+
+void QMapboxGL::resize(const QSize& size)
+{
+ QSize converted = size / d_ptr->getPixelRatio();
+ if (d_ptr->size == converted) return;
+
+ glViewport(0, 0, converted.width(), converted.height());
+
+ d_ptr->size = converted;
+ d_ptr->mapObj->update(mbgl::Update::Dimensions);
+}
+
+void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &sprite)
+{
+ if (sprite.isNull()) return;
+
+ const QImage swapped = sprite
+ .rgbSwapped()
+ .convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ auto img = std::make_unique<uint8_t[]>(swapped.byteCount());
+ memcpy(img.get(), swapped.constBits(), swapped.byteCount());
+
+ d_ptr->mapObj->addAnnotationIcon(name.toStdString(), std::make_shared<mbgl::SpriteImage>(
+ mbgl::PremultipliedImage { size_t(swapped.width()), size_t(swapped.height()), std::move(img) }, 1.0));
+}
+
+QPointF QMapboxGL::pixelForCoordinate(const Coordinate &coordinate_) const
+{
+ const mbgl::ScreenCoordinate pixel =
+ d_ptr->mapObj->pixelForLatLng(mbgl::LatLng { coordinate_.first, coordinate_.second });
+
+ return QPointF(pixel.x, pixel.y);
+}
+
+Coordinate QMapboxGL::coordinateForPixel(const QPointF &pixel) const
+{
+ const mbgl::LatLng latLng =
+ d_ptr->mapObj->latLngForPixel(mbgl::ScreenCoordinate { pixel.x(), pixel.y() });
+
+ return Coordinate(latLng.latitude, latLng.longitude);
+}
+
+CoordinateZoom QMapboxGL::coordinateZoomForBounds(const Coordinate &sw, Coordinate &ne) const
+{
+ auto bounds = mbgl::LatLngBounds::hull(mbgl::LatLng { sw.first, sw.second }, mbgl::LatLng { ne.first, ne.second });
+ mbgl::CameraOptions camera = d_ptr->mapObj->cameraForLatLngBounds(bounds, d_ptr->margins);
+
+ return {{ (*camera.center).latitude, (*camera.center).longitude }, *camera.zoom };
+}
+
+CoordinateZoom QMapboxGL::coordinateZoomForBounds(const Coordinate &sw, Coordinate &ne,
+ double newBearing, double newPitch)
+{
+ // FIXME: mbgl::Map::cameraForLatLngBounds should
+ // take bearing and pitch as input too, so this
+ // hack won't be needed.
+ double currentBearing = bearing();
+ double currentPitch = pitch();
+
+ setBearing(newBearing);
+ setPitch(newPitch);
+
+ auto bounds = mbgl::LatLngBounds::hull(mbgl::LatLng { sw.first, sw.second }, mbgl::LatLng { ne.first, ne.second });
+ mbgl::CameraOptions camera = d_ptr->mapObj->cameraForLatLngBounds(bounds, d_ptr->margins);
+
+ setBearing(currentBearing);
+ setPitch(currentPitch);
+
+ return {{ (*camera.center).latitude, (*camera.center).longitude }, *camera.zoom };
+}
+
+void QMapboxGL::setMargins(const QMargins &margins_)
+{
+ d_ptr->margins = {
+ static_cast<double>(margins_.top()),
+ static_cast<double>(margins_.left()),
+ static_cast<double>(margins_.bottom()),
+ static_cast<double>(margins_.right())
+ };
+}
+
+QMargins QMapboxGL::margins() const
+{
+ return QMargins(
+ d_ptr->margins.left,
+ d_ptr->margins.top,
+ d_ptr->margins.right,
+ d_ptr->margins.bottom
+ );
+}
+
+void QMapboxGL::addCustomLayer(const QString &id,
+ QMapbox::CustomLayerInitializeFunction initFn,
+ QMapbox::CustomLayerRenderFunction renderFn,
+ QMapbox::CustomLayerDeinitializeFunction deinitFn,
+ void *context_,
+ char *before)
+{
+ d_ptr->mapObj->addLayer(std::make_unique<mbgl::style::CustomLayer>(
+ id.toStdString(),
+ reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initFn),
+ // This cast is safe as long as both mbgl:: and QMapbox::
+ // CustomLayerRenderParameters members remains the same.
+ (mbgl::style::CustomLayerRenderFunction)renderFn,
+ reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitFn),
+ context_),
+ before ? mbgl::optional<std::string>(before) : mbgl::optional<std::string>());
+}
+
+void QMapboxGL::removeCustomLayer(const QString& id)
+{
+ d_ptr->mapObj->removeLayer(id.toStdString());
+}
+
+void QMapboxGL::render()
+{
+ d_ptr->dirty = false;
+ d_ptr->mapObj->render();
+}
+
+void QMapboxGL::connectionEstablished()
+{
+ d_ptr->connectionEstablished();
+}
+
+QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings)
+ : QObject(q)
+ , size(0, 0)
+ , q_ptr(q)
+ , fileSourceObj(std::make_unique<mbgl::DefaultFileSource>(
+ settings.cacheDatabasePath().toStdString(),
+ settings.assetPath().toStdString(),
+ settings.cacheDatabaseMaximumSize()))
+ , mapObj(std::make_unique<mbgl::Map>(
+ *this, *fileSourceObj,
+ static_cast<mbgl::MapMode>(settings.mapMode()),
+ static_cast<mbgl::GLContextMode>(settings.contextMode()),
+ static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
+ static_cast<mbgl::ViewportMode>(settings.viewportMode())))
+{
+ qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange");
+
+ fileSourceObj->setAccessToken(settings.accessToken().toStdString());
+ connect(this, SIGNAL(needsRendering()), q_ptr, SIGNAL(needsRendering()), Qt::QueuedConnection);
+ connect(this, SIGNAL(mapChanged(QMapboxGL::MapChange)), q_ptr, SIGNAL(mapChanged(QMapboxGL::MapChange)), Qt::QueuedConnection);
+}
+
+QMapboxGLPrivate::~QMapboxGLPrivate()
+{
+}
+
+float QMapboxGLPrivate::getPixelRatio() const
+{
+#if QT_VERSION >= 0x050000
+ // QWindow is the most reliable pixel ratio because QGuiApplication returns
+ // the maximum pixel ratio of all available QScreen objects - this is not
+ // valid for cases e.g. where two or more QScreen objects with different
+ // pixel ratios are present and the window shows on the screen with lower
+ // pixel ratio.
+ static const float pixelRatio = QGuiApplication::allWindows().first()->devicePixelRatio();
+#else
+ static const float pixelRatio = 1.0;
+#endif
+ return pixelRatio;
+}
+
+std::array<uint16_t, 2> QMapboxGLPrivate::getSize() const
+{
+ return {{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }};
+}
+
+std::array<uint16_t, 2> QMapboxGLPrivate::getFramebufferSize() const
+{
+ return {{ static_cast<uint16_t>(size.width() * getPixelRatio()),
+ static_cast<uint16_t>(size.height() * getPixelRatio()) }};
+}
+
+void QMapboxGLPrivate::invalidate()
+{
+ if (!dirty) {
+ emit needsRendering();
+ dirty = true;
+ }
+}
+
+void QMapboxGLPrivate::notifyMapChange(mbgl::MapChange change)
+{
+ emit mapChanged(static_cast<QMapboxGL::MapChange>(change));
+}
+
+void QMapboxGLPrivate::connectionEstablished()
+{
+ mbgl::NetworkStatus::Reachable();
+}
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
new file mode 100644
index 0000000000..fdbfb7f2c1
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -0,0 +1,49 @@
+#ifndef QMAPBOXGL_P_H
+#define QMAPBOXGL_P_H
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/view.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/util/geo.hpp>
+
+#include <QMapboxGL>
+#include <QObject>
+#include <QSize>
+
+class QMapboxGLPrivate : public QObject, public mbgl::View
+{
+ Q_OBJECT
+
+public:
+ explicit QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &);
+ virtual ~QMapboxGLPrivate();
+
+ // mbgl::View implementation.
+ float getPixelRatio() const final;
+ std::array<uint16_t, 2> getSize() const final;
+ std::array<uint16_t, 2> getFramebufferSize() const final;
+
+ void activate() final {}
+ void deactivate() final {}
+ void invalidate() final;
+ void notifyMapChange(mbgl::MapChange change) final;
+
+ mbgl::EdgeInsets margins;
+ QSize size;
+
+ QMapboxGL *q_ptr = nullptr;
+
+ std::unique_ptr<mbgl::DefaultFileSource> fileSourceObj;
+ std::unique_ptr<mbgl::Map> mapObj;
+
+ bool dirty = false;
+
+public slots:
+ void connectionEstablished();
+
+signals:
+ void needsRendering();
+ void mapChanged(QMapboxGL::MapChange);
+};
+
+#endif // QMAPBOXGL_P_H
diff --git a/platform/qt/src/qquickmapboxgl.cpp b/platform/qt/src/qquickmapboxgl.cpp
new file mode 100644
index 0000000000..2961925c8b
--- /dev/null
+++ b/platform/qt/src/qquickmapboxgl.cpp
@@ -0,0 +1,248 @@
+#include "qquickmapboxglrenderer.hpp"
+
+#include <mbgl/util/constants.hpp>
+
+#include <QQuickMapboxGL>
+
+#include <QDebug>
+#include <QQuickItem>
+#include <QString>
+#include <QtGlobal>
+
+#include <cmath>
+
+QQuickMapboxGL::QQuickMapboxGL(QQuickItem *parent_)
+ : QQuickFramebufferObject(parent_)
+{
+}
+
+QQuickMapboxGL::~QQuickMapboxGL()
+{
+}
+
+QQuickFramebufferObject::Renderer *QQuickMapboxGL::createRenderer() const
+{
+ return new QQuickMapboxGLRenderer();
+}
+
+void QQuickMapboxGL::setPlugin(QDeclarativeGeoServiceProvider *)
+{
+ qWarning() << __PRETTY_FUNCTION__
+ << "Not implemented.";
+}
+
+QDeclarativeGeoServiceProvider *QQuickMapboxGL::plugin() const
+{
+ return nullptr;
+}
+
+void QQuickMapboxGL::setMinimumZoomLevel(qreal zoom)
+{
+ zoom = qMax(mbgl::util::MIN_ZOOM, zoom);
+ zoom = qMin(m_maximumZoomLevel, zoom);
+
+ if (m_minimumZoomLevel == zoom) {
+ return;
+ }
+
+ m_minimumZoomLevel = zoom;
+ setZoomLevel(m_zoomLevel); // Constrain.
+
+ emit minimumZoomLevelChanged();
+}
+
+qreal QQuickMapboxGL::minimumZoomLevel() const
+{
+ return m_minimumZoomLevel;
+}
+
+void QQuickMapboxGL::setMaximumZoomLevel(qreal zoom)
+{
+ zoom = qMin(mbgl::util::MAX_ZOOM, zoom);
+ zoom = qMax(m_minimumZoomLevel, zoom);
+
+ if (m_maximumZoomLevel == zoom) {
+ return;
+ }
+
+ m_maximumZoomLevel = zoom;
+ setZoomLevel(m_zoomLevel); // Constrain.
+
+ emit maximumZoomLevelChanged();
+}
+
+qreal QQuickMapboxGL::maximumZoomLevel() const
+{
+ return m_maximumZoomLevel;
+}
+
+void QQuickMapboxGL::setZoomLevel(qreal zoom)
+{
+ zoom = qMin(m_maximumZoomLevel, zoom);
+ zoom = qMax(m_minimumZoomLevel, zoom);
+
+ if (m_zoomLevel == zoom) {
+ return;
+ }
+
+ m_zoomLevel = zoom;
+
+ m_syncState |= ZoomNeedsSync;
+ update();
+
+ emit zoomLevelChanged(m_zoomLevel);
+}
+
+qreal QQuickMapboxGL::zoomLevel() const
+{
+ return m_zoomLevel;
+}
+
+void QQuickMapboxGL::setCenter(const QGeoCoordinate &coordinate)
+{
+ if (m_center == coordinate) {
+ return;
+ }
+
+ m_center = coordinate;
+
+ m_syncState |= CenterNeedsSync;
+ update();
+
+ emit centerChanged(m_center);
+}
+
+QGeoCoordinate QQuickMapboxGL::center() const
+{
+ return m_center;
+}
+
+QGeoServiceProvider::Error QQuickMapboxGL::error() const
+{
+ return QGeoServiceProvider::NoError;
+}
+
+QString QQuickMapboxGL::errorString() const
+{
+ return QString();
+}
+
+void QQuickMapboxGL::setVisibleRegion(const QGeoShape &shape)
+{
+ m_visibleRegion = shape;
+}
+
+QGeoShape QQuickMapboxGL::visibleRegion() const
+{
+ return m_visibleRegion;
+}
+
+void QQuickMapboxGL::setCopyrightsVisible(bool)
+{
+ qWarning() << __PRETTY_FUNCTION__
+ << "Not implemented.";
+}
+
+bool QQuickMapboxGL::copyrightsVisible() const
+{
+ return false;
+}
+
+void QQuickMapboxGL::setColor(const QColor &)
+{
+ // TODO: can be made functional after landing #837
+ qWarning() << __PRETTY_FUNCTION__
+ << "Use Mapbox Studio to change the map background color.";
+}
+
+QColor QQuickMapboxGL::color() const
+{
+ return QColor();
+}
+
+void QQuickMapboxGL::pan(int dx, int dy)
+{
+ m_pan += QPointF(dx, -dy);
+
+ m_syncState |= PanNeedsSync;
+ update();
+}
+
+void QQuickMapboxGL::setStyle(const QString &styleUrl)
+{
+ if (m_style == styleUrl) {
+ return;
+ }
+
+ m_style = styleUrl;
+
+ m_syncState |= StyleNeedsSync;
+ update();
+
+ emit styleChanged();
+}
+
+QString QQuickMapboxGL::style() const
+{
+ return m_style;
+}
+
+void QQuickMapboxGL::setBearing(qreal angle)
+{
+ angle = std::fmod(angle, 360.);
+
+ if (m_bearing == angle) {
+ return;
+ }
+
+ m_bearing = angle;
+
+ m_syncState |= BearingNeedsSync;
+ update();
+
+ emit bearingChanged(m_bearing);
+}
+
+qreal QQuickMapboxGL::bearing() const
+{
+ return m_bearing;
+}
+
+void QQuickMapboxGL::setPitch(qreal angle)
+{
+ angle = qMin(qMax(0., angle), mbgl::util::PITCH_MAX * mbgl::util::RAD2DEG);
+
+ if (m_pitch == angle) {
+ return;
+ }
+
+ m_pitch = angle;
+
+ m_syncState |= PitchNeedsSync;
+ update();
+
+ emit pitchChanged(m_pitch);
+}
+
+qreal QQuickMapboxGL::pitch() const
+{
+ return m_pitch;
+}
+
+QPointF QQuickMapboxGL::swapPan()
+{
+ QPointF oldPan = m_pan;
+
+ m_pan = QPointF(0, 0);
+
+ return oldPan;
+}
+
+int QQuickMapboxGL::swapSyncState()
+{
+ int oldState = m_syncState;
+
+ m_syncState = NothingNeedsSync;
+
+ return oldState;
+}
diff --git a/platform/qt/src/qquickmapboxglrenderer.cpp b/platform/qt/src/qquickmapboxglrenderer.cpp
new file mode 100644
index 0000000000..6212c4f128
--- /dev/null
+++ b/platform/qt/src/qquickmapboxglrenderer.cpp
@@ -0,0 +1,78 @@
+#include "qquickmapboxglrenderer.hpp"
+
+#include <QMapboxGL>
+#include <QQuickMapboxGL>
+
+#include <QSize>
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLFramebufferObjectFormat>
+#include <QQuickWindow>
+
+QQuickMapboxGLRenderer::QQuickMapboxGLRenderer()
+{
+ QMapbox::initializeGLExtensions();
+
+ QMapboxGLSettings settings;
+ settings.setAccessToken(qgetenv("MAPBOX_ACCESS_TOKEN"));
+ settings.setCacheDatabasePath("/tmp/mbgl-cache.db");
+ settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024);
+ settings.setViewportMode(QMapboxGLSettings::FlippedYViewport);
+
+ m_map.reset(new QMapboxGL(nullptr, settings));
+}
+
+QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer()
+{
+}
+
+QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const QSize &size)
+{
+ m_map->resize(size);
+
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+
+ return new QOpenGLFramebufferObject(size, format);
+}
+
+void QQuickMapboxGLRenderer::render()
+{
+ m_map->render();
+}
+
+void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item)
+{
+ if (!m_initialized) {
+ auto qquickMapbox = static_cast<QQuickMapboxGL*>(item);
+
+ QObject::connect(m_map.data(), &QMapboxGL::needsRendering, qquickMapbox, &QQuickMapboxGL::update);
+ QObject::connect(this, &QQuickMapboxGLRenderer::centerChanged, qquickMapbox, &QQuickMapboxGL::setCenter);
+
+ m_initialized = true;
+ }
+
+ auto quickMap = static_cast<QQuickMapboxGL*>(item);
+ auto syncStatus = quickMap->swapSyncState();
+
+ if (syncStatus & QQuickMapboxGL::CenterNeedsSync || syncStatus & QQuickMapboxGL::ZoomNeedsSync) {
+ const auto& center = quickMap->center();
+ m_map->setCoordinateZoom({ center.latitude(), center.longitude() }, quickMap->zoomLevel());
+ }
+
+ if (syncStatus & QQuickMapboxGL::StyleNeedsSync) {
+ m_map->setStyleURL(quickMap->style());
+ }
+
+ if (syncStatus & QQuickMapboxGL::PanNeedsSync) {
+ m_map->moveBy(quickMap->swapPan());
+ emit centerChanged(QGeoCoordinate(m_map->latitude(), m_map->longitude()));
+ }
+
+ if (syncStatus & QQuickMapboxGL::BearingNeedsSync) {
+ m_map->setBearing(quickMap->bearing());
+ }
+
+ if (syncStatus & QQuickMapboxGL::PitchNeedsSync) {
+ m_map->setPitch(quickMap->pitch());
+ }
+}
diff --git a/platform/qt/src/qquickmapboxglrenderer.hpp b/platform/qt/src/qquickmapboxglrenderer.hpp
new file mode 100644
index 0000000000..80a5cb3278
--- /dev/null
+++ b/platform/qt/src/qquickmapboxglrenderer.hpp
@@ -0,0 +1,35 @@
+#ifndef QQUICKMAPBOXGLRENDERER_H
+#define QQUICKMAPBOXGLRENDERER_H
+
+#include <QObject>
+#include <QQuickFramebufferObject>
+#include <QScopedPointer>
+
+class QGeoCoordinate;
+class QMapboxGL;
+class QOpenGLFramebufferObject;
+class QSize;
+
+class QQuickMapboxGLRenderer : public QObject, public QQuickFramebufferObject::Renderer
+{
+ Q_OBJECT
+
+public:
+ QQuickMapboxGLRenderer();
+ virtual ~QQuickMapboxGLRenderer();
+
+ virtual QOpenGLFramebufferObject* createFramebufferObject(const QSize& size);
+
+ virtual void render();
+ virtual void synchronize(QQuickFramebufferObject *item);
+
+signals:
+ void centerChanged(const QGeoCoordinate &coordinate);
+
+private:
+ bool m_initialized = false;
+
+ QScopedPointer<QMapboxGL> m_map;
+};
+
+#endif // QQUICKMAPBOXGLRENDERER_H
diff --git a/platform/qt/src/run_loop.cpp b/platform/qt/src/run_loop.cpp
new file mode 100644
index 0000000000..d33eb9cda6
--- /dev/null
+++ b/platform/qt/src/run_loop.cpp
@@ -0,0 +1,144 @@
+#include "run_loop_impl.hpp"
+
+#include <mbgl/util/thread_local.hpp>
+
+#include <QCoreApplication>
+
+#include <functional>
+#include <utility>
+
+#include <cassert>
+
+namespace {
+
+using namespace mbgl::util;
+static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
+
+}
+
+namespace mbgl {
+namespace util {
+
+void RunLoop::Impl::onReadEvent(int fd) {
+ readPoll[fd].second(fd, Event::Read);
+}
+
+void RunLoop::Impl::onWriteEvent(int fd) {
+ writePoll[fd].second(fd, Event::Write);
+}
+
+RunLoop* RunLoop::Get() {
+ return current.get();
+}
+
+RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
+ // XXX: We should probably throw an runtime exception
+ // here instead of creating a QCoreApplication which is
+ // way too intrusive. This is a hack mostly for the unit
+ // tests to work, as you always need a QCoreApplication
+ // prior to run a Qt app.
+ if (!QCoreApplication::instance()) {
+ static const char* argv[] = { "mbgl" };
+ static int argc = 1;
+
+ // We need to keep this around because it would otherwise crash
+ // on Qt4 due to a bug on QNetworkConfigurationManager when recreating
+ // a QCoreApplication: https://bugreports.qt.io/browse/QTBUG-36897
+ static auto* app = new QCoreApplication(argc, const_cast<char**>(argv));
+ Q_UNUSED(app);
+ }
+
+ switch (type) {
+ case Type::New:
+ impl->loop = std::make_unique<QEventLoop>();
+ break;
+ case Type::Default:
+ // Use QCoreApplication::instance().
+ break;
+ }
+
+ impl->type = type;
+
+ current.set(this);
+ impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
+}
+
+RunLoop::~RunLoop() {
+ MBGL_VERIFY_THREAD(tid);
+
+ current.set(nullptr);
+}
+
+LOOP_HANDLE RunLoop::getLoopHandle() {
+ throw std::runtime_error("Should not be used in Qt.");
+
+ return nullptr;
+}
+
+void RunLoop::push(std::shared_ptr<WorkTask> task) {
+ withMutex([&] { queue.push(task); });
+ impl->async->send();
+}
+
+void RunLoop::run() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (impl->type == Type::Default) {
+ QCoreApplication::instance()->exec();
+ } else {
+ impl->loop->exec();
+ }
+}
+
+void RunLoop::stop() {
+ invoke([&] {
+ if (impl->type == Type::Default) {
+ QCoreApplication::instance()->exit();
+ } else {
+ impl->loop->exit();
+ }
+ });
+}
+
+void RunLoop::runOnce() {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (impl->type == Type::Default) {
+ QCoreApplication::instance()->processEvents();
+ } else {
+ impl->loop->processEvents();
+ }
+}
+
+void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& cb) {
+ MBGL_VERIFY_THREAD(tid);
+
+ if (event == Event::Read || event == Event::ReadWrite) {
+ auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Read);
+ QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onReadEvent(int)));
+ impl->readPoll[fd] = WatchPair(std::move(notifier), std::move(cb));
+ }
+
+ if (event == Event::Write || event == Event::ReadWrite) {
+ auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Write);
+ QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onWriteEvent(int)));
+ impl->writePoll[fd] = WatchPair(std::move(notifier), std::move(cb));
+ }
+}
+
+void RunLoop::removeWatch(int fd) {
+ MBGL_VERIFY_THREAD(tid);
+
+ auto writePollIter = impl->writePoll.find(fd);
+ if (writePollIter != impl->writePoll.end()) {
+ impl->writePoll.erase(writePollIter);
+ }
+
+ auto readPollIter = impl->readPoll.find(fd);
+ if (readPollIter != impl->readPoll.end()) {
+ impl->readPoll.erase(readPollIter);
+ }
+}
+
+}
+}
diff --git a/platform/qt/src/run_loop_impl.hpp b/platform/qt/src/run_loop_impl.hpp
new file mode 100644
index 0000000000..6cabbbb425
--- /dev/null
+++ b/platform/qt/src/run_loop_impl.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <QEventLoop>
+#include <QObject>
+#include <QSocketNotifier>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace util {
+
+using WatchCallback = std::function<void(int, RunLoop::Event)>;
+using WatchPair = std::pair<std::unique_ptr<QSocketNotifier>, WatchCallback>;
+
+class RunLoop::Impl : public QObject {
+ Q_OBJECT
+
+public:
+ Impl() = default;
+
+ RunLoop::Type type;
+
+ std::unique_ptr<QEventLoop> loop;
+ std::unique_ptr<AsyncTask> async;
+
+ std::unordered_map<int, WatchPair> readPoll;
+ std::unordered_map<int, WatchPair> writePoll;
+
+public slots:
+ void onReadEvent(int fd);
+ void onWriteEvent(int fd);
+};
+
+}
+}
diff --git a/platform/qt/src/timer.cpp b/platform/qt/src/timer.cpp
new file mode 100644
index 0000000000..6d0ccb41d7
--- /dev/null
+++ b/platform/qt/src/timer.cpp
@@ -0,0 +1,57 @@
+#include <mbgl/util/timer.hpp>
+
+#include <mbgl/util/run_loop.hpp>
+
+#include <memory>
+
+#include "timer_impl.hpp"
+
+namespace mbgl {
+namespace util {
+
+Timer::Impl::Impl() {
+#if QT_VERSION >= 0x050000
+ timer.setTimerType(Qt::PreciseTimer);
+#endif
+ connect(&timer, SIGNAL(timeout()), this, SLOT(timerFired()));
+}
+
+void Timer::Impl::start(uint64_t timeout, uint64_t repeat_, std::function<void ()>&& cb) {
+ repeat = repeat_;
+ callback = std::move(cb);
+
+ timer.setSingleShot(true);
+ timer.start(timeout);
+}
+
+void Timer::Impl::stop() {
+ timer.stop();
+}
+
+void Timer::Impl::timerFired() {
+ if (repeat) {
+ timer.setSingleShot(false);
+ timer.start(repeat);
+ }
+
+ callback();
+}
+
+Timer::Timer()
+ : impl(std::make_unique<Impl>()) {
+}
+
+Timer::~Timer() = default;
+
+void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) {
+ impl->start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count(),
+ std::chrono::duration_cast<std::chrono::milliseconds>(repeat).count(),
+ std::move(cb));
+}
+
+void Timer::stop() {
+ impl->stop();
+}
+
+}
+}
diff --git a/platform/qt/src/timer_impl.hpp b/platform/qt/src/timer_impl.hpp
new file mode 100644
index 0000000000..a54abfd8b5
--- /dev/null
+++ b/platform/qt/src/timer_impl.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/timer.hpp>
+
+#include <QObject>
+#include <QTimer>
+
+namespace mbgl {
+namespace util {
+
+class Timer::Impl : public QObject {
+ Q_OBJECT
+
+public:
+ Impl();
+
+ void start(uint64_t timeout, uint64_t repeat, std::function<void ()>&& cb);
+ void stop();
+
+public slots:
+ void timerFired();
+
+private:
+ uint64_t repeat;
+ std::function<void()> callback;
+
+ QTimer timer;
+};
+
+}
+}