diff --git a/CHANGELOG.md b/CHANGELOG.md index dfa1add83..43e35db36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## master +### Features +- Added support for [image expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-image) in core library. Runtime APIs for image expression will be implemented separately. [#15877](https://github.com/mapbox/mapbox-gl-native/pull/15877) +- Introduce `OfflineManager#packDatabase` and `OfflineRegion#deleteAndSkipPackDatabase` API in order to decouple offline storage vacuum and delete region operations and thus to gain performance benefits e.g. when several regions should be deleted in a row [#15899](https://github.com/mapbox/mapbox-gl-native/pull/15899) + +### Bugs +- Fixed Geo JSON source flickering on style transition [#15907](https://github.com/mapbox/mapbox-gl-native/pull/15907) + +### Performance improvements +- Convert GeoJSON features to tiles for the loaded source description in a background thread and thus unblock the UI thread [#15885](https://github.com/mapbox/mapbox-gl-native/pull/15885) + ## 8.5.0-alpha.4 - October 24, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native-android/compare/android-v8.5.0-alpha.3...android-v8.5.0-alpha.4) since [Mapbox Maps SDK for Android v8.5.0-alpha.3](https://github.com/mapbox/mapbox-gl-native-android/releases/tag/android-v8.5.0-alpha.3): ### Bug fixes diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java index faed46662..3975e0134 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java @@ -8,6 +8,7 @@ import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; + import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.R; @@ -306,6 +307,47 @@ public class OfflineManager { }); } + /** + * Packs the existing database file into a minimal amount of disk space. + *
+ * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + *
+ * + * @param callback the callback to be invoked when the database was reset or when the operation erred. + */ + public void packDatabase(@Nullable final FileSourceCallback callback) { + fileSource.activate(); + nativePackDatabase(new FileSourceCallback() { + @Override + public void onSuccess() { + handler.post(new Runnable() { + @Override + public void run() { + fileSource.deactivate(); + if (callback != null) { + callback.onSuccess(); + } + } + }); + } + + @Override + public void onError(@NonNull final String message) { + handler.post(new Runnable() { + @Override + public void run() { + fileSource.deactivate(); + if (callback != null) { + callback.onError(message); + } + } + }); + } + }); + } + /** * Forces re-validation of the ambient cache. *@@ -648,6 +690,9 @@ public class OfflineManager { @Keep private native void nativeResetDatabase(@Nullable FileSourceCallback callback); + @Keep + private native void nativePackDatabase(@Nullable FileSourceCallback callback); + @Keep private native void nativeInvalidateAmbientCache(@Nullable FileSourceCallback callback); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java index f13128da6..b188d60a9 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java @@ -401,7 +401,55 @@ public class OfflineRegion { if (!isDeleted) { isDeleted = true; fileSource.activate(); - deleteOfflineRegion(new OfflineRegionDeleteCallback() { + deleteOfflineRegion(true /*pack*/, new OfflineRegionDeleteCallback() { + @Override + public void onDelete() { + handler.post(new Runnable() { + @Override + public void run() { + fileSource.deactivate(); + callback.onDelete(); + OfflineRegion.this.finalize(); + } + }); + } + + @Override + public void onError(final String error) { + handler.post(new Runnable() { + @Override + public void run() { + isDeleted = false; + fileSource.deactivate(); + callback.onError(error); + } + }); + } + }); + } + } + + /** + * Same as {@link OfflineRegion#delete} but skipping database file packing for performance reasons. + *
+ * Database file packing can be done later with {@link OfflineManager#packDatabase}. + * This method is a useful optimization e.g. when several regions should be deleted in a row. + *
+ *+ * When the operation is complete or encounters an error, the given callback will be + * executed on the main thread. + *
+ *+ * After you call this method, you may not call any additional methods on this object. + *
+ * + * @param callback the callback to be invoked + */ + public void deleteAndSkipPackDatabase(@NonNull final OfflineRegionDeleteCallback callback) { + if (!isDeleted) { + isDeleted = true; + fileSource.activate(); + deleteOfflineRegion(false /*pack*/, new OfflineRegionDeleteCallback() { @Override public void onDelete() { handler.post(new Runnable() { @@ -521,7 +569,7 @@ public class OfflineRegion { private native void getOfflineRegionStatus(OfflineRegionStatusCallback callback); @Keep - private native void deleteOfflineRegion(OfflineRegionDeleteCallback callback); + private native void deleteOfflineRegion(boolean pack, OfflineRegionDeleteCallback callback); @Keep private native void updateOfflineRegionMetadata(byte[] metadata, OfflineRegionUpdateMetadataCallback callback); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index 34b330880..0003006b7 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -5,6 +5,7 @@ import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.Size; + import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -3150,7 +3151,7 @@ public class Expression { * If set, the min-fraction-digits and max-fraction-digits arguments specify the minimum and maximum number * of fractional digits to include. * - * @param number number expression + * @param number number expression * @param options number formatting options * @return expression */ @@ -3169,7 +3170,7 @@ public class Expression { * If set, the min-fraction-digits and max-fraction-digits arguments specify the minimum and maximum number * of fractional digits to include. * - * @param number number expression + * @param number number expression * @param options number formatting options * @return expression */ @@ -3409,6 +3410,49 @@ public class Expression { return new FormatEntry(literal(text), null); } + /** + * Returns image expression for use in '*-pattern' and 'icon-image' layer properties. Compared to + * string literals that can be used to represent an image, image expression allows to determine an + * image's availability at runtime, thus, can be used in conditional coalesce operator. + * + *+ * Example usage: + *
+ *
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * iconImage(image(get("key-to-feature")))
+ * );
+ * }
+ *
+ *
+ * + * Example usage with coalesce operator: + *
+ *
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * iconImage(
+ * coalesce(
+ * image(literal("maki-11")),
+ * image(literal("bicycle-15")),
+ * image(literal("default-icon"))
+ * )
+ * )
+ * );
+ * }
+ *
+ *
+ * @param input expression input
+ * @return expression
+ * @see Image expression
+ */
+ public static Expression image(@NonNull Expression input) {
+ return new Expression("image", input);
+ }
+
/**
* Asserts that the input value is an object. If it is not, the expression is an error
* The asserted input value is returned as result.
@@ -4430,7 +4474,7 @@ public class Expression {
/**
* Create an option option entry that is encapsulated as a json object member for an expression.
*
- * @param type json object member name
+ * @param type json object member name
* @param value json object member value
*/
Option(@NonNull String type, @NonNull Expression value) {
diff --git a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt
index a3214c9f1..da4cfbaf0 100644
--- a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt
+++ b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt
@@ -85,4 +85,20 @@ class CacheTest {
}
countDownLatch.await()
}
+
+ @Test
+ fun testSetPackDatabase() {
+ rule.activity.runOnUiThread {
+ OfflineManager.getInstance(context).packDatabase(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ countDownLatch.countDown()
+ }
+
+ override fun onError(message: String) {
+ Assert.assertNull("onError should not be called", message)
+ }
+ })
+ }
+ countDownLatch.await()
+ }
}
\ No newline at end of file
diff --git a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
index 1104cbed9..141114c1a 100644
--- a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
+++ b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
@@ -151,6 +151,8 @@ public class <%- camelize(type) %>LayerTest extends BaseLayerTest {
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>("{token}"));
<% if (property.type === 'formatted') { -%>
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), <%- defaultExpressionJava(property) %>(Expression.formatEntry(Expression.toString(Expression.get("token")))));
+<% } else if (property.type === 'resolvedImage') {-%>
+ assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), <%- defaultExpressionJava(property) %>(Expression.toString(Expression.get("token"))));
<% } else {-%>
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), Expression.toString(Expression.get("token")));
<% } -%>
@@ -169,6 +171,8 @@ public class <%- camelize(type) %>LayerTest extends BaseLayerTest {
// Set and Get
<% if (property.type === 'formatted') { -%>
Expression expression = <%- defaultExpressionJava(property) %>(Expression.formatEntry(Expression.get("undefined"), FormatOption.formatFontScale(2.0), FormatOption.formatTextFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"})));
+<% } else if (property.type === 'resolvedImage') { -%>
+ Expression expression = <%- defaultExpressionJava(property) %>(string(Expression.get("undefined")));
<% } else { -%>
Expression expression = <%- defaultExpressionJava(property) %>(Expression.get("undefined"));
<% } -%>
diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js
index 652b8a98a..ea68a059d 100755
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -72,6 +72,7 @@ global.propertyType = function propertyType(property) {
case 'formatted':
return 'Formatted';
case 'string':
+ case 'resolvedImage':
return 'String';
case 'enum':
return 'String';
@@ -93,6 +94,7 @@ global.propertyJavaType = function propertyType(property) {
case 'formatted':
return 'Formatted';
case 'string':
+ case 'resolvedImage':
return 'String';
case 'enum':
return 'String';
@@ -141,6 +143,7 @@ global.propertyNativeType = function (property) {
return 'float';
case 'formatted':
case 'string':
+ case 'resolvedImage': // TODO: replace once we implement image expressions
return 'std::string';
case 'enum':
if(property['light-property']){
@@ -176,7 +179,10 @@ global.defaultExpressionJava = function(property) {
return 'number';
case 'formatted':
return 'format';
+ case 'resolvedImage':
+ return "image";
case 'string':
+ case 'image':
return "string";
case 'enum':
return "string";
@@ -203,6 +209,7 @@ global.defaultValueJava = function(property) {
case 'formatted':
return 'new Formatted(new FormattedSection("default"))'
case 'string':
+ case 'resolvedImage':
return '"' + property['default'] + '"';
case 'enum':
return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]);
@@ -335,6 +342,7 @@ global.evaluatedType = function (property) {
return 'float';
case 'formatted':
case 'string':
+ case 'image':
return 'std::string';
case 'enum':
return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`;
diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native
index ab7cd57de..4c1d03c25 160000
--- a/vendor/mapbox-gl-native
+++ b/vendor/mapbox-gl-native
@@ -1 +1 @@
-Subproject commit ab7cd57de037bc6e71e327cffeb421e08e3b2480
+Subproject commit 4c1d03c257754ecc1555f3e31b3c6ccf45c4bf7b