summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java257
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java75
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt89
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/CacheManagementActivity.kt79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_cache_management.xml32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml1
-rw-r--r--platform/android/scripts/exclude-activity-gen.json3
-rw-r--r--platform/android/src/offline/offline_manager.cpp98
-rw-r--r--platform/android/src/offline/offline_manager.hpp15
-rw-r--r--platform/android/src/offline/offline_region.cpp36
-rw-r--r--platform/android/src/offline/offline_region.hpp11
16 files changed, 722 insertions, 38 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index 535107c529..5bd0dd4bf6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -4,11 +4,10 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
-import android.support.annotation.AnyThread;
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;
@@ -54,7 +53,7 @@ public class OfflineManager {
private final FileSource fileSource;
// Makes sure callbacks come back to the main thread
- private Handler handler;
+ private final Handler handler = new Handler(Looper.getMainLooper());
// This object is implemented as a singleton
@SuppressLint("StaticFieldLeak")
@@ -157,15 +156,6 @@ public class OfflineManager {
return instance;
}
- @AnyThread
- private Handler getHandler() {
- if (handler == null) {
- handler = new Handler(Looper.getMainLooper());
- }
-
- return handler;
- }
-
/**
* Retrieve all regions in the offline database.
* <p>
@@ -181,7 +171,7 @@ public class OfflineManager {
@Override
public void onList(final OfflineRegion[] offlineRegions) {
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
fileSource.deactivate();
@@ -192,7 +182,7 @@ public class OfflineManager {
@Override
public void onError(final String error) {
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
fileSource.deactivate();
@@ -234,7 +224,7 @@ public class OfflineManager {
public void run() {
String errorMessage = null;
if (src.canWrite()) {
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
// path writable, merge and update schema in place if necessary
@@ -246,7 +236,7 @@ public class OfflineManager {
final File dst = new File(FileSource.getInternalCachePath(context), src.getName());
try {
copyTempDatabaseFile(src, dst);
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
// merge and update schema using the copy
@@ -264,7 +254,7 @@ public class OfflineManager {
if (errorMessage != null) {
final String finalErrorMessage = errorMessage;
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
callback.onError(finalErrorMessage);
@@ -275,6 +265,219 @@ public class OfflineManager {
}).start();
}
+ /**
+ * Delete existing database and re-initialize.
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @param callback the callback to be invoked when the database was reset or when the operation erred.
+ */
+ public void resetDatabase(@Nullable final FileSourceCallback callback) {
+ fileSource.activate();
+ nativeResetDatabase(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 revalidation of the ambient cache.
+ * <p>
+ * Forces Mapbox GL Native to revalidate resources stored in the ambient
+ * cache with the tile server before using them, making sure they
+ * are the latest version. This is more efficient than cleaning the
+ * cache because if the resource is considered valid after the server
+ * lookup, it will not get downloaded again.
+ * <p>
+ * Resources overlapping with offline regions will not be affected
+ * by this call.
+ * </p>
+ *
+ * @param callback the callback to be invoked when the ambient cache was invalidated or when the operation erred.
+ */
+ public void invalidateAmbientCache(@Nullable final FileSourceCallback callback) {
+ fileSource.activate();
+ nativeInvalidateAmbientCache(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);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Erase resources from the ambient cache, freeing storage space.
+ * <p>
+ * Erases the ambient cache, freeing resources. This operation can be
+ * potentially slow because it will trigger a VACUUM on SQLite,
+ * forcing the database to move pages on the filesystem.
+ * </p>
+ * <p>
+ * Resources overlapping with offline regions will not be affected
+ * by this call.
+ * </p>
+ *
+ * @param callback the callback to be invoked when the ambient cache was cleared or when the operation erred.
+ */
+ public void clearAmbientCache(@Nullable final FileSourceCallback callback) {
+ fileSource.activate();
+ nativeClearAmbientCache(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);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Sets the maximum size in bytes for the ambient cache.
+ * <p>
+ * This call is potentially expensive because it will try
+ * to trim the data in case the database is larger than the
+ * size defined. The size of offline regions are not affected
+ * by this settings, but the ambient cache will always try
+ * to not exceed the maximum size defined, taking into account
+ * the current size for the offline regions.
+ * </p>
+ * <p>
+ * Note that if you use the SDK's offline functionality, your ability to set the ambient cache size will be limited.
+ * Space that offline regions take up detract from the space available for ambient caching, and the ambient cache
+ * size does not block offline downloads. For example: if the maximum cache size is set to 50 MB and 40 MB are
+ * already used by offline regions, the ambient cache size will effectively be 10 MB.
+ * </p>
+ * <p>
+ * Setting the size to 0 will disable the cache if there is no
+ * offline region on the database.
+ * </p>
+ * <[
+ * <p>
+ * This method should always be called at the start of an app, before setting the style and loading a map.
+ * Otherwise, the map will instantiate with the default cache size of 50 MB.
+ * </p>
+ *
+ * @param size the maximum size of the ambient cache
+ * @param callback the callback to be invoked when the the maximum size has been set or when the operation erred.
+ */
+ public void setMaximumAmbientCacheSize(long size, @Nullable final FileSourceCallback callback) {
+ fileSource.activate();
+ nativeSetMaximumAmbientCacheSize(size, 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) {
+ fileSource.activate();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ fileSource.deactivate();
+ if (callback != null) {
+ callback.onError(message);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * This callback receives an asynchronous response indicating if an operation has succeeded or failed.
+ */
+ @Keep
+ public interface FileSourceCallback {
+
+ /**
+ * Receives the success of an operation
+ */
+ void onSuccess();
+
+ /**
+ * Receives an error message if an operation was not successful
+ *
+ * @param message the error message
+ */
+ void onError(@NonNull String message);
+
+ }
+
private static void copyTempDatabaseFile(@NonNull File sourceFile, File destFile) throws IOException {
if (!destFile.exists() && !destFile.createNewFile()) {
throw new IOException("Unable to copy database file for merge.");
@@ -308,7 +511,7 @@ public class OfflineManager {
if (isTemporaryFile) {
file.delete();
}
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
fileSource.deactivate();
@@ -322,7 +525,7 @@ public class OfflineManager {
if (isTemporaryFile) {
file.delete();
}
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
fileSource.deactivate();
@@ -365,7 +568,7 @@ public class OfflineManager {
@Override
public void onCreate(final OfflineRegion offlineRegion) {
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
ConnectivityReceiver.instance(context).deactivate();
@@ -377,7 +580,7 @@ public class OfflineManager {
@Override
public void onError(final String error) {
- getHandler().post(new Runnable() {
+ handler.post(new Runnable() {
@Override
public void run() {
ConnectivityReceiver.instance(context).deactivate();
@@ -431,6 +634,18 @@ public class OfflineManager {
@Keep
private native void mergeOfflineRegions(FileSource fileSource, String path, MergeOfflineRegionsCallback callback);
+ @Keep
+ private native void nativeResetDatabase(@Nullable FileSourceCallback callback);
+
+ @Keep
+ private native void nativeInvalidateAmbientCache(@Nullable FileSourceCallback callback);
+
+ @Keep
+ private native void nativeClearAmbientCache(@Nullable FileSourceCallback callback);
+
+ @Keep
+ private native void nativeSetMaximumAmbientCacheSize(long size, @Nullable FileSourceCallback callback);
+
/**
* Insert the provided resource into the ambient cache
* This method mimics the caching that would take place if the equivalent
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
index 863219854b..2217850a2e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -7,7 +7,6 @@ import android.support.annotation.IntDef;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
@@ -148,6 +147,25 @@ public class OfflineRegion {
}
/**
+ * This callback receives an asynchronous response containing a notification when
+ * an offline region has been invalidated, or a {@link String} error message otherwise.
+ */
+ @Keep
+ public interface OfflineRegionInvalidateCallback {
+ /**
+ * Receives the invalidate notification
+ */
+ void onInvalidate();
+
+ /**
+ * Receives the error message
+ *
+ * @param error the error message
+ */
+ void onError(String error);
+ }
+
+ /**
* This callback receives an asynchronous response containing the newly update
* OfflineMetadata in the database, or an error message otherwise.
*/
@@ -337,14 +355,14 @@ public class OfflineRegion {
* @param callback the callback to invoked.
*/
public void getStatus(@NonNull final OfflineRegionStatusCallback callback) {
- FileSource.getInstance(Mapbox.getApplicationContext()).activate();
+ fileSource.activate();
getOfflineRegionStatus(new OfflineRegionStatusCallback() {
@Override
public void onStatus(final OfflineRegionStatus status) {
handler.post(new Runnable() {
@Override
public void run() {
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ fileSource.deactivate();
callback.onStatus(status);
}
});
@@ -355,7 +373,7 @@ public class OfflineRegion {
handler.post(new Runnable() {
@Override
public void run() {
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ fileSource.deactivate();
callback.onError(error);
}
});
@@ -383,14 +401,14 @@ public class OfflineRegion {
public void delete(@NonNull final OfflineRegionDeleteCallback callback) {
if (!isDeleted) {
isDeleted = true;
- FileSource.getInstance(Mapbox.getApplicationContext()).activate();
+ fileSource.activate();
deleteOfflineRegion(new OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
handler.post(new Runnable() {
@Override
public void run() {
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ fileSource.deactivate();
callback.onDelete();
OfflineRegion.this.finalize();
}
@@ -403,7 +421,7 @@ public class OfflineRegion {
@Override
public void run() {
isDeleted = false;
- FileSource.getInstance(Mapbox.getApplicationContext()).deactivate();
+ fileSource.deactivate();
callback.onError(error);
}
});
@@ -413,6 +431,46 @@ public class OfflineRegion {
}
/**
+ * Invalidate all the tiles from an offline region forcing Mapbox GL to revalidate
+ * the tiles with the server before using. This is more efficient than deleting the
+ * offline region and downloading it again because if the data on the cache matches
+ * the server, no new data gets transmitted.
+ *
+ * @param callback the callback to be invoked
+ */
+ public void invalidate(@Nullable final OfflineRegionInvalidateCallback callback) {
+ fileSource.activate();
+ invalidateOfflineRegion(new OfflineRegionInvalidateCallback() {
+
+ @Override
+ public void onInvalidate() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ fileSource.deactivate();
+ if (callback != null) {
+ callback.onInvalidate();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onError(@NonNull final String message) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ fileSource.deactivate();
+ if (callback != null) {
+ callback.onError(message);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
* Update an offline region metadata from the database.
* <p>
* When the operation is complete or encounters an error, the given callback will be
@@ -469,4 +527,7 @@ public class OfflineRegion {
@Keep
private native void updateOfflineRegionMetadata(byte[] metadata, OfflineRegionUpdateMetadataCallback callback);
+ @Keep
+ private native void invalidateOfflineRegion(OfflineRegionInvalidateCallback callback);
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
index b3b7b61831..cdf197411a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
@@ -11,7 +11,6 @@ import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
-
import com.mapbox.mapboxsdk.MapStrictMode;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt
new file mode 100644
index 0000000000..299e193c96
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt
@@ -0,0 +1,89 @@
+package com.mapbox.mapboxsdk.testapp.offline
+
+import android.content.Context
+import android.support.test.rule.ActivityTestRule
+import android.support.test.runner.AndroidJUnit4
+import com.mapbox.mapboxsdk.offline.OfflineManager
+import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
+
+@RunWith(AndroidJUnit4::class)
+class CacheTest {
+
+ @Rule
+ @JvmField
+ var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
+
+ private val context: Context by lazy { rule.activity }
+
+ private val countDownLatch = CountDownLatch(1)
+
+ @Test
+ fun testSetMaximumAmbientCacheSize() {
+ rule.activity.runOnUiThread {
+ OfflineManager.getInstance(context).setMaximumAmbientCacheSize(10000000, object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ countDownLatch.countDown()
+ }
+
+ override fun onError(message: String) {
+ Assert.assertNull("onError should not be called", message)
+ }
+ })
+ }
+ countDownLatch.await()
+ }
+
+ @Test
+ fun testSetClearAmbientCache() {
+ rule.activity.runOnUiThread {
+ OfflineManager.getInstance(context).clearAmbientCache(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ countDownLatch.countDown()
+ }
+
+ override fun onError(message: String) {
+ Assert.assertNull("onError should not be called", message)
+ }
+ })
+ }
+ countDownLatch.await()
+ }
+
+ @Test
+ fun testSetInvalidateAmbientCache() {
+ rule.activity.runOnUiThread {
+ OfflineManager.getInstance(context).invalidateAmbientCache(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ countDownLatch.countDown()
+ }
+
+ override fun onError(message: String) {
+ Assert.assertNull("onError should not be called", message)
+ }
+ })
+ }
+ countDownLatch.await()
+ }
+
+ @Test
+ fun testSetResetDatabase() {
+ rule.activity.runOnUiThread {
+ OfflineManager.getInstance(context).resetDatabase(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ countDownLatch.countDown()
+ }
+
+ override fun onError(message: String) {
+ Assert.assertNull("onError should not be called", message)
+ }
+ })
+ }
+ countDownLatch.await()
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt
index 3f98937527..6b73623ae7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt
@@ -35,7 +35,7 @@ class OfflineManagerTest : AppCenter() {
@Test(timeout = 30_000)
fun a_copyFileFromAssets() {
val latch = CountDownLatch(1)
- rule.runOnUiThread {
+ rule.activity.runOnUiThread {
FileUtils.CopyFileFromAssetsTask(rule.activity, object : FileUtils.OnFileCopiedFromAssetsListener {
override fun onFileCopiedFromAssets() {
latch.countDown()
@@ -52,7 +52,7 @@ class OfflineManagerTest : AppCenter() {
@Test(timeout = 30_000)
fun b_mergeRegion() {
val latch = CountDownLatch(1)
- rule.runOnUiThread {
+ rule.activity.runOnUiThread {
OfflineManager.getInstance(context).mergeOfflineRegions(
FileSource.getResourcesCachePath(rule.activity) + "/" + TEST_DB_FILE_NAME,
object : OfflineManager.MergeOfflineRegionsCallback {
@@ -72,7 +72,7 @@ class OfflineManagerTest : AppCenter() {
@Test(timeout = 30_000)
fun c_listRegion() {
val latch = CountDownLatch(1)
- rule.runOnUiThread {
+ rule.activity.runOnUiThread {
OfflineManager.getInstance(context).listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
override fun onList(offlineRegions: Array<out OfflineRegion>?) {
assert(offlineRegions?.size == 1)
@@ -89,9 +89,26 @@ class OfflineManagerTest : AppCenter() {
}
@Test(timeout = 30_000)
- fun d_deleteRegion() {
+ fun d_invalidateRegion() {
val latch = CountDownLatch(1)
- rule.runOnUiThread {
+ rule.activity.runOnUiThread {
+ mergedRegion.invalidate(object : OfflineRegion.OfflineRegionInvalidateCallback {
+ override fun onInvalidate() {
+ latch.countDown()
+ }
+
+ override fun onError(error: String?) {
+ throw RuntimeException("Unable to delete region")
+ }
+ })
+ }
+ latch.await()
+ }
+
+ @Test(timeout = 30_000)
+ fun e_deleteRegion() {
+ val latch = CountDownLatch(1)
+ rule.activity.runOnUiThread {
mergedRegion.delete(object : OfflineRegion.OfflineRegionDeleteCallback {
override fun onDelete() {
latch.countDown()
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index bb2bef35fb..d2a5032c81 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -709,6 +709,17 @@
android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
+ android:name=".activity.storage.CacheManagementActivity"
+ android:description="@string/description_cache_management"
+ android:label="@string/activity_cache_management">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_storage" />
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity" />
+ </activity>
+ <activity
android:name=".activity.maplayout.BottomSheetActivity"
android:description="@string/description_bottom_sheet"
android:label="@string/activity_bottom_sheet">
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
index 037c51f723..11ee68702f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java
@@ -12,7 +12,6 @@ import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
-
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.offline.OfflineRegion;
import com.mapbox.mapboxsdk.testapp.R;
@@ -25,7 +24,8 @@ import java.util.List;
/**
* Test activity showing integration of deleting an OfflineRegion.
*/
-public class DeleteRegionActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
+public class DeleteRegionActivity extends AppCompatActivity implements AdapterView.OnItemClickListener,
+ AdapterView.OnItemLongClickListener {
private OfflineRegionAdapter adapter;
@@ -34,10 +34,11 @@ public class DeleteRegionActivity extends AppCompatActivity implements AdapterVi
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offline_region_delete);
- ListView listView = (ListView) findViewById(R.id.listView);
+ ListView listView = findViewById(R.id.listView);
listView.setAdapter(adapter = new OfflineRegionAdapter(this));
listView.setEmptyView(findViewById(android.R.id.empty));
listView.setOnItemClickListener(this);
+ listView.setOnItemLongClickListener(this);
}
@Override
@@ -58,6 +59,23 @@ public class DeleteRegionActivity extends AppCompatActivity implements AdapterVi
builder.show();
}
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ final OfflineRegion region = adapter.getItem(position);
+ region.invalidate(new OfflineRegion.OfflineRegionInvalidateCallback() {
+ @Override
+ public void onInvalidate() {
+ Toast.makeText(DeleteRegionActivity.this, "Invalidate region success", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onError(String error) {
+ Toast.makeText(DeleteRegionActivity.this, "Error:" + error, Toast.LENGTH_LONG).show();
+ }
+ });
+ return true;
+ }
+
private void delete(OfflineRegion region) {
region.delete(new OfflineRegion.OfflineRegionDeleteCallback() {
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/CacheManagementActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/CacheManagementActivity.kt
new file mode 100644
index 0000000000..c9fdb79e6e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/CacheManagementActivity.kt
@@ -0,0 +1,79 @@
+package com.mapbox.mapboxsdk.testapp.activity.storage
+
+import android.os.Bundle
+import android.os.Looper
+import android.support.design.widget.Snackbar
+import android.support.v7.app.AppCompatActivity
+import com.mapbox.mapboxsdk.offline.OfflineManager
+import com.mapbox.mapboxsdk.storage.FileSource
+import com.mapbox.mapboxsdk.testapp.R
+import junit.framework.Assert.assertTrue
+import kotlinx.android.synthetic.main.activity_cache_management.*
+
+/**
+ * Test activity showcasing the cache management APIs
+ */
+class CacheManagementActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_cache_management)
+
+ val fileSource = OfflineManager.getInstance(this)
+ resetDatabaseButton.setOnClickListener {
+ fileSource.resetDatabase(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ showSnackbar("Reset database success")
+ }
+
+ override fun onError(message: String) {
+ showSnackbar("Reset database fail: $message")
+ }
+ })
+ }
+
+ invalidateAmbientCacheButton.setOnClickListener {
+ fileSource.invalidateAmbientCache(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ showSnackbar("Invalidate ambient cache success")
+ }
+
+ override fun onError(message: String) {
+ showSnackbar("Invalidate ambient cache fail: $message")
+ }
+ })
+ }
+
+ clearAmbientCacheButton.setOnClickListener {
+ fileSource.clearAmbientCache(object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ showSnackbar("Clear ambient cache success")
+ }
+
+ override fun onError(message: String) {
+ showSnackbar("Clear ambient cache fail: $message")
+ }
+ })
+ }
+
+ setMaximumAmbientCacheSizeButton.setOnClickListener {
+ fileSource.setMaximumAmbientCacheSize(5000000, object : OfflineManager.FileSourceCallback {
+ override fun onSuccess() {
+ showSnackbar("Set maximum ambient cache size success")
+ }
+
+ override fun onError(message: String) {
+ showSnackbar("Set maximum ambient cache size fail: $message")
+ }
+ })
+ }
+ }
+
+ fun showSnackbar(message: String) {
+ // validate that all callbacks occur on main thread
+ assertTrue(Looper.myLooper() == Looper.getMainLooper())
+
+ // show snackbar
+ Snackbar.make(container, message, Snackbar.LENGTH_SHORT).show()
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_cache_management.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_cache_management.xml
new file mode 100644
index 0000000000..a79ed9352b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_cache_management.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/resetDatabaseButton"
+ android:text="Reset Database"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/invalidateAmbientCacheButton"
+ android:text="Invalidate Ambient Cache"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/clearAmbientCacheButton"
+ android:text="Clear Ambient Cache"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/setMaximumAmbientCacheSizeButton"
+ android:text="Set Maximum Ambient Cache Size to 5mb"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
index 684220f2c4..082eb39256 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -51,6 +51,7 @@
<string name="description_map_in_dialog">Display a map inside a dialog fragment</string>
<string name="description_circle_layer">Show bus stops and route in Singapore</string>
<string name="description_url_transform">Transform urls on the fly</string>
+ <string name="description_cache_management">Control the cache management with FileSource API</string>
<string name="description_restricted_bounds">Limit viewport to Iceland</string>
<string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
<string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
index 027198c71b..94566ea995 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -50,6 +50,7 @@
<string name="activity_map_visibility">Visibility Map</string>
<string name="activity_map_in_dialog">Dialog with map</string>
<string name="activity_url_transform">Url transform</string>
+ <string name="activity_cache_management">Cache management</string>
<string name="activity_restricted_bounds">Restrict camera to a bounds</string>
<string name="activity_fill_extrusion_layer">Fill extrusions</string>
<string name="activity_building_fill_extrusion_layer">Building layer</string>
diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json
index f6156eb0ea..0f5bb81b55 100644
--- a/platform/android/scripts/exclude-activity-gen.json
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -51,5 +51,6 @@
"FragmentBackStackActivity",
"ChildFragmentMapInDialogActivity",
"PerformanceMeasurementActivity",
- "DownloadRegionActivity"
+ "DownloadRegionActivity",
+ "CacheManagementActivity"
]
diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp
index 54b1142845..029252f786 100644
--- a/platform/android/src/offline/offline_manager.cpp
+++ b/platform/android/src/offline/offline_manager.cpp
@@ -104,10 +104,104 @@ void OfflineManager::mergeOfflineRegions(jni::JNIEnv& env_, const jni::Object<Fi
});
}
+void OfflineManager::resetDatabase(jni::JNIEnv& env_, const jni::Object<FileSourceCallback>& callback_) {
+ auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
+
+ fileSource->resetDatabase([
+ //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile
+ callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback))
+ ](std::exception_ptr exception) mutable {
+
+ // Reattach, the callback comes from a different thread
+ android::UniqueEnv env = android::AttachEnv();
+
+ if (exception) {
+ OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make<jni::String>(*env, mbgl::util::toString(exception)));
+ } else {
+ OfflineManager::FileSourceCallback::onSuccess(*env, *callback);
+ }
+ });
+}
+
+void OfflineManager::invalidateAmbientCache(jni::JNIEnv& env_, const jni::Object<FileSourceCallback>& callback_) {
+ auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
+
+ fileSource->invalidateAmbientCache([
+ //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile
+ callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback))
+ ](std::exception_ptr exception) mutable {
+
+ // Reattach, the callback comes from a different thread
+ android::UniqueEnv env = android::AttachEnv();
+
+ if (exception) {
+ OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make<jni::String>(*env, mbgl::util::toString(exception)));
+ } else {
+ OfflineManager::FileSourceCallback::onSuccess(*env, *callback);
+ }
+ });
+}
+
+void OfflineManager::clearAmbientCache(jni::JNIEnv& env_, const jni::Object<FileSourceCallback>& callback_) {
+ auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
+
+ fileSource->clearAmbientCache([
+ //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile
+ callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback))
+ ](std::exception_ptr exception) mutable {
+
+ // Reattach, the callback comes from a different thread
+ android::UniqueEnv env = android::AttachEnv();
+
+ if (exception) {
+ OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make<jni::String>(*env, mbgl::util::toString(exception)));
+ } else {
+ OfflineManager::FileSourceCallback::onSuccess(*env, *callback);
+ }
+ });
+}
+
+void OfflineManager::setMaximumAmbientCacheSize(jni::JNIEnv& env_, const jni::jlong size_, const jni::Object<FileSourceCallback>& callback_) {
+ auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
+
+ fileSource->setMaximumAmbientCacheSize(size_, [
+ //Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile
+ callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback))
+ ](std::exception_ptr exception) mutable {
+
+ // Reattach, the callback comes from a different thread
+ android::UniqueEnv env = android::AttachEnv();
+
+ if (exception) {
+ OfflineManager::FileSourceCallback::onError(*env, *callback, jni::Make<jni::String>(*env, mbgl::util::toString(exception)));
+ } else {
+ OfflineManager::FileSourceCallback::onSuccess(*env, *callback);
+ }
+ });
+}
+
+// FileSource::FileSourceCallback //
+
+void OfflineManager::FileSourceCallback::onSuccess(jni::JNIEnv& env,
+ const jni::Object<OfflineManager::FileSourceCallback>& callback) {
+ static auto& javaClass = jni::Class<OfflineManager::FileSourceCallback>::Singleton(env);
+ static auto method = javaClass.GetMethod<void ()>(env, "onSuccess");
+ callback.Call(env, method);
+}
+
+void OfflineManager::FileSourceCallback::onError(jni::JNIEnv& env,
+ const jni::Object<OfflineManager::FileSourceCallback>& callback,
+ const jni::String& message) {
+ static auto& javaClass = jni::Class<OfflineManager::FileSourceCallback>::Singleton(env);
+ static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
+ callback.Call(env, method, message);
+}
+
void OfflineManager::registerNative(jni::JNIEnv& env) {
jni::Class<ListOfflineRegionsCallback>::Singleton(env);
jni::Class<CreateOfflineRegionCallback>::Singleton(env);
jni::Class<MergeOfflineRegionsCallback>::Singleton(env);
+ jni::Class<FileSourceCallback>::Singleton(env);
static auto& javaClass = jni::Class<OfflineManager>::Singleton(env);
@@ -121,6 +215,10 @@ void OfflineManager::registerNative(jni::JNIEnv& env) {
METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"),
METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"),
METHOD(&OfflineManager::mergeOfflineRegions, "mergeOfflineRegions"),
+ METHOD(&OfflineManager::resetDatabase, "nativeResetDatabase"),
+ METHOD(&OfflineManager::invalidateAmbientCache, "nativeInvalidateAmbientCache"),
+ METHOD(&OfflineManager::clearAmbientCache, "nativeClearAmbientCache"),
+ METHOD(&OfflineManager::setMaximumAmbientCacheSize, "nativeSetMaximumAmbientCacheSize"),
METHOD(&OfflineManager::putResourceWithUrl, "putResourceWithUrl"));
}
diff --git a/platform/android/src/offline/offline_manager.hpp b/platform/android/src/offline/offline_manager.hpp
index d0b637b900..058cfb5b48 100644
--- a/platform/android/src/offline/offline_manager.hpp
+++ b/platform/android/src/offline/offline_manager.hpp
@@ -55,6 +55,14 @@ public:
mbgl::OfflineRegions&);
};
+ struct FileSourceCallback {
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$FileSourceCallback";}
+
+ static void onSuccess(jni::JNIEnv&, const jni::Object<OfflineManager::FileSourceCallback>&);
+
+ static void onError(jni::JNIEnv&, const jni::Object<OfflineManager::FileSourceCallback>&, const jni::String&);
+ };
+
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; };
static void registerNative(jni::JNIEnv&);
@@ -85,6 +93,13 @@ public:
const jni::String& eTag,
jboolean mustRevalidate);
+ void resetDatabase(jni::JNIEnv&, const jni::Object<FileSourceCallback>& callback_);
+
+ void invalidateAmbientCache(jni::JNIEnv&, const jni::Object<FileSourceCallback>& callback_);
+
+ void clearAmbientCache(jni::JNIEnv&, const jni::Object<FileSourceCallback>& callback_);
+
+ void setMaximumAmbientCacheSize(jni::JNIEnv&, const jni::jlong size, const jni::Object<FileSourceCallback>& callback_);
private:
std::shared_ptr<mbgl::DefaultFileSource> fileSource;
diff --git a/platform/android/src/offline/offline_region.cpp b/platform/android/src/offline/offline_region.cpp
index e0f28631b4..ac9f491ab6 100644
--- a/platform/android/src/offline/offline_region.cpp
+++ b/platform/android/src/offline/offline_region.cpp
@@ -119,6 +119,24 @@ void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, const jni::Object<Off
});
}
+void OfflineRegion::invalidateOfflineRegion(jni::JNIEnv& env_, const jni::Object<OfflineRegionInvalidateCallback>& callback_) {
+ auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
+
+ fileSource->invalidateOfflineRegion(*region, [
+ //Ensure the object is not gc'd in the meanwhile
+ callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback))
+ ](std::exception_ptr error) mutable {
+ // Reattach, the callback comes from a different thread
+ android::UniqueEnv env = android::AttachEnv();
+
+ if (error) {
+ OfflineRegionInvalidateCallback::onError(*env, *callback, error);
+ } else {
+ OfflineRegionInvalidateCallback::onInvalidate(*env, *callback);
+ }
+ });
+}
+
void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, const jni::Array<jni::jbyte>& jMetadata, const jni::Object<OfflineRegionUpdateMetadataCallback>& callback_) {
auto metadata = OfflineRegion::metadata(env_, jMetadata);
auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
@@ -182,6 +200,7 @@ void OfflineRegion::registerNative(jni::JNIEnv& env) {
jni::Class<OfflineRegionStatusCallback>::Singleton(env);
jni::Class<OfflineRegionDeleteCallback>::Singleton(env);
jni::Class<OfflineRegionUpdateMetadataCallback>::Singleton(env);
+ jni::Class<OfflineRegionInvalidateCallback>::Singleton(env);
static auto& javaClass = jni::Class<OfflineRegion>::Singleton(env);
@@ -195,6 +214,7 @@ void OfflineRegion::registerNative(jni::JNIEnv& env) {
METHOD(&OfflineRegion::setOfflineRegionDownloadState, "setOfflineRegionDownloadState"),
METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"),
METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"),
+ METHOD(&OfflineRegion::invalidateOfflineRegion, "invalidateOfflineRegion"),
METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata")
);
}
@@ -260,5 +280,21 @@ void OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& e
callback.Call(env, method, OfflineRegion::metadata(env, std::move(*metadata)));
}
+// OfflineRegionInvalidateCallback //
+
+void OfflineRegion::OfflineRegionInvalidateCallback::onError(jni::JNIEnv& env,
+ const jni::Object<OfflineRegion::OfflineRegionInvalidateCallback>& callback,
+ std::exception_ptr error) {
+ static auto& javaClass = jni::Class<OfflineRegion::OfflineRegionInvalidateCallback>::Singleton(env);
+ static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
+ callback.Call(env, method, jni::Make<jni::String>(env, mbgl::util::toString(error)));
+}
+
+void OfflineRegion::OfflineRegionInvalidateCallback::onInvalidate(jni::JNIEnv& env, const jni::Object<OfflineRegion::OfflineRegionInvalidateCallback>& callback) {
+ static auto& javaClass = jni::Class<OfflineRegion::OfflineRegionInvalidateCallback>::Singleton(env);
+ static auto method = javaClass.GetMethod<void ()>(env, "onInvalidate");
+ callback.Call(env, method);
+}
+
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/offline/offline_region.hpp b/platform/android/src/offline/offline_region.hpp
index 4618e1abbd..dda253469e 100644
--- a/platform/android/src/offline/offline_region.hpp
+++ b/platform/android/src/offline/offline_region.hpp
@@ -37,6 +37,15 @@ public:
static void onDelete(jni::JNIEnv&, const jni::Object<OfflineRegionDeleteCallback>&);
};
+ class OfflineRegionInvalidateCallback {
+ public:
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionInvalidateCallback"; };
+
+ static void onError(jni::JNIEnv&, const jni::Object<OfflineRegionInvalidateCallback>&, std::exception_ptr);
+
+ static void onInvalidate(jni::JNIEnv&, const jni::Object<OfflineRegionInvalidateCallback>&);
+ };
+
class OfflineRegionUpdateMetadataCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"; };
@@ -62,6 +71,8 @@ public:
void deleteOfflineRegion(jni::JNIEnv&, const jni::Object<OfflineRegionDeleteCallback>&);
+ void invalidateOfflineRegion(jni::JNIEnv&, const jni::Object<OfflineRegionInvalidateCallback>&);
+
void updateOfflineRegionMetadata(jni::JNIEnv&, const jni::Array<jni::jbyte>&, const jni::Object<OfflineRegionUpdateMetadataCallback>&);
static jni::Local<jni::Object<OfflineRegion>> New(jni::JNIEnv&, const jni::Object<FileSource>&, mbgl::OfflineRegion);