From 5c037f4745e5a9a3c5f5d050c59d39e3f763bb5a Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Fri, 15 Jul 2016 09:58:31 -0400 Subject: [PATCH] [android] Introduce a getDatabasePath() method that enables switching storage paths Fixes #5589 --- .../mapboxsdk/constants/MapboxConstants.java | 10 +++ .../mapbox/mapboxsdk/maps/NativeMapView.java | 3 +- .../mapboxsdk/offline/OfflineManager.java | 63 ++++++++++++++++++- .../src/main/AndroidManifest.xml | 5 ++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index ece992ad549..7936ab4d142 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -30,6 +30,16 @@ public class MapboxConstants { */ public static final String KEY_META_DATA_STAGING_ACCESS_TOKEN = "com.mapbox.TestEventsAccessToken"; + /** + * Key used to switch storage to external in AndroidManifest.xml + */ + public final static String KEY_META_DATA_SET_STORAGE_EXTERNAL = "com.mapbox.SetStorageExternal"; + + /** + * Default value for KEY_META_DATA_SET_STORAGE_EXTERNAL (default is internal storage) + */ + public final static boolean DEFAULT_SET_STORAGE_EXTERNAL = false; + /** * Default animation time */ 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 5a1cfa7a6f6..e908912846a 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 @@ -15,6 +15,7 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; import com.mapbox.mapboxsdk.layers.CustomLayer; +import com.mapbox.mapboxsdk.offline.OfflineManager; import java.util.List; @@ -51,7 +52,7 @@ final class NativeMapView { public NativeMapView(MapView mapView) { Context context = mapView.getContext(); - String dataPath = context.getFilesDir().getAbsolutePath(); + 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 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 e81c366dba0..ba6434e06d8 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,11 +1,17 @@ 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 android.util.Log; + import com.mapbox.mapboxsdk.MapboxAccountManager; +import com.mapbox.mapboxsdk.constants.MapboxConstants; + import java.io.File; /** @@ -88,7 +94,7 @@ public interface CreateOfflineRegionCallback { private OfflineManager(Context context) { // Get a pointer to the DefaultFileSource instance - String assetRoot = context.getFilesDir().getAbsolutePath(); + String assetRoot = getDatabasePath(context); String cachePath = assetRoot + File.separator + DATABASE_NAME; mDefaultFileSourcePtr = createDefaultFileSource(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE); @@ -100,6 +106,61 @@ private OfflineManager(Context context) { 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 e) { + Log.e(LOG_TAG, "Failed to read the package metadata: " + e.getMessage()); + } catch (Exception e) { + Log.e(LOG_TAG, "Failed to read the storage key: " + e.getMessage()); + } + + String databasePath = null; + if (setStorageExternal && isExternalStorageReadable()) { + try { + // Try getting the external storage path + databasePath = context.getExternalFilesDir(null).getAbsolutePath(); + } catch (NullPointerException e) { + Log.e(LOG_TAG, "Failed to obtain the external storage path: " + e.getMessage()); + } + } + + 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 + * (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 + */ + public static boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + + Log.w(LOG_TAG, "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/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index b1d7249a04c..8a7e70b7338 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -305,6 +305,11 @@ android:name="com.mapbox.TestEventsAccessToken" android:value="sk.eyJ1IjoiYmxlZWdlIiwiYSI6InNpcml1c2x5In0.KyT-boMyC_xZYTYojTc8zg" /> + + + + +