[android] Feature: Change path of the resources cache db
This commit is contained in:
committed by
Łukasz Paczos
parent
42144ce1dc
commit
5e58a0d81d
@@ -8,12 +8,11 @@ import android.os.Looper;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import android.support.annotation.RestrictTo;
|
||||
import com.mapbox.mapboxsdk.LibraryLoader;
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
|
||||
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
@@ -135,24 +134,17 @@ public class OfflineManager {
|
||||
deleteAmbientDatabase(this.context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current instance of the offline manager.
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
public static void clear() {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
private void deleteAmbientDatabase(final Context context) {
|
||||
// Delete the file in a separate thread to avoid affecting the UI
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String path = FileSource.getInternalCachePath(context) + File.separator + "mbgl-cache.db";
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
Logger.d(TAG, String.format("Old ambient cache database deleted to save space: %s", path));
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Logger.e(TAG, "Failed to delete old ambient cache database: ", exception);
|
||||
MapStrictMode.strictModeViolation(exception);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
final String path = FileSource.getInternalCachePath(context) + File.separator + "mbgl-cache.db";
|
||||
FileUtils.deleteFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
package com.mapbox.mapboxsdk.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
import com.mapbox.mapboxsdk.offline.OfflineManager;
|
||||
import com.mapbox.mapboxsdk.utils.ThreadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
/**
|
||||
* Holds a central reference to the core's DefaultFileSource for as long as
|
||||
* there are active mapviews / offline managers
|
||||
@@ -28,6 +32,8 @@ import com.mapbox.mapboxsdk.log.Logger;
|
||||
public class FileSource {
|
||||
|
||||
private static final String TAG = "Mbgl-FileSource";
|
||||
private static final String MAPBOX_SHARED_PREFERENCES = "MapboxSharedPreferences";
|
||||
private static final String MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH = "fileSourceResourcesCachePath";
|
||||
private static final Lock resourcesCachePathLoaderLock = new ReentrantLock();
|
||||
private static final Lock internalCachePathLoaderLock = new ReentrantLock();
|
||||
@Nullable
|
||||
@@ -53,6 +59,29 @@ public class FileSource {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback receives an asynchronous response containing the new path of the
|
||||
* resources cache database.
|
||||
*/
|
||||
@Keep
|
||||
public interface SetResourcesCachePathCallback {
|
||||
|
||||
/**
|
||||
* Receives the new database path
|
||||
*
|
||||
* @param path the path of the current resources cache database
|
||||
*/
|
||||
void onSuccess(String path);
|
||||
|
||||
/**
|
||||
* Receives an error message if setting the path was not successful
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
void onError(String message);
|
||||
|
||||
}
|
||||
|
||||
// File source instance is kept alive after initialization
|
||||
private static FileSource INSTANCE;
|
||||
|
||||
@@ -79,17 +108,51 @@ public class FileSource {
|
||||
*/
|
||||
@NonNull
|
||||
private static String getCachePath(@NonNull Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE);
|
||||
String cachePath = preferences.getString(MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH, null);
|
||||
|
||||
if (!isPathWritable(cachePath)) {
|
||||
// Use default path
|
||||
cachePath = getDefaultCachePath(context);
|
||||
|
||||
// Reset stored cache path
|
||||
SharedPreferences.Editor editor =
|
||||
context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
|
||||
editor.remove(MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH).apply();
|
||||
}
|
||||
|
||||
return cachePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default resources cache path depending on the external storage configuration
|
||||
*
|
||||
* @param context the context to derive the files directory path from
|
||||
* @return the default directory path
|
||||
*/
|
||||
@NonNull
|
||||
private static String getDefaultCachePath(@NonNull Context context) {
|
||||
if (isExternalStorageConfiguration(context) && isExternalStorageReadable()) {
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir != null) {
|
||||
return externalFilesDir.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
return context.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
private static boolean isExternalStorageConfiguration(@NonNull Context context) {
|
||||
// Default value
|
||||
boolean isExternalStorageConfiguration = 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);
|
||||
PackageManager.GET_META_DATA);
|
||||
if (appInfo.metaData != null) {
|
||||
isExternalStorageConfiguration = appInfo.metaData.getBoolean(
|
||||
MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
|
||||
MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL
|
||||
MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
|
||||
MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL
|
||||
);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException exception) {
|
||||
@@ -99,24 +162,7 @@ public class FileSource {
|
||||
Logger.e(TAG, "Failed to read the storage key: ", exception);
|
||||
MapStrictMode.strictModeViolation(exception);
|
||||
}
|
||||
|
||||
String cachePath = null;
|
||||
if (isExternalStorageConfiguration && isExternalStorageReadable()) {
|
||||
try {
|
||||
// Try getting the external storage path
|
||||
cachePath = context.getExternalFilesDir(null).getAbsolutePath();
|
||||
} catch (NullPointerException exception) {
|
||||
Logger.e(TAG, "Failed to obtain the external storage path: ", exception);
|
||||
MapStrictMode.strictModeViolation(exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (cachePath == null) {
|
||||
// Default to internal storage
|
||||
cachePath = context.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
return cachePath;
|
||||
return isExternalStorageConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,8 +182,8 @@ public class FileSource {
|
||||
}
|
||||
|
||||
Logger.w(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).");
|
||||
+ " make sure you've requested READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE"
|
||||
+ " permissions in your app Manifest (defaulting to internal storage).");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -166,9 +212,9 @@ public class FileSource {
|
||||
@NonNull
|
||||
@Override
|
||||
protected String[] doInBackground(Context... contexts) {
|
||||
return new String[] {
|
||||
getCachePath(contexts[0]),
|
||||
contexts[0].getCacheDir().getAbsolutePath()
|
||||
return new String[]{
|
||||
getCachePath(contexts[0]),
|
||||
contexts[0].getCacheDir().getAbsolutePath()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -217,6 +263,92 @@ public class FileSource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the path of the resources cache database.
|
||||
* Note that the external storage setting needs to be activated in the manifest.
|
||||
*
|
||||
* @param context the context of the path
|
||||
* @param path the new database path
|
||||
* @param callback the callback to obtain the result
|
||||
*/
|
||||
public static void setResourcesCachePath(@NonNull Context context,
|
||||
@NonNull final String path,
|
||||
@NonNull final SetResourcesCachePathCallback callback) {
|
||||
|
||||
if (getInstance(context).isActivated()) {
|
||||
final String activatedMessage = "Cannot set path, file source is activated!";
|
||||
Logger.w(TAG, activatedMessage);
|
||||
callback.onError(activatedMessage);
|
||||
} else {
|
||||
if (path.equals(resourcesCachePath)) {
|
||||
// no need to change the path
|
||||
callback.onSuccess(path);
|
||||
} else {
|
||||
final WeakReference<Context> contextWeakReference = new WeakReference<>(context);
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Context context = contextWeakReference.get();
|
||||
final String message;
|
||||
if (context != null) {
|
||||
if (!isPathWritable(path)) {
|
||||
message = "Path is not writable: " + path;
|
||||
} else {
|
||||
message = null;
|
||||
|
||||
final SharedPreferences.Editor editor =
|
||||
context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
|
||||
if (!editor.putString(MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH, path).commit()) {
|
||||
Logger.w(TAG, "Cannot store cache path in shared preferences.");
|
||||
}
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setResourcesCachePath(context, path);
|
||||
callback.onSuccess(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
message = "Context is null";
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
Logger.w(TAG, message);
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onError(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setResourcesCachePath(@NonNull Context context, @NonNull String path) {
|
||||
resourcesCachePathLoaderLock.lock();
|
||||
resourcesCachePath = path;
|
||||
reinitializeOfflineManager(context);
|
||||
resourcesCachePathLoaderLock.unlock();
|
||||
}
|
||||
|
||||
private static boolean isPathWritable(String path) {
|
||||
if (path == null || path.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return new File(path).canWrite();
|
||||
}
|
||||
|
||||
private static void reinitializeOfflineManager(@NonNull Context context) {
|
||||
final FileSource fileSource = FileSource.getInstance(context);
|
||||
fileSource.initialize(Mapbox.getAccessToken(), resourcesCachePath, context.getResources().getAssets());
|
||||
OfflineManager.clear();
|
||||
}
|
||||
|
||||
private static void lockPathLoaders() {
|
||||
internalCachePathLoaderLock.lock();
|
||||
resourcesCachePathLoaderLock.lock();
|
||||
|
||||
@@ -3,11 +3,15 @@ package com.mapbox.mapboxsdk.utils;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
private static final String TAG = "Mbgl-FileUtils";
|
||||
|
||||
/**
|
||||
* Task checking whether app's process can read a file.
|
||||
*/
|
||||
@@ -121,4 +125,30 @@ public class FileUtils {
|
||||
*/
|
||||
void onError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file asynchronously in a separate thread.
|
||||
*
|
||||
* @param path the path of the file that should be deleted
|
||||
*/
|
||||
public static void deleteFile(@NonNull final String path) {
|
||||
// Delete the file in a separate thread to avoid affecting the UI
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
if (file.delete()) {
|
||||
Logger.d(TAG, "File deleted to save space: " + path);
|
||||
} else {
|
||||
Logger.e(TAG, "Failed to delete file: " + path);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Logger.e(TAG, "Failed to delete file: ", exception);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user