[android] when changing the resource path, recreate database instead of the whole FileSource
This commit is contained in:
committed by
Łukasz Paczos
parent
3f7b70fdb0
commit
b7702995fc
@@ -8,7 +8,6 @@ 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.Mapbox;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
@@ -134,14 +133,6 @@ 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) {
|
||||
final String path = FileSource.getInternalCachePath(context) + File.separator + "mbgl-cache.db";
|
||||
FileUtils.deleteFile(path);
|
||||
@@ -461,17 +452,17 @@ public class OfflineManager {
|
||||
* resource were requested in the process of map rendering.
|
||||
* Use this method to pre-warm the cache with resources you know
|
||||
* will be requested.
|
||||
*
|
||||
* <p>
|
||||
* This call is asynchronous: the data may not be immediately available
|
||||
* for in-progress requests, although subsequent requests should have
|
||||
* access to the cached data.
|
||||
*
|
||||
* @param url The URL of the resource to insert
|
||||
* @param data Response data to store for this resource. Data is expected to be uncompressed;
|
||||
* internally, the cache will compress data as necessary.
|
||||
* @param modified Optional "modified" response header, in seconds since 1970, or 0 if not set
|
||||
* @param expires Optional "expires" response header, in seconds since 1970, or 0 if not set
|
||||
* @param etag Optional "entity tag" response header
|
||||
* @param url The URL of the resource to insert
|
||||
* @param data Response data to store for this resource. Data is expected to be uncompressed;
|
||||
* internally, the cache will compress data as necessary.
|
||||
* @param modified Optional "modified" response header, in seconds since 1970, or 0 if not set
|
||||
* @param expires Optional "expires" response header, in seconds since 1970, or 0 if not set
|
||||
* @param etag Optional "entity tag" response header
|
||||
* @param mustRevalidate Indicates whether response can be used after it's stale
|
||||
*/
|
||||
@Keep
|
||||
|
||||
@@ -7,17 +7,16 @@ 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.FileUtils;
|
||||
import com.mapbox.mapboxsdk.utils.ThreadUtils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -64,7 +63,7 @@ public class FileSource {
|
||||
* resources cache database.
|
||||
*/
|
||||
@Keep
|
||||
public interface SetResourcesCachePathCallback {
|
||||
public interface ResourcesCachePathChangeCallback {
|
||||
|
||||
/**
|
||||
* Receives the new database path
|
||||
@@ -117,7 +116,7 @@ public class FileSource {
|
||||
|
||||
// Reset stored cache path
|
||||
SharedPreferences.Editor editor =
|
||||
context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
|
||||
context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
|
||||
editor.remove(MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH).apply();
|
||||
}
|
||||
|
||||
@@ -148,11 +147,11 @@ public class FileSource {
|
||||
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) {
|
||||
@@ -182,8 +181,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;
|
||||
}
|
||||
@@ -212,9 +211,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()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -273,67 +272,66 @@ public class FileSource {
|
||||
*/
|
||||
public static void setResourcesCachePath(@NonNull Context context,
|
||||
@NonNull final String path,
|
||||
@NonNull final SetResourcesCachePathCallback callback) {
|
||||
|
||||
@NonNull ResourcesCachePathChangeCallback callback) {
|
||||
final String fileSourceActivatedMessage = "Cannot set path, file source is activated."
|
||||
+ " Make sure that the map or a resources download is not running.";
|
||||
if (getInstance(context).isActivated()) {
|
||||
final String activatedMessage = "Cannot set path, file source is activated!";
|
||||
Logger.w(TAG, activatedMessage);
|
||||
callback.onError(activatedMessage);
|
||||
Logger.w(TAG, fileSourceActivatedMessage);
|
||||
callback.onError(fileSourceActivatedMessage);
|
||||
} else if (path.equals(resourcesCachePath)) {
|
||||
// no need to change the path
|
||||
callback.onSuccess(path);
|
||||
} 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 WeakReference<Context> contextWeakReference = new WeakReference<>(context);
|
||||
final WeakReference<ResourcesCachePathChangeCallback> callbackWeakReference = new WeakReference<>(callback);
|
||||
new FileUtils.CheckFileWritePermissionTask(new FileUtils.OnCheckFileWritePermissionListener() {
|
||||
@Override
|
||||
public void onWritePermissionGranted() {
|
||||
final Context context = contextWeakReference.get();
|
||||
final ResourcesCachePathChangeCallback callback = callbackWeakReference.get();
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (callback == null) {
|
||||
Logger.w(TAG, "Lost callback reference.");
|
||||
return;
|
||||
} else if (context == null) {
|
||||
String lostContextMessage = "Lost context reference.";
|
||||
Logger.w(TAG, lostContextMessage);
|
||||
callback.onError(lostContextMessage);
|
||||
return;
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
// verify fileSource's activation again after the async task returns
|
||||
if (getInstance(context).isActivated()) {
|
||||
Logger.w(TAG, fileSourceActivatedMessage);
|
||||
callback.onError(fileSourceActivatedMessage);
|
||||
} else {
|
||||
final SharedPreferences.Editor editor =
|
||||
context.getSharedPreferences(MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE).edit();
|
||||
editor.putString(MAPBOX_SHARED_PREFERENCE_RESOURCES_CACHE_PATH, path);
|
||||
editor.apply();
|
||||
setResourcesCachePath(context, path);
|
||||
callback.onSuccess(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
final ResourcesCachePathChangeCallback callback = callbackWeakReference.get();
|
||||
if (callback != null) {
|
||||
String message = "Path is not writable: " + path;
|
||||
Logger.e(TAG, message);
|
||||
callback.onError(message);
|
||||
}
|
||||
}
|
||||
}).execute(new File(path));
|
||||
}
|
||||
}
|
||||
|
||||
private static void setResourcesCachePath(@NonNull Context context, @NonNull String path) {
|
||||
resourcesCachePathLoaderLock.lock();
|
||||
resourcesCachePath = path;
|
||||
reinitializeOfflineManager(context);
|
||||
resourcesCachePathLoaderLock.unlock();
|
||||
getInstance(context).setResourceCachePath(path);
|
||||
}
|
||||
|
||||
private static boolean isPathWritable(String path) {
|
||||
@@ -343,12 +341,6 @@ public class FileSource {
|
||||
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();
|
||||
@@ -396,6 +388,9 @@ public class FileSource {
|
||||
@Keep
|
||||
public native void setResourceTransform(final ResourceTransformCallback callback);
|
||||
|
||||
@Keep
|
||||
private native void setResourceCachePath(String path);
|
||||
|
||||
@Keep
|
||||
private native void initialize(String accessToken, String cachePath, AssetManager assetManager);
|
||||
|
||||
|
||||
@@ -12,27 +12,36 @@ import android.widget.AdapterView
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.mapbox.mapboxsdk.log.Logger
|
||||
import com.mapbox.mapboxsdk.offline.OfflineManager
|
||||
import com.mapbox.mapboxsdk.offline.OfflineRegion
|
||||
import com.mapbox.mapboxsdk.storage.FileSource
|
||||
import com.mapbox.mapboxsdk.testapp.R
|
||||
import kotlinx.android.synthetic.main.activity_change_resources_cache_path.*
|
||||
import java.io.File
|
||||
|
||||
class ChangeResourcesCachePathActivity : AppCompatActivity(),
|
||||
AdapterView.OnItemClickListener,
|
||||
FileSource.SetResourcesCachePathCallback {
|
||||
AdapterView.OnItemClickListener,
|
||||
FileSource.ResourcesCachePathChangeCallback {
|
||||
|
||||
lateinit var adapter: PathAdapter
|
||||
companion object {
|
||||
private const val TAG = "Mbgl-ChangeResourcesCachePathActivity"
|
||||
}
|
||||
|
||||
private lateinit var adapter: PathAdapter
|
||||
|
||||
private lateinit var offlineManager: OfflineManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_change_resources_cache_path)
|
||||
|
||||
Thread(Runnable {
|
||||
adapter = PathAdapter(this, obtainFilesPaths(this))
|
||||
listView.adapter = adapter
|
||||
listView.emptyView = empty
|
||||
listView.onItemClickListener = this
|
||||
}).start()
|
||||
adapter = PathAdapter(this, obtainExternalFilesPaths())
|
||||
listView.adapter = adapter
|
||||
listView.emptyView = empty
|
||||
listView.onItemClickListener = this
|
||||
|
||||
offlineManager = OfflineManager.getInstance(this)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
@@ -55,23 +64,33 @@ class ChangeResourcesCachePathActivity : AppCompatActivity(),
|
||||
override fun onSuccess(path: String?) {
|
||||
listView.onItemClickListener = this
|
||||
Toast.makeText(this, "New path: $path", Toast.LENGTH_LONG).show()
|
||||
|
||||
offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
|
||||
override fun onList(offlineRegions: Array<out OfflineRegion>?) {
|
||||
Logger.i(TAG, "Number of saved offline regions in the new path: ${offlineRegions?.size.toString()}")
|
||||
}
|
||||
|
||||
override fun onError(error: String?) {
|
||||
Logger.e(TAG, error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun obtainFilesPaths(context: Context): List<String> {
|
||||
private fun Context.obtainExternalFilesPaths(): List<String> {
|
||||
val paths = ArrayList<String>()
|
||||
paths.add(context.filesDir.absolutePath)
|
||||
paths.add(this.filesDir.absolutePath)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
paths.addAll(obtainExternalFilesPathsKitKat(context))
|
||||
paths.addAll(obtainExternalFilesPathsKitKat())
|
||||
} else {
|
||||
paths.addAll(obtainExternalFilesPathsLegacy(context))
|
||||
paths.addAll(obtainExternalFilesPathsLegacy())
|
||||
}
|
||||
paths.add("${File.separator}invalid${File.separator}cache${File.separator}path")
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun obtainExternalFilesPathsLegacy(context: Context): List<String> {
|
||||
private fun Context.obtainExternalFilesPathsLegacy(): List<String> {
|
||||
val postFix =
|
||||
"${File.separator}Android${File.separator}data${File.separator}${context.packageName}${File.separator}files"
|
||||
"${File.separator}Android${File.separator}data${File.separator}${this.packageName}${File.separator}files"
|
||||
val paths = ArrayList<String>()
|
||||
val externalStorage = System.getenv("EXTERNAL_STORAGE")
|
||||
val secondaryStorage = System.getenv("SECONDARY_STORAGE")
|
||||
@@ -88,9 +107,9 @@ class ChangeResourcesCachePathActivity : AppCompatActivity(),
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private fun obtainExternalFilesPathsKitKat(context: Context): List<String> {
|
||||
private fun Context.obtainExternalFilesPathsKitKat(): List<String> {
|
||||
val paths = ArrayList<String>()
|
||||
val extDirs = context.getExternalFilesDirs(null)
|
||||
val extDirs = this.getExternalFilesDirs(null)
|
||||
for (dir in extDirs) {
|
||||
if (dir != null) {
|
||||
paths.add(dir.absolutePath)
|
||||
@@ -105,12 +124,10 @@ class ChangeResourcesCachePathActivity : AppCompatActivity(),
|
||||
return paths[position]
|
||||
}
|
||||
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return position.toLong()
|
||||
}
|
||||
|
||||
|
||||
override fun getCount(): Int {
|
||||
return paths.size
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@ FileSource::FileSource(jni::JNIEnv& _env,
|
||||
const jni::String& accessToken,
|
||||
const jni::String& _cachePath,
|
||||
const jni::Object<AssetManager>& assetManager) {
|
||||
mapbox::sqlite::setTempPath(jni::Make<std::string>(_env, _cachePath));
|
||||
std::string path = jni::Make<std::string>(_env, _cachePath);
|
||||
mapbox::sqlite::setTempPath(path);
|
||||
|
||||
// Create a core default file source
|
||||
fileSource = std::make_unique<mbgl::DefaultFileSource>(
|
||||
jni::Make<std::string>(_env, _cachePath) + "/mbgl-offline.db",
|
||||
path + DATABASE_FILE,
|
||||
std::make_unique<AssetManagerFileSource>(_env, assetManager));
|
||||
|
||||
// Set access token
|
||||
@@ -69,6 +70,12 @@ void FileSource::setResourceTransform(jni::JNIEnv& env, const jni::Object<FileSo
|
||||
}
|
||||
}
|
||||
|
||||
void FileSource::setResourceCachePath(jni::JNIEnv& env, const jni::String& path) {
|
||||
std::string newPath = jni::Make<std::string>(env, path);
|
||||
mapbox::sqlite::setTempPath(newPath);
|
||||
fileSource->setResourceCachePath(newPath + DATABASE_FILE);
|
||||
}
|
||||
|
||||
void FileSource::resume(jni::JNIEnv&) {
|
||||
if (!activationCounter) {
|
||||
activationCounter = optional<int>(1) ;
|
||||
@@ -129,6 +136,7 @@ void FileSource::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&FileSource::setAccessToken, "setAccessToken"),
|
||||
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
|
||||
METHOD(&FileSource::setResourceTransform, "setResourceTransform"),
|
||||
METHOD(&FileSource::setResourceCachePath, "setResourceCachePath"),
|
||||
METHOD(&FileSource::resume, "activate"),
|
||||
METHOD(&FileSource::pause, "deactivate"),
|
||||
METHOD(&FileSource::isResumed, "isActivated")
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
|
||||
void setResourceTransform(jni::JNIEnv&, const jni::Object<FileSource::ResourceTransformCallback>&);
|
||||
|
||||
void setResourceCachePath(jni::JNIEnv&, const jni::String&);
|
||||
|
||||
void resume(jni::JNIEnv&);
|
||||
|
||||
void pause(jni::JNIEnv&);
|
||||
@@ -52,6 +54,7 @@ public:
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
private:
|
||||
const std::string DATABASE_FILE = "/mbgl-offline.db";
|
||||
optional<int> activationCounter;
|
||||
std::unique_ptr<Actor<ResourceTransform>> resourceTransform;
|
||||
std::unique_ptr<mbgl::DefaultFileSource> fileSource;
|
||||
|
||||
Reference in New Issue
Block a user