[android] expose offline database merge API
This commit is contained in:
@@ -2,10 +2,12 @@ package com.mapbox.mapboxsdk.offline;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.LibraryLoader;
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
@@ -15,6 +17,11 @@ import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* The offline manager is the main entry point for offline-related functionality.
|
||||
@@ -92,6 +99,27 @@ public class OfflineManager {
|
||||
void onError(String error);
|
||||
}
|
||||
|
||||
/**
|
||||
* This callback receives an asynchronous response containing a list of all
|
||||
* OfflineRegion added to the database during the merge.
|
||||
*/
|
||||
@Keep
|
||||
public interface MergeOfflineRegionsCallback {
|
||||
/**
|
||||
* Receives the list of merged offline regions.
|
||||
*
|
||||
* @param offlineRegions the offline region array
|
||||
*/
|
||||
void onMerge(OfflineRegion[] offlineRegions);
|
||||
|
||||
/**
|
||||
* Receives the error message.
|
||||
*
|
||||
* @param error the error message
|
||||
*/
|
||||
void onError(String error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
@@ -183,6 +211,143 @@ public class OfflineManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge offline regions from a secondary database into the main offline database.
|
||||
* <p>
|
||||
* When the merge is completed, or fails, the {@link MergeOfflineRegionsCallback} will be invoked on the main thread.
|
||||
* <p>
|
||||
* The secondary database may need to be upgraded to the latest schema.
|
||||
* This is done in-place and requires write-access to the provided path.
|
||||
* If the app's process doesn't have write-access to the provided path,
|
||||
* the file will be copied to the temporary, internal directory for the duration of the merge.
|
||||
* <p>
|
||||
* Only resources and tiles that belong to a region will be copied over. Identical
|
||||
* regions will be flattened into a single new region in the main database.
|
||||
* <p>
|
||||
* The operation will be aborted and {@link MergeOfflineRegionsCallback#onError(String)} with an appropriate message
|
||||
* will be invoked if the merge would result in the offline tile count limit being exceeded.
|
||||
* <p>
|
||||
* Merged regions may not be in a completed status if the secondary database
|
||||
* does not contain all the tiles or resources required by the region definition.
|
||||
*
|
||||
* @param path secondary database writable path
|
||||
* @param callback completion/error callback
|
||||
*/
|
||||
public void mergeOfflineRegions(@NonNull String path, @NonNull final MergeOfflineRegionsCallback callback) {
|
||||
File src = new File(path);
|
||||
if (!src.canRead()) {
|
||||
// path not readable, abort
|
||||
callback.onError("Secondary database needs to be located in a readable path.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (src.canWrite()) {
|
||||
// path writable, merge and update schema in place if necessary
|
||||
mergeOfflineDatabaseFiles(src, callback, false);
|
||||
} else {
|
||||
// path not writable, copy the the file to temp directory, then merge and update schema on a copy if necessary
|
||||
File dst = new File(FileSource.getInternalCachePath(context), src.getName());
|
||||
new CopyTempDatabaseFileTask(this, callback).execute(src, dst);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CopyTempDatabaseFileTask extends AsyncTask<Object, Void, Object> {
|
||||
private final WeakReference<OfflineManager> offlineManagerWeakReference;
|
||||
private final WeakReference<MergeOfflineRegionsCallback> callbackWeakReference;
|
||||
|
||||
CopyTempDatabaseFileTask(OfflineManager offlineManager, MergeOfflineRegionsCallback callback) {
|
||||
this.offlineManagerWeakReference = new WeakReference<>(offlineManager);
|
||||
this.callbackWeakReference = new WeakReference<>(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(Object... objects) {
|
||||
File src = (File) objects[0];
|
||||
File dst = (File) objects[1];
|
||||
|
||||
try {
|
||||
copyTempDatabaseFile(src, dst);
|
||||
return dst;
|
||||
} catch (IOException ex) {
|
||||
return ex.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object object) {
|
||||
MergeOfflineRegionsCallback callback = callbackWeakReference.get();
|
||||
if (callback != null) {
|
||||
OfflineManager offlineManager = offlineManagerWeakReference.get();
|
||||
if (object instanceof File && offlineManager != null) {
|
||||
// successfully copied the file, perform merge
|
||||
File dst = (File) object;
|
||||
offlineManager.mergeOfflineDatabaseFiles(dst, callback, true);
|
||||
} else if (object instanceof String) {
|
||||
// error occurred
|
||||
callback.onError((String) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyTempDatabaseFile(File sourceFile, File destFile) throws IOException {
|
||||
if (!destFile.exists() && !destFile.createNewFile()) {
|
||||
throw new IOException("Unable to copy database file for merge.");
|
||||
}
|
||||
|
||||
FileChannel source = null;
|
||||
FileChannel destination = null;
|
||||
|
||||
try {
|
||||
source = new FileInputStream(sourceFile).getChannel();
|
||||
destination = new FileOutputStream(destFile).getChannel();
|
||||
destination.transferFrom(source, 0, source.size());
|
||||
} catch (IOException ex) {
|
||||
throw new IOException(String.format("Unable to copy database file for merge. %s", ex.getMessage()));
|
||||
} finally {
|
||||
if (source != null) {
|
||||
source.close();
|
||||
}
|
||||
if (destination != null) {
|
||||
destination.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeOfflineDatabaseFiles(@NonNull File file, @NonNull final MergeOfflineRegionsCallback callback,
|
||||
boolean isTemporaryFile) {
|
||||
fileSource.activate();
|
||||
mergeOfflineRegions(fileSource, file.getAbsolutePath(), new MergeOfflineRegionsCallback() {
|
||||
@Override
|
||||
public void onMerge(OfflineRegion[] offlineRegions) {
|
||||
getHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fileSource.deactivate();
|
||||
if (isTemporaryFile) {
|
||||
file.delete();
|
||||
}
|
||||
callback.onMerge(offlineRegions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
getHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fileSource.deactivate();
|
||||
if (isTemporaryFile) {
|
||||
file.delete();
|
||||
}
|
||||
callback.onError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an offline region in the database.
|
||||
* <p>
|
||||
@@ -272,4 +437,6 @@ public class OfflineManager {
|
||||
private native void createOfflineRegion(FileSource fileSource, OfflineRegionDefinition definition,
|
||||
byte[] metadata, CreateOfflineRegionCallback callback);
|
||||
|
||||
@Keep
|
||||
private native void mergeOfflineRegions(FileSource fileSource, String path, MergeOfflineRegionsCallback callback);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.mapbox.mapboxsdk.testapp.offline
|
||||
|
||||
import android.R
|
||||
import android.content.Context
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.IdlingRegistry
|
||||
import android.support.test.espresso.UiController
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.idling.CountingIdlingResource
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap
|
||||
import com.mapbox.mapboxsdk.offline.OfflineManager
|
||||
import com.mapbox.mapboxsdk.offline.OfflineRegion
|
||||
import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke
|
||||
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest
|
||||
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineManagerTest : BaseActivityTest() {
|
||||
|
||||
companion object {
|
||||
private const val TEST_DB_FILE_NAME = "offline.db"
|
||||
}
|
||||
|
||||
private val context: Context by lazy { rule.activity }
|
||||
|
||||
private lateinit var offlineIdlingResource: CountingIdlingResource
|
||||
|
||||
override fun getActivityClass(): Class<*> {
|
||||
return EspressoTestActivity::class.java
|
||||
}
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
offlineIdlingResource = CountingIdlingResource("idling_resource")
|
||||
IdlingRegistry.getInstance().register(offlineIdlingResource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun offlineMergeListDeleteTest() {
|
||||
validateTestSetup()
|
||||
|
||||
invoke(mapboxMap) { _: UiController, _: MapboxMap ->
|
||||
offlineIdlingResource.increment()
|
||||
copyAsset(context)
|
||||
OfflineManager.getInstance(context).mergeOfflineRegions(
|
||||
context.filesDir.absolutePath + "/" + TEST_DB_FILE_NAME,
|
||||
object : OfflineManager.MergeOfflineRegionsCallback {
|
||||
override fun onMerge(offlineRegions: Array<out OfflineRegion>?) {
|
||||
assert(offlineRegions?.size == 1)
|
||||
offlineIdlingResource.decrement()
|
||||
}
|
||||
|
||||
override fun onError(error: String?) {
|
||||
throw RuntimeException("Unable to merge external offline database. $error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
invoke(mapboxMap) { _: UiController, _: MapboxMap ->
|
||||
offlineIdlingResource.increment()
|
||||
OfflineManager.getInstance(context).listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
|
||||
override fun onList(offlineRegions: Array<out OfflineRegion>?) {
|
||||
assert(offlineRegions?.size == 1)
|
||||
if (offlineRegions != null) {
|
||||
for (region in offlineRegions) {
|
||||
offlineIdlingResource.increment()
|
||||
region.delete(object : OfflineRegion.OfflineRegionDeleteCallback {
|
||||
override fun onDelete() {
|
||||
offlineIdlingResource.decrement()
|
||||
}
|
||||
|
||||
override fun onError(error: String?) {
|
||||
throw RuntimeException("Unable to delete region with ID: ${region.id}. $error")
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
throw RuntimeException("Unable to find merged region.")
|
||||
}
|
||||
offlineIdlingResource.decrement()
|
||||
}
|
||||
|
||||
override fun onError(error: String?) {
|
||||
throw RuntimeException("Unable to obtain offline regions list. $error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// waiting for offline idling resource
|
||||
onView(withId(R.id.content)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
override fun afterTest() {
|
||||
super.afterTest()
|
||||
IdlingRegistry.getInstance().unregister(offlineIdlingResource)
|
||||
}
|
||||
|
||||
private fun copyAsset(context: Context) {
|
||||
val bufferSize = 1024
|
||||
val assetManager = context.assets
|
||||
val inputStream = assetManager.open(TEST_DB_FILE_NAME)
|
||||
val outputStream = FileOutputStream(File(context.filesDir.absoluteFile, TEST_DB_FILE_NAME))
|
||||
|
||||
try {
|
||||
inputStream.copyTo(outputStream, bufferSize)
|
||||
} finally {
|
||||
inputStream.close()
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
package="com.mapbox.mapboxsdk.testapp">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".MapboxApplication"
|
||||
@@ -815,6 +816,17 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.offline.MergeOfflineRegionsActivity"
|
||||
android:description="@string/description_offline_merge"
|
||||
android:label="@string/activity_offline_merge">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_offline" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.location.LocationMapChangeActivity"
|
||||
android:description="@string/description_location_map_change"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,129 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.offline
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.Toast
|
||||
import com.mapbox.mapboxsdk.Mapbox
|
||||
import com.mapbox.mapboxsdk.constants.Style
|
||||
import com.mapbox.mapboxsdk.log.Logger
|
||||
import com.mapbox.mapboxsdk.offline.OfflineManager
|
||||
import com.mapbox.mapboxsdk.offline.OfflineRegion
|
||||
import com.mapbox.mapboxsdk.testapp.R
|
||||
import kotlinx.android.synthetic.main.activity_merge_offline_regions.*
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class MergeOfflineRegionsActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val TEST_DB_FILE_NAME = "offline.db"
|
||||
private const val PERMISSIONS_REQUEST_CODE = 11
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_merge_offline_regions)
|
||||
|
||||
// forcing offline state
|
||||
Mapbox.setConnected(false)
|
||||
|
||||
mapView.setStyleUrl(Style.SATELLITE)
|
||||
|
||||
mapView.onCreate(savedInstanceState)
|
||||
load_region_btn.setOnClickListener {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSIONS_REQUEST_CODE)
|
||||
} else {
|
||||
mergeDb()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PERMISSIONS_REQUEST_CODE) {
|
||||
for (result in grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
mergeDb()
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeDb() {
|
||||
// copy db asset to internal memory
|
||||
copyAsset()
|
||||
|
||||
OfflineManager.getInstance(this).mergeOfflineRegions(
|
||||
this.filesDir.absolutePath + "/" + TEST_DB_FILE_NAME,
|
||||
object : OfflineManager.MergeOfflineRegionsCallback {
|
||||
override fun onMerge(offlineRegions: Array<OfflineRegion>) {
|
||||
mapView.setStyleUrl(Style.SATELLITE)
|
||||
Toast.makeText(
|
||||
this@MergeOfflineRegionsActivity,
|
||||
String.format("Merged %d regions.", offlineRegions.size),
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onError(error: String) {
|
||||
Logger.e("MBGL_OFFLINE_DB_MERGE", error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun copyAsset() {
|
||||
val bufferSize = 1024
|
||||
val assetManager = this.assets
|
||||
val inputStream = assetManager.open(TEST_DB_FILE_NAME)
|
||||
val outputStream = FileOutputStream(File(this.filesDir, TEST_DB_FILE_NAME))
|
||||
|
||||
try {
|
||||
inputStream.copyTo(outputStream, bufferSize)
|
||||
} finally {
|
||||
inputStream.close()
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mapView.onStart()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mapView.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mapView.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapView.onStop()
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
mapView.onLowMemory()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mapView.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
mapView.onSaveInstanceState(outState)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -10,6 +9,6 @@
|
||||
<com.mapbox.mapboxsdk.maps.MapView
|
||||
android:id="@id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".activity.offline.MergeOfflineRegionsActivity">
|
||||
|
||||
<com.mapbox.mapboxsdk.maps.MapView
|
||||
android:id="@id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/load_region_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MERGE OFFLINE DB" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -70,6 +70,7 @@
|
||||
<string name="description_hillshade">Example raster-dem source and hillshade layer</string>
|
||||
<string name="description_heatmaplayer">Use HeatmapLayer to visualise earthquakes</string>
|
||||
<string name="description_gesture_detector">Manipulate gestures detector\'s settings</string>
|
||||
<string name="description_offline_merge">Merge external offline database</string>
|
||||
<string name="description_draggable_marker">Click to add a marker, long-click to drag</string>
|
||||
<string name="description_location_map_change">Change map\'s style while location is displayed</string>
|
||||
<string name="description_location_modes">Showcases location render and tracking modes</string>
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
<string name="activity_hillshade">Hillshade</string>
|
||||
<string name="activity_heatmaplayer">Heatmap layer</string>
|
||||
<string name="activity_gesture_detector">Gestures detector</string>
|
||||
<string name="activity_offline_merge">Offline DB merge</string>
|
||||
<string name="activity_draggable_maker">Draggable marker</string>
|
||||
<string name="activity_location_map_change">Simple Location Activity</string>
|
||||
<string name="activity_location_modes">Location Modes Activity</string>
|
||||
|
||||
@@ -37,5 +37,6 @@
|
||||
"SimpleMapActivity",
|
||||
"RenderTestActivity",
|
||||
"SymbolLayerActivity",
|
||||
"LocationFragmentActivity"
|
||||
"LocationFragmentActivity",
|
||||
"MergeOfflineRegionsActivity"
|
||||
]
|
||||
|
||||
@@ -34,7 +34,7 @@ void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, const jni::Object<Fil
|
||||
|
||||
if (regions) {
|
||||
OfflineManager::ListOfflineRegionsCallback::onList(
|
||||
*env, *jFileSource, *callback, std::move(*regions));
|
||||
*env, *jFileSource, *callback, *regions);
|
||||
} else {
|
||||
OfflineManager::ListOfflineRegionsCallback::onError(
|
||||
*env, *callback, regions.error());
|
||||
@@ -70,7 +70,7 @@ void OfflineManager::createOfflineRegion(jni::JNIEnv& env_,
|
||||
|
||||
if (region) {
|
||||
OfflineManager::CreateOfflineRegionCallback::onCreate(
|
||||
*env, *jFileSource, *callback, std::move(*region)
|
||||
*env, *jFileSource, *callback, *region
|
||||
);
|
||||
} else {
|
||||
OfflineManager::CreateOfflineRegionCallback::onError(
|
||||
@@ -79,9 +79,36 @@ void OfflineManager::createOfflineRegion(jni::JNIEnv& env_,
|
||||
});
|
||||
}
|
||||
|
||||
void OfflineManager::mergeOfflineRegions(jni::JNIEnv& env_, const jni::Object<FileSource>& jFileSource_,
|
||||
const jni::String& jString_,
|
||||
const jni::Object<MergeOfflineRegionsCallback>& callback_) {
|
||||
auto globalCallback = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, callback_);
|
||||
auto globalFilesource = jni::NewGlobal<jni::EnvAttachingDeleter>(env_, jFileSource_);
|
||||
|
||||
auto path = jni::Make<std::string>(env_, jString_);
|
||||
fileSource.mergeOfflineRegions(path, [
|
||||
//Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile
|
||||
callback = std::make_shared<decltype(globalCallback)>(std::move(globalCallback)),
|
||||
jFileSource = std::make_shared<decltype(globalFilesource)>(std::move(globalFilesource))
|
||||
](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> regions) mutable {
|
||||
|
||||
// Reattach, the callback comes from a different thread
|
||||
android::UniqueEnv env = android::AttachEnv();
|
||||
|
||||
if (regions) {
|
||||
OfflineManager::MergeOfflineRegionsCallback::onMerge(
|
||||
*env, *jFileSource, *callback, *regions);
|
||||
} else {
|
||||
OfflineManager::MergeOfflineRegionsCallback::onError(
|
||||
*env, *callback, regions.error());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OfflineManager::registerNative(jni::JNIEnv& env) {
|
||||
jni::Class<ListOfflineRegionsCallback>::Singleton(env);
|
||||
jni::Class<CreateOfflineRegionCallback>::Singleton(env);
|
||||
jni::Class<MergeOfflineRegionsCallback>::Singleton(env);
|
||||
|
||||
static auto& javaClass = jni::Class<OfflineManager>::Singleton(env);
|
||||
|
||||
@@ -93,7 +120,8 @@ void OfflineManager::registerNative(jni::JNIEnv& env) {
|
||||
"finalize",
|
||||
METHOD(&OfflineManager::setOfflineMapboxTileCountLimit, "setOfflineMapboxTileCountLimit"),
|
||||
METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"),
|
||||
METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"));
|
||||
METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"),
|
||||
METHOD(&OfflineManager::mergeOfflineRegions, "mergeOfflineRegions"));
|
||||
}
|
||||
|
||||
// OfflineManager::ListOfflineRegionsCallback //
|
||||
@@ -110,13 +138,13 @@ void OfflineManager::ListOfflineRegionsCallback::onError(jni::JNIEnv& env,
|
||||
void OfflineManager::ListOfflineRegionsCallback::onList(jni::JNIEnv& env,
|
||||
const jni::Object<FileSource>& jFileSource,
|
||||
const jni::Object<OfflineManager::ListOfflineRegionsCallback>& callback,
|
||||
mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) {
|
||||
mbgl::OfflineRegions& regions) {
|
||||
static auto& javaClass = jni::Class<OfflineManager::ListOfflineRegionsCallback>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::Array<jni::Object<OfflineRegion>>)>(env, "onList");
|
||||
|
||||
std::size_t index = 0;
|
||||
auto jregions = jni::Array<jni::Object<OfflineRegion>>::New(env, regions->size());
|
||||
for (auto& region : *regions) {
|
||||
auto jregions = jni::Array<jni::Object<OfflineRegion>>::New(env, regions.size());
|
||||
for (auto& region : regions) {
|
||||
jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region)));
|
||||
index++;
|
||||
}
|
||||
@@ -138,11 +166,39 @@ void OfflineManager::CreateOfflineRegionCallback::onError(jni::JNIEnv& env,
|
||||
void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env,
|
||||
const jni::Object<FileSource>& jFileSource,
|
||||
const jni::Object<OfflineManager::CreateOfflineRegionCallback>& callback,
|
||||
mbgl::optional<mbgl::OfflineRegion> region) {
|
||||
mbgl::OfflineRegion& region) {
|
||||
static auto& javaClass = jni::Class<OfflineManager::CreateOfflineRegionCallback>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::Object<OfflineRegion>)>(env, "onCreate");
|
||||
|
||||
callback.Call(env, method, OfflineRegion::New(env, jFileSource, std::move(*region)));
|
||||
callback.Call(env, method, OfflineRegion::New(env, jFileSource, std::move(region)));
|
||||
}
|
||||
|
||||
// OfflineManager::MergeOfflineRegionsCallback //
|
||||
|
||||
void OfflineManager::MergeOfflineRegionsCallback::onError(jni::JNIEnv& env,
|
||||
const jni::Object<OfflineManager::MergeOfflineRegionsCallback>& callback,
|
||||
std::exception_ptr error) {
|
||||
static auto& javaClass = jni::Class<OfflineManager::MergeOfflineRegionsCallback>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
|
||||
|
||||
callback.Call(env, method, jni::Make<jni::String>(env, mbgl::util::toString(error)));
|
||||
}
|
||||
|
||||
void OfflineManager::MergeOfflineRegionsCallback::onMerge(jni::JNIEnv& env,
|
||||
const jni::Object<FileSource>& jFileSource,
|
||||
const jni::Object<MergeOfflineRegionsCallback>& callback,
|
||||
mbgl::OfflineRegions& regions) {
|
||||
static auto& javaClass = jni::Class<OfflineManager::MergeOfflineRegionsCallback>::Singleton(env);
|
||||
static auto method = javaClass.GetMethod<void (jni::Array<jni::Object<OfflineRegion>>)>(env, "onMerge");
|
||||
|
||||
std::size_t index = 0;
|
||||
auto jregions = jni::Array<jni::Object<OfflineRegion>>::New(env, regions.size());
|
||||
for (auto& region : regions) {
|
||||
jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region)));
|
||||
index++;
|
||||
}
|
||||
|
||||
callback.Call(env, method, jregions);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "../file_source.hpp"
|
||||
#include "offline_region.hpp"
|
||||
#include "offline_region_definition.hpp"
|
||||
#include "../java_types.hpp"
|
||||
|
||||
|
||||
namespace mbgl {
|
||||
@@ -25,7 +26,7 @@ public:
|
||||
static void onList(jni::JNIEnv&,
|
||||
const jni::Object<FileSource>&,
|
||||
const jni::Object<OfflineManager::ListOfflineRegionsCallback>&,
|
||||
mbgl::optional<std::vector<mbgl::OfflineRegion>>);
|
||||
mbgl::OfflineRegions&);
|
||||
};
|
||||
|
||||
class CreateOfflineRegionCallback {
|
||||
@@ -37,7 +38,19 @@ public:
|
||||
static void onCreate(jni::JNIEnv&,
|
||||
const jni::Object<FileSource>&,
|
||||
const jni::Object<OfflineManager::CreateOfflineRegionCallback>&,
|
||||
mbgl::optional<mbgl::OfflineRegion>);
|
||||
mbgl::OfflineRegion&);
|
||||
};
|
||||
|
||||
class MergeOfflineRegionsCallback {
|
||||
public:
|
||||
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$MergeOfflineRegionsCallback";}
|
||||
|
||||
static void onError(jni::JNIEnv&, const jni::Object<OfflineManager::MergeOfflineRegionsCallback>&, std::exception_ptr);
|
||||
|
||||
static void onMerge(jni::JNIEnv&,
|
||||
const jni::Object<FileSource>&,
|
||||
const jni::Object<MergeOfflineRegionsCallback>&,
|
||||
mbgl::OfflineRegions&);
|
||||
};
|
||||
|
||||
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; };
|
||||
@@ -57,6 +70,11 @@ public:
|
||||
const jni::Array<jni::jbyte>& metadata,
|
||||
const jni::Object<OfflineManager::CreateOfflineRegionCallback>& callback);
|
||||
|
||||
void mergeOfflineRegions(jni::JNIEnv&,
|
||||
const jni::Object<FileSource>&,
|
||||
const jni::String&,
|
||||
const jni::Object<MergeOfflineRegionsCallback>&);
|
||||
|
||||
private:
|
||||
mbgl::DefaultFileSource& fileSource;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user