From ea273d4e75f51a6cb7a6fc7573c7722e34e730f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 16 Jan 2017 21:17:04 +0100 Subject: [android] expose DefaultFileSource via jni.hpp Instantiate only one DefaultFileSource in the global Mapbox singleton, instead of one per Map object --- .../src/main/java/com/mapbox/mapboxsdk/Mapbox.java | 108 +++++++++++++++++---- .../com/mapbox/mapboxsdk/http/HTTPRequest.java | 4 + .../java/com/mapbox/mapboxsdk/maps/MapView.java | 8 -- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 18 +--- .../mapbox/mapboxsdk/maps/MapboxMapOptions.java | 29 ------ .../com/mapbox/mapboxsdk/maps/NativeMapView.java | 41 +------- .../mapbox/mapboxsdk/offline/OfflineManager.java | 67 +------------ .../mapboxsdk/storage/DefaultFileSource.java | 91 +++++++++++++++++ .../src/main/res-public/values/public.xml | 2 - .../src/main/res/values/attrs.xml | 1 - .../mapboxsdk/testapp/MapboxApplication.java | 9 +- .../com/mapbox/weartestapp/MapboxApplication.java | 11 ++- platform/android/config.cmake | 4 + platform/android/src/jni.cpp | 40 ++------ platform/android/src/jni.hpp | 3 - platform/android/src/native_map_view.cpp | 19 ++-- platform/android/src/native_map_view.hpp | 13 ++- .../src/storage/default_file_source_peer.cpp | 54 +++++++++++ .../src/storage/default_file_source_peer.hpp | 40 ++++++++ 19 files changed, 340 insertions(+), 222 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/DefaultFileSource.java create mode 100644 platform/android/src/storage/default_file_source_peer.cpp create mode 100644 platform/android/src/storage/default_file_source_peer.hpp diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index 9d20e968c8..d093a86ff0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -1,6 +1,9 @@ package com.mapbox.mapboxsdk; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Environment; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.annotation.NonNull; @@ -10,13 +13,20 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.telemetry.MapboxEventManager; +import com.mapbox.mapboxsdk.storage.DefaultFileSource; -public final class Mapbox { +import java.io.File; + +import timber.log.Timber; +public final class Mapbox { private static Mapbox INSTANCE; private Context context; - private String accessToken; private Boolean connected; + private DefaultFileSource fileSource; + + // Default database name + private static final String DATABASE_NAME = "mbgl-offline.db"; /** * Get an instance of Mapbox. @@ -28,29 +38,27 @@ public final class Mapbox { * @param accessToken Mapbox access token * @return the single instance of Mapbox */ - public static synchronized Mapbox getInstance(@NonNull Context context, @NonNull String accessToken) { + public static synchronized Mapbox getInstance(@NonNull Context context, + @NonNull String accessToken, String apiBaseURL) { if (INSTANCE == null) { + validateAccessToken(accessToken); Context appContext = context.getApplicationContext(); - INSTANCE = new Mapbox(appContext, accessToken); + INSTANCE = new Mapbox(appContext, accessToken, apiBaseURL); MapboxEventManager.getMapboxEventManager().initialize(appContext, accessToken); ConnectivityReceiver.instance(appContext); } return INSTANCE; } - private Mapbox(@NonNull Context context, @NonNull String accessToken) { + private Mapbox(@NonNull Context context, @NonNull String accessToken, String apiBaseURL) { this.context = context; - this.accessToken = accessToken; - } - /** - * Access Token for this application. - * - * @return Mapbox Access Token - */ - public static String getAccessToken() { - validateAccessToken(); - return INSTANCE.accessToken; + String databasePath = getDatabasePath(context) + File.separator + DATABASE_NAME; + this.fileSource = new DefaultFileSource(databasePath, context.getPackageCodePath()); + this.fileSource.setAccessToken(accessToken); + if (apiBaseURL != null && !TextUtils.isEmpty(apiBaseURL)) { + this.fileSource.setAPIBaseURL(apiBaseURL); + } } /** @@ -58,8 +66,7 @@ public final class Mapbox { * * @throws InvalidAccessTokenException exception thrown when not using a valid accessToken */ - private static void validateAccessToken() throws InvalidAccessTokenException { - String accessToken = INSTANCE.accessToken; + private static void validateAccessToken(@NonNull String accessToken) throws InvalidAccessTokenException { if (TextUtils.isEmpty(accessToken) || (!accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("pk.") && !accessToken.toLowerCase(MapboxConstants.MAPBOX_LOCALE).startsWith("sk."))) { throw new InvalidAccessTokenException(); @@ -73,6 +80,13 @@ public final class Mapbox { return INSTANCE.context; } + /** + * Default file source + */ + public static DefaultFileSource getDefaultFileSource() { + return INSTANCE.fileSource; + } + /** * Manually sets the connectivity state of the app. This is useful for apps that control their * own connectivity state and want to bypass any checks to the ConnectivityManager. @@ -101,4 +115,62 @@ public final class Mapbox { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return (activeNetwork != null && activeNetwork.isConnected()); } -} \ No newline at end of file + + /** + * Checks if external storage is available to at least read. In order for this to work, make + * sure you include <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + * (or WRITE_EXTERNAL_STORAGE) for API level < 18 in your app Manifest. + *

+ * Code from https://developer.android.com/guide/topics/data/data-storage.html#filesExternal + *

+ * + * @return true if external storage is readable + */ + public static boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + + Timber.w("External storage was requested but it isn't readable. For API level < 18" + + " make sure you've requested READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE" + + " permissions in your app Manifest (defaulting to internal storage)."); + + return false; + } + + public static String getDatabasePath(Context context) { + // Default value + boolean setStorageExternal = MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL; + + try { + // Try getting a custom value from the app Manifest + ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA); + setStorageExternal = appInfo.metaData.getBoolean( + MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL, + MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL); + } catch (PackageManager.NameNotFoundException exception) { + Timber.e("Failed to read the package metadata: ", exception); + } catch (Exception exception) { + Timber.e("Failed to read the storage key: ", exception); + } + + String databasePath = null; + if (setStorageExternal && isExternalStorageReadable()) { + try { + // Try getting the external storage path + databasePath = context.getExternalFilesDir(null).getAbsolutePath(); + } catch (NullPointerException exception) { + Timber.e("Failed to obtain the external storage path: ", exception); + } + } + + if (databasePath == null) { + // Default to internal storage + databasePath = context.getFilesDir().getAbsolutePath(); + } + + return databasePath; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index 2c6251638a..a179dc1f46 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -16,6 +16,7 @@ import java.net.NoRouteToHostException; import java.net.ProtocolException; import java.net.SocketException; import java.net.UnknownHostException; +import java.net.MalformedURLException; import java.util.concurrent.locks.ReentrantLock; import javax.net.ssl.SSLException; @@ -62,6 +63,9 @@ class HTTPRequest implements Callback { } HttpUrl httpUrl = HttpUrl.parse(resourceUrl); + if (httpUrl == null) { + throw new MalformedURLException("Could not load invalid URL: " + resourceUrl); + } final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE); if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn") || host.endsWith(".mapbox.cn")) { 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 3cb074d209..e3f2124d9f 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 @@ -21,7 +21,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.v7.app.AlertDialog; -import android.text.TextUtils; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -199,8 +198,6 @@ public class MapView extends FrameLayout { */ @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - if (savedInstanceState == null) { MapboxEvent.trackMapLoadEvent(); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { @@ -405,11 +402,6 @@ public class MapView extends FrameLayout { return; } - // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - } - nativeMapView.setStyleUrl(url); } 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 f460a77229..30abee0d2c 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 @@ -16,7 +16,6 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; -import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -93,7 +92,6 @@ public final class MapboxMap { // Map configuration setDebugActive(options.getDebugActive()); - setApiBaseUrl(options); setStyleUrl(options); } @@ -758,17 +756,6 @@ public final class MapboxMap { nativeMapView.cycleDebugOptions(); } - // - // API endpoint config - // - - private void setApiBaseUrl(@NonNull MapboxMapOptions options) { - String apiBaseUrl = options.getApiBaseUrl(); - if (!TextUtils.isEmpty(apiBaseUrl)) { - nativeMapView.setApiBaseUrl(apiBaseUrl); - } - } - // // Styling // @@ -837,10 +824,7 @@ public final class MapboxMap { private void setStyleUrl(@NonNull MapboxMapOptions options) { String style = options.getStyle(); if (!TextUtils.isEmpty(style)) { - // stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242 - if (TextUtils.isEmpty(nativeMapView.getAccessToken())) { - nativeMapView.setAccessToken(Mapbox.getAccessToken()); - } + setStyleUrl(style); setStyleUrl(style); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 518ef47329..af99e6657e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -77,8 +77,6 @@ public class MapboxMapOptions implements Parcelable { private int myLocationAccuracyTintColor; private int myLocationAccuracyAlpha; - private String apiBaseUrl; - @Deprecated private boolean textureMode; @@ -141,7 +139,6 @@ public class MapboxMapOptions implements Parcelable { myLocationAccuracyTintColor = in.readInt(); style = in.readString(); - apiBaseUrl = in.readString(); textureMode = in.readByte() != 0; } @@ -172,7 +169,6 @@ public class MapboxMapOptions implements Parcelable { try { mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build()); mapboxMapOptions.styleUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_styleUrl)); - mapboxMapOptions.apiBaseUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUrl)); mapboxMapOptions.zoomGesturesEnabled( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiZoomGestures, true)); @@ -283,17 +279,6 @@ public class MapboxMapOptions implements Parcelable { return mapboxMapOptions; } - /** - * Specifies the URL used for API endpoint. - * - * @param apiBaseUrl The base of our API endpoint - * @return This - */ - public MapboxMapOptions apiBaseUrl(String apiBaseUrl) { - this.apiBaseUrl = apiBaseUrl; - return this; - } - /** * Specifies a the initial camera position for the map view. * @@ -655,15 +640,6 @@ public class MapboxMapOptions implements Parcelable { return this; } - /** - * Get the current configured API endpoint base URL. - * - * @return Base URL to be used API endpoint. - */ - public String getApiBaseUrl() { - return apiBaseUrl; - } - /** * Get the current configured initial camera position for a map view. * @@ -1003,7 +979,6 @@ public class MapboxMapOptions implements Parcelable { dest.writeInt(myLocationAccuracyTintColor); dest.writeString(style); - dest.writeString(apiBaseUrl); dest.writeByte((byte) (textureMode ? 1 : 0)); } @@ -1114,9 +1089,6 @@ public class MapboxMapOptions implements Parcelable { if (style != null ? !style.equals(options.style) : options.style != null) { return false; } - if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) { - return false; - } return false; } @@ -1156,7 +1128,6 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); result = 31 * result + myLocationAccuracyTintColor; result = 31 * result + myLocationAccuracyAlpha; - result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); result = 31 * result + (textureMode ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); return result; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 05d1bf1750..831f281401 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -13,6 +13,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.Surface; +import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Polygon; @@ -20,7 +21,7 @@ import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; -import com.mapbox.mapboxsdk.offline.OfflineManager; +import com.mapbox.mapboxsdk.storage.DefaultFileSource; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException; import com.mapbox.mapboxsdk.style.sources.NoSuchSourceException; @@ -70,15 +71,10 @@ final class NativeMapView { public NativeMapView(MapView mapView) { Context context = mapView.getContext(); - String dataPath = OfflineManager.getDatabasePath(context); - // With the availability of offline, we're unifying the ambient (cache) and the offline - // databases to be in the same folder, outside cache, to avoid automatic deletion from - // the system - String cachePath = dataPath; + DefaultFileSource fileSource = Mapbox.getDefaultFileSource(); pixelRatio = context.getResources().getDisplayMetrics().density; - String apkPath = context.getPackageCodePath(); int availableProcessors = Runtime.getRuntime().availableProcessors(); ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -97,7 +93,7 @@ final class NativeMapView { } onMapChangedListeners = new CopyOnWriteArrayList<>(); this.mapView = mapView; - nativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory); + nativeMapViewPtr = nativeCreate(fileSource, pixelRatio, availableProcessors, totalMemory); } // @@ -294,20 +290,6 @@ final class NativeMapView { return nativeGetStyleJson(nativeMapViewPtr); } - public void setAccessToken(String accessToken) { - if (isDestroyedOn("setAccessToken")) { - return; - } - nativeSetAccessToken(nativeMapViewPtr, accessToken); - } - - public String getAccessToken() { - if (isDestroyedOn("getAccessToken")) { - return null; - } - return nativeGetAccessToken(nativeMapViewPtr); - } - public void cancelTransitions() { if (isDestroyedOn("cancelTransitions")) { return; @@ -884,13 +866,6 @@ final class NativeMapView { nativeScheduleTakeSnapshot(nativeMapViewPtr); } - public void setApiBaseUrl(String baseUrl) { - if (isDestroyedOn("setApiBaseUrl")) { - return; - } - nativeSetAPIBaseURL(nativeMapViewPtr, baseUrl); - } - public float getPixelRatio() { return pixelRatio; } @@ -938,7 +913,7 @@ final class NativeMapView { // JNI methods // - private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, + private native long nativeCreate(DefaultFileSource fileSource, float pixelRatio, int availableProcessors, long totalMemory); private native void nativeDestroy(long nativeMapViewPtr); @@ -983,10 +958,6 @@ final class NativeMapView { private native String nativeGetStyleJson(long nativeMapViewPtr); - private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken); - - private native String nativeGetAccessToken(long nativeMapViewPtr); - private native void nativeCancelTransitions(long nativeMapViewPtr); private native void nativeSetGestureInProgress(long nativeMapViewPtr, boolean inProgress); @@ -1131,8 +1102,6 @@ final class NativeMapView { private native Feature[] nativeQueryRenderedFeaturesForBox(long nativeMapViewPtr, float left, float top, float right, float bottom, String[] layerIds); - private native void nativeSetAPIBaseURL(long nativeMapViewPtr, String baseUrl); - int getWidth() { if (isDestroyedOn("")) { return 0; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java index beaea73024..04a4a00292 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java @@ -1,15 +1,11 @@ package com.mapbox.mapboxsdk.offline; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.constants.MapboxConstants; import java.io.File; @@ -92,74 +88,17 @@ public class OfflineManager { * Constructors */ private OfflineManager(Context context) { + // TODO: r // Get a pointer to the DefaultFileSource instance - String assetRoot = getDatabasePath(context); + String assetRoot = Mapbox.getDatabasePath(context); String cachePath = assetRoot + File.separator + DATABASE_NAME; mDefaultFileSourcePtr = createDefaultFileSource(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE); - setAccessToken(mDefaultFileSourcePtr, Mapbox.getAccessToken()); + setAccessToken(mDefaultFileSourcePtr, Mapbox.getDefaultFileSource().getAccessToken()); // Delete any existing previous ambient cache database deleteAmbientDatabase(context); } - public static String getDatabasePath(Context context) { - // Default value - boolean setStorageExternal = MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL; - - try { - // Try getting a custom value from the app Manifest - ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA); - setStorageExternal = appInfo.metaData.getBoolean( - MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL, - MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL); - } catch (PackageManager.NameNotFoundException exception) { - Timber.e("Failed to read the package metadata: ", exception); - } catch (Exception exception) { - Timber.e("Failed to read the storage key: ", exception); - } - - String databasePath = null; - if (setStorageExternal && isExternalStorageReadable()) { - try { - // Try getting the external storage path - databasePath = context.getExternalFilesDir(null).getAbsolutePath(); - } catch (NullPointerException exception) { - Timber.e("Failed to obtain the external storage path: ", exception); - } - } - - if (databasePath == null) { - // Default to internal storage - databasePath = context.getFilesDir().getAbsolutePath(); - } - - return databasePath; - } - - /** - * Checks if external storage is available to at least read. In order for this to work, make - * sure you include <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - * (or WRITE_EXTERNAL_STORAGE) for API level < 18 in your app Manifest. - *

- * Code from https://developer.android.com/guide/topics/data/data-storage.html#filesExternal - *

- * - * @return true if external storage is readable - */ - public static boolean isExternalStorageReadable() { - String state = Environment.getExternalStorageState(); - if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { - return true; - } - - Timber.w("External storage was requested but it isn't readable. For API level < 18" - + " make sure you've requested READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE" - + " permissions in your app Manifest (defaulting to internal storage)."); - - return false; - } - private void deleteAmbientDatabase(final Context context) { // Delete the file in a separate thread to avoid affecting the UI new Thread(new Runnable() { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/DefaultFileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/DefaultFileSource.java new file mode 100644 index 0000000000..02e3ed0908 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/DefaultFileSource.java @@ -0,0 +1,91 @@ +package com.mapbox.mapboxsdk.storage; + +import android.support.annotation.UiThread; +import android.support.annotation.NonNull; + +@UiThread +public class DefaultFileSource { + // + // Native peer management + // + static { + System.loadLibrary("mapbox-gl"); + } + + // Pointer to the native peer + private long peer; + + // Initializes the native peer object + private native void initialize(String cachePath, String assetRoot, long maximumCacheSize); + + @Override + protected native void finalize() throws Throwable; + + // + // Constants + // + + /* + * 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 static final long DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024; + + // + // Constructors + // + + /** + * Creates a new DefaultFileSource object. + * + * @param cachePath Path to the cache database file + * @param assetRoot Path to the APK that contains the assets + * @param maximumCacheSize Maximum size of the database file, in bytes + */ + public DefaultFileSource(@NonNull String cachePath, @NonNull String assetRoot, long maximumCacheSize) { + initialize(cachePath, assetRoot, maximumCacheSize); + } + + /** + * Creates a new DefaultFileSource object. + * + * @param cachePath Path to the cache database file + * @param assetRoot Path to the APK that contains the assets + */ + public DefaultFileSource(@NonNull String cachePath, @NonNull String assetRoot) { + initialize(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE); + } + + // + // Methods + // + + /** + * Sets the API base URL when connecting to Mapbox servers. Defaults to https://api.mapbox.com. + * + * @param apiBaseURL New path prefix of Mapbox-hosted assets + */ + public native void setAPIBaseURL(@NonNull String apiBaseURL); + + /** + * Returns the currently set API base URL for Mapbox-hosted assets. + * + * @return The path prefix for Mapbox-hosted assets + */ + public native String getAPIBaseURL(); + + /** + * Sets the access token for Mapbox resources. + * + * @param accessToken New access token for Mapbox-hosted assets + */ + public native void setAccessToken(@NonNull String accessToken); + + /** + * Returns the current Mapbox access token. + * + * @return The access token currently used for requesting Mapbox-hosted assets + */ + public native String getAccessToken(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index 5b2945d55d..8e18c6ee59 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -8,9 +8,7 @@ - - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index 12c395d46f..77b95a0ede 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -4,7 +4,6 @@ - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java index a10c6eaad3..1964bad825 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java @@ -37,7 +37,14 @@ public class MapboxApplication extends Application { .penaltyDeath() .build()); - Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token)); + // Obtain the API base URL, but don't expect it to exist. + String apiBaseURL = null; + int apiBaseURLID = getResources().getIdentifier("mapbox_api_base_url", "string", getPackageName()); + if (apiBaseURLID != 0) { + apiBaseURL = getString(apiBaseURLID); + } + + Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token), apiBaseURL); } private void initializeLogger() { diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java index cbbdcb8493..f8c72b327a 100644 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java +++ b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java @@ -11,7 +11,16 @@ public class MapboxApplication extends Application { @Override public void onCreate() { super.onCreate(); - Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token)); + + // Obtain the API base URL, but don't expect it to exist. + String apiBaseURL = null; + int apiBaseURLID = getResources().getIdentifier("mapbox_api_base_url", "string", getPackageName()); + if (apiBaseURLID != 0) { + apiBaseURL = getString(apiBaseURLID); + } + + Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token), apiBaseURL); + LeakCanary.install(this); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 635c27a44f..61669d48a7 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -89,6 +89,10 @@ macro(mbgl_platform_core) PRIVATE platform/default/mbgl/util/default_thread_pool.cpp PRIVATE platform/default/mbgl/util/default_thread_pool.hpp + # Storage + PRIVATE platform/android/src/storage/default_file_source_peer.cpp + PRIVATE platform/android/src/storage/default_file_source_peer.hpp + # Conversion C++ -> Java platform/android/src/conversion/constant.hpp platform/android/src/conversion/conversion.hpp diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 5182e268f3..d136766455 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -14,6 +14,7 @@ #include "connectivity_listener.hpp" #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" +#include "storage/default_file_source_peer.hpp" #include "conversion/conversion.hpp" #include "conversion/collection.hpp" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -46,9 +48,6 @@ void RegisterNativeHTTPRequest(JNIEnv&); JavaVM* theJVM; -std::string cachePath; -std::string dataPath; -std::string apkPath; std::string androidRelease; jni::jmethodID* onInvalidateId = nullptr; @@ -299,12 +298,9 @@ namespace { using namespace mbgl::android; using DebugOptions = mbgl::MapDebugOptions; -jlong nativeCreate(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* dataPath_, jni::jstring* apkPath_, jfloat pixelRatio, jint availableProcessors, jlong totalMemory) { +jlong nativeCreate(JNIEnv *env, jni::jobject* obj, jni::Object fileSource, jfloat pixelRatio, jint availableProcessors, jlong totalMemory) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); - cachePath = std_string_from_jstring(env, cachePath_); - dataPath = std_string_from_jstring(env, dataPath_); - apkPath = std_string_from_jstring(env, apkPath_); - return reinterpret_cast(new NativeMapView(env, jni::Unwrap(obj), pixelRatio, availableProcessors, totalMemory)); + return reinterpret_cast(new NativeMapView(env, jni::Unwrap(obj), fileSource, pixelRatio, availableProcessors, totalMemory)); } void nativeDestroy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { @@ -419,12 +415,6 @@ jni::jobject* nativeGetClasses(JNIEnv *env, jni::jobject* obj, jlong nativeMapVi return std_vector_string_to_jobject(env, nativeMapView->getMap().getClasses()); } -void nativeSetAPIBaseURL(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getFileSource().setAPIBaseURL(std_string_from_jstring(env, url)); -} - void nativeSetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); @@ -449,18 +439,6 @@ jni::jstring* nativeGetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMap return std_string_to_jstring(env, nativeMapView->getMap().getStyleJSON()); } -void nativeSetAccessToken(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* accessToken) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getFileSource().setAccessToken(std_string_from_jstring(env, accessToken)); -} - -jni::jstring* nativeGetAccessToken(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - return std_string_to_jstring(env, nativeMapView->getFileSource().getAccessToken()); -} - void nativeCancelTransitions(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); @@ -1728,6 +1706,9 @@ void registerNatives(JavaVM *vm) { registerNativeSources(env); ConnectivityListener::registerNative(env); + mbgl::Log::Error(mbgl::Event::JNI, "registering DefaultFileSource peer"); + DefaultFileSourcePeer::registerNative(env); + latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng"); latLngClass = jni::NewGlobalRef(env, latLngClass).release(); latLngConstructorId = &jni::GetMethodID(env, *latLngClass, "", "(DD)V"); @@ -1804,7 +1785,7 @@ void registerNatives(JavaVM *vm) { #define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod( #name, sig ) jni::RegisterNatives(env, nativeMapViewClass, - MAKE_NATIVE_METHOD(nativeCreate, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J"), + MAKE_NATIVE_METHOD(nativeCreate, "(Lcom/mapbox/mapboxsdk/storage/DefaultFileSource;FIJ)J"), MAKE_NATIVE_METHOD(nativeDestroy, "(J)V"), MAKE_NATIVE_METHOD(nativeInitializeDisplay, "(J)V"), MAKE_NATIVE_METHOD(nativeTerminateDisplay, "(J)V"), @@ -1825,8 +1806,6 @@ void registerNatives(JavaVM *vm) { MAKE_NATIVE_METHOD(nativeGetStyleUrl, "(J)Ljava/lang/String;"), MAKE_NATIVE_METHOD(nativeSetStyleJson, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeGetStyleJson, "(J)Ljava/lang/String;"), - MAKE_NATIVE_METHOD(nativeSetAccessToken, "(JLjava/lang/String;)V"), - MAKE_NATIVE_METHOD(nativeGetAccessToken, "(J)Ljava/lang/String;"), MAKE_NATIVE_METHOD(nativeCancelTransitions, "(J)V"), MAKE_NATIVE_METHOD(nativeSetGestureInProgress, "(JZ)V"), MAKE_NATIVE_METHOD(nativeMoveBy, "(JDDJ)V"), @@ -1889,8 +1868,7 @@ void registerNatives(JavaVM *vm) { MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"), MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V"), MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForPoint, "(JFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;"), - MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForBox, "(JFFFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;"), - MAKE_NATIVE_METHOD(nativeSetAPIBaseURL, "(JLjava/lang/String;)V") + MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForBox, "(JFFFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;") ); // Offline begin diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp index 90ec914cf2..f6a71c13e4 100644 --- a/platform/android/src/jni.hpp +++ b/platform/android/src/jni.hpp @@ -11,9 +11,6 @@ namespace android { extern JavaVM* theJVM; -extern std::string cachePath; -extern std::string dataPath; -extern std::string apkPath; extern std::string androidRelease; extern jmethodID onInvalidateId; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 8234b74af2..3e2013c0d3 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -37,10 +38,16 @@ void log_egl_string(EGLDisplay display, EGLint name, const char *label) { } } -NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int availableProcessors_, size_t totalMemory_) +NativeMapView::NativeMapView(JNIEnv* env_, + jobject obj_, + jni::Object fileSource_, + float pixelRatio, + int availableProcessors_, + size_t totalMemory_) : env(env_), availableProcessors(availableProcessors_), totalMemory(totalMemory_), + fileSource(fileSource_.NewGlobalRef(*env)), threadPool(4) { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView"); @@ -58,13 +65,13 @@ NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int a return; } - fileSource = std::make_unique( - mbgl::android::cachePath + "/mbgl-offline.db", - mbgl::android::apkPath); + jni::Field peerField{ *env, DefaultFileSourcePeer::javaClass, + "peer" }; + auto fileSourcePtr = reinterpret_cast(fileSource->Get(*env, peerField)); map = std::make_unique( *this, mbgl::Size{ static_cast(width), static_cast(height) }, - pixelRatio, *fileSource, threadPool, MapMode::Continuous); + pixelRatio, fileSourcePtr->getFileSource(), threadPool, MapMode::Continuous); float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1; float cpuFactor = availableProcessors; @@ -218,8 +225,6 @@ void NativeMapView::render() { mbgl::Map &NativeMapView::getMap() { return *map; } -mbgl::DefaultFileSource &NativeMapView::getFileSource() { return *fileSource; } - void NativeMapView::initializeDisplay() { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeDisplay"); diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index e7379700a9..ea75ef7702 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -5,7 +5,8 @@ #include #include #include -#include + +#include "storage/default_file_source_peer.hpp" #include #include @@ -17,7 +18,12 @@ namespace android { class NativeMapView : public mbgl::View, public mbgl::Backend { public: - NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory); + NativeMapView(JNIEnv* env, + jobject obj, + jni::Object fileSourcePeer, + float pixelRatio, + int availableProcessors, + size_t totalMemory); virtual ~NativeMapView(); mbgl::Size getFramebufferSize() const; @@ -29,7 +35,6 @@ public: void notifyMapChange(mbgl::MapChange) override; mbgl::Map &getMap(); - mbgl::DefaultFileSource &getFileSource(); void initializeDisplay(); void terminateDisplay(); @@ -96,7 +101,7 @@ private: size_t totalMemory = 0; // Ensure these are initialised last - std::unique_ptr fileSource; + jni::UniqueObject fileSource; mbgl::ThreadPool threadPool; std::unique_ptr map; mbgl::EdgeInsets insets; diff --git a/platform/android/src/storage/default_file_source_peer.cpp b/platform/android/src/storage/default_file_source_peer.cpp new file mode 100644 index 0000000000..6f8a36b78a --- /dev/null +++ b/platform/android/src/storage/default_file_source_peer.cpp @@ -0,0 +1,54 @@ +#include "default_file_source_peer.hpp" + +#include + +namespace mbgl { +namespace android { + +jni::Class DefaultFileSourcePeer::javaClass; + +#define NATIVE_METHOD(MethodPtr, name) \ + jni::MakeNativePeerMethod(name) + +void DefaultFileSourcePeer::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + jni::RegisterNativePeer( + env, javaClass, "peer", + std::make_unique, + "initialize", "finalize", + NATIVE_METHOD(&DefaultFileSourcePeer::setAPIBaseURL, "setAPIBaseURL"), + NATIVE_METHOD(&DefaultFileSourcePeer::getAPIBaseURL, "getAPIBaseURL"), + NATIVE_METHOD(&DefaultFileSourcePeer::setAccessToken, "setAccessToken"), + NATIVE_METHOD(&DefaultFileSourcePeer::getAccessToken, "getAccessToken")); +} + +DefaultFileSourcePeer::DefaultFileSourcePeer(jni::JNIEnv& env, + jni::String cachePath, + jni::String assetRoot, + jni::jlong maximumCacheSize) + : native(std::make_unique(jni::Make(env, cachePath), + jni::Make(env, assetRoot), + maximumCacheSize)) { +} + +DefaultFileSourcePeer::~DefaultFileSourcePeer() = default; + +void DefaultFileSourcePeer::setAPIBaseURL(jni::JNIEnv& env, jni::String baseURL) { + native->setAPIBaseURL(jni::Make(env, baseURL)); +} + +jni::String DefaultFileSourcePeer::getAPIBaseURL(jni::JNIEnv& env) { + return jni::Make(env, native->getAPIBaseURL()); +} + +void DefaultFileSourcePeer::setAccessToken(jni::JNIEnv& env, jni::String accessToken) { + native->setAccessToken(jni::Make(env, accessToken)); +} + +jni::String DefaultFileSourcePeer::getAccessToken(jni::JNIEnv& env) { + return jni::Make(env, native->getAccessToken()); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/storage/default_file_source_peer.hpp b/platform/android/src/storage/default_file_source_peer.hpp new file mode 100644 index 0000000000..c0f60447b3 --- /dev/null +++ b/platform/android/src/storage/default_file_source_peer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +namespace mbgl { + +class DefaultFileSource; + +namespace android { + +class DefaultFileSourcePeer { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/DefaultFileSource"; } + static jni::Class javaClass; + static void registerNative(jni::JNIEnv&); + + DefaultFileSourcePeer(jni::JNIEnv&, + jni::String cachePath, + jni::String assetRoot, + jni::jlong maximumCacheSize); + ~DefaultFileSourcePeer(); + + void setAPIBaseURL(jni::JNIEnv&, jni::String); + jni::String getAPIBaseURL(jni::JNIEnv&); + + void setAccessToken(jni::JNIEnv&, jni::String); + jni::String getAccessToken(jni::JNIEnv&); + + DefaultFileSource& getFileSource() { + return *native; + } + +private: + const std::unique_ptr native; +}; + +} // namespace android +} // namespace mbgl -- cgit v1.2.1