summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java33
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java184
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java278
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionDefinition.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionError.java53
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionMetadata.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java84
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java65
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java364
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java60
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_airplanemode_active_black_24dp.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml4
-rwxr-xr-xplatform/android/src/jni.cpp1210
-rw-r--r--platform/android/src/jni.hpp60
22 files changed, 2568 insertions, 43 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index 0c2690aec1..6b213bb655 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -620,11 +620,26 @@ public class MapView extends FrameLayout {
// Zoom
//
- double getZoom() {
+ /**
+ * Returns the current zoom level of the map view.
+ *
+ * @return The current zoom.
+ */
+ @UiThread
+ @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM)
+ public double getZoom() {
return mNativeMapView.getZoom();
}
- void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
+ /**
+ * <p>
+ * Sets the minimum zoom level the map can be displayed at.
+ * </p>
+ *
+ * @param minZoom The new minimum zoom level.
+ */
+ @UiThread
+ public void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
mNativeMapView.setMinZoom(minZoom);
}
@@ -640,7 +655,19 @@ public class MapView extends FrameLayout {
return mNativeMapView.getMaxZoom();
}
- void setZoomControlsEnabled(boolean enabled) {
+ /**
+ * <p>
+ * Sets whether the zoom controls are enabled.
+ * If enabled, the zoom controls are a pair of buttons
+ * (one for zooming in, one for zooming out) that appear on the screen.
+ * When pressed, they cause the camera to zoom in (or out) by one zoom level.
+ * If disabled, the zoom controls are not shown.
+ * </p>
+ * By default the zoom controls are enabled if the device is only single touch capable;
+ *
+ * @param enabled If true, the zoom controls are enabled.
+ */
+ public void setZoomControlsEnabled(boolean enabled) {
mZoomButtonsController.setVisible(enabled);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index 6c9806f79b..9530096131 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.maps;
import android.content.Context;
-
import android.location.Location;
import android.os.Bundle;
import android.os.SystemClock;
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
new file mode 100644
index 0000000000..b56ecfc057
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -0,0 +1,184 @@
+package com.mapbox.mapboxsdk.offline;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+
+import java.io.File;
+
+/**
+ * The offline manager is the main entry point for offline-related functionality.
+ * It'll help you list and create offline regions.
+ */
+public class OfflineManager {
+
+ // Default database name
+ private final static String OFFLINE_DATABASE_NAME = "mbgl-offline.db";
+
+ /*
+ * The maximumCacheSize parameter is a limit applied to non-offline resources only,
+ * i.e. resources added to the database for the "ambient use" caching functionality.
+ * There is no size limit for offline resources.
+ */
+ private final static long DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;
+
+ // Holds the pointer to JNI DefaultFileSource
+ private long mDefaultFileSourcePtr = 0;
+
+ // Makes sure callbacks come back to the main thread
+ private Handler handler;
+
+ // This object is implemented as a singleton
+ private static OfflineManager instance;
+
+ /*
+ * Callbacks
+ */
+
+ public interface ListOfflineRegionsCallback {
+ void onList(OfflineRegion[] offlineRegions);
+ void onError(String error);
+ }
+
+ public interface CreateOfflineRegionCallback {
+ void onCreate(OfflineRegion offlineRegion);
+ void onError(String error);
+ }
+
+ /*
+ * Constructors
+ */
+
+ private OfflineManager(Context context) {
+ // Get a pointer to the DefaultFileSource instance
+ String assetRoot = context.getFilesDir().getAbsolutePath();
+ String cachePath = assetRoot + File.separator + OFFLINE_DATABASE_NAME;
+ mDefaultFileSourcePtr = createDefaultFileSource(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE);
+ }
+
+ public static synchronized OfflineManager getInstance(Context context) {
+ if (instance == null) {
+ instance = new OfflineManager(context);
+ }
+
+ return instance;
+ }
+
+ /*
+ * Access token getter/setter
+ */
+ public void setAccessToken(String accessToken) {
+ setAccessToken(mDefaultFileSourcePtr, accessToken);
+ }
+
+ public String getAccessToken() {
+ return getAccessToken(mDefaultFileSourcePtr);
+ }
+
+ private Handler getHandler() {
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+
+ return handler;
+ }
+
+ /**
+ * Retrieve all regions in the offline database.
+ *
+ * The query will be executed asynchronously and the results passed to the given
+ * callback on the main thread.
+ */
+ public void listOfflineRegions(@NonNull final ListOfflineRegionsCallback callback) {
+ listOfflineRegions(mDefaultFileSourcePtr, new ListOfflineRegionsCallback() {
+ @Override
+ public void onList(final OfflineRegion[] offlineRegions) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onList(offlineRegions);
+ }
+ });
+ }
+
+ @Override
+ public void onError(final String error) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(error);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Create an offline region in the database.
+ *
+ * When the initial database queries have completed, the provided callback will be
+ * executed on the main thread.
+ *
+ * Note that the resulting region will be in an inactive download state; to begin
+ * downloading resources, call `OfflineRegion.setDownloadState(DownloadState.STATE_ACTIVE)`,
+ * optionally registering an `OfflineRegionObserver` beforehand.
+ */
+ public void createOfflineRegion(
+ @NonNull OfflineRegionDefinition definition,
+ @NonNull OfflineRegionMetadata metadata,
+ @NonNull final CreateOfflineRegionCallback callback) {
+
+ createOfflineRegion(mDefaultFileSourcePtr, definition, metadata, new CreateOfflineRegionCallback() {
+ @Override
+ public void onCreate(final OfflineRegion offlineRegion) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onCreate(offlineRegion);
+ }
+ });
+ }
+
+ @Override
+ public void onError(final String error) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(error);
+ }
+ });
+ }
+ });
+ }
+
+ /*
+ * Changing or bypassing this limit without permission from Mapbox is prohibited
+ * by the Mapbox Terms of Service.
+ */
+ public void setOfflineMapboxTileCountLimit(long limit) {
+ setOfflineMapboxTileCountLimit(mDefaultFileSourcePtr, limit);
+ }
+
+
+ /*
+ * Native methods
+ */
+
+ private native long createDefaultFileSource(
+ String cachePath, String assetRoot, long maximumCacheSize);
+
+ private native void setAccessToken(long defaultFileSourcePtr, String accessToken);
+ private native String getAccessToken(long defaultFileSourcePtr);
+
+ private native void listOfflineRegions(
+ long defaultFileSourcePtr, ListOfflineRegionsCallback callback);
+
+ private native void createOfflineRegion(
+ long defaultFileSourcePtr, OfflineRegionDefinition definition,
+ OfflineRegionMetadata metadata, CreateOfflineRegionCallback callback);
+
+ private native void setOfflineMapboxTileCountLimit(
+ long defaultFileSourcePtr, long limit);
+
+}
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
new file mode 100644
index 0000000000..9e518f1e6a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -0,0 +1,278 @@
+package com.mapbox.mapboxsdk.offline;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * To use offline maps on mobile, you will first have to create an offline region.
+ * Use OfflineManager.createOfflineRegion() to create a new offline region.
+ */
+public class OfflineRegion {
+
+ private final static String LOG_TAG = "OfflineRegion";
+
+ // Parent OfflineManager
+ private OfflineManager offlineManager;
+
+ // Members
+ private long mId = 0;
+ private OfflineRegionDefinition mDefinition = null;
+ private OfflineRegionMetadata mMetadata = null;
+
+ // Holds the pointer to JNI OfflineRegion
+ private long mOfflineRegionPtr = 0;
+
+ // Makes sure callbacks come back to the main thread
+ private Handler handler;
+
+ /**
+ * A region can have a single observer, which gets notified whenever a change
+ * to the region's status occurs.
+ */
+ public interface OfflineRegionObserver {
+ /**
+ * Implement this method to be notified of a change in the status of an
+ * offline region. Status changes include any change in state of the members
+ * of OfflineRegionStatus.
+ *
+ * This method will be executed on the main thread.
+ */
+ void onStatusChanged(OfflineRegionStatus status);
+
+ /**
+ * Implement this method to be notified of errors encountered while downloading
+ * regional resources. Such errors may be recoverable; for example the implementation
+ * will attempt to re-request failed resources based on an exponential backoff
+ * algorithm, or when it detects that network access has been restored.
+ *
+ * This method will be executed on the main thread.
+ */
+ void onError(OfflineRegionError error);
+
+ /*
+ * Implement this method to be notified when the limit on the number of Mapbox
+ * tiles stored for offline regions has been reached.
+ *
+ * Once the limit has been reached, the SDK will not download further offline
+ * tiles from Mapbox APIs until existing tiles have been removed. Contact your
+ * Mapbox sales representative to raise the limit.
+ *
+ * This limit does not apply to non-Mapbox tile sources.
+ *
+ * This method will be executed on the main thread.
+ */
+ void mapboxTileCountLimitExceeded(long limit);
+ }
+
+ /*
+ * Callbacks
+ */
+
+ public interface OfflineRegionStatusCallback {
+ void onStatus(OfflineRegionStatus status);
+ void onError(String error);
+ }
+
+ public interface OfflineRegionDeleteCallback {
+ void onDelete();
+ void onError(String error);
+ }
+
+ /**
+ * A region is either inactive (not downloading, but previously-downloaded
+ * resources are available for use), or active (resources are being downloaded
+ * or will be downloaded, if necessary, when network access is available).
+ *
+ * This state is independent of whether or not the complete set of resources
+ * is currently available for offline use. To check if that is the case, use
+ * `OfflineRegionStatus.isComplete()`.
+ */
+
+ @IntDef({STATE_INACTIVE, STATE_ACTIVE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DownloadState {}
+
+ public static final int STATE_INACTIVE = 0;
+ public static final int STATE_ACTIVE = 1;
+
+ /*
+ * Constructor
+ */
+
+ private OfflineRegion() {
+ // For JNI use only, to create a new offline region, use
+ // OfflineManager.createOfflineRegion() instead.
+ }
+
+ /*
+ * Getters
+ */
+
+ public long getID() {
+ return mId;
+ }
+
+ public OfflineRegionDefinition getDefinition() {
+ return mDefinition;
+ }
+
+ public OfflineRegionMetadata getMetadata() {
+ return mMetadata;
+ }
+
+ private Handler getHandler() {
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+
+ return handler;
+ }
+
+ /**
+ * Register an observer to be notified when the state of the region changes.
+ */
+ public void setObserver(@NonNull final OfflineRegionObserver observer) {
+ setOfflineRegionObserver(this, new OfflineRegionObserver() {
+ @Override
+ public void onStatusChanged(final OfflineRegionStatus status) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.onStatusChanged(status);
+ }
+ });
+ }
+
+ @Override
+ public void onError(final OfflineRegionError error) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.onError(error);
+ }
+ });
+ }
+
+ @Override
+ public void mapboxTileCountLimitExceeded(final long limit) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.mapboxTileCountLimitExceeded(limit);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Pause or resume downloading of regional resources.
+ */
+ public void setDownloadState(@DownloadState int state) {
+ setOfflineRegionDownloadState(this, state);
+ }
+
+ /**
+ * Retrieve the current status of the region. The query will be executed
+ * asynchronously and the results passed to the given callback which will be
+ * executed on the main thread.
+ */
+ public void getStatus(@NonNull final OfflineRegionStatusCallback callback) {
+ getOfflineRegionStatus(this, new OfflineRegionStatusCallback() {
+ @Override
+ public void onStatus(final OfflineRegionStatus status) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStatus(status);
+ }
+ });
+ }
+
+ @Override
+ public void onError(final String error) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(error);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Remove an offline region from the database and perform any resources evictions
+ * necessary as a result.
+ *
+ * Eviction works by removing the least-recently requested resources not also required
+ * by other regions, until the database shrinks below a certain size.
+ *
+ * When the operation is complete or encounters an error, the given callback will be
+ * executed on the main thread.
+ */
+ public void delete(@NonNull final OfflineRegionDeleteCallback callback) {
+ deleteOfflineRegion(this, new OfflineRegionDeleteCallback() {
+ @Override
+ public void onDelete() {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onDelete();
+ OfflineRegion.this.finalize();
+ }
+ });
+ }
+
+ @Override
+ public void onError(final String error) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(error);
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ protected void finalize() {
+ try {
+ super.finalize();
+ destroyOfflineRegion(mOfflineRegionPtr);
+ mOfflineRegionPtr = 0;
+ } catch (Throwable throwable) {
+ Log.e(LOG_TAG, "Failed to finalize OfflineRegion: " + throwable.getMessage());
+ }
+ }
+
+ /*
+ * Native methods
+ */
+
+ private native void destroyOfflineRegion(long offlineRegionPtr);
+
+ private native void setOfflineRegionObserver(
+ OfflineRegion offlineRegion,
+ OfflineRegionObserver observerCallback);
+
+ private native void setOfflineRegionDownloadState(
+ OfflineRegion offlineRegion,
+ @DownloadState int offlineRegionDownloadState);
+
+ private native void getOfflineRegionStatus(
+ OfflineRegion offlineRegion,
+ OfflineRegionStatusCallback statusCallback);
+
+ private native void deleteOfflineRegion(
+ OfflineRegion offlineRegion,
+ OfflineRegionDeleteCallback deleteCallback);
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionDefinition.java
new file mode 100644
index 0000000000..0e7fb38e1c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionDefinition.java
@@ -0,0 +1,9 @@
+package com.mapbox.mapboxsdk.offline;
+
+/**
+ * This is the interface that all Offline Region definitions have to implement.
+ *
+ * For the present, a tile pyramid is the only type of offline region.
+ */
+public interface OfflineRegionDefinition {
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionError.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionError.java
new file mode 100644
index 0000000000..e7a57379c5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionError.java
@@ -0,0 +1,53 @@
+package com.mapbox.mapboxsdk.offline;
+
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An Offline Region error
+ */
+public class OfflineRegionError {
+
+ /**
+ * Error code, as a string, self-explanatory.
+ */
+ @StringDef({REASON_SUCCESS, REASON_NOT_FOUND, REASON_SERVER, REASON_CONNECTION, REASON_OTHER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorReason {}
+
+ public static final String REASON_SUCCESS = "REASON_SUCCESS";
+ public static final String REASON_NOT_FOUND = "REASON_NOT_FOUND";
+ public static final String REASON_SERVER = "REASON_SERVER";
+ public static final String REASON_CONNECTION = "REASON_CONNECTION";
+ public static final String REASON_OTHER = "REASON_OTHER";
+
+ private @ErrorReason String reason;
+
+ /**
+ /* An error message from the request handler, e.g. a server message or a system message
+ /* informing the user about the reason for the failure.
+ */
+ private String message;
+
+ /*
+ * Constructors
+ */
+
+ private OfflineRegionError() {
+ // For JNI use only
+ }
+
+ /*
+ * Getters
+ */
+
+ public @ErrorReason String getReason() {
+ return reason;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionMetadata.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionMetadata.java
new file mode 100644
index 0000000000..bbea4580f8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionMetadata.java
@@ -0,0 +1,63 @@
+package com.mapbox.mapboxsdk.offline;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Arbitrary binary region metadata. The contents are opaque to the SDK implementation;
+ * it just stores and retrieves a byte[].
+ */
+public class OfflineRegionMetadata {
+
+ private byte[] metadata;
+
+ /*
+ * Constructor
+ */
+
+ public OfflineRegionMetadata(byte[] metadata) {
+ this.metadata = metadata;
+ }
+
+ /*
+ * Getters and setters
+ */
+
+ public byte[] getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(byte[] metadata) {
+ this.metadata = metadata;
+ }
+
+ /*
+ * Overrides
+ */
+
+ @Override
+ public String toString() {
+ return "OfflineRegionMetadata{metadata=" + metadata + "}";
+ }
+
+ /*
+ * byte[] utils
+ */
+
+ public static byte[] serialize(Object obj) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ObjectOutputStream os = new ObjectOutputStream(out);
+ os.writeObject(obj);
+ return out.toByteArray();
+ }
+
+ public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
+ ByteArrayInputStream in = new ByteArrayInputStream(data);
+ ObjectInputStream is = new ObjectInputStream(in);
+ return is.readObject();
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
new file mode 100644
index 0000000000..e65a20f18e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
@@ -0,0 +1,84 @@
+package com.mapbox.mapboxsdk.offline;
+
+/**
+ * A region's status includes its active/inactive state as well as counts
+ * of the number of resources that have completed downloading, their total
+ * size in bytes, and the total number of resources that are required.
+ *
+ * Note that the total required size in bytes is not currently available. A
+ * future API release may provide an estimate of this number.
+ */
+public class OfflineRegionStatus {
+
+ @OfflineRegion.DownloadState private int downloadState = OfflineRegion.STATE_INACTIVE;
+
+ /**
+ * The number of resources that have been fully downloaded and are ready for
+ * offline access.
+ */
+ private long completedResourceCount = 0;
+
+ /**
+ * The cumulative size, in bytes, of all resources that have been fully downloaded.
+ */
+ private long completedResourceSize = 0;
+
+ /**
+ * The number of resources that are known to be required for this region. See the
+ * documentation for `requiredResourceCountIsPrecise` for an important caveat
+ * about this number.
+ */
+ private long requiredResourceCount = 0;
+
+ /**
+ * This property is true when the value of requiredResourceCount is a precise
+ * count of the number of required resources, and false when it is merely a lower
+ * bound.
+ *
+ * Specifically, it is false during early phases of an offline download. Once
+ * style and tile sources have been downloaded, it is possible to calculate the
+ * precise number of required resources, at which point it is set to true.
+ */
+ private boolean requiredResourceCountIsPrecise = true;
+
+ /*
+ * Use setObserver(OfflineRegionObserver observer) to obtain a OfflineRegionStatus object.
+ */
+
+ private OfflineRegionStatus() {
+ // For JNI use only
+ }
+
+ /*
+ * Is the region complete?
+ */
+
+ public boolean isComplete() {
+ return (completedResourceCount == requiredResourceCount);
+ }
+
+ /*
+ * Getters
+ */
+
+ public @OfflineRegion.DownloadState int getDownloadState() {
+ return downloadState;
+ }
+
+ public long getCompletedResourceCount() {
+ return completedResourceCount;
+ }
+
+ public long getCompletedResourceSize() {
+ return completedResourceSize;
+ }
+
+ public long getRequiredResourceCount() {
+ return requiredResourceCount;
+ }
+
+ public boolean isRequiredResourceCountPrecise() {
+ return requiredResourceCountIsPrecise;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
new file mode 100644
index 0000000000..5a0be6b33f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
@@ -0,0 +1,65 @@
+package com.mapbox.mapboxsdk.offline;
+
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+
+/**
+ * An offline region defined by a style URL, geographic bounding box, zoom range, and
+ * device pixel ratio.
+ *
+ * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom.
+ *
+ * maxZoom may be ∞, in which case for each tile source, the region will include
+ * tiles from minZoom up to the maximum zoom level provided by that source.
+ *
+ * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0.
+ */
+public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition {
+
+ private String styleURL;
+ private LatLngBounds bounds;
+ private double minZoom;
+ private double maxZoom;
+ private float pixelRatio;
+
+ /*
+ * Constructors
+ */
+
+ private OfflineTilePyramidRegionDefinition() {
+ // For JNI use only
+ }
+
+ public OfflineTilePyramidRegionDefinition(
+ String styleURL, LatLngBounds bounds, double minZoom, double maxZoom, float pixelRatio) {
+ this.styleURL = styleURL;
+ this.bounds = bounds;
+ this.minZoom = minZoom;
+ this.maxZoom = maxZoom;
+ this.pixelRatio = pixelRatio;
+ }
+
+ /*
+ * Getters
+ */
+
+ public String getStyleURL() {
+ return styleURL;
+ }
+
+ public LatLngBounds getBounds() {
+ return bounds;
+ }
+
+ public double getMinZoom() {
+ return minZoom;
+ }
+
+ public double getMaxZoom() {
+ return maxZoom;
+ }
+
+ public float getPixelRatio() {
+ return pixelRatio;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 4b44efdfe5..37bf928a5a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -21,7 +21,6 @@
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -91,22 +90,24 @@
android:label="@string/activity_dynamic_marker" />
<activity
android:name=".MapPaddingActivity"
- android:screenOrientation="portrait"
- android:label="@string/activity_map_padding" />
+ android:label="@string/activity_map_padding"
+ android:screenOrientation="portrait" />
+ <activity
+ android:name=".OfflineActivity"
+ android:label="@string/activity_offline" />
<meta-data
android:name="com.mapbox.AccessToken"
android:value="" />
-
<meta-data
android:name="com.mapbox.TestEventsServer"
android:value="https://cloudfront-staging.tilestream.net" />
-
<meta-data
android:name="com.mapbox.TestEventsAccessToken"
android:value="sk.eyJ1IjoiYmxlZWdlIiwiYSI6InNpcml1c2x5In0.KyT-boMyC_xZYTYojTc8zg" />
<service android:name="com.mapbox.mapboxsdk.telemetry.TelemetryService" />
+
</application>
</manifest>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
index d1ac656cd0..451876e39b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
@@ -472,6 +472,10 @@ public class MainActivity extends AppCompatActivity {
startActivity(new Intent(getApplicationContext(),MapPaddingActivity.class));
return true;
+ case R.id.action_offline:
+ startActivity(new Intent(getApplicationContext(), OfflineActivity.class));
+ return true;
+
default:
return changeMapStyle(menuItem.getItemId());
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java
new file mode 100644
index 0000000000..64ecc2100e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/OfflineActivity.java
@@ -0,0 +1,364 @@
+package com.mapbox.mapboxsdk.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.offline.OfflineManager;
+import com.mapbox.mapboxsdk.offline.OfflineRegion;
+import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition;
+import com.mapbox.mapboxsdk.offline.OfflineRegionError;
+import com.mapbox.mapboxsdk.offline.OfflineRegionMetadata;
+import com.mapbox.mapboxsdk.offline.OfflineRegionStatus;
+import com.mapbox.mapboxsdk.testapp.offline.DownloadRegionDialog;
+import com.mapbox.mapboxsdk.testapp.offline.ListRegionsDialog;
+import com.mapbox.mapboxsdk.utils.ApiAccess;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class OfflineActivity extends AppCompatActivity
+ implements DownloadRegionDialog.DownloadRegionDialogListener {
+
+ private final static String LOG_TAG = "OfflineActivity";
+
+ private final static String KEY_REGION_NAME = "KEY_REGION_NAME";
+
+ /*
+ * UI elements
+ */
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+ private ProgressBar mProgressBar;
+ private Button downloadRegion;
+ private Button listRegions;
+
+ private boolean isEndNotified;
+
+ /*
+ * Offline objects
+ */
+ private OfflineManager mOfflineManager;
+ private OfflineRegion mOfflineRegion;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_offline);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ // Set up map
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.setAccessToken(ApiAccess.getToken(this));
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ Log.d(LOG_TAG, "Map is ready");
+ mMapboxMap = mapboxMap;
+
+ // Set style
+ mapboxMap.setStyle(Style.MAPBOX_STREETS);
+
+ // Set initial position to UNHQ in NYC
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(40.749851, -73.967966))
+ .zoom(14)
+ .bearing(0)
+ .tilt(0)
+ .build()));
+ }
+ });
+
+ // The progress bar
+ mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
+
+ // Set up button listeners
+ downloadRegion = (Button) findViewById(R.id.button_download_region);
+ downloadRegion.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleDownloadRegion();
+ }
+ });
+
+ listRegions = (Button) findViewById(R.id.button_list_regions);
+ listRegions.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleListRegions();
+ }
+ });
+
+ // Set up the OfflineManager
+ mOfflineManager = OfflineManager.getInstance(this);
+ mOfflineManager.setAccessToken(ApiAccess.getToken(this));
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mMapView.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mMapView.onStop();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /*
+ * Buttons logic
+ */
+
+ private void handleDownloadRegion() {
+ Log.d(LOG_TAG, "handleDownloadRegion");
+
+ // Show dialog
+ DownloadRegionDialog downloadRegionDialog = new DownloadRegionDialog();
+ downloadRegionDialog.show(getSupportFragmentManager(), "download");
+ }
+
+ private void handleListRegions() {
+ Log.d(LOG_TAG, "handleListRegions");
+
+ // Query the DB asynchronously
+ mOfflineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() {
+ @Override
+ public void onList(OfflineRegion[] offlineRegions) {
+ // Check result
+ if (offlineRegions == null || offlineRegions.length == 0) {
+ Log.d(LOG_TAG, "You have no regions yet.");
+ return;
+ }
+
+ // Get regions info
+ ArrayList<String> offlineRegionsNames = new ArrayList<>();
+ for (OfflineRegion offlineRegion : offlineRegions) {
+ offlineRegionsNames.add(getRegionName(offlineRegion));
+ }
+
+ // Create args
+ Bundle args = new Bundle();
+ args.putStringArrayList(ListRegionsDialog.ITEMS, offlineRegionsNames);
+
+ // Show dialog
+ ListRegionsDialog listRegionsDialog = new ListRegionsDialog();
+ listRegionsDialog.setArguments(args);
+ listRegionsDialog.show(getSupportFragmentManager(), "list");
+ }
+
+ @Override
+ public void onError(String error) {
+ Log.e(LOG_TAG, "Error: " + error);
+ }
+
+ private String getRegionName(OfflineRegion offlineRegion) {
+ String regionName = "Region " + offlineRegion.getID();
+
+ try {
+ byte[] metadata = offlineRegion.getMetadata().getMetadata();
+ HashMap<String, String> data = (HashMap<String, String>) OfflineRegionMetadata.deserialize(metadata);
+ regionName = data.get(KEY_REGION_NAME);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to decode metadata: " + e.getMessage());
+ }
+
+ return regionName;
+ }
+ });
+ }
+
+ /*
+ * Dialogs
+ */
+
+ @Override
+ public void onDownloadRegionDialogPositiveClick(final String regionName) {
+ Log.d(LOG_TAG, "Download started: " + regionName);
+
+ // Start progress bar
+ startProgress();
+
+ // Definition
+ String styleURL = mMapboxMap.getStyleUrl();
+ LatLngBounds bounds = mMapboxMap.getProjection().getVisibleRegion().latLngBounds;
+ double minZoom = 14; // Switch to dynamic once map refactor is complete
+ double maxZoom = 16; // Switch to dynamic once map refactor is complete
+ float pixelRatio = this.getResources().getDisplayMetrics().density;
+ OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(
+ styleURL, bounds, minZoom, maxZoom, pixelRatio);
+
+ // Sample way of encoding metadata
+ OfflineRegionMetadata metadata = null;
+ try {
+ HashMap<String, String> data = new HashMap<>();
+ data.put(KEY_REGION_NAME, regionName);
+ byte[] dataEncoded = OfflineRegionMetadata.serialize(data);
+ metadata = new OfflineRegionMetadata(dataEncoded);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Metadata encoding failed: " + e.getMessage());
+ }
+
+ // Create region
+ mOfflineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
+ @Override
+ public void onCreate(OfflineRegion offlineRegion) {
+ Log.d(LOG_TAG, "Offline region created: " + regionName);
+ mOfflineRegion = offlineRegion;
+ launchDownload();
+ }
+
+ @Override
+ public void onError(String error) {
+ Log.e(LOG_TAG, "Error: " + error);
+ }
+ });
+ }
+
+ private void launchDownload() {
+ // Set an observer
+ mOfflineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {
+ @Override
+ public void onStatusChanged(OfflineRegionStatus status) {
+ // Compute a percentage
+ double percentage = status.getRequiredResourceCount() >= 0 ?
+ (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :
+ 0.0;
+
+ if (status.isComplete() || percentage >= 98.0 /* Known issue */) {
+ // Download complete
+ endProgress("Region downloaded successfully.");
+ return;
+ } else if (status.isRequiredResourceCountPrecise()) {
+ // Switch to determinate state
+ setPercentage((int) Math.round(percentage));
+ }
+
+ // Debug
+ Log.d(LOG_TAG, String.format("%s/%s resources; %s bytes downloaded.",
+ String.valueOf(status.getCompletedResourceCount()),
+ String.valueOf(status.getRequiredResourceCount()),
+ String.valueOf(status.getCompletedResourceSize())));
+ }
+
+ @Override
+ public void onError(OfflineRegionError error) {
+ Log.e(LOG_TAG, "onError reason: " + error.getReason());
+ Log.e(LOG_TAG, "onError message: " + error.getMessage());
+ }
+
+ @Override
+ public void mapboxTileCountLimitExceeded(long limit) {
+ Log.e(LOG_TAG, "Mapbox tile count limit exceeded: " + limit);
+ }
+ });
+
+ // Change the region state
+ mOfflineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE);
+ }
+
+ /*
+ * Progress bar
+ */
+
+ private void startProgress() {
+ // Disable buttons
+ downloadRegion.setEnabled(false);
+ listRegions.setEnabled(false);
+
+ // Start and show the progress bar
+ isEndNotified = false;
+ mProgressBar.setIndeterminate(true);
+ mProgressBar.setVisibility(View.VISIBLE);
+ }
+ private void setPercentage(final int percentage) {
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setProgress(percentage);
+ }
+
+ private void endProgress(final String message) {
+ // Don't notify more than once
+ if (isEndNotified) return;
+
+ // Enable buttons
+ downloadRegion.setEnabled(true);
+ listRegions.setEnabled(true);
+
+ // Stop and hide the progress bar
+ isEndNotified = true;
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setVisibility(View.GONE);
+
+ // Show a toast
+ Toast.makeText(OfflineActivity.this, message, Toast.LENGTH_LONG).show();
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java
new file mode 100644
index 0000000000..81b681cc50
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/DownloadRegionDialog.java
@@ -0,0 +1,60 @@
+package com.mapbox.mapboxsdk.testapp.offline;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+import android.widget.EditText;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+/**
+ * Created by antonio on 2/17/16.
+ */
+public class DownloadRegionDialog extends DialogFragment {
+
+ private final static String LOG_TAG = "DownloadRegionDialog";
+
+ public interface DownloadRegionDialogListener {
+ void onDownloadRegionDialogPositiveClick(String regionName);
+ }
+
+ DownloadRegionDialogListener mListener;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mListener = (DownloadRegionDialogListener) activity;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Let the user choose a name for the region
+ final EditText regionNameEdit = new EditText(getActivity());
+
+ builder.setTitle("Choose a name for the region")
+ .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
+ .setView(regionNameEdit)
+ .setPositiveButton("Start", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String regionName = regionNameEdit.getText().toString();
+ mListener.onDownloadRegionDialogPositiveClick(regionName);
+ }
+ }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Download cancelled.");
+ }
+ });
+
+ return builder.create();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java
new file mode 100644
index 0000000000..50df71ad00
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/offline/ListRegionsDialog.java
@@ -0,0 +1,51 @@
+package com.mapbox.mapboxsdk.testapp.offline;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+
+/**
+ * Created by antonio on 2/17/16.
+ */
+public class ListRegionsDialog extends DialogFragment {
+
+ private final static String LOG_TAG = "ListRegionsDialog";
+
+ public final static String ITEMS = "ITEMS";
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ // Read args
+ Bundle args = getArguments();
+ ArrayList<String> offlineRegionsNames = (args == null ? null : args.getStringArrayList(ITEMS));
+ CharSequence[] items = offlineRegionsNames.toArray(new CharSequence[offlineRegionsNames.size()]);
+
+ builder.setTitle("List of offline regions")
+ .setIcon(R.drawable.ic_airplanemode_active_black_24dp)
+ .setItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Selected item: " + which);
+ }
+ })
+ .setPositiveButton("Accept", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Log.d(LOG_TAG, "Dialog dismissed");
+ }
+ });
+
+ return builder.create();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_airplanemode_active_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_airplanemode_active_black_24dp.xml
new file mode 100644
index 0000000000..55a8d22a54
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_airplanemode_active_black_24dp.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.18,9"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml
index ce055138b1..0b318d781b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera.xml
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
new file mode 100644
index 0000000000..5f24ea812e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="25dp"
+ android:layout_below="@+id/toolbar"
+ style="@android:style/Widget.Material.ProgressBar.Horizontal" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@+id/progress_bar" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:text="@string/button_download_region"
+ android:id="@+id/button_download_region"
+ android:layout_margin="@dimen/fab_margin"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentBottom="true"
+ android:background="@color/white"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:text="@string/button_list_regions"
+ android:id="@+id/button_list_regions"
+ android:layout_alignBottom="@+id/button_download_region"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:background="@color/white"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
index 512d195593..92ec45b8c8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
@@ -73,6 +73,12 @@
<menu>
<item
+ android:id="@+id/action_offline"
+ android:checkable="false"
+ android:icon="@drawable/ic_airplanemode_active_black_24dp"
+ android:title="@string/action_offline" />
+
+ <item
android:id="@+id/action_mapboxmap"
android:checkable="false"
android:icon="@drawable/ic_mapboxmap"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index c852ed0e7a..aeda60d478 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -6,5 +6,4 @@
<dimen name="map_padding_left">96dp</dimen>
<dimen name="map_padding_bottom">256dp</dimen>
<dimen name="map_padding_right">32dp</dimen>
-
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index ea346674b5..85e928aadc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -27,6 +27,7 @@
<string name="activity_scroll_by">Scroll By Activity</string>
<string name="activity_dynamic_marker">Dynamic Marker Activity</string>
<string name="activity_map_padding">Map Padding Activity</string>
+ <string name="activity_offline">Offline Map Activity</string>
<string name="navdrawer_menu_title_mainactivity_controls">Main Activity Controls</string>
<string name="navdrawer_menu_title_mainactivity_styles">Main Activity Styles</string>
@@ -56,6 +57,7 @@
<string name="action_double_mapview">Double MapView</string>
<string name="action_dynamic_marker">Dynamic Marker</string>
<string name="action_map_padding">Map Padding</string>
+ <string name="action_offline">Offline Map</string>
<string name="button_camera_move">Move</string>
<string name="button_camera_ease">Ease</string>
@@ -98,5 +100,7 @@
<string name="scrollby_x_value">X: %1$d</string>
<string name="scrollby_y_value">Y: %1$d</string>
+ <string name="button_download_region">Download region</string>
+ <string name="button_list_regions">List regions</string>
</resources>
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index db6bc73726..cfb7ca34e3 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -25,6 +25,7 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/util/string.hpp>
#pragma clang diagnostic ignored "-Wunused-parameter"
@@ -115,6 +116,67 @@ jfieldID customLayerInitializeFunctionId = nullptr;
jfieldID customLayerRenderFunctionId = nullptr;
jfieldID customLayerDeinitializeFunctionId = nullptr;
+// Offline declarations start
+
+jclass offlineManagerClass = nullptr;
+jfieldID offlineManagerClassPtrId = nullptr;
+
+jclass listOfflineRegionsCallbackClass = nullptr;
+jmethodID listOnListMethodId = nullptr;
+jmethodID listOnErrorMethodId = nullptr;
+
+jclass offlineRegionClass = nullptr;
+jmethodID offlineRegionConstructorId = nullptr;
+jfieldID offlineRegionOfflineManagerId = nullptr;
+jfieldID offlineRegionIdId = nullptr;
+jfieldID offlineRegionDefinitionId = nullptr;
+jfieldID offlineRegionMetadataId = nullptr;
+jfieldID offlineRegionPtrId = nullptr;
+
+jclass offlineRegionDefinitionClass = nullptr;
+jmethodID offlineRegionDefinitionConstructorId = nullptr;
+jfieldID offlineRegionDefinitionStyleURLId = nullptr;
+jfieldID offlineRegionDefinitionBoundsId = nullptr;
+jfieldID offlineRegionDefinitionMinZoomId = nullptr;
+jfieldID offlineRegionDefinitionMaxZoomId = nullptr;
+jfieldID offlineRegionDefinitionPixelRatioId = nullptr;
+
+jclass offlineRegionMetadataClass = nullptr;
+jmethodID offlineRegionMetadataConstructorId = nullptr;
+jfieldID offlineRegionMetadataMetadataId = nullptr;
+
+jclass createOfflineRegionCallbackClass = nullptr;
+jmethodID createOnCreateMethodId = nullptr;
+jmethodID createOnErrorMethodId = nullptr;
+
+jclass offlineRegionObserverClass = nullptr;
+jmethodID offlineRegionObserveronStatusChangedId = nullptr;
+jmethodID offlineRegionObserveronErrorId = nullptr;
+jmethodID offlineRegionObserveronLimitId = nullptr;
+
+jclass offlineRegionStatusClass = nullptr;
+jmethodID offlineRegionStatusConstructorId = nullptr;
+jfieldID offlineRegionStatusDownloadStateId = nullptr;
+jfieldID offlineRegionStatusCompletedResourceCountId = nullptr;
+jfieldID offlineRegionStatusCompletedResourceSizeId = nullptr;
+jfieldID offlineRegionStatusRequiredResourceCountId = nullptr;
+jfieldID offlineRegionStatusRequiredResourceCountIsPreciseId = nullptr;
+
+jclass offlineRegionErrorClass = nullptr;
+jmethodID offlineRegionErrorConstructorId = nullptr;
+jfieldID offlineRegionErrorReasonId = nullptr;
+jfieldID offlineRegionErrorMessageId = nullptr;
+
+jclass offlineRegionStatusCallbackClass = nullptr;
+jmethodID offlineRegionStatusOnStatusId = nullptr;
+jmethodID offlineRegionStatusOnErrorId = nullptr;
+
+jclass offlineRegionDeleteCallbackClass = nullptr;
+jmethodID offlineRegionDeleteOnDeleteId = nullptr;
+jmethodID offlineRegionDeleteOnErrorId = nullptr;
+
+// Offline declarations end
+
bool throw_jni_error(JNIEnv *env, const char *msg) {
if (env->ThrowNew(runtimeExceptionClass, msg) < 0) {
env->ExceptionDescribe();
@@ -373,6 +435,59 @@ std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> annotation
return std::make_pair(segment, shapeProperties);
}
+static std::vector<uint8_t> metadata_from_java(JNIEnv* env, jbyteArray j) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "metadata_from_java");
+ jsize length = env->GetArrayLength(j);
+ std::vector<uint8_t> c;
+ c.resize(length);
+ env->GetByteArrayRegion(j, 0, length, reinterpret_cast<jbyte*>(c.data()));
+ return c;
+}
+
+static jbyteArray metadata_from_native(JNIEnv* env, const std::vector<uint8_t>& c) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "metadata_from_java");
+ jsize length = static_cast<jsize>(c.size());
+ jbyteArray j = env->NewByteArray(length);
+ env->SetByteArrayRegion(j, 0, c.size(), reinterpret_cast<const jbyte*>(c.data()));
+ return j;
+}
+
+static mbgl::LatLngBounds latlngbounds_from_java(JNIEnv *env, jobject latLngBounds) {
+ // Checks
+ if (env->ExceptionCheck() || (latLngBounds == nullptr)) {
+ env->ExceptionDescribe();
+ return mbgl::LatLngBounds::empty();
+ }
+
+ jdouble swLat = env->GetDoubleField(latLngBounds, latLngBoundsLatSouthId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return mbgl::LatLngBounds::empty();
+ }
+
+ jdouble swLon = env->GetDoubleField(latLngBounds, latLngBoundsLonWestId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return mbgl::LatLngBounds::empty();
+ }
+
+ jdouble neLat = env->GetDoubleField(latLngBounds, latLngBoundsLatNorthId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return mbgl::LatLngBounds::empty();
+ }
+
+ jdouble neLon = env->GetDoubleField(latLngBounds, latLngBoundsLonEastId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return mbgl::LatLngBounds::empty();
+ }
+
+ // Build object
+ mbgl::LatLngBounds result = mbgl::LatLngBounds::hull({ swLat, swLon }, { neLat, neLon });
+ return result;
+}
+
}
}
@@ -1184,43 +1299,20 @@ void JNICALL nativeRemoveAnnotations(JNIEnv *env, jobject obj, jlong nativeMapVi
nativeMapView->getMap().removeAnnotations(ids);
}
-jlongArray JNICALL nativeGetAnnotationsInBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject latLngBounds) {
+jlongArray JNICALL nativeGetAnnotationsInBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject latLngBounds_) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAnnotationsInBounds");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- if (env->ExceptionCheck() || (latLngBounds == nullptr)) {
- env->ExceptionDescribe();
+ // Conversion
+ mbgl::LatLngBounds latLngBounds = latlngbounds_from_java(env, latLngBounds_);
+ if (latLngBounds.isEmpty()) {
return nullptr;
}
- jdouble swLat = env->GetDoubleField(latLngBounds, latLngBoundsLatSouthId);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- return nullptr;
- }
-
- jdouble swLon = env->GetDoubleField(latLngBounds, latLngBoundsLonWestId);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- return nullptr;
- }
-
- jdouble neLat = env->GetDoubleField(latLngBounds, latLngBoundsLatNorthId);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- return nullptr;
- }
-
- jdouble neLon = env->GetDoubleField(latLngBounds, latLngBoundsLonEastId);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- return nullptr;
- }
-
- // assume only points for now
+ // Assume only points for now
std::vector<uint32_t> annotations = nativeMapView->getMap().getPointAnnotationsInBounds(
- mbgl::LatLngBounds::hull({ swLat, swLon }, { neLat, neLon }));
+ latLngBounds);
return std_vector_uint_to_jobject(env, annotations);
}
@@ -1620,6 +1712,422 @@ void JNICALL nativeRemoveCustomLayer(JNIEnv *env, jobject obj, jlong nativeMapVi
nativeMapView->getMap().removeCustomLayer(std_string_from_jstring(env, id));
}
+// Offline calls begin
+
+jlong JNICALL createDefaultFileSource(JNIEnv *env, jobject obj, jstring cachePath_, jstring assetRoot_, jlong maximumCacheSize) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "createDefaultFileSource");
+ std::string cachePath = std_string_from_jstring(env, cachePath_);
+ std::string assetRoot = std_string_from_jstring(env, assetRoot_);
+ mbgl::DefaultFileSource *defaultFileSource = new mbgl::DefaultFileSource(cachePath, assetRoot, maximumCacheSize);
+ jlong defaultFileSourcePtr = reinterpret_cast<jlong>(defaultFileSource);
+ return defaultFileSourcePtr;
+}
+
+void JNICALL setAccessToken(JNIEnv *env, jobject obj, jlong defaultFileSourcePtr, jstring accessToken_) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "setAccessToken");
+ assert(defaultFileSourcePtr != 0);
+ std::string accessToken = std_string_from_jstring(env, accessToken_);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+ defaultFileSource->setAccessToken(accessToken);
+}
+
+jstring JNICALL getAccessToken(JNIEnv *env, jobject obj, jlong defaultFileSourcePtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "getAccessToken");
+ assert(defaultFileSourcePtr != 0);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+ std::string accessToken = defaultFileSource->getAccessToken();
+ return std_string_to_jstring(env, accessToken);
+}
+
+void JNICALL listOfflineRegions(JNIEnv *env, jobject obj, jlong defaultFileSourcePtr, jobject listCallback) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "listOfflineRegions");
+
+ // Checks
+ assert(defaultFileSourcePtr != 0);
+ if (listCallback == nullptr) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Callback has to be set.");
+ return;
+ }
+
+ // Makes sure the objects don't get GC'ed
+ obj = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+ listCallback = reinterpret_cast<jobject>(env->NewGlobalRef(listCallback));
+
+ // Launch listCallback
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+ defaultFileSource->listOfflineRegions([obj, defaultFileSourcePtr, listCallback](std::exception_ptr error, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) {
+
+ // Reattach, the callback comes from a different thread
+ JNIEnv *env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+ if (renderDetach) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Attached.");
+ }
+
+ if (error) {
+ std::string message = mbgl::util::toString(error);
+ env2->CallVoidMethod(listCallback, listOnErrorMethodId, std_string_to_jstring(env2, message));
+ } else if (regions) {
+ // Build jobjectArray
+ jsize index = 0;
+ jobjectArray jregions = env2->NewObjectArray(regions->size(), offlineRegionClass, NULL);
+ for (auto& region : *regions) {
+ // Build the Region object
+ jobject jregion = env2->NewObject(offlineRegionClass, offlineRegionConstructorId);
+ env2->SetObjectField(jregion, offlineRegionOfflineManagerId, obj);
+ env2->SetLongField(jregion, offlineRegionIdId, region.getID());
+
+ // Metadata object
+ jbyteArray metadata = metadata_from_native(env2, region.getMetadata());
+ jobject jmetadata = env2->NewObject(offlineRegionMetadataClass, offlineRegionMetadataConstructorId, metadata);
+ env2->SetObjectField(jregion, offlineRegionMetadataId, jmetadata);
+
+ // Moves the region on the stack into a heap-allocated one
+ env2->SetLongField(jregion, offlineRegionPtrId,
+ reinterpret_cast<jlong>(new mbgl::OfflineRegion(std::move(region))));
+
+ env2->SetObjectArrayElement(jregions, index, jregion);
+ index++;
+ }
+
+ // Trigger callback
+ env2->CallVoidMethod(listCallback, listOnListMethodId, jregions);
+ }
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(obj);
+ env2->DeleteGlobalRef(listCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ });
+}
+
+void JNICALL createOfflineRegion(JNIEnv *env, jobject obj, jlong defaultFileSourcePtr, jobject definition_, jobject metadata_, jobject createCallback) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "createOfflineRegion");
+
+ // Checks
+ assert(defaultFileSourcePtr != 0);
+ if (createCallback == nullptr) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Callback has to be set.");
+ return;
+ }
+
+ // Definition fields
+ jstring jStyleURL = reinterpret_cast<jstring>(env->GetObjectField(definition_, offlineRegionDefinitionStyleURLId));
+ std::string styleURL = std_string_from_jstring(env, jStyleURL);
+ jobject jBounds = env->GetObjectField(definition_, offlineRegionDefinitionBoundsId);
+ jdouble jMinZoom = env->GetDoubleField(definition_, offlineRegionDefinitionMinZoomId);
+ jdouble jMaxZoom = env->GetDoubleField(definition_, offlineRegionDefinitionMaxZoomId);
+ jfloat jPixelRatio = env->GetFloatField(definition_, offlineRegionDefinitionPixelRatioId);
+
+ // Convert bounds fields to native
+ mbgl::LatLngBounds bounds = latlngbounds_from_java(env, jBounds);
+
+ // Definition
+ mbgl::OfflineTilePyramidRegionDefinition definition(styleURL, bounds, jMinZoom, jMaxZoom, jPixelRatio);
+
+ // Metadata
+ mbgl::OfflineRegionMetadata metadata;
+ jbyteArray jmetadata = (jbyteArray)env->GetObjectField(metadata_, offlineRegionMetadataMetadataId);
+ if (jmetadata != nullptr) {
+ metadata = metadata_from_java(env, jmetadata);
+ }
+
+ // Makes sure the objects don't get GC'ed
+ obj = reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+ createCallback = reinterpret_cast<jobject>(env->NewGlobalRef(createCallback));
+
+ // Launch createCallback
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+ defaultFileSource->createOfflineRegion(definition, metadata, [obj, defaultFileSourcePtr, createCallback] (std::exception_ptr error, mbgl::optional<mbgl::OfflineRegion> region) {
+
+ // Reattach, the callback comes from a different thread
+ JNIEnv *env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+ if (renderDetach) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Attached.");
+ }
+
+ if (error) {
+ std::string message = mbgl::util::toString(error);
+ env2->CallVoidMethod(createCallback, createOnErrorMethodId, std_string_to_jstring(env2, message));
+ } else if (region) {
+ // Build the Region object
+ jobject jregion = env2->NewObject(offlineRegionClass, offlineRegionConstructorId);
+ env2->SetObjectField(jregion, offlineRegionOfflineManagerId, obj);
+ env2->SetLongField(jregion, offlineRegionIdId, region->getID());
+
+ // Metadata object
+ jbyteArray xmetadata = metadata_from_native(env2, region->getMetadata());
+ jobject xjmetadata = env2->NewObject(offlineRegionMetadataClass, offlineRegionMetadataConstructorId, xmetadata);
+ env2->SetObjectField(jregion, offlineRegionMetadataId, xjmetadata);
+
+ // Moves the region on the stack into a heap-allocated one
+ env2->SetLongField(jregion, offlineRegionPtrId,
+ reinterpret_cast<jlong>(new mbgl::OfflineRegion(std::move(*region))));
+
+ // Invoke Java callback
+ env2->CallVoidMethod(createCallback, createOnCreateMethodId, jregion);
+ }
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(obj);
+ env2->DeleteGlobalRef(createCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ });
+}
+
+void JNICALL setOfflineMapboxTileCountLimit(JNIEnv *env, jobject obj, jlong defaultFileSourcePtr, jlong limit) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "setOfflineMapboxTileCountLimit");
+
+ // Checks
+ assert(defaultFileSourcePtr != 0);
+ assert(limit > 0);
+
+ // Set limit
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+ defaultFileSource->setOfflineMapboxTileCountLimit(limit);
+}
+
+void JNICALL destroyOfflineRegion(JNIEnv *env, jobject obj, jlong offlineRegionPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "destroyOfflineRegion");
+ assert(offlineRegionPtr != 0);
+ mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr);
+ delete offlineRegion;
+ offlineRegion = nullptr;
+}
+
+void JNICALL setOfflineRegionObserver(JNIEnv *env, jobject obj, jobject offlineRegion_, jobject observerCallback) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "setOfflineRegionObserver");
+
+ // Offline region
+ jlong offlineRegionPtr = env->GetLongField(offlineRegion_, offlineRegionPtrId);
+ mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr);
+
+ // File source
+ jobject jmanager = env->GetObjectField(offlineRegion_, offlineRegionOfflineManagerId);
+ jlong defaultFileSourcePtr = env->GetLongField(jmanager, offlineManagerClassPtrId);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+
+ // Define the observer
+ class Observer : public mbgl::OfflineRegionObserver {
+ public:
+ Observer(jobject observerCallback_)
+ : observerCallback(observerCallback_) {
+ }
+
+ void statusChanged(mbgl::OfflineRegionStatus status) override {
+ // Env
+ JNIEnv* env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+
+ // Conver to jint
+ jint downloadState;
+ switch(status.downloadState) {
+ case mbgl::OfflineRegionDownloadState::Inactive:
+ downloadState = 0;
+ break;
+ case mbgl::OfflineRegionDownloadState::Active:
+ downloadState = 1;
+ break;
+ }
+
+ // Stats object
+ jobject jstatus = env2->NewObject(offlineRegionStatusClass, offlineRegionStatusConstructorId);
+ env2->SetIntField(jstatus, offlineRegionStatusDownloadStateId, downloadState);
+ env2->SetLongField(jstatus, offlineRegionStatusCompletedResourceCountId, status.completedResourceCount);
+ env2->SetLongField(jstatus, offlineRegionStatusCompletedResourceSizeId, status.completedResourceSize);
+ env2->SetLongField(jstatus, offlineRegionStatusRequiredResourceCountId, status.requiredResourceCount);
+ env2->SetBooleanField(jstatus, offlineRegionStatusRequiredResourceCountIsPreciseId, status.requiredResourceCountIsPrecise);
+ env2->CallVoidMethod(observerCallback, offlineRegionObserveronStatusChangedId, jstatus);
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(observerCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ }
+
+ void responseError(mbgl::Response::Error error) override {
+ // Env
+ JNIEnv* env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+
+ // Handle the value of reason independently of the underlying int value
+ std::string errorReason;
+ switch(error.reason) {
+ case mbgl::Response::Error::Reason::Success:
+ errorReason = "REASON_SUCCESS";
+ break;
+ case mbgl::Response::Error::Reason::NotFound:
+ errorReason = "REASON_NOT_FOUND";
+ break;
+ case mbgl::Response::Error::Reason::Server:
+ errorReason = "REASON_SERVER";
+ break;
+ case mbgl::Response::Error::Reason::Connection:
+ errorReason = "REASON_CONNECTION";
+ break;
+ case mbgl::Response::Error::Reason::Other:
+ errorReason = "REASON_OTHER";
+ break;
+ }
+
+ // Error object
+ jobject jerror = env2->NewObject(offlineRegionErrorClass, offlineRegionErrorConstructorId);
+ env2->SetObjectField(jerror, offlineRegionErrorReasonId, std_string_to_jstring(env2, errorReason));
+ env2->SetObjectField(jerror, offlineRegionErrorMessageId, std_string_to_jstring(env2, error.message));
+ env2->CallVoidMethod(observerCallback, offlineRegionObserveronErrorId, jerror);
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(observerCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ }
+
+ void mapboxTileCountLimitExceeded(uint64_t limit) override {
+ // Env
+ JNIEnv* env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+
+ // Send limit
+ env2->CallVoidMethod(observerCallback, offlineRegionObserveronLimitId, limit);
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(observerCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ }
+
+ jobject observerCallback;
+ };
+
+ // Makes sure the callback doesn't get GC'ed
+ observerCallback = reinterpret_cast<jobject>(env->NewGlobalRef(observerCallback));
+
+ // Set the observer
+ defaultFileSource->setOfflineRegionObserver(*offlineRegion, std::make_unique<Observer>(observerCallback));
+}
+
+void JNICALL setOfflineRegionDownloadState(JNIEnv *env, jobject obj, jobject offlineRegion_, jint offlineRegionDownloadState) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "setOfflineRegionDownloadState");
+
+ // State
+ mbgl::OfflineRegionDownloadState state;
+ if (offlineRegionDownloadState == 0) {
+ state = mbgl::OfflineRegionDownloadState::Inactive;
+ } else if (offlineRegionDownloadState == 1) {
+ state = mbgl::OfflineRegionDownloadState::Active;
+ } else {
+ mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active).");
+ return;
+ }
+
+ // Offline region
+ jlong offlineRegionPtr = env->GetLongField(offlineRegion_, offlineRegionPtrId);
+ mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr);
+
+ // File source
+ jobject jmanager = env->GetObjectField(offlineRegion_, offlineRegionOfflineManagerId);
+ jlong defaultFileSourcePtr = env->GetLongField(jmanager, offlineManagerClassPtrId);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+
+ // Set new state
+ defaultFileSource->setOfflineRegionDownloadState(*offlineRegion, state);
+}
+
+void JNICALL getOfflineRegionStatus(JNIEnv *env, jobject obj, jobject offlineRegion_, jobject statusCallback) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "getOfflineRegionStatus");
+
+ // Offline region
+ jlong offlineRegionPtr = env->GetLongField(offlineRegion_, offlineRegionPtrId);
+ mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr);
+
+ // File source
+ jobject jmanager = env->GetObjectField(offlineRegion_, offlineRegionOfflineManagerId);
+ jlong defaultFileSourcePtr = env->GetLongField(jmanager, offlineManagerClassPtrId);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+
+ // Makes sure the callback doesn't get GC'ed
+ statusCallback = reinterpret_cast<jobject>(env->NewGlobalRef(statusCallback));
+
+ // Set new state
+ defaultFileSource->getOfflineRegionStatus(*offlineRegion, [statusCallback](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionStatus> status) {
+
+ // Reattach, the callback comes from a different thread
+ JNIEnv *env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+ if (renderDetach) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Attached.");
+ }
+
+ if (error) {
+ std::string message = mbgl::util::toString(error);
+ env2->CallVoidMethod(statusCallback, offlineRegionStatusOnErrorId, std_string_to_jstring(env2, message));
+ } else if (status) {
+ // Conver to jint
+ jint downloadState = -1;
+ if (status->downloadState == mbgl::OfflineRegionDownloadState::Inactive) {
+ downloadState = 0;
+ } else if (status->downloadState == mbgl::OfflineRegionDownloadState::Active) {
+ downloadState = 1;
+ } else {
+ mbgl::Log::Error(mbgl::Event::JNI, "Unsupported OfflineRegionDownloadState value.");
+ return;
+ }
+
+ // Stats object
+ jobject jstatus = env2->NewObject(offlineRegionStatusClass, offlineRegionStatusConstructorId);
+ env2->SetIntField(jstatus, offlineRegionStatusDownloadStateId, downloadState);
+ env2->SetLongField(jstatus, offlineRegionStatusCompletedResourceCountId, status->completedResourceCount);
+ env2->SetLongField(jstatus, offlineRegionStatusCompletedResourceSizeId, status->completedResourceSize);
+ env2->SetLongField(jstatus, offlineRegionStatusRequiredResourceCountId, status->requiredResourceCount);
+ env2->SetBooleanField(jstatus, offlineRegionStatusRequiredResourceCountIsPreciseId, status->requiredResourceCountIsPrecise);
+ env2->CallVoidMethod(statusCallback, offlineRegionStatusOnStatusId, jstatus);
+ }
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(statusCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ });
+}
+
+void JNICALL deleteOfflineRegion(JNIEnv *env, jobject obj, jobject offlineRegion_, jobject deleteCallback) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "deleteOfflineRegion");
+
+ // Offline region
+ jlong offlineRegionPtr = env->GetLongField(offlineRegion_, offlineRegionPtrId);
+ mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr);
+
+ // File source
+ jobject jmanager = env->GetObjectField(offlineRegion_, offlineRegionOfflineManagerId);
+ jlong defaultFileSourcePtr = env->GetLongField(jmanager, offlineManagerClassPtrId);
+ mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr);
+
+ // Makes sure the callback doesn't get GC'ed
+ deleteCallback = reinterpret_cast<jobject>(env->NewGlobalRef(deleteCallback));
+
+ // Set new state
+ defaultFileSource->deleteOfflineRegion(std::move(*offlineRegion), [deleteCallback](std::exception_ptr error) {
+
+ // Reattach, the callback comes from a different thread
+ JNIEnv *env2;
+ jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread");
+ if (renderDetach) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Attached.");
+ }
+
+ if (error) {
+ std::string message = mbgl::util::toString(error);
+ env2->CallVoidMethod(deleteCallback, offlineRegionDeleteOnErrorId, std_string_to_jstring(env2, message));
+ } else {
+ std::string message = mbgl::util::toString(error);
+ env2->CallVoidMethod(deleteCallback, offlineRegionDeleteOnDeleteId);
+ }
+
+ // Delete global refs and detach when we're done
+ env2->DeleteGlobalRef(deleteCallback);
+ detach_jni_thread(theJVM, &env2, renderDetach);
+ });
+}
+
+// Offline calls end
+
}
extern "C" {
@@ -1946,63 +2454,356 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
httpContextClass = env->FindClass("com/mapbox/mapboxsdk/http/HTTPContext");
if (httpContextClass == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
httpContextGetInstanceId = env->GetStaticMethodID(httpContextClass, "getInstance", "()Lcom/mapbox/mapboxsdk/http/HTTPContext;");
if (httpContextGetInstanceId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
httpContextCreateRequestId = env->GetMethodID(httpContextClass, "createRequest", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/mapbox/mapboxsdk/http/HTTPContext$HTTPRequest;");
if (httpContextCreateRequestId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
httpRequestClass = env->FindClass("com/mapbox/mapboxsdk/http/HTTPContext$HTTPRequest");
if (httpRequestClass == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
httpRequestStartId = env->GetMethodID(httpRequestClass, "start", "()V");
if (httpRequestStartId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
httpRequestCancelId = env->GetMethodID(httpRequestClass, "cancel", "()V");
if (httpRequestCancelId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerClass = env->FindClass("com/mapbox/mapboxsdk/layers/CustomLayer");
if (customLayerClass == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerIdId = env->GetFieldID(customLayerClass, "mID", "Ljava/lang/String;");
if (customLayerIdId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerContextId = env->GetFieldID(customLayerClass, "mContext", "J");
if (customLayerContextId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerInitializeFunctionId = env->GetFieldID(customLayerClass, "mInitializeFunction", "J");
if (customLayerInitializeFunctionId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerRenderFunctionId = env->GetFieldID(customLayerClass, "mRenderFunction", "J");
if (customLayerRenderFunctionId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
}
customLayerDeinitializeFunctionId = env->GetFieldID(customLayerClass, "mDeinitializeFunction", "J");
if (customLayerDeinitializeFunctionId == nullptr) {
env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ // Offline definitions begin
+
+ offlineManagerClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineManager");
+ if (offlineManagerClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineManagerClassPtrId = env->GetFieldID(offlineManagerClass, "mDefaultFileSourcePtr", "J");
+ if (offlineManagerClassPtrId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ listOfflineRegionsCallbackClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback");
+ if (listOfflineRegionsCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ listOnListMethodId = env->GetMethodID(listOfflineRegionsCallbackClass, "onList", "([Lcom/mapbox/mapboxsdk/offline/OfflineRegion;)V");
+ if (listOnListMethodId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ listOnErrorMethodId = env->GetMethodID(listOfflineRegionsCallbackClass, "onError", "(Ljava/lang/String;)V");
+ if (listOnErrorMethodId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegion");
+ if (offlineRegionClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionConstructorId = env->GetMethodID(offlineRegionClass, "<init>", "()V");
+ if (offlineRegionConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionOfflineManagerId = env->GetFieldID(offlineRegionClass, "offlineManager", "Lcom/mapbox/mapboxsdk/offline/OfflineManager;");
+ if (offlineRegionOfflineManagerId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionIdId = env->GetFieldID(offlineRegionClass, "mId", "J");
+ if (offlineRegionIdId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionId = env->GetFieldID(offlineRegionClass, "mDefinition", "Lcom/mapbox/mapboxsdk/offline/OfflineRegionDefinition;");
+ if (offlineRegionDefinitionId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionMetadataId = env->GetFieldID(offlineRegionClass, "mMetadata", "Lcom/mapbox/mapboxsdk/offline/OfflineRegionMetadata;");
+ if (offlineRegionMetadataId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionPtrId = env->GetFieldID(offlineRegionClass, "mOfflineRegionPtr", "J");
+ if (offlineRegionPtrId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ // This needs to be updated once we support more than one type of region definition
+ offlineRegionDefinitionClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition");
+ if (offlineRegionDefinitionClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionConstructorId = env->GetMethodID(offlineRegionDefinitionClass, "<init>", "()V");
+ if (offlineRegionDefinitionConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionStyleURLId = env->GetFieldID(offlineRegionDefinitionClass, "styleURL", "Ljava/lang/String;");
+ if (offlineRegionDefinitionStyleURLId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionBoundsId = env->GetFieldID(offlineRegionDefinitionClass, "bounds", "Lcom/mapbox/mapboxsdk/geometry/LatLngBounds;");
+ if (offlineRegionDefinitionBoundsId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionMinZoomId = env->GetFieldID(offlineRegionDefinitionClass, "minZoom", "D");
+ if (offlineRegionDefinitionMinZoomId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionMaxZoomId = env->GetFieldID(offlineRegionDefinitionClass, "maxZoom", "D");
+ if (offlineRegionDefinitionMaxZoomId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDefinitionPixelRatioId = env->GetFieldID(offlineRegionDefinitionClass, "pixelRatio", "F");
+ if (offlineRegionDefinitionPixelRatioId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionMetadataClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegionMetadata");
+ if (offlineRegionMetadataClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionMetadataConstructorId = env->GetMethodID(offlineRegionMetadataClass, "<init>", "([B)V");
+ if (offlineRegionMetadataConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionMetadataMetadataId = env->GetFieldID(offlineRegionMetadataClass, "metadata", "[B");
+ if (offlineRegionMetadataMetadataId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ createOfflineRegionCallbackClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback");
+ if (createOfflineRegionCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ createOnCreateMethodId = env->GetMethodID(createOfflineRegionCallbackClass, "onCreate", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;)V");
+ if (createOnCreateMethodId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ createOnErrorMethodId = env->GetMethodID(createOfflineRegionCallbackClass, "onError", "(Ljava/lang/String;)V");
+ if (createOnErrorMethodId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionObserverClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver");
+ if (offlineRegionObserverClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
}
+ offlineRegionObserveronStatusChangedId = env->GetMethodID(offlineRegionObserverClass, "onStatusChanged", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionStatus;)V");
+ if (offlineRegionObserveronStatusChangedId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionObserveronErrorId = env->GetMethodID(offlineRegionObserverClass, "onError", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionError;)V");
+ if (offlineRegionObserveronErrorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionObserveronLimitId = env->GetMethodID(offlineRegionObserverClass, "mapboxTileCountLimitExceeded", "(J)V");
+ if (offlineRegionObserveronLimitId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegionStatus");
+ if (offlineRegionStatusClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusConstructorId = env->GetMethodID(offlineRegionStatusClass, "<init>", "()V");
+ if (offlineRegionStatusConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusDownloadStateId = env->GetFieldID(offlineRegionStatusClass, "downloadState", "I");
+ if (offlineRegionStatusDownloadStateId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusCompletedResourceCountId = env->GetFieldID(offlineRegionStatusClass, "completedResourceCount", "J");
+ if (offlineRegionStatusCompletedResourceCountId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusCompletedResourceSizeId = env->GetFieldID(offlineRegionStatusClass, "completedResourceSize", "J");
+ if (offlineRegionStatusCompletedResourceSizeId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusRequiredResourceCountId = env->GetFieldID(offlineRegionStatusClass, "requiredResourceCount", "J");
+ if (offlineRegionStatusRequiredResourceCountId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusRequiredResourceCountIsPreciseId = env->GetFieldID(offlineRegionStatusClass, "requiredResourceCountIsPrecise", "Z");
+ if (offlineRegionStatusRequiredResourceCountIsPreciseId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionErrorClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegionError");
+ if (offlineRegionErrorClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionErrorConstructorId = env->GetMethodID(offlineRegionErrorClass, "<init>", "()V");
+ if (offlineRegionErrorConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionErrorReasonId = env->GetFieldID(offlineRegionErrorClass, "reason", "Ljava/lang/String;");
+ if (offlineRegionErrorReasonId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionErrorMessageId = env->GetFieldID(offlineRegionErrorClass, "message", "Ljava/lang/String;");
+ if (offlineRegionErrorMessageId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusCallbackClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback");
+ if (offlineRegionStatusCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusOnStatusId = env->GetMethodID(offlineRegionStatusCallbackClass, "onStatus", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionStatus;)V");
+ if (offlineRegionStatusOnStatusId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionStatusOnErrorId = env->GetMethodID(offlineRegionStatusCallbackClass, "onError", "(Ljava/lang/String;)V");
+ if (offlineRegionStatusOnErrorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDeleteCallbackClass = env->FindClass("com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback");
+ if (offlineRegionDeleteCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDeleteOnDeleteId = env->GetMethodID(offlineRegionDeleteCallbackClass, "onDelete", "()V");
+ if (offlineRegionDeleteOnDeleteId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ offlineRegionDeleteOnErrorId = env->GetMethodID(offlineRegionDeleteCallbackClass, "onError", "(Ljava/lang/String;)V");
+ if (offlineRegionDeleteOnErrorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ // Offline definitions end
+
const std::vector<JNINativeMethod> methods = {
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J",
reinterpret_cast<void *>(&nativeCreate)},
@@ -2139,6 +2940,37 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_ERR;
}
+ // Offline register begin
+
+ const std::vector<JNINativeMethod> offlineManagerMethods = {
+ {"createDefaultFileSource", "(Ljava/lang/String;Ljava/lang/String;J)J", reinterpret_cast<void *>(&createDefaultFileSource)},
+ {"setAccessToken", "(JLjava/lang/String;)V", reinterpret_cast<void *>(&setAccessToken)},
+ {"getAccessToken", "(J)Ljava/lang/String;", reinterpret_cast<void *>(&getAccessToken)},
+ {"listOfflineRegions", "(JLcom/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback;)V", reinterpret_cast<void *>(&listOfflineRegions)},
+ {"createOfflineRegion", "(JLcom/mapbox/mapboxsdk/offline/OfflineRegionDefinition;Lcom/mapbox/mapboxsdk/offline/OfflineRegionMetadata;Lcom/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback;)V", reinterpret_cast<void *>(&createOfflineRegion)},
+ {"setOfflineMapboxTileCountLimit", "(JJ)V", reinterpret_cast<void *>(&setOfflineMapboxTileCountLimit)}
+ };
+
+ if (env->RegisterNatives(offlineManagerClass, offlineManagerMethods.data(), offlineManagerMethods.size()) < 0) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ const std::vector<JNINativeMethod> offlineRegionMethods = {
+ {"destroyOfflineRegion", "(J)V", reinterpret_cast<void *>(&destroyOfflineRegion)},
+ {"setOfflineRegionObserver", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver;)V", reinterpret_cast<void *>(&setOfflineRegionObserver)},
+ {"setOfflineRegionDownloadState", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;I)V", reinterpret_cast<void *>(&setOfflineRegionDownloadState)},
+ {"getOfflineRegionStatus", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback;)V", reinterpret_cast<void *>(&getOfflineRegionStatus)},
+ {"deleteOfflineRegion", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback;)V", reinterpret_cast<void *>(&deleteOfflineRegion)}
+ };
+
+ if (env->RegisterNatives(offlineRegionClass, offlineRegionMethods.data(), offlineRegionMethods.size()) < 0) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ // Offline register end
+
latLngClass = reinterpret_cast<jclass>(env->NewGlobalRef(latLngClass));
if (latLngClass == nullptr) {
env->ExceptionDescribe();
@@ -2313,6 +3145,264 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
env->DeleteGlobalRef(httpContextClass);
}
+ // Offline global definitions begin
+
+ offlineManagerClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineManagerClass));
+ if (offlineManagerClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(httpRequestClass);
+ }
+
+ listOfflineRegionsCallbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(listOfflineRegionsCallbackClass));
+ if (listOfflineRegionsCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ }
+
+ offlineRegionClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionClass));
+ if (offlineRegionClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ }
+
+ offlineRegionDefinitionClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionDefinitionClass));
+ if (offlineRegionDefinitionClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ }
+
+ offlineRegionMetadataClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionMetadataClass));
+ if (offlineRegionMetadataClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ }
+
+ createOfflineRegionCallbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(createOfflineRegionCallbackClass));
+ if (createOfflineRegionCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ }
+
+ offlineRegionObserverClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionObserverClass));
+ if (offlineRegionObserverClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ }
+
+ offlineRegionStatusClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionStatusClass));
+ if (offlineRegionStatusClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ env->DeleteGlobalRef(offlineRegionObserverClass);
+ }
+
+ offlineRegionErrorClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionErrorClass));
+ if (offlineRegionErrorClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ env->DeleteGlobalRef(offlineRegionObserverClass);
+ env->DeleteGlobalRef(offlineRegionStatusClass);
+ }
+
+ offlineRegionStatusCallbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionStatusCallbackClass));
+ if (offlineRegionStatusCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ env->DeleteGlobalRef(offlineRegionObserverClass);
+ env->DeleteGlobalRef(offlineRegionStatusClass);
+ env->DeleteGlobalRef(offlineRegionErrorClass);
+ }
+
+ offlineRegionDeleteCallbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(offlineRegionDeleteCallbackClass));
+ if (offlineRegionDeleteCallbackClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngBoundsClass);
+ env->DeleteGlobalRef(iconClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ env->DeleteGlobalRef(offlineManagerClass);
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ env->DeleteGlobalRef(offlineRegionClass);
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ env->DeleteGlobalRef(offlineRegionObserverClass);
+ env->DeleteGlobalRef(offlineRegionStatusClass);
+ env->DeleteGlobalRef(offlineRegionErrorClass);
+ env->DeleteGlobalRef(offlineRegionStatusCallbackClass);
+ }
+
+ // Offline global definitions end
+
char release[PROP_VALUE_MAX] = "";
__system_property_get("ro.build.version.release", release);
androidRelease = std::string(release);
@@ -2415,6 +3505,68 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
httpRequestStartId = nullptr;
httpRequestCancelId = nullptr;
+ // Offline delete begins
+
+ env->DeleteGlobalRef(offlineManagerClass);
+ offlineManagerClassPtrId = nullptr;
+ offlineManagerClassPtrId = nullptr;
+
+ env->DeleteGlobalRef(listOfflineRegionsCallbackClass);
+ listOnListMethodId = nullptr;
+ listOnErrorMethodId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionClass);
+ offlineRegionConstructorId = nullptr;
+ offlineRegionOfflineManagerId = nullptr;
+ offlineRegionIdId = nullptr;
+ offlineRegionDefinitionId = nullptr;
+ offlineRegionMetadataId = nullptr;
+ offlineRegionPtrId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionDefinitionClass);
+ offlineRegionDefinitionConstructorId = nullptr;
+ offlineRegionDefinitionStyleURLId = nullptr;
+ offlineRegionDefinitionBoundsId = nullptr;
+ offlineRegionDefinitionMinZoomId = nullptr;
+ offlineRegionDefinitionMaxZoomId = nullptr;
+ offlineRegionDefinitionPixelRatioId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionMetadataClass);
+ offlineRegionMetadataConstructorId = nullptr;
+ offlineRegionMetadataMetadataId = nullptr;
+
+ env->DeleteGlobalRef(createOfflineRegionCallbackClass);
+ createOnCreateMethodId = nullptr;
+ createOnErrorMethodId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionObserverClass);
+ offlineRegionObserveronStatusChangedId = nullptr;
+ offlineRegionObserveronErrorId = nullptr;
+ offlineRegionObserveronLimitId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionStatusClass);
+ offlineRegionStatusConstructorId = nullptr;
+ offlineRegionStatusDownloadStateId = nullptr;
+ offlineRegionStatusCompletedResourceCountId = nullptr;
+ offlineRegionStatusCompletedResourceSizeId = nullptr;
+ offlineRegionStatusRequiredResourceCountId = nullptr;
+ offlineRegionStatusRequiredResourceCountIsPreciseId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionErrorClass);
+ offlineRegionErrorConstructorId = nullptr;
+ offlineRegionErrorReasonId = nullptr;
+ offlineRegionErrorMessageId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionStatusCallbackClass);
+ offlineRegionStatusOnStatusId = nullptr;
+ offlineRegionStatusOnErrorId = nullptr;
+
+ env->DeleteGlobalRef(offlineRegionDeleteCallbackClass);
+ offlineRegionDeleteOnDeleteId = nullptr;
+ offlineRegionDeleteOnErrorId = nullptr;
+
+ // Offline delete ends
+
theJVM = nullptr;
}
}
diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp
index ae0624c4b3..2995ea0b35 100644
--- a/platform/android/src/jni.hpp
+++ b/platform/android/src/jni.hpp
@@ -103,6 +103,66 @@ extern jclass httpRequestClass;
extern jmethodID httpRequestStartId;
extern jmethodID httpRequestCancelId;
+// Offline declarations start
+
+extern jclass offlineManagerClass;
+extern jfieldID offlineManagerClassPtrId;
+
+extern jclass listOfflineRegionsCallbackClass;
+extern jmethodID listOnListMethodId;
+extern jmethodID listOnErrorMethodId;
+
+extern jclass offlineRegionClass;
+extern jfieldID offlineRegionOfflineManagerId;
+extern jfieldID offlineRegionIdId;
+extern jfieldID offlineRegionDefinitionId;
+extern jfieldID offlineRegionMetadataId;
+extern jfieldID offlineRegionPtrId;
+
+extern jclass offlineRegionDefinitionClass;
+extern jmethodID offlineRegionDefinitionConstructorId;
+extern jfieldID offlineRegionDefinitionStyleURLId;
+extern jfieldID offlineRegionDefinitionBoundsId;
+extern jfieldID offlineRegionDefinitionMinZoomId;
+extern jfieldID offlineRegionDefinitionMaxZoomId;
+extern jfieldID offlineRegionDefinitionPixelRatioId;
+
+extern jclass offlineRegionMetadataClass;
+extern jmethodID offlineRegionMetadataConstructorId;
+extern jfieldID offlineRegionMetadataMetadataId;
+
+extern jclass createOfflineRegionCallbackClass;
+extern jmethodID createOnCreateMethodId;
+extern jmethodID createOnErrorMethodId;
+
+extern jclass offlineRegionObserverClass;
+extern jmethodID offlineRegionObserveronStatusChangedId;
+extern jmethodID offlineRegionObserveronErrorId;
+extern jmethodID offlineRegionObserveronLimitId;
+
+extern jclass offlineRegionStatusClass;
+extern jmethodID offlineRegionStatusConstructorId;
+extern jfieldID offlineRegionStatusDownloadStateId;
+extern jfieldID offlineRegionStatusCompletedResourceCountId;
+extern jfieldID offlineRegionStatusCompletedResourceSizeId;
+extern jfieldID offlineRegionStatusRequiredResourceCountId;
+extern jfieldID offlineRegionStatusRequiredResourceCountIsIndeterminateId;
+
+extern jclass offlineRegionErrorClass;
+extern jmethodID offlineRegionErrorConstructorId;
+extern jfieldID offlineRegionErrorReasonId;
+extern jfieldID offlineRegionErrorMessageId;
+
+extern jclass offlineRegionStatusCallbackClass;
+extern jmethodID offlineRegionStatusOnStatusId;
+extern jmethodID offlineRegionStatusOnErrorId;
+
+extern jclass offlineRegionDeleteCallbackClass;
+extern jmethodID offlineRegionDeleteOnDeleteId;
+extern jmethodID offlineRegionDeleteOnErrorId;
+
+// Offline declarations end
+
extern bool throw_jni_error(JNIEnv *env, const char *msg);
extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);