diff options
Diffstat (limited to 'platform')
58 files changed, 1131 insertions, 819 deletions
diff --git a/platform/android/.gitignore b/platform/android/.gitignore index 81eeaad167..7a3db0aafd 100644 --- a/platform/android/.gitignore +++ b/platform/android/.gitignore @@ -7,17 +7,16 @@ # Build files build/ +.externalNativeBuild *.so *.apk -# JNI -MapboxGLAndroidSDK/src/main/jniLibs/ - # Lib assets MapboxGLAndroidSDK/src/main/assets/ # Local settings local.properties +/configuration.gradle # Token file MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 2ce3780421..f2d28e53d1 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -31,6 +31,66 @@ android { buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"MapboxEventsAndroid/%s\"", project.VERSION_NAME) } + defaultPublishConfig project.hasProperty("mapbox.buildtype") ? project.getProperty("mapbox.buildtype") : "debug" + + // We sometimes want to invoke Gradle without building a native dependency, e.g. when we just want + // to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are + // added. When another ABI is specified explicitly, we're just going to build that ABI. In all other + // cases, all ABIs are built. + // When invoking from the command line, set `-Pmapbox.abis=...` to only build the desired architectures. + // When building from Android Studio, gradle.properties sets `android.buildOnlyTargetAbi=true` so that + // only the architecture for the device you're running on gets built. + def abi = 'all' + if (!project.hasProperty('android.injected.invoked.from.ide')) { + // Errors when the user invokes Gradle from the command line and didn't set mapbox.abis + abi = project.getProperty("mapbox.abis") + } + + if (abi != 'none') { + externalNativeBuild { + cmake { + path "../../../CMakeLists.txt" + } + } + } + + defaultConfig { + if (abi != 'none') { + externalNativeBuild { + cmake { + arguments "-DANDROID_TOOLCHAIN=clang" + arguments "-DANDROID_STL=c++_static" + arguments "-DANDROID_CPP_FEATURES=rtti;exceptions" + arguments "-DMBGL_PLATFORM=android" + arguments "-DMASON_PLATFORM=android" + arguments "-DNodeJS_EXECUTABLE=" + rootProject.ext.node + arguments "-Dnpm_EXECUTABLE=" + rootProject.ext.npm + + // Enable ccache if the user has installed it. + if (rootProject.ext.ccache?.trim()) { + arguments "-DANDROID_CCACHE=" + rootProject.ext.ccache + // ccache splits up the compile command until multiple invocations and uses -E + // with one of them, and clang doesn't like unused arguments in that case. + cFlags "-Qunused-arguments" + cppFlags "-Qunused-arguments" + } + + targets "mapbox-gl" + targets "example-custom-layer" + if (project.hasProperty("mapbox.with_test")) { + targets "mbgl-test" + } + + if (abi != 'all') { + abiFilters abi.split(' ') + } else { + abiFilters "armeabi", "armeabi-v7a", "mips", "x86", "arm64-v8a", "x86_64" + } + } + } + } + } + // avoid naming conflicts, force usage of prefix resourcePrefix 'mapbox_' diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 7a6f777a3b..9f555da5f8 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -14,3 +14,7 @@ POM_DEVELOPER_NAME=Mapbox POM_NAME=Mapbox Android SDK POM_ARTIFACT_ID=mapbox-android-sdk POM_PACKAGING=aar + +# Only build native dependencies for the current ABI +# See https://code.google.com/p/android/issues/detail?id=221098#c20 +android.buildOnlyTargetAbi=true 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 0bd9523f4f..9adefa3221 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 @@ -121,6 +121,7 @@ public class MapboxConstants { public static final String STATE_COMPASS_MARGIN_RIGHT = "mapbox_compassMarginRight"; public static final String STATE_COMPASS_MARGIN_BOTTOM = "mapbox_compassMarginBottom"; public static final String STATE_COMPASS_FADE_WHEN_FACING_NORTH = "mapbox_compassFade"; + public static final String STATE_COMPASS_IMAGE_BITMAP = "mapbox_compassImage"; public static final String STATE_LOGO_GRAVITY = "mapbox_logoGravity"; public static final String STATE_LOGO_MARGIN_LEFT = "mapbox_logoMarginLeft"; public static final String STATE_LOGO_MARGIN_TOP = "mapbox_logoMarginTop"; 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 6467033ead..2fd02c76e5 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 @@ -47,6 +47,7 @@ public class MapboxMapOptions implements Parcelable { private boolean fadeCompassFacingNorth = true; private int compassGravity = Gravity.TOP | Gravity.END; private int[] compassMargins; + private Drawable compassImage; private boolean logoEnabled = true; private int logoGravity = Gravity.BOTTOM | Gravity.START; @@ -100,6 +101,11 @@ public class MapboxMapOptions implements Parcelable { compassMargins = in.createIntArray(); fadeCompassFacingNorth = in.readByte() != 0; + Bitmap compassBitmap = in.readParcelable(getClass().getClassLoader()); + if (compassBitmap != null) { + compassImage = new BitmapDrawable(compassBitmap); + } + logoEnabled = in.readByte() != 0; logoGravity = in.readInt(); logoMargins = in.createIntArray(); @@ -147,7 +153,7 @@ public class MapboxMapOptions implements Parcelable { textureMode = in.readByte() != 0; } - public static Bitmap getBitmapFromDrawable(Drawable drawable) { + static Bitmap getBitmapFromDrawable(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } else { @@ -208,6 +214,12 @@ public class MapboxMapOptions implements Parcelable { DIMENSION_TEN_DP * pxlRatio))}); mapboxMapOptions.compassFadesWhenFacingNorth(typedArray.getBoolean( R.styleable.mapbox_MapView_mapbox_uiCompassFadeFacingNorth, true)); + Drawable compassDrawable = typedArray.getDrawable( + R.styleable.mapbox_MapView_mapbox_uiCompassDrawable); + if (compassDrawable == null) { + compassDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_compass_icon); + } + mapboxMapOptions.compassImage(compassDrawable); mapboxMapOptions.logoEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiLogo, true)); mapboxMapOptions.logoGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiLogoGravity, @@ -401,6 +413,20 @@ public class MapboxMapOptions implements Parcelable { } /** + * Specifies the image of the CompassView. + * <p> + * By default this value is R.drawable.mapbox_compass_icon. + * </p> + * + * @param compass the drawable to show as image compass + * @return This + */ + public MapboxMapOptions compassImage(Drawable compass) { + this.compassImage = compass; + return this; + } + + /** * Specifies the visibility state of a logo for a map view. * * @param enabled True and logo is shown @@ -743,6 +769,15 @@ public class MapboxMapOptions implements Parcelable { } /** + * Get the current configured CompassView image. + * + * @return the drawable used as compass image + */ + public Drawable getCompassImage() { + return compassImage; + } + + /** * Get the current configured visibility state for mapbox_compass_icon for a map view. * * @return Visibility state of the mapbox_compass_icon @@ -993,6 +1028,8 @@ public class MapboxMapOptions implements Parcelable { dest.writeInt(compassGravity); dest.writeIntArray(compassMargins); dest.writeByte((byte) (fadeCompassFacingNorth ? 1 : 0)); + dest.writeParcelable(compassImage != null + ? getBitmapFromDrawable(compassImage) : null, flags); dest.writeByte((byte) (logoEnabled ? 1 : 0)); dest.writeInt(logoGravity); @@ -1052,6 +1089,11 @@ public class MapboxMapOptions implements Parcelable { if (fadeCompassFacingNorth != options.fadeCompassFacingNorth) { return false; } + if (compassImage != null + ? !compassImage.equals(options.compassImage) + : options.compassImage != null) { + return false; + } if (compassGravity != options.compassGravity) { return false; } @@ -1157,6 +1199,7 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + (compassEnabled ? 1 : 0); result = 31 * result + (fadeCompassFacingNorth ? 1 : 0); result = 31 * result + compassGravity; + result = 31 * result + (compassImage != null ? compassImage.hashCode() : 0); result = 31 * result + Arrays.hashCode(compassMargins); result = 31 * result + (logoEnabled ? 1 : 0); result = 31 * result + logoGravity; 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 1460f08e10..c5eaf2deb9 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 @@ -938,7 +938,11 @@ final class NativeMapView { protected void onMapChanged(int rawChange) { if (onMapChangedListeners != null) { for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - onMapChangedListener.onMapChanged(rawChange); + try { + onMapChangedListener.onMapChanged(rawChange); + } catch (RuntimeException err) { + Timber.e("Exception (%s) in MapView.OnMapChangedListener: %s", err.getClass(), err.getMessage()); + } } } } 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 8a3ae1e4f3..bcb4ca4afc 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 @@ -3,8 +3,12 @@ package com.mapbox.mapboxsdk.maps; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PointF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; @@ -22,6 +26,8 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.utils.ColorUtils; +import java.io.ByteArrayOutputStream; + /** * Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings(). */ @@ -143,6 +149,7 @@ public final class UiSettings { setCompassMargins(tenDp, tenDp, tenDp, tenDp); } setCompassFadeFacingNorth(options.getCompassFadeFacingNorth()); + setCompassImage(options.getCompassImage()); } private void saveCompass(Bundle outState) { @@ -153,6 +160,16 @@ public final class UiSettings { outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM, getCompassMarginBottom()); outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT, getCompassMarginRight()); outState.putBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH, isCompassFadeWhenFacingNorth()); + outState.putByteArray(MapboxConstants.STATE_COMPASS_IMAGE_BITMAP, + convert(MapboxMapOptions.getBitmapFromDrawable(getCompassImage()))); + } + + private byte[] convert(Bitmap resource) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + resource.compress(Bitmap.CompressFormat.PNG, 100, stream); + byte[] byteArray = stream.toByteArray(); + + return byteArray; } private void restoreCompass(Bundle savedInstanceState) { @@ -163,6 +180,15 @@ public final class UiSettings { savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT), savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM)); setCompassFadeFacingNorth(savedInstanceState.getBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH)); + setCompassImage(decode(savedInstanceState.getByteArray(MapboxConstants.STATE_COMPASS_IMAGE_BITMAP))); + } + + private Drawable decode(byte[] bitmap) { + Bitmap compass = BitmapFactory.decodeByteArray(bitmap, 0, bitmap.length); + + Drawable compassImage = new BitmapDrawable(compassView.getResources(), compass); + + return compassImage; } private void initialiseLogo(MapboxMapOptions options, Resources resources) { @@ -297,6 +323,18 @@ public final class UiSettings { } /** + * Specifies the CompassView image. + * <p> + * By default this value is R.drawable.mapbox_compass_icon. + * </p> + * + * @param compass the drawable to show as image compass + */ + public void setCompassImage(Drawable compass) { + compassView.setCompassImage(compass); + } + + /** * Returns whether the compass performs a fading animation out when facing north. * * @return True if the compass will fade, false if it remains visible @@ -364,6 +402,15 @@ public final class UiSettings { return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).bottomMargin; } + /** + * Get the current configured CompassView image. + * + * @return the drawable used as compass image + */ + public Drawable getCompassImage() { + return compassView.getCompassImage(); + } + void update(@NonNull CameraPosition cameraPosition) { if (!isCompassEnabled()) { return; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 5c9cf93ebc..48bfae4a6e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -2,9 +2,9 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.content.Context; import android.graphics.PointF; +import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; @@ -13,7 +13,6 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.maps.FocalPointChangeListener; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -55,7 +54,6 @@ public final class CompassView extends AppCompatImageView implements Runnable, F } private void initialize(Context context) { - setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.mapbox_compass_icon)); setEnabled(false); // Layout params @@ -140,6 +138,24 @@ public final class CompassView extends AppCompatImageView implements Runnable, F return fadeCompassViewFacingNorth; } + /** + * Set the CompassView image. + * + * @param compass the drawable to use as compass image + */ + public void setCompassImage(Drawable compass) { + setImageDrawable(compass); + } + + /** + * Get the current configured CompassView image. + * + * @return the drawable used as compass image + */ + public Drawable getCompassImage() { + return getDrawable(); + } + @Override public void run() { if (isFacingNorth() && fadeCompassViewFacingNorth) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index 641020906a..7283aced5f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -54,6 +54,7 @@ <public name="mapbox_uiCompassMarginRight" type="attr" /> <public name="mapbox_uiCompassMarginBottom" type="attr" /> <public name="mapbox_uiCompassFadeFacingNorth" type="attr" /> + <public name="mapbox_uiCompassDrawable" type="attr" /> <!--Logo--> <public name="mapbox_uiLogo" type="attr" /> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index 738cae07be..e17f01d075 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -64,6 +64,7 @@ <attr name="mapbox_uiCompassMarginRight" format="dimension"/> <attr name="mapbox_uiCompassMarginBottom" format="dimension"/> <attr name="mapbox_uiCompassFadeFacingNorth" format="boolean"/> + <attr name="mapbox_uiCompassDrawable" format="reference"/> <!--Logo--> <attr name="mapbox_uiLogo" format="boolean"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/README.md b/platform/android/MapboxGLAndroidSDKTestApp/README.md index 0acb509b3e..c7a918cd48 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/README.md +++ b/platform/android/MapboxGLAndroidSDKTestApp/README.md @@ -1,5 +1,15 @@ # Mapbox GL Test App +## Building + +To be able to run any Gradle commands, you'll need to create the configuration file by running + +``` +$ make android-configuration +``` + +from the root folder. + ## Testing ### Running Espresso tests on a device @@ -28,7 +38,7 @@ On a terminal, within `mapbox-gl-native/android/java`, run the tests (`cC` stands for `connectedCheck`): ``` -$ ./gradlew cC -p MapboxGLAndroidSDKTestApp +$ ./gradlew -Pmapbox.abis=all cC -p MapboxGLAndroidSDKTestApp ``` Then: @@ -63,7 +73,7 @@ You can also have a run configuration: You can also run the tests from the command line with: ``` -$ ./gradlew test --continue -p MapboxGLAndroidSDKTestApp +$ ./gradlew -Pmapbox.abis=none test -p MapboxGLAndroidSDKTestApp ``` ### Running the UI/Application Exerciser Monkey diff --git a/platform/android/bitrise.yml b/platform/android/bitrise.yml index de1095409e..53637fd498 100644 --- a/platform/android/bitrise.yml +++ b/platform/android/bitrise.yml @@ -14,30 +14,7 @@ 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 Checkstyle - run_if: '{{enveq "SKIPCI" "false"}}' - inputs: - - content: |- - #!/bin/bash - # Run checkstyle gradle task on all modules - cd platform/android && ./gradlew checkstyle - - script: title: Configure Google Cloud SDK - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -58,16 +35,17 @@ workflows: wget -O secret.json "$BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" - script: title: Build libmapbox-gl.so for armeabi-v7a - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash + # Accept Android SDK license + echo "Accepting Android SDK license..." + mkdir -p "${ANDROID_HOME}/licenses" + echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${ANDROID_HOME}/licenses/android-sdk-license" echo "Compile libmapbox-gl.so for armeabi-v7a abi:" - export BUILDTYPE=Debug - make android-lib-arm-v7 + BUILDTYPE=Debug make android-lib-arm-v7 - script: title: Compile Core tests - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -75,7 +53,6 @@ workflows: BUILDTYPE=Debug make android-test-lib-arm-v7 - script: title: Run local JVM Unit tests on phone module - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -83,7 +60,6 @@ workflows: make run-android-unit-test - script: title: Run local JVM Unit tests on wear module - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -91,15 +67,20 @@ workflows: make run-android-wear-unit-test - script: title: Generate Espresso sanity tests - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash echo "Generate these test locally by executing:" make test-code-android - script: + title: Run Checkstyle + inputs: + - content: |- + #!/bin/bash + # Run checkstyle gradle task on all modules + make android-checkstyle + - script: title: Run Firebase instrumentation tests - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -107,7 +88,7 @@ workflows: wget -O platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml "$BITRISEIO_TEST_ACCESS_TOKEN_UI_TEST_URL" echo "Build seperate test apk:" - make android-ui-test + make android-ui-test-arm-v7 echo "Run tests on firebase:" gcloud auth activate-service-account --key-file secret.json --project android-gl-native @@ -115,7 +96,6 @@ workflows: gcloud beta test android run --type instrumentation --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest.apk --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 15m --test-targets "class com.mapbox.mapboxsdk.testapp.maps.widgets.AttributionTest" - script: title: Download Firebase results - run_if: '{{enveq "SKIPCI" "false"}}' is_always_run: true inputs: - content: |- @@ -131,13 +111,11 @@ workflows: find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk -type f -name "logcat" -print0 | xargs -0 -Imylogcat mv -i mylogcat ./ cat logcat | ndk-stack -sym build/android-arm-v7/Debug - deploy-to-bitrise-io: - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - deploy_path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk - is_compress: 'true' - notify_user_groups: none - slack: - run_if: '{{enveq "SKIPCI" "false"}}' title: Post to Slack inputs: - webhook_url: "$SLACK_HOOK_URL" @@ -181,15 +159,19 @@ workflows: inputs: - content: |- #!/bin/bash - echo "Compile libmapbox-gl.so for all supported abi's and create an aar file:" - BUILDTYPE=Release make apackage + # Accept Android SDK license + echo "Accepting Android SDK license..." + mkdir -p "${ANDROID_HOME}/licenses" + echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${ANDROID_HOME}/licenses/android-sdk-license" + echo "Compile libmapbox-gl.so for all supportd abi's:" + export BUILDTYPE=Release make apackage - script: title: Publish to maven inputs: - content: |- #!/bin/bash echo "Upload aar file to maven:" - cd platform/android && ./gradlew :MapboxGLAndroidSDK:uploadArchives + make run-android-upload-archives - slack: title: Post to Slack inputs: @@ -208,6 +190,10 @@ workflows: inputs: - content: |- #!/bin/bash + # Accept Android SDK license + echo "Accepting Android SDK license..." + mkdir -p "${ANDROID_HOME}/licenses" + echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${ANDROID_HOME}/licenses/android-sdk-license" echo "Compile libmapbox-gl.so for all supportd abi's:" export BUILDTYPE=Release make apackage @@ -231,7 +217,7 @@ workflows: - content: |- #!/bin/bash echo "Run tests on device farm:" - cd platform/android && ./gradlew devicefarmUpload + make run-android-ui-test-aws - slack: title: Post to Slack inputs: @@ -257,10 +243,13 @@ workflows: inputs: - content: |- #!/bin/bash + # Accept Android SDK license + echo "Accepting Android SDK license..." + mkdir -p "${ANDROID_HOME}/licenses" + echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${ANDROID_HOME}/licenses/android-sdk-license" echo "Compile libmapbox-gl.so for all supportd abi's:" export BUILDTYPE=Release make apackage - cd platform/android && ./gradlew :MapboxGLAndroidSDK:assembleRelease - script: title: Log metrics inputs: diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 4219f2bdee..5e45232716 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -21,3 +21,6 @@ task wrapper(type: Wrapper) { apply from: rootProject.file('dependencies.gradle') +// Load build system information. If this file does not exist, run +// `make platform/android/configuration.gradle` +apply from: rootProject.file('configuration.gradle') diff --git a/platform/android/config.cmake b/platform/android/config.cmake index fdf03b4ec8..1a68ee065d 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -22,15 +22,6 @@ mason_use(sqlite VERSION 3.14.2) mason_use(gtest VERSION 1.8.0) mason_use(icu VERSION 58.1-min-size) -set(ANDROID_SDK_PROJECT_DIR ${CMAKE_SOURCE_DIR}/platform/android/MapboxGLAndroidSDK) -set(ANDROID_JNI_TARGET_DIR ${ANDROID_SDK_PROJECT_DIR}/src/main/jniLibs/${ANDROID_ABI}) -set(ANDROID_ASSETS_TARGET_DIR ${ANDROID_SDK_PROJECT_DIR}/src/main/assets) -set(ANDROID_TEST_APP_JNI_TARGET_DIR ${CMAKE_SOURCE_DIR}/platform/android/MapboxGLAndroidSDKTestApp/src/main/jniLibs/${ANDROID_ABI}) - -if (NOT DEFINED ANDROID_TOOLCHAIN_PREFIX) - set(ANDROID_TOOLCHAIN_PREFIX "${MASON_XC_ROOT}/bin/${ANDROID_TOOLCHAIN}-") -endif() - ## mbgl core ## macro(mbgl_platform_core) @@ -251,11 +242,6 @@ target_link_libraries(mapbox-gl PUBLIC -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script ) -# Create a stripped version of the library and copy it to the JNIDIR. -add_custom_command(TARGET mapbox-gl POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${ANDROID_JNI_TARGET_DIR} - COMMAND ${ANDROID_TOOLCHAIN_PREFIX}strip $<TARGET_FILE:mapbox-gl> -o ${ANDROID_JNI_TARGET_DIR}/$<TARGET_FILE_NAME:mapbox-gl>) - ## Test library ## add_library(mbgl-test SHARED @@ -312,11 +298,6 @@ target_add_mason_package(mbgl-test PRIVATE boost) target_add_mason_package(mbgl-test PRIVATE geojson) target_add_mason_package(mbgl-test PRIVATE geojsonvt) -add_custom_command(TARGET mbgl-test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stripped - COMMAND ${ANDROID_TOOLCHAIN_PREFIX}strip $<TARGET_FILE:mapbox-gl> -o ${CMAKE_CURRENT_BINARY_DIR}/stripped/$<TARGET_FILE_NAME:mapbox-gl> - COMMAND ${ANDROID_TOOLCHAIN_PREFIX}strip $<TARGET_FILE:mbgl-test> -o ${CMAKE_CURRENT_BINARY_DIR}/stripped/$<TARGET_FILE_NAME:mbgl-test>) - ## Custom layer example ## add_library(example-custom-layer SHARED @@ -334,7 +315,3 @@ target_link_libraries(example-custom-layer PUBLIC -Wl,--gc-sections PUBLIC -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script ) - -add_custom_command(TARGET example-custom-layer POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${ANDROID_TEST_APP_JNI_TARGET_DIR} - COMMAND ${ANDROID_TOOLCHAIN_PREFIX}strip $<TARGET_FILE:example-custom-layer> -o ${ANDROID_TEST_APP_JNI_TARGET_DIR}/$<TARGET_FILE_NAME:example-custom-layer>) diff --git a/platform/android/scripts/debug.sh b/platform/android/scripts/debug.sh deleted file mode 100755 index c3390b4ce1..0000000000 --- a/platform/android/scripts/debug.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/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_ANDROID_ARCH=x86 -export MASON_ANDROID_PLATFORM=9 -export MASON_ANDROID_NDK_VERSION=r13b - -export MASON_XC_ROOT=`scripts/mason.sh PREFIX android-ndk VERSION ${MASON_ANDROID_ARCH}-${MASON_ANDROID_PLATFORM}-${MASON_ANDROID_NDK_VERSION}` -source ${MASON_XC_ROOT}/toolchain.sh - -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_ARCH} == 'arm-v8' || ${MASON_ANDROID_ARCH} == 'x86-64' || ${MASON_ANDROID_ARCH} == 'mips-64' ]]; then - adb pull /system/bin/app_process64 ~/.android/debugging - adb pull /system/bin/linker64 ~/.android/debugging - fi - - cp ${MASON_XC_ROOT}/prebuilt/gdbserver/gdbserver \ - platform/android/MapboxGLAndroidSDK/src/main/jniLibs/${ANDROID_ABI}/gdbserver.so -fi - -adb install -r -t -d -g 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`' & - -${MASON_XC_ROOT}/bin/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_ARCH}-${MASON_ANDROID_PLATFORM}/Debug/lib.target/" diff --git a/platform/android/scripts/metrics.sh b/platform/android/scripts/metrics.sh index 37d8c1de65..974ee5e8db 100755 --- a/platform/android/scripts/metrics.sh +++ b/platform/android/scripts/metrics.sh @@ -4,12 +4,12 @@ set -e set -o pipefail # Track individual architectures -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/armeabi/libmapbox-gl.so" "Platform=Android,Arch=arm-v5" -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so" "Platform=Android,Arch=arm-v7" -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/arm64-v8a/libmapbox-gl.so" "Platform=Android,Arch=arm-v8" -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/x86/libmapbox-gl.so" "Platform=Android,Arch=x86" -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/x86_64/libmapbox-gl.so" "Platform=Android,Arch=x86_64" -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/src/main/jniLibs/mips/libmapbox-gl.so" "Platform=Android,Arch=mips" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/armeabi/libmapbox-gl.so" "Platform=Android,Arch=arm-v5" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/armeabi-v7a/libmapbox-gl.so" "Platform=Android,Arch=arm-v7" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/arm64-v8a/libmapbox-gl.so" "Platform=Android,Arch=arm-v8" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/x86/libmapbox-gl.so" "Platform=Android,Arch=x86" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/x86_64/libmapbox-gl.so" "Platform=Android,Arch=x86_64" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/mips/libmapbox-gl.so" "Platform=Android,Arch=mips" # Track overall library size -scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/outputs/aar/MapboxGLAndroidSDK-release.aar" "Platform=Android,Arch=Archive" +scripts/log_binary_size.sh "platform/android/MapboxGLAndroidSDK/build/outputs/aar/MapboxGLAndroidSDK-release.aar" "Platform=Android,Arch=Archive" diff --git a/platform/android/scripts/ndk.sh b/platform/android/scripts/ndk.sh deleted file mode 100755 index 96a314a3c2..0000000000 --- a/platform/android/scripts/ndk.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail -set -u - -# This script produces an env.sh file, which contains the paths to CMake, and the flags required to -# create a build. It first tries to use the Android NDK, but falls back to installing it via Mason. - -function error { >&2 echo -e "\033[1m\033[31m$@\033[0m"; } -function warning { >&2 echo -e "\033[1m\033[33m$@\033[0m"; } -function status { >&2 echo -e "\033[1m\033[36m$@\033[0m"; } -function info { >&2 echo -e "\033[1m\033[32m$@\033[0m"; } - -if [ "$#" -ne 3 ]; then - error "Usage: $0 <short arch> <long arch> <api level>" -fi - -NDK_ANDROID_VERSION=$1-$3 -ANDROID_NATIVE_API_LEVEL=$3 -ANDROID_ABI=$2 - -function mason_ndk { - local CMAKE=${CMAKE:-cmake} - MASON_XC_ROOT="`${CMAKE} -P cmake/mason.cmake PREFIX android-ndk VERSION ${NDK_ANDROID_VERSION}-r13b`" - - local TOOLCHAIN="${MASON_XC_ROOT}/toolchain.cmake" - if [ ! -f "${TOOLCHAIN}" ]; then - error "Can't find CMake toolchain file at ${TOOLCHAIN}." - exit 1 - fi - - info "Using Mason-provided Android NDK at ${MASON_XC_ROOT}" - echo CMAKE=\"${CMAKE}\" - echo CMAKE_GENERATOR=\"Ninja\" - echo CMAKE_ARGS=\" \ - -DCMAKE_MAKE_PROGRAM=`pwd`/${NINJA} \ - -DMASON_XC_ROOT=${MASON_XC_ROOT} \ - -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} \ - \" -} - -function system_ndk { - if [[ ${USE_MASON_NDK:-} ]]; then - return 1 - fi - - if [ -f platform/android/local.properties ]; then - local SDK_DIR=$(sed -n -e 's/^sdk.dir=\(.*\)$/\1/p' platform/android/local.properties) - fi - - if [ ! -d "${SDK_DIR:-}" ]; then - if [ ! -z "${ANDROID_HOME:-}" ]; then - local SDK_DIR="${ANDROID_HOME}" - else - error "Can't find the Android SDK. Set \$ANDROID_HOME to the SDK path." - exit 1 - fi - fi - - local NDK_DIR="${ANDROID_NDK_HOME:-${SDK_DIR}/ndk-bundle}" - if [ ! -d "${NDK_DIR}" ]; then - warning "Can't find the Android NDK. If it is installed, set \$ANDROID_NDK_HOME to the NDK path." - return 1 - fi - - # Try to install CMake if it's not installed yet. - mkdir -p "${SDK_DIR}/cmake" - local CMAKE_VERSION=/$(ls "${SDK_DIR}/cmake" | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -n 1) - local CMAKE="${SDK_DIR}/cmake${CMAKE_VERSION:-}/bin/cmake" - if [ ! -f "${CMAKE}" ]; then - status "Trying to install CMake..." - mkdir -p "${SDK_DIR}/licenses" - echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "${SDK_DIR}/licenses/android-sdk-license" - "${SDK_DIR}/tools/bin/sdkmanager" --list | grep cmake | tail -n 1 | cut -d \| -f 1 | xargs "${SDK_DIR}/tools/bin/sdkmanager" >&2 - CMAKE_VERSION=/$(ls "${SDK_DIR}/cmake" | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n | tail -n 1) - CMAKE="${SDK_DIR}/cmake${CMAKE_VERSION:-}/bin/cmake" - if [ ! -f "${CMAKE}" ]; then - error "Can't find CMake at ${CMAKE}." - return 1 - fi - fi - - local NINJA="${SDK_DIR}/cmake${CMAKE_VERSION:-}/bin/ninja" - if [ ! -f "${NINJA}" ]; then - error "Can't find Ninja at ${NINJA}." - return 1 - fi - - local TOOLCHAIN="${NDK_DIR}/build/cmake/android.toolchain.cmake" - if [ ! -f "${TOOLCHAIN}" ]; then - error "Can't find CMake toolchain file at ${TOOLCHAIN}." - return 1 - fi - - info "Using system-provided Android NDK at ${NDK_DIR}" - echo CMAKE=\"${CMAKE}\" - echo CMAKE_GENERATOR=\"Android Gradle - Ninja\" - echo CMAKE_ARGS=\" \ - -DANDROID_ABI=${ANDROID_ABI} \ - -DANDROID_NDK=${NDK_DIR} \ - -DCMAKE_MAKE_PROGRAM=${NINJA} \ - -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} \ - -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ - -DANDROID_TOOLCHAIN=clang \ - -DANDROID_STL=c++_static \ - -DANDROID_CPP_FEATURES=rtti\;exceptions \ - \" -} - -system_ndk || mason_ndk diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 696849b11b..af8ae16bf6 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -181,6 +181,70 @@ void NativeMapView::notifyMapChange(mbgl::MapChange change) { javaPeer->Call(*_env, onMapChanged, (int) change); } +void NativeMapView::onCameraWillChange(MapObserver::CameraChangeMode mode) { + if (mode == MapObserver::CameraChangeMode::Immediate) { + notifyMapChange(MapChange::MapChangeRegionWillChange); + } else { + notifyMapChange(MapChange::MapChangeRegionWillChangeAnimated); + } +} + +void NativeMapView::onCameraIsChanging() { + notifyMapChange(MapChange::MapChangeRegionIsChanging); +} + +void NativeMapView::onCameraDidChange(MapObserver::CameraChangeMode mode) { + if (mode == MapObserver::CameraChangeMode::Immediate) { + notifyMapChange(MapChange::MapChangeRegionDidChange); + } else { + notifyMapChange(MapChange::MapChangeRegionDidChangeAnimated); + } +} + +void NativeMapView::onWillStartLoadingMap() { + notifyMapChange(MapChange::MapChangeWillStartLoadingMap); +} + +void NativeMapView::onDidFinishLoadingMap() { + notifyMapChange(MapChange::MapChangeDidFinishLoadingMap); +} + +void NativeMapView::onDidFailLoadingMap(std::exception_ptr) { + notifyMapChange(MapChange::MapChangeDidFailLoadingMap); +} + +void NativeMapView::onWillStartRenderingFrame() { + notifyMapChange(MapChange::MapChangeWillStartRenderingFrame); +} + +void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderMode mode) { + if (mode == MapObserver::RenderMode::Partial) { + notifyMapChange(MapChange::MapChangeDidFinishRenderingFrame); + } else { + notifyMapChange(MapChange::MapChangeDidFinishRenderingFrameFullyRendered); + } +} + +void NativeMapView::onWillStartRenderingMap() { + notifyMapChange(MapChange::MapChangeWillStartRenderingMap); +} + +void NativeMapView::onDidFinishRenderingMap(MapObserver::RenderMode mode) { + if (mode == MapObserver::RenderMode::Partial) { + notifyMapChange(MapChange::MapChangeDidFinishRenderingMap); + } else { + notifyMapChange(MapChange::MapChangeDidFinishRenderingMapFullyRendered); + } +} + +void NativeMapView::onDidFinishLoadingStyle() { + notifyMapChange(MapChange::MapChangeDidFinishLoadingStyle); +} + +void NativeMapView::onSourceChanged(mbgl::style::Source&) { + notifyMapChange(MapChange::MapChangeSourceDidChange); +} + // JNI Methods // void NativeMapView::initializeDisplay(jni::JNIEnv&) { diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 4afea036bf..fbfe69040e 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/map/backend.hpp> +#include <mbgl/map/change.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/map.hpp> #include <mbgl/map/view.hpp> @@ -22,6 +23,7 @@ #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" +#include <exception> #include <string> #include <jni.h> #include <android/native_window.h> @@ -51,7 +53,23 @@ public: // mbgl::Backend // void invalidate() override; - void notifyMapChange(mbgl::MapChange) override; + + // Deprecated // + void notifyMapChange(mbgl::MapChange); + + // mbgl::Backend (mbgl::MapObserver) // + void onCameraWillChange(MapObserver::CameraChangeMode) override; + void onCameraIsChanging() override; + void onCameraDidChange(MapObserver::CameraChangeMode) override; + void onWillStartLoadingMap() override; + void onDidFinishLoadingMap() override; + void onDidFailLoadingMap(std::exception_ptr) override; + void onWillStartRenderingFrame() override; + void onDidFinishRenderingFrame(MapObserver::RenderMode) override; + void onWillStartRenderingMap() override; + void onDidFinishRenderingMap(MapObserver::RenderMode) override; + void onDidFinishLoadingStyle() override; + void onSourceChanged(mbgl::style::Source&) override; // JNI // diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md index 9341cf1e1e..6d8541a406 100644 --- a/platform/android/tests/docs/UI_TESTS.md +++ b/platform/android/tests/docs/UI_TESTS.md @@ -25,7 +25,7 @@ On a terminal, within `mapbox-gl-native/android/java`, run the tests (`cC` stands for `connectedCheck`): ``` -$ ./gradlew cC -p MapboxGLAndroidSDKTestApp +$ ./gradlew -Pmapbox.abis=all cC -p MapboxGLAndroidSDKTestApp ``` Then: @@ -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 run tests on AWS device farm you need to execute `./gradlew devicefarmUpload`. +To run tests on AWS device farm you need to execute `./gradlew -Pmapbox.abis=none devicefarmUpload`. You can configure the different steps in the testapp `build.gradle`. AWS credentials are found in bitrise. diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md index e32a36ef78..fefb435684 100644 --- a/platform/android/tests/docs/UNIT_TESTS.md +++ b/platform/android/tests/docs/UNIT_TESTS.md @@ -69,7 +69,7 @@ If you like, you can also run with test coverage enabled. This will show you the You can also run the tests from the command line with: ``` -$ ./gradlew test --continue -p MapboxGLAndroidSDKTestApp +$ ./gradlew -Pmapbox.abis=none test -p MapboxGLAndroidSDKTestApp ``` ## Running Unit tests on CI @@ -80,7 +80,7 @@ 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): ``` -$ ./gradlew testReleaseUnitTest --continue +$ ./gradlew -Pmapbox.abis=none testReleaseUnitTest ``` diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index 5216e921f1..c06fd8b0e7 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -42,6 +42,10 @@ typedef NS_ENUM(NSInteger, MGLErrorCode) { MGLErrorCodeBadServerResponse = 2, /** An attempt to establish a connection failed. */ MGLErrorCodeConnectionFailed = 3, + /** A style parse error occurred while attempting to load the map. */ + MGLErrorCodeParseStyleFailed = 4, + /** An attempt to load the style failed. */ + MGLErrorCodeLoadStyleFailed = 5, }; /** Options for enabling debugging features in an `MGLMapView` instance. */ diff --git a/platform/default/async_task.cpp b/platform/default/async_task.cpp index 05cf759863..5fa9db8d33 100644 --- a/platform/default/async_task.cpp +++ b/platform/default/async_task.cpp @@ -7,12 +7,6 @@ #include <uv.h> -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int -#else -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle -#endif - namespace mbgl { namespace util { @@ -45,7 +39,7 @@ public: } private: - static void asyncCallback(UV_ASYNC_PARAMS(handle)) { + static void asyncCallback(uv_async_t* handle) { reinterpret_cast<Impl*>(handle->data)->task(); } diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp index c105fd6b84..6ae19356fb 100644 --- a/platform/default/mbgl/gl/headless_backend.cpp +++ b/platform/default/mbgl/gl/headless_backend.cpp @@ -50,10 +50,4 @@ void HeadlessBackend::invalidate() { assert(false); } -void HeadlessBackend::notifyMapChange(MapChange change) { - if (mapChangeCallback) { - mapChangeCallback(change); - } -} - } // namespace mbgl diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp index e632d0feb6..63e3cec9ee 100644 --- a/platform/default/mbgl/gl/headless_backend.hpp +++ b/platform/default/mbgl/gl/headless_backend.hpp @@ -18,9 +18,6 @@ public: ~HeadlessBackend() override; void invalidate() override; - void notifyMapChange(MapChange) override; - - void setMapChangeCallback(std::function<void(MapChange)>&& cb) { mapChangeCallback = std::move(cb); } struct Impl { virtual ~Impl() {} @@ -45,8 +42,6 @@ private: bool extensionsLoaded = false; bool active = false; - - std::function<void(MapChange)> mapChangeCallback; }; } // namespace mbgl diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp index 1ebbade7ab..98d1badcb5 100644 --- a/platform/default/run_loop.cpp +++ b/platform/default/run_loop.cpp @@ -13,11 +13,7 @@ namespace { using namespace mbgl::util; static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -void dummyCallback(uv_async_t*, int) {} -#else void dummyCallback(uv_async_t*) {} -#endif } // namespace @@ -84,13 +80,8 @@ public: RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) { switch (type) { case Type::New: -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - impl->loop = uv_loop_new(); - if (impl->loop == nullptr) { -#else impl->loop = new uv_loop_t; if (uv_loop_init(impl->loop) != 0) { -#endif throw std::runtime_error("Failed to initialize loop."); } break; @@ -129,14 +120,10 @@ RunLoop::~RunLoop() { impl->async.reset(); runOnce(); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_loop_delete(impl->loop); -#else if (uv_loop_close(impl->loop) == UV_EBUSY) { - throw std::runtime_error("Failed to close loop."); + assert(false && "Failed to close loop."); } delete impl->loop; -#endif } LOOP_HANDLE RunLoop::getLoopHandle() { diff --git a/platform/default/timer.cpp b/platform/default/timer.cpp index 473f059133..cd0e6da6aa 100644 --- a/platform/default/timer.cpp +++ b/platform/default/timer.cpp @@ -4,12 +4,6 @@ #include <uv.h> -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer, int -#else -#define UV_TIMER_PARAMS(timer) uv_timer_t *timer -#endif - namespace mbgl { namespace util { @@ -46,7 +40,7 @@ public: } private: - static void timerCallback(UV_TIMER_PARAMS(handle)) { + static void timerCallback(uv_timer_t* handle) { reinterpret_cast<Impl*>(handle->data)->cb(); } diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 4070a0fe9b..39dca8080b 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -538,16 +538,6 @@ void GLFWView::setWindowTitle(const std::string& title) { glfwSetWindowTitle(window, (std::string { "Mapbox GL: " } + title).c_str()); } -void GLFWView::setMapChangeCallback(std::function<void(mbgl::MapChange)> callback) { - this->mapChangeCallback = callback; -} - -void GLFWView::notifyMapChange(mbgl::MapChange change) { - if (mapChangeCallback) { - mapChangeCallback(change); - } -} - namespace mbgl { namespace platform { diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp index a426e58a94..bfabf6cc68 100644 --- a/platform/glfw/glfw_view.hpp +++ b/platform/glfw/glfw_view.hpp @@ -60,9 +60,6 @@ private: // Internal void report(float duration); - void setMapChangeCallback(std::function<void(mbgl::MapChange)> callback); - void notifyMapChange(mbgl::MapChange change) override; - mbgl::Color makeRandomColor() const; mbgl::Point<double> makeRandomPoint() const; static std::shared_ptr<const mbgl::SpriteImage> @@ -81,8 +78,6 @@ private: mbgl::AnnotationIDs annotationIDs; std::vector<std::string> spriteIDs; - std::function<void(mbgl::MapChange)> mapChangeCallback; - private: mbgl::Map* map = nullptr; diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 40795a2aef..e75f90dd5d 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## master + +* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) + ## 3.5.0 ### Packaging @@ -68,6 +72,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue that sometimes caused crashes when the SDK interacted with the file system in the background. ([#8125](https://github.com/mapbox/mapbox-gl-native/pull/8125)) * Added a `MGLDistanceFormatter` class for formatting geographic distances. ([#7888](https://github.com/mapbox/mapbox-gl-native/pull/7888)) * Fixed an issue that was causing the system location indicator to stay on in background after telemetry was disabled. ([#7833](https://github.com/mapbox/mapbox-gl-native/pull/7833)) +* Added support for predicates in rendered feature querying [8256](https://github.com/mapbox/mapbox-gl-native/pull/8246) +* Added a nightly build of the dynamic framework. ([#8337](https://github.com/mapbox/mapbox-gl-native/pull/8337)) ## 3.4.2 - February 21, 2017 @@ -117,7 +123,7 @@ This is the final scheduled version of the Mapbox iOS SDK that supports iOS 7. ( * A source’s tiles are no longer rendered when the map is outside the source’s supported zoom levels. ([#6345](https://github.com/mapbox/mapbox-gl-native/pull/6345)) * Improved style parsing performance. ([#6170](https://github.com/mapbox/mapbox-gl-native/pull/6170)) * Improved feature querying performance. ([#6514](https://github.com/mapbox/mapbox-gl-native/pull/6514)) -* Fixed an issue where shapes that cannot currently be visually represented as annotations were still shown on the map as point annotations. ([#6764](https://github.com/mapbox/mapbox-gl-native/issues/6764)) +* Fixed an issue where shapes that cannot currently be visually represented as annotations were still shown on the map as point annotations. ([#6764](https://github.com/mapbox/mapbox-gl-native/issues/6764)) ### User location diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md index c93ab6b698..e0df7c48f6 100644 --- a/platform/ios/INSTALL.md +++ b/platform/ios/INSTALL.md @@ -50,6 +50,10 @@ bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework/strip-fra (The last step, courtesy of [Realm](https://github.com/realm/realm-cocoa/), is required for working around an [iOS App Store bug](http://www.openradar.me/radar?id=6409498411401216) when archiving universal binaries.) +##### Nightly builds + +A nightly build of the dynamic framework, based on the master branch, is available for download [here](https://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-ios-sdk-nightly-dynamic.zip). + #### Static framework You can alternatively install the SDK as a static framework: @@ -82,7 +86,15 @@ You can alternatively install the SDK as a static framework: To test pre-releases and/or betas, you can reference the pre-release like so in your Podfile: ```rb -pod 'Mapbox-iOS-SDK', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/<insert branch or tag>/ios/Mapbox-iOS-SDK.podspec' +pod 'Mapbox-iOS-SDK', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/<insert branch or tag>/platform/ios/Mapbox-iOS-SDK.podspec' +``` + +##### Testing nightly releases with CocoaPods + +To test a nightly dynamic framework build, update your app’s `Podfile` to point to: + +```rb +pod 'Mapbox-iOS-SDK-nightly-dynamic', podspec: 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec' ``` ##### Using your own build with CocoaPods diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec new file mode 100644 index 0000000000..62d76d4897 --- /dev/null +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -0,0 +1,30 @@ +Pod::Spec.new do |m| + + version = '3.5.0-beta.2' + + m.name = 'Mapbox-iOS-SDK-nightly-dynamic' + m.version = "#{version}-nightly" + + 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.' + m.homepage = 'https://www.mapbox.com/ios-sdk/' + m.license = { :type => 'BSD', :file => 'LICENSE.md' } + m.author = { 'Mapbox' => 'mobile@mapbox.com' } + m.screenshot = "https://www.mapbox.com/ios-sdk/api/#{version}/img/screenshot.png" + m.social_media_url = 'https://twitter.com/mapbox' + 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-nightly-dynamic.zip", + :flatten => true + } + + m.platform = :ios + m.ios.deployment_target = '8.0' + + m.requires_arc = true + + m.vendored_frameworks = 'dynamic/Mapbox.framework' + m.module_name = 'Mapbox' + +end diff --git a/platform/ios/bitrise.yml b/platform/ios/bitrise.yml index 93d1d4afd8..53287ec06d 100644 --- a/platform/ios/bitrise.yml +++ b/platform/ios/bitrise.yml @@ -11,22 +11,7 @@ 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: Install Dependencies - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -37,7 +22,6 @@ workflows: - is_debug: 'yes' - script: title: Generate Workspace - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - content: |- #!/bin/bash @@ -47,18 +31,15 @@ workflows: - is_debug: 'yes' - xcode-test: title: Run SDK Unit Tests - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - project_path: platform/ios/ios.xcworkspace - scheme: CI - deploy-to-bitrise-io: title: Deploy to Bitrise.io - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - notify_user_groups: none - slack: title: Post to Slack - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - webhook_url: "$SLACK_HOOK_URL" - channel: "#gl-bots" @@ -102,4 +83,20 @@ workflows: export FORMAT=dynamic make ipackage-strip CLOUDWATCH=true platform/ios/scripts/metrics.sh + platform/ios/scripts/deploy-nightly.sh - is_debug: 'yes' + - slack: + title: Post to Slack + inputs: + - webhook_url: "$SLACK_HOOK_URL" + - channel: "#gl-bots" + - from_username: 'Bitrise iOS Nightly \U0001F31D' + - from_username_on_error: 'Bitrise iOS Nightly \U0001F31D' + - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> + for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}> + completed successfully.' + - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> + for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}> + 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/ios/resources/Base.lproj/Localizable.strings b/platform/ios/resources/Base.lproj/Localizable.strings index ab071a4505..fc2e8c8fec 100644 --- a/platform/ios/resources/Base.lproj/Localizable.strings +++ b/platform/ios/resources/Base.lproj/Localizable.strings @@ -31,6 +31,12 @@ /* Accessibility label */ "INFO_A11Y_LABEL" = "About this map"; +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "The map failed to load because an unknown error occurred."; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "The map failed to load because the style can't be loaded."; + /* Accessibility label */ "LOGO_A11Y_LABEL" = "Mapbox"; @@ -40,9 +46,16 @@ /* Map accessibility value */ "MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible"; +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted."; + /* Action sheet title */ "SDK_NAME" = "Mapbox iOS SDK"; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible."; + /* Developer-only SDK update notification; {latest version, in format x.x.x} */ "SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK version %@ is now available:"; diff --git a/platform/ios/scripts/deploy-nightly.sh b/platform/ios/scripts/deploy-nightly.sh new file mode 100755 index 0000000000..9fec4df58a --- /dev/null +++ b/platform/ios/scripts/deploy-nightly.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail +set -u + +function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; } +function finish { >&2 echo -en "\033[0m"; } +trap finish EXIT + +export TRAVIS_REPO_SLUG=mapbox-gl-native + +DATE=`date +%Y-%m-%d` +NIGHTLY_TYPE="nightly-dynamic" + +step "Uploading ${NIGHTLY_TYPE} build for ${DATE} to s3…" + +./platform/ios/scripts/publish.sh "${NIGHTLY_TYPE}" "${DATE}" + +step "Finished deploying ${NIGHTLY_TYPE} build in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds" diff --git a/platform/ios/scripts/publish.sh b/platform/ios/scripts/publish.sh index e080ee825c..2934e10217 100755 --- a/platform/ios/scripts/publish.sh +++ b/platform/ios/scripts/publish.sh @@ -24,7 +24,7 @@ fi # zip # cd build/ios/pkg -ZIP=mapbox-ios-sdk-${PUBLISH_VERSION}${PUBLISH_STYLE}.zip +ZIP="mapbox-ios-sdk-${PUBLISH_VERSION}${PUBLISH_STYLE}.zip" step "Compressing ${ZIP}…" rm -f ../${ZIP} zip -r ../${ZIP} * @@ -35,4 +35,15 @@ zip -r ../${ZIP} * step "Uploading ${ZIP} to s3…" REPO_NAME=$(basename $TRAVIS_REPO_SLUG) aws s3 cp ../${ZIP} s3://mapbox/$REPO_NAME/ios/builds/ --acl public-read -echo http://mapbox.s3.amazonaws.com/$REPO_NAME/ios/builds/${ZIP} +echo "URL: https://mapbox.s3.amazonaws.com/$REPO_NAME/ios/builds/${ZIP}" + +# +# update nightly +# +if [[ ${PUBLISH_VERSION} =~ "nightly" ]]; then + step "Updating ${PUBLISH_VERSION} to ${PUBLISH_STYLE}…" + GENERIC_NIGHTLY_FILENAME="mapbox-ios-sdk-${PUBLISH_VERSION}.zip" + aws s3 cp \ + s3://mapbox/$REPO_NAME/ios/builds/${ZIP} \ + s3://mapbox/$REPO_NAME/ios/builds/${GENERIC_NIGHTLY_FILENAME} --acl public-read +fi diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 1baebf60bc..1ccd5ce355 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -22,6 +22,7 @@ #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/map/backend.hpp> #include <mbgl/math/wrap.hpp> +#include <mbgl/util/exception.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/image.hpp> @@ -30,6 +31,7 @@ #include <mbgl/util/chrono.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/string.hpp> #import "Mapbox.h" #import "MGLFeature_Private.h" @@ -1173,7 +1175,8 @@ public: } - (void)notifyGestureDidBegin { - [self notifyMapChange:mbgl::MapChangeRegionWillChange]; + BOOL animated = NO; + [self cameraWillChangeAnimated:animated]; _mbglMap->setGestureInProgress(true); _changeDelimiterSuppressionDepth++; } @@ -1187,7 +1190,8 @@ public: } if ( ! drift) { - [self notifyMapChange:mbgl::MapChangeRegionDidChange]; + BOOL animated = NO; + [self cameraDidChangeAnimated:animated]; } } @@ -1224,7 +1228,7 @@ public: [pan setTranslation:CGPointZero inView:pan.view]; } - [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; + [self cameraIsChanging]; } else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) { @@ -1307,7 +1311,7 @@ public: _mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate), mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } - [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; + [self cameraIsChanging]; } else if (pinch.state == UIGestureRecognizerStateEnded || pinch.state == UIGestureRecognizerStateCancelled) { @@ -1406,9 +1410,8 @@ public: { _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } - - [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; - + + [self cameraIsChanging]; } else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled) { @@ -1661,7 +1664,7 @@ public: _mbglMap->scaleBy(scale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } - [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; + [self cameraIsChanging]; } else if (quickZoom.state == UIGestureRecognizerStateEnded || quickZoom.state == UIGestureRecognizerStateCancelled) { @@ -1700,7 +1703,7 @@ public: _mbglMap->setPitch(pitchNew, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }); } - [self notifyMapChange:mbgl::MapChangeRegionIsChanging]; + [self cameraIsChanging]; } else if (twoFingerDrag.state == UIGestureRecognizerStateEnded || twoFingerDrag.state == UIGestureRecognizerStateCancelled) { @@ -4737,150 +4740,160 @@ public: } } -- (void)notifyMapChange:(mbgl::MapChange)change -{ - // Ignore map updates when the Map object isn't set. +- (void)cameraWillChangeAnimated:(BOOL)animated { if (!_mbglMap) { return; } - switch (change) + if ( ! _userLocationAnnotationIsSelected + || self.userTrackingMode == MGLUserTrackingModeNone + || self.userTrackingState != MGLUserTrackingStateChanged) { - case mbgl::MapChangeRegionWillChange: - case mbgl::MapChangeRegionWillChangeAnimated: + UIView<MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation; + BOOL dismissesAutomatically = (calloutView + && [calloutView respondsToSelector:@selector(dismissesAutomatically)] + && calloutView.dismissesAutomatically); + // dismissesAutomatically is an optional property and we want to dismiss + // the callout view if it's unimplemented. + if (dismissesAutomatically || (calloutView && ![calloutView respondsToSelector:@selector(dismissesAutomatically)])) { - if ( ! _userLocationAnnotationIsSelected - || self.userTrackingMode == MGLUserTrackingModeNone - || self.userTrackingState != MGLUserTrackingStateChanged) - { - UIView<MGLCalloutView> *calloutView = self.calloutViewForSelectedAnnotation; - BOOL dismissesAutomatically = (calloutView - && [calloutView respondsToSelector:@selector(dismissesAutomatically)] - && calloutView.dismissesAutomatically); - // dismissesAutomatically is an optional property and we want to dismiss - // the callout view if it's unimplemented. - if (dismissesAutomatically || (calloutView && ![calloutView respondsToSelector:@selector(dismissesAutomatically)])) - { - [self deselectAnnotation:self.selectedAnnotation animated:NO]; - } - } - - if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) - { - BOOL animated = change == mbgl::MapChangeRegionWillChangeAnimated; - [self.delegate mapView:self regionWillChangeAnimated:animated]; - } - break; + [self deselectAnnotation:self.selectedAnnotation animated:NO]; } - case mbgl::MapChangeRegionIsChanging: - { - [self updateCompass]; + } - if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)]) - { - [self.delegate mapViewRegionIsChanging:self]; - } - break; - } - case mbgl::MapChangeRegionDidChange: - case mbgl::MapChangeRegionDidChangeAnimated: - { - [self updateCompass]; + if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) + { + [self.delegate mapView:self regionWillChangeAnimated:animated]; + } +} - 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]; - } - break; - } - case mbgl::MapChangeWillStartLoadingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) - { - [self.delegate mapViewWillStartLoadingMap:self]; - } - break; - } - case mbgl::MapChangeDidFinishLoadingMap: - { - [self.style willChangeValueForKey:@"sources"]; - [self.style didChangeValueForKey:@"sources"]; - [self.style willChangeValueForKey:@"layers"]; - [self.style didChangeValueForKey:@"layers"]; - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) - { - [self.delegate mapViewDidFinishLoadingMap:self]; - } - break; - } - case mbgl::MapChangeDidFailLoadingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) - { - NSError *error = [NSError errorWithDomain:MGLErrorDomain code:0 userInfo:nil]; - [self.delegate mapViewDidFailLoadingMap:self withError:error]; - } - break; - } - case mbgl::MapChangeWillStartRenderingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) - { - [self.delegate mapViewWillStartRenderingMap:self]; - } - break; - } - case mbgl::MapChangeDidFinishRenderingMap: - case mbgl::MapChangeDidFinishRenderingMapFullyRendered: - { - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) - { - [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:(change == mbgl::MapChangeDidFinishRenderingMapFullyRendered)]; - } - break; - } - case mbgl::MapChangeWillStartRenderingFrame: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) - { - [self.delegate mapViewWillStartRenderingFrame:self]; - } - break; - } - case mbgl::MapChangeDidFinishRenderingFrame: - case mbgl::MapChangeDidFinishRenderingFrameFullyRendered: - { - if (_isChangingAnnotationLayers) - { - _isChangingAnnotationLayers = NO; - [self.style didChangeValueForKey:@"layers"]; - } - [self updateAnnotationViews]; - [self updateCalloutView]; - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) - { - [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:(change == mbgl::MapChangeDidFinishRenderingFrameFullyRendered)]; - } - break; - } - case mbgl::MapChangeDidFinishLoadingStyle: - { - self.style = [[MGLStyle alloc] initWithMapView:self]; - if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) - { - [self.delegate mapView:self didFinishLoadingStyle:self.style]; - } - break; - } - case mbgl::MapChangeSourceDidChange: +- (void)cameraIsChanging { + if (!_mbglMap) { + return; + } + + [self updateCompass]; + + if ([self.delegate respondsToSelector:@selector(mapViewRegionIsChanging:)]) + { + [self.delegate mapViewRegionIsChanging:self]; + } +} + +- (void)cameraDidChangeAnimated:(BOOL)animated { + if (!_mbglMap) { + return; + } + + [self updateCompass]; + + if ( ! [self isSuppressingChangeDelimiters] && [self.delegate respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) + { + if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { - break; + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); } + [self.delegate mapView:self regionDidChangeAnimated:animated]; + } +} + +- (void)mapViewWillStartLoadingMap { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) + { + [self.delegate mapViewWillStartLoadingMap:self]; + } +} + +- (void)mapViewDidFinishLoadingMap { + if (!_mbglMap) { + return; + } + + [self.style willChangeValueForKey:@"sources"]; + [self.style didChangeValueForKey:@"sources"]; + [self.style willChangeValueForKey:@"layers"]; + [self.style didChangeValueForKey:@"layers"]; + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) + { + [self.delegate mapViewDidFinishLoadingMap:self]; + } +} + +- (void)mapViewDidFailLoadingMapWithError:(NSError *)error { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) + { + [self.delegate mapViewDidFailLoadingMap:self withError:error]; + } +} + +- (void)mapViewWillStartRenderingFrame { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) + { + [self.delegate mapViewWillStartRenderingFrame:self]; + } +} + +- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered { + if (!_mbglMap) { + return; + } + + if (_isChangingAnnotationLayers) + { + _isChangingAnnotationLayers = NO; + [self.style didChangeValueForKey:@"layers"]; + } + [self updateAnnotationViews]; + [self updateCalloutView]; + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) + { + [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered]; + } +} + +- (void)mapViewWillStartRenderingMap { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) + { + [self.delegate mapViewWillStartRenderingMap:self]; + } +} + +- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) + { + [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:fullyRendered]; + } +} + +- (void)didFinishLoadingStyle { + if (!_mbglMap) { + return; + } + + self.style = [[MGLStyle alloc] initWithMapView:self]; + if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) + { + [self.delegate mapView:self didFinishLoadingStyle:self.style]; } } @@ -5349,9 +5362,74 @@ public: } } - void notifyMapChange(mbgl::MapChange change) override - { - [nativeView notifyMapChange:change]; + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [nativeView cameraWillChangeAnimated:animated]; + } + + void onCameraIsChanging() override { + [nativeView cameraIsChanging]; + } + + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) override { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [nativeView cameraDidChangeAnimated:animated]; + } + + void onWillStartLoadingMap() override { + [nativeView mapViewWillStartLoadingMap]; + } + + void onDidFinishLoadingMap() override { + [nativeView mapViewDidFinishLoadingMap]; + } + + void onDidFailLoadingMap(std::exception_ptr exception) override { + NSString *description; + MGLErrorCode code; + try { + std::rethrow_exception(exception); + } catch (const mbgl::util::StyleParseException&) { + code = MGLErrorCodeParseStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); + } catch (const mbgl::util::StyleLoadException&) { + code = MGLErrorCodeLoadStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); + } catch (const mbgl::util::NotFoundException&) { + code = MGLErrorCodeNotFound; + description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); + } catch (...) { + code = MGLErrorCodeUnknown; + description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); + } + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: description, + NSLocalizedFailureReasonErrorKey: @(mbgl::util::toString(exception).c_str()), + }; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; + [nativeView mapViewDidFailLoadingMapWithError:error]; + } + + void onWillStartRenderingFrame() override { + [nativeView mapViewWillStartRenderingFrame]; + } + + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) override { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [nativeView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; + } + + void onWillStartRenderingMap() override { + [nativeView mapViewWillStartRenderingMap]; + } + + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) override { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; + } + + void onDidFinishLoadingStyle() override { + [nativeView didFinishLoadingStyle]; } void invalidate() override diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 7e3eac9877..152fdc382b 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog for Mapbox macOS SDK +## master + +* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418)) + ## 0.4.0 ### Internationalization diff --git a/platform/macos/bitrise.yml b/platform/macos/bitrise.yml index 2de6bce1fc..1f2495dab2 100644 --- a/platform/macos/bitrise.yml +++ b/platform/macos/bitrise.yml @@ -12,59 +12,24 @@ 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: Install Dependencies - run_if: '{{enveq "SKIPCI" "false"}}' + title: Build inputs: - content: |- #!/bin/bash set -eu -o pipefail brew install cmake gem install xcpretty --no-rdoc --no-ri - - is_debug: 'yes' - - script: - title: Generate Workspace - run_if: '{{enveq "SKIPCI" "false"}}' - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - export BUILDTYPE=Debug - make xproj - - is_debug: 'yes' - - script: - title: Run Core and SDK Unit Tests - run_if: '{{enveq "SKIPCI" "false"}}' - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail export BUILDTYPE=Debug export XCPRETTY="| tee ${BITRISE_DEPLOY_DIR}/raw-xcodebuild-output.txt | xcpretty --color --report html --output ${BITRISE_DEPLOY_DIR}/xcode-test-results.html" make run-test - - is_debug: 'yes' - deploy-to-bitrise-io: title: Deploy to Bitrise.io - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - deploy_path: "test/fixtures" - notify_user_groups: none - is_compress: 'true' - slack: title: Post to Slack - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - webhook_url: "$SLACK_HOOK_URL" - channel: "#gl-bots" @@ -83,28 +48,13 @@ workflows: nightly-release: steps: - script: - title: Install Dependencies + title: Build inputs: - content: |- #!/bin/bash set -eu -o pipefail brew install cmake - - is_debug: 'yes' - - script: - title: Configure AWS-CLI - inputs: - - content: |- - #!/bin/bash - apt-get install -y python-pip python-dev build-essential pip install awscli - - script: - title: Build package - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - export BUILDTYPE=Release - export SYMBOLS=NO - make xpackage + gem install xcpretty --no-rdoc --no-ri + BUILDTYPE=Release SYMBOLS=NO make xpackage CLOUDWATCH=true platform/macos/scripts/metrics.sh - - is_debug: 'yes' diff --git a/platform/macos/sdk/Base.lproj/Localizable.strings b/platform/macos/sdk/Base.lproj/Localizable.strings index b7a4a21173..68360320eb 100644 --- a/platform/macos/sdk/Base.lproj/Localizable.strings +++ b/platform/macos/sdk/Base.lproj/Localizable.strings @@ -1,6 +1,18 @@ +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "The map failed to load because an unknown error occurred."; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "The map failed to load because the style can't be loaded."; + /* Accessibility title */ "MAP_A11Y_TITLE" = "Mapbox"; +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted."; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible."; + /* Label of Zoom In button */ "ZOOM_IN_LABEL" = "+"; diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 8711950554..028d41ceda 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -35,8 +35,10 @@ #import <mbgl/math/wrap.hpp> #import <mbgl/util/constants.hpp> #import <mbgl/util/chrono.hpp> +#import <mbgl/util/exception.hpp> #import <mbgl/util/run_loop.hpp> #import <mbgl/util/shared_thread_pool.hpp> +#import <mbgl/util/string.hpp> #import <map> #import <unordered_map> @@ -813,133 +815,150 @@ public: [self.layer setNeedsDisplay]; } -- (void)notifyMapChange:(mbgl::MapChange)change { - // Ignore map updates when the Map object isn't set. +- (void)cameraWillChangeAnimated:(BOOL)animated { if (!_mbglMap) { return; } - switch (change) { - case mbgl::MapChangeRegionWillChange: - case mbgl::MapChangeRegionWillChangeAnimated: - { - if ([self.delegate respondsToSelector:@selector(mapView:cameraWillChangeAnimated:)]) { - BOOL animated = change == mbgl::MapChangeRegionWillChangeAnimated; - [self.delegate mapView:self cameraWillChangeAnimated:animated]; - } - break; - } - case mbgl::MapChangeRegionIsChanging: - { - // Update a minimum of UI that needs to stay attached to the map - // while animating. - [self updateCompass]; - [self updateAnnotationCallouts]; + if ([self.delegate respondsToSelector:@selector(mapView:cameraWillChangeAnimated:)]) { + [self.delegate mapView:self cameraWillChangeAnimated:animated]; + } +} - if ([self.delegate respondsToSelector:@selector(mapViewCameraIsChanging:)]) { - [self.delegate mapViewCameraIsChanging:self]; - } - break; - } - case mbgl::MapChangeRegionDidChange: - case mbgl::MapChangeRegionDidChangeAnimated: - { - // Update all UI at the end of an animation or atomic change to the - // viewport. More expensive updates can happen here, but care should - // still be taken to minimize the work done here because scroll - // gesture recognition and momentum scrolling is performed as a - // series of atomic changes, not an animation. - [self updateZoomControls]; - [self updateCompass]; - [self updateAnnotationCallouts]; - [self updateAnnotationTrackingAreas]; +- (void)cameraIsChanging { + if (!_mbglMap) { + return; + } - if ([self.delegate respondsToSelector:@selector(mapView:cameraDidChangeAnimated:)]) { - BOOL animated = change == mbgl::MapChangeRegionDidChangeAnimated; - [self.delegate mapView:self cameraDidChangeAnimated:animated]; - } - break; - } - case mbgl::MapChangeWillStartLoadingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) { - [self.delegate mapViewWillStartLoadingMap:self]; - } - break; - } - case mbgl::MapChangeDidFinishLoadingMap: - { - [self.style willChangeValueForKey:@"sources"]; - [self.style didChangeValueForKey:@"sources"]; - [self.style willChangeValueForKey:@"layers"]; - [self.style didChangeValueForKey:@"layers"]; - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) { - [self.delegate mapViewDidFinishLoadingMap:self]; - } - break; - } - case mbgl::MapChangeDidFailLoadingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) { - NSError *error = [NSError errorWithDomain:MGLErrorDomain code:0 userInfo:nil]; - [self.delegate mapViewDidFailLoadingMap:self withError:error]; - } - break; - } - case mbgl::MapChangeWillStartRenderingMap: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) { - [self.delegate mapViewWillStartRenderingMap:self]; - } - break; - } - case mbgl::MapChangeDidFinishRenderingMap: - case mbgl::MapChangeDidFinishRenderingMapFullyRendered: - { - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) { - BOOL fullyRendered = change == mbgl::MapChangeDidFinishRenderingMapFullyRendered; - [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:fullyRendered]; - } - break; - } - case mbgl::MapChangeWillStartRenderingFrame: - { - if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) { - [self.delegate mapViewWillStartRenderingFrame:self]; - } - break; - } - case mbgl::MapChangeDidFinishRenderingFrame: - case mbgl::MapChangeDidFinishRenderingFrameFullyRendered: - { - if (_isChangingAnnotationLayers) { - _isChangingAnnotationLayers = NO; - [self.style didChangeValueForKey:@"layers"]; - } - if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) { - BOOL fullyRendered = change == mbgl::MapChangeDidFinishRenderingFrameFullyRendered; - [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered]; - } - break; - } - case mbgl::MapChangeDidFinishLoadingStyle: - { - self.style = [[MGLStyle alloc] initWithMapView:self]; - if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) - { - [self.delegate mapView:self didFinishLoadingStyle:self.style]; - } - break; - } - case mbgl::MapChangeSourceDidChange: - { - [self installAttributionView]; - self.needsUpdateConstraints = YES; - break; - } + // Update a minimum of UI that needs to stay attached to the map + // while animating. + [self updateCompass]; + [self updateAnnotationCallouts]; + + if ([self.delegate respondsToSelector:@selector(mapViewCameraIsChanging:)]) { + [self.delegate mapViewCameraIsChanging:self]; + } +} + +- (void)cameraDidChangeAnimated:(BOOL)animated { + if (!_mbglMap) { + return; + } + + // Update all UI at the end of an animation or atomic change to the + // viewport. More expensive updates can happen here, but care should + // still be taken to minimize the work done here because scroll + // gesture recognition and momentum scrolling is performed as a + // series of atomic changes, not an animation. + [self updateZoomControls]; + [self updateCompass]; + [self updateAnnotationCallouts]; + [self updateAnnotationTrackingAreas]; + + if ([self.delegate respondsToSelector:@selector(mapView:cameraDidChangeAnimated:)]) { + [self.delegate mapView:self cameraDidChangeAnimated:animated]; + } +} + +- (void)mapViewWillStartLoadingMap { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) { + [self.delegate mapViewWillStartLoadingMap:self]; + } +} + +- (void)mapViewDidFinishLoadingMap { + if (!_mbglMap) { + return; + } + + [self.style willChangeValueForKey:@"sources"]; + [self.style didChangeValueForKey:@"sources"]; + [self.style willChangeValueForKey:@"layers"]; + [self.style didChangeValueForKey:@"layers"]; + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) { + [self.delegate mapViewDidFinishLoadingMap:self]; + } +} + +- (void)mapViewDidFailLoadingMapWithError:(NSError *)error { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) { + [self.delegate mapViewDidFailLoadingMap:self withError:error]; + } +} + +- (void)mapViewWillStartRenderingFrame { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) { + [self.delegate mapViewWillStartRenderingFrame:self]; + } +} + +- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered { + if (!_mbglMap) { + return; + } + + if (_isChangingAnnotationLayers) { + _isChangingAnnotationLayers = NO; + [self.style didChangeValueForKey:@"layers"]; + } + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) { + [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered]; + } +} + +- (void)mapViewWillStartRenderingMap { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) { + [self.delegate mapViewWillStartRenderingMap:self]; + } +} + +- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered { + if (!_mbglMap) { + return; + } + + if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) { + [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:fullyRendered]; } } +- (void)mapViewDidFinishLoadingStyle { + if (!_mbglMap) { + return; + } + + self.style = [[MGLStyle alloc] initWithMapView:self]; + if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)]) + { + [self.delegate mapView:self didFinishLoadingStyle:self.style]; + } +} + +- (void)sourceDidChange { + if (!_mbglMap) { + return; + } + + [self installAttributionView]; + self.needsUpdateConstraints = YES; +} + #pragma mark Printing - (void)print:(__unused id)sender { @@ -2750,8 +2769,78 @@ public: MGLMapViewImpl(MGLMapView *nativeView_) : nativeView(nativeView_) {} - void notifyMapChange(mbgl::MapChange change) override { - [nativeView notifyMapChange:change]; + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [nativeView cameraWillChangeAnimated:animated]; + } + + void onCameraIsChanging() override { + [nativeView cameraIsChanging]; + } + + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) override { + bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated; + [nativeView cameraDidChangeAnimated:animated]; + } + + void onWillStartLoadingMap() override { + [nativeView mapViewWillStartLoadingMap]; + } + + void onDidFinishLoadingMap() override { + [nativeView mapViewDidFinishLoadingMap]; + } + + void onDidFailLoadingMap(std::exception_ptr exception) override { + NSString *description; + MGLErrorCode code; + try { + std::rethrow_exception(exception); + } catch (const mbgl::util::StyleParseException&) { + code = MGLErrorCodeParseStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description"); + } catch (const mbgl::util::StyleLoadException&) { + code = MGLErrorCodeLoadStyleFailed; + description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description"); + } catch (const mbgl::util::NotFoundException&) { + code = MGLErrorCodeNotFound; + description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description"); + } catch (...) { + code = MGLErrorCodeUnknown; + description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description"); + } + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: description, + NSLocalizedFailureReasonErrorKey: @(mbgl::util::toString(exception).c_str()), + }; + NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo]; + [nativeView mapViewDidFailLoadingMapWithError:error]; + } + + void onWillStartRenderingFrame() override { + [nativeView mapViewWillStartRenderingFrame]; + } + + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) override { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [nativeView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered]; + } + + void onWillStartRenderingMap() override { + [nativeView mapViewWillStartRenderingMap]; + } + + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) override { + bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full; + [nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered]; + } + + void onDidFinishLoadingStyle() override { + [nativeView mapViewDidFinishLoadingStyle]; + } + + void onSourceChanged(mbgl::style::Source&) override { + [nativeView sourceDidChange]; } void invalidate() override { diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md index 6f5afbbd6c..12032b4037 100644 --- a/platform/node/CHANGELOG.md +++ b/platform/node/CHANGELOG.md @@ -1,3 +1,16 @@ +# 3.4.7 - March 15, 2017 + +- Fixed MacOS Release builds ([8409](https://github.com/mapbox/mapbox-gl-native/pull/8409)) + +# 3.4.6 - March 14, 2017 + +- Publishes `Release` build on Mac ([#8407](https://github.com/mapbox/mapbox-gl-native/pull/8407)) +- Fixes the publish binary build process ([#8406](https://github.com/mapbox/mapbox-gl-native/pull/8406)) + +# 3.4.5 - March 14, 2017 + +- Fixed a memory hang issue after GlyphAtlas was refactored ([#8394](https://github.com/mapbox/mapbox-gl-native/pull/8394)) + # 3.4.4 - January 10, 2017 - Updates the node binary publish location on s3 to reflect new package name ([#7653](https://github.com/mapbox/mapbox-gl-native/pull/7653)) diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml index 20dcfb1908..68829d966e 100644 --- a/platform/node/bitrise.yml +++ b/platform/node/bitrise.yml @@ -3,85 +3,65 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git trigger_map: - tag: "node-v*" - workflow: primary + workflow: publish - push_branch: "*" workflow: primary - pull_request_target_branch: "*" workflow: primary +shortcuts: + slack: &slack + title: Post to Slack + inputs: + - webhook_url: "$SLACK_HOOK_URL" + - channel: "#gl-bots" + - from_username: 'Bitrise Node macOS' + - from_username_on_error: 'Bitrise Node macOS' + - 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 + 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: Check for publishing - run_if: '{{enveq "SKIPCI" "false"}}' - inputs: - - content: |- - #!/bin/bash - PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)") - if [[ "${BITRISE_GIT_TAG:-}" == "node-v${PACKAGE_JSON_VERSION}" ]]; then - envman add --key PUBLISH --value true - fi - - script: - title: Run build script - run_if: '{{enveq "SKIPCI" "false"}}' + title: Test inputs: - content: |- #!/bin/bash set -eu -o pipefail brew update - brew install cmake brew unlink node - brew install awscli node@4 + brew install cmake awscli node@4 brew link node@4 --force gem install xcpretty --no-rdoc --no-ri - make node - - script: - title: Run test script - run_if: '{{and (enveq "SKIPCI" "false") (enveq "PUBLISH" "")}}' - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - make test-node || envman add --key RESULT --value $? + make test-node || RESULT=$? ./platform/node/scripts/after_script.sh ${BITRISE_BUILD_NUMBER} + exit ${RESULT:-0} + - slack: *slack + + publish: + steps: - script: - title: Run publish script - run_if: '{{enveq "SKIPCI" "false"}}' + title: Publish inputs: - content: |- #!/bin/bash set -eu -o pipefail + brew update + brew unlink node + brew install cmake awscli node@4 + brew link node@4 --force + gem install xcpretty --no-rdoc --no-ri + export BUILDTYPE=Release + export PUBLISH=true + make test-node ./platform/node/scripts/after_success.sh - exit ${RESULT:-0} - - slack: - title: Post to Slack - run_if: '{{enveq "SKIPCI" "false"}}' - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise Node macOS' - - from_username_on_error: 'Bitrise Node macOS' - - 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 + - slack: *slack diff --git a/platform/node/scripts/after_success.sh b/platform/node/scripts/after_success.sh index 7ef8f53545..95921a42c2 100755 --- a/platform/node/scripts/after_success.sh +++ b/platform/node/scripts/after_success.sh @@ -4,7 +4,7 @@ set -e set -o pipefail if [[ -n ${PUBLISH:-} ]]; then - if [[ "${BUILDTYPE}" == "Debug" ]]; then + if [[ "${BUILDTYPE}" != "Release" ]]; then echo "Please run this script in release mode (BUILDTYPE=Release)." exit 1 else diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index f6b672efee..a3a11a1907 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -14,12 +14,6 @@ #include <unistd.h> -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int -#else -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle -#endif - namespace node_mbgl { struct NodeMap::RenderOptions { @@ -45,6 +39,13 @@ static const char* releasedMessage() { return "Map resources have already been released"; } +NodeBackend::NodeBackend() + : HeadlessBackend(sharedDisplay()) {} + +void NodeBackend::onDidFailLoadingMap(std::exception_ptr error) { + std::rethrow_exception(error); +} + void NodeMap::Init(v8::Local<v8::Object> target) { v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); @@ -959,7 +960,6 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) ->NumberValue() : 1.0; }()), - backend(sharedDisplay()), map(std::make_unique<mbgl::Map>(backend, mbgl::Size{ 256, 256 }, pixelRatio, @@ -968,14 +968,8 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) mbgl::MapMode::Still)), async(new uv_async_t) { - backend.setMapChangeCallback([&](mbgl::MapChange change) { - if (change == mbgl::MapChangeDidFailLoadingMap) { - throw std::runtime_error("Requires a map style to be a valid style JSON"); - } - }); - async->data = this; - uv_async_init(uv_default_loop(), async, [](UV_ASYNC_PARAMS(h)) { + uv_async_init(uv_default_loop(), async, [](uv_async_t* h) { reinterpret_cast<NodeMap *>(h->data)->renderFinished(); }); diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 47a5385ad6..bfc0a7eab6 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -7,6 +7,8 @@ #include <mbgl/gl/headless_backend.hpp> #include <mbgl/gl/offscreen_view.hpp> +#include <exception> + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wshadow" @@ -15,6 +17,12 @@ namespace node_mbgl { +class NodeBackend : public mbgl::HeadlessBackend { +public: + NodeBackend(); + void onDidFailLoadingMap(std::exception_ptr) final; +}; + class NodeMap : public Nan::ObjectWrap, public mbgl::FileSource { public: @@ -59,7 +67,7 @@ public: std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback); const float pixelRatio; - mbgl::HeadlessBackend backend; + NodeBackend backend; std::unique_ptr<mbgl::OffscreenView> view; NodeThreadPool threadpool; std::unique_ptr<mbgl::Map> map; diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index de16710f78..09373b1779 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -122,9 +122,19 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf } void NodeRequest::Execute() { + asyncExecute = std::make_unique<mbgl::util::AsyncTask>([this] { doExecute(); Unref(); }); + asyncExecute->send(); + + Ref(); +} + +void NodeRequest::doExecute() { + Nan::HandleScope scope; + v8::Local<v8::Value> argv[] = { handle() }; Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); + asyncExecute.reset(); } NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) { diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp index 7d7679a3c7..356566132b 100644 --- a/platform/node/src/node_request.hpp +++ b/platform/node/src/node_request.hpp @@ -8,6 +8,9 @@ #include <mbgl/storage/resource.hpp> #include <mbgl/storage/file_source.hpp> +#include <mbgl/util/async_task.hpp> + +#include <memory> namespace node_mbgl { @@ -35,9 +38,12 @@ public: void Execute(); private: + void doExecute(); + NodeMap* target; mbgl::FileSource::Callback callback; NodeAsyncRequest* asyncRequest = nullptr; + std::unique_ptr<mbgl::util::AsyncTask> asyncExecute; }; } diff --git a/platform/node/src/util/async_queue.hpp b/platform/node/src/util/async_queue.hpp index 87737437c3..a084b866ae 100644 --- a/platform/node/src/util/async_queue.hpp +++ b/platform/node/src/util/async_queue.hpp @@ -8,12 +8,6 @@ #include <queue> #include <string> -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle, int -#else -#define UV_ASYNC_PARAMS(handle) uv_async_t *handle -#endif - namespace node_mbgl { namespace util { @@ -23,7 +17,7 @@ public: AsyncQueue(uv_loop_t *loop, std::function<void(T &)> fn) : callback(fn) { async.data = this; - uv_async_init(loop, &async, [](UV_ASYNC_PARAMS(handle)) { + uv_async_init(loop, &async, [](uv_async_t* handle) { auto q = reinterpret_cast<AsyncQueue *>(handle->data); q->process(); }); diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js index 145f62ef5f..e5c756adb6 100644 --- a/platform/node/test/js/map.test.js +++ b/platform/node/test/js/map.test.js @@ -311,11 +311,11 @@ test('Map', function(t) { t.throws(function() { map.load('foo bar'); - }, /Requires a map style to be a valid style JSON/); + }, /Failed to parse style: 1 - Invalid value./); t.throws(function() { map.load('""'); - }, /Requires a map style to be a valid style JSON/); + }, /Failed to parse style: style must be an object/); map.release(); t.end(); @@ -335,7 +335,7 @@ test('Map', function(t) { t.throws(function() { map.load('invalid'); - }, /Requires a map style to be a valid style JSON/); + }, /Failed to parse style: 0 - Invalid value./); }); t.test('accepts an empty stylesheet string', function(t) { diff --git a/platform/node/test/memory.test.js b/platform/node/test/memory.test.js index e23cb60f89..997ccdbbe1 100644 --- a/platform/node/test/memory.test.js +++ b/platform/node/test/memory.test.js @@ -1,8 +1,7 @@ 'use strict'; -var fs = require('fs'); +var mockfs = require('./mockfs'); var mbgl = require('../index'); -var path = require('path'); var test = require('tape'); var testParams = { @@ -12,20 +11,6 @@ var testParams = { ratio: 2 }; -function readFixture(file) { - return fs.readFileSync(path.join('test/fixtures/resources', file)); -} - -var style_raster = readFixture('style_raster.json').toString('utf8'); -var style_vector = readFixture('style_vector.json').toString('utf8'); -var sprite_json = readFixture('sprite.json'); -var sprite_png = readFixture('sprite.png'); -var glyph = readFixture('glyphs.pbf'); -var source_raster = readFixture('source_raster.json'); -var source_vector = readFixture('source_vector.json'); -var tile_raster = readFixture('raster.tile'); -var tile_vector = readFixture('vector.tile'); - test('Memory', function(t) { // Trigger garbage collection before starting test, then initialize // heap size @@ -34,25 +19,7 @@ test('Memory', function(t) { var options = { request: function(req, callback) { - if (req.url == null) { - t.fail('invalid file request'); - } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) { - callback(null, { data: sprite_json }); - } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('png')) { - callback(null, { data: sprite_png }); - } else if (req.url.indexOf('fonts') > -1 && req.url.endsWith('pbf')) { - callback(null, { data: glyph }); - } else if (req.url.endsWith('mapbox.satellite')) { - callback(null, { data: source_raster }); - } else if (req.url.indexOf('satellite') > -1 && (req.url.endsWith('png') || req.url.endsWith('webp'))) { - callback(null, { data: tile_raster }); - } else if (req.url.endsWith('mapbox.mapbox-streets-v7')) { - callback(null, { data: source_vector }); - } else if (req.url.indexOf('streets') > -1 && req.url.endsWith('pbf')) { - callback(null, { data: tile_vector }); - } else { - t.fail('unhandled file request: ' + req.url); - } + callback(null, { data: mockfs.dataForRequest(req) }); }, ratio: testParams.ratio, }; @@ -75,9 +42,9 @@ test('Memory', function(t) { var map = mapPool.shift(); if (Math.floor(Math.random() * 2)) { - map.load(style_raster); + map.load(mockfs.style_raster); } else { - map.load(style_vector); + map.load(mockfs.style_vector); } map.render({ zoom: 16 }, function(err, pixels) { diff --git a/platform/node/test/mockfs.js b/platform/node/test/mockfs.js new file mode 100644 index 0000000000..dfa5a425e3 --- /dev/null +++ b/platform/node/test/mockfs.js @@ -0,0 +1,53 @@ +"use strict"; + +var fs = require('fs'); +var path = require('path'); + +function readFixture(file) { + return fs.readFileSync(path.join('test/fixtures/resources', file)); +}; + +var style_raster = readFixture('style_raster.json').toString('utf8'); +var style_vector = readFixture('style_vector.json').toString('utf8'); +var sprite_json = readFixture('sprite.json'); +var sprite_png = readFixture('sprite.png'); +var glyph = readFixture('glyphs.pbf'); +var source_raster = readFixture('source_raster.json'); +var source_vector = readFixture('source_vector.json'); +var tile_raster = readFixture('raster.tile'); +var tile_vector = readFixture('vector.tile'); + +function dataForRequest(req) { + if (req.url == null) { + return null; + } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) { + return sprite_json; + } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('png')) { + return sprite_png; + } else if (req.url.indexOf('fonts') > -1 && req.url.endsWith('pbf')) { + return glyph; + } else if (req.url.endsWith('mapbox.satellite')) { + return source_raster; + } else if (req.url.indexOf('satellite') > -1 && (req.url.endsWith('png') || req.url.endsWith('webp'))) { + return tile_raster; + } else if (req.url.endsWith('mapbox.mapbox-streets-v7')) { + return source_vector; + } else if (req.url.indexOf('streets') > -1 && req.url.endsWith('pbf')) { + return tile_vector; + } else { + return null; + } +}; + +module.exports = { + dataForRequest: dataForRequest, + style_raster: style_raster, + style_vector: style_vector, + sprite_json: sprite_json, + sprite_png: sprite_png, + glyph: glyph, + source_raster: source_raster, + source_vector: source_vector, + tile_raster: tile_raster, + tile_vector: tile_vector +}; diff --git a/platform/qt/bitrise-qt4.yml b/platform/qt/bitrise-qt4.yml index 8b327d0974..0a08d684fc 100644 --- a/platform/qt/bitrise-qt4.yml +++ b/platform/qt/bitrise-qt4.yml @@ -9,22 +9,7 @@ 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 @@ -40,7 +25,6 @@ workflows: - is_debug: 'yes' - slack: title: Post to Slack - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - webhook_url: "$SLACK_HOOK_URL" - channel: "#gl-bots" diff --git a/platform/qt/bitrise-qt5.yml b/platform/qt/bitrise-qt5.yml index 036ec044c5..4438b50a8f 100644 --- a/platform/qt/bitrise-qt5.yml +++ b/platform/qt/bitrise-qt5.yml @@ -9,22 +9,7 @@ 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 @@ -43,14 +28,12 @@ workflows: - is_debug: 'yes' - deploy-to-bitrise-io: title: Deploy to Bitrise.io - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - deploy_path: "test/fixtures" - notify_user_groups: none - is_compress: 'true' - slack: title: Post to Slack - run_if: '{{enveq "SKIPCI" "false"}}' inputs: - webhook_url: "$SLACK_HOOK_URL" - channel: "#gl-bots" diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index af7ed6275f..117fce515c 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -94,7 +94,6 @@ class Q_DECL_EXPORT QMapboxGL : public QObject Q_PROPERTY(QMargins margins READ margins WRITE setMargins) public: - // Reflects mbgl::MapChange. enum MapChange { MapChangeRegionWillChange = 0, MapChangeRegionWillChangeAnimated, diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index 62bfde4fa8..126ece1efa 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -1,7 +1,6 @@ #include "qmapbox.hpp" #include <mbgl/gl/extension.hpp> -#include <mbgl/map/change.hpp> #include <mbgl/storage/network_status.hpp> #include <mbgl/util/default_styles.hpp> #include <mbgl/util/geometry.hpp> diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 55470a65e1..ab727a62e7 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -57,24 +57,6 @@ static_assert(mbgl::underlying_type(QMapboxGLSettings::ConstrainWidthAndHeight) 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::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"); -static_assert(mbgl::underlying_type(QMapboxGL::MapChangeDidFinishLoadingStyle) == mbgl::underlying_type(mbgl::MapChangeDidFinishLoadingStyle), "error"); -static_assert(mbgl::underlying_type(QMapboxGL::MapChangeSourceDidChange) == mbgl::underlying_type(mbgl::MapChangeSourceDidChange), "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"); @@ -1587,19 +1569,87 @@ void QMapboxGLPrivate::invalidate() } } -void QMapboxGLPrivate::notifyMapChange(mbgl::MapChange change) +void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) { - if (change == mbgl::MapChangeSourceDidChange) { - std::string attribution; - for (const auto& source : mapObj->getSources()) { - // Avoid duplicates by using the most complete attribution HTML snippet. - if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) - attribution = *source->getAttribution(); - } - emit copyrightsChanged(QString::fromStdString(attribution)); + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionWillChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionWillChangeAnimated); + } +} + +void QMapboxGLPrivate::onCameraIsChanging() +{ + emit mapChanged(QMapboxGL::MapChangeRegionIsChanging); +} + +void QMapboxGLPrivate::onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) +{ + if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) { + emit mapChanged(QMapboxGL::MapChangeRegionDidChange); + } else { + emit mapChanged(QMapboxGL::MapChangeRegionDidChangeAnimated); + } +} + +void QMapboxGLPrivate::onWillStartLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); +} + +void QMapboxGLPrivate::onDidFinishLoadingMap() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); +} + +void QMapboxGLPrivate::onDidFailLoadingMap(std::exception_ptr) +{ + emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); +} + +void QMapboxGLPrivate::onWillStartRenderingFrame() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingFrame); +} + +void QMapboxGLPrivate::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrame); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingFrameFullyRendered); } +} + +void QMapboxGLPrivate::onWillStartRenderingMap() +{ + emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); +} - emit mapChanged(static_cast<QMapboxGL::MapChange>(change)); +void QMapboxGLPrivate::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) +{ + if (mode == mbgl::MapObserver::RenderMode::Partial) { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMap); + } else { + emit mapChanged(QMapboxGL::MapChangeDidFinishRenderingMapFullyRendered); + } +} + +void QMapboxGLPrivate::onDidFinishLoadingStyle() +{ + emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingStyle); +} + +void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&) +{ + std::string attribution; + for (const auto& source : mapObj->getSources()) { + // Avoid duplicates by using the most complete attribution HTML snippet. + if (source->getAttribution() && (attribution.size() < source->getAttribution()->size())) + attribution = *source->getAttribution(); + } + emit copyrightsChanged(QString::fromStdString(attribution)); + emit mapChanged(QMapboxGL::MapChangeSourceDidChange); } void QMapboxGLPrivate::connectionEstablished() diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index ee7bff0872..2d8bfaaf53 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -29,7 +29,20 @@ public: void activate() final {} void deactivate() final {} void invalidate() final; - void notifyMapChange(mbgl::MapChange) final; + + // mbgl::Backend (mbgl::MapObserver) implementation. + void onCameraWillChange(mbgl::MapObserver::CameraChangeMode) final; + void onCameraIsChanging() final; + void onCameraDidChange(mbgl::MapObserver::CameraChangeMode) final; + void onWillStartLoadingMap() final; + void onDidFinishLoadingMap() final; + void onDidFailLoadingMap(std::exception_ptr) final; + void onWillStartRenderingFrame() final; + void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode) final; + void onWillStartRenderingMap() final; + void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode) final; + void onDidFinishLoadingStyle() final; + void onSourceChanged(mbgl::style::Source&) final; mbgl::EdgeInsets margins; QSize size { 0, 0 }; |