diff options
author | Tobrun <tobrun.van.nuland@gmail.com> | 2016-09-28 07:48:01 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-28 07:48:01 +0200 |
commit | 3835ed556b03a7246d238e1ee1a52f3aac29ec98 (patch) | |
tree | d0e976a9b0a8f9b9513f757c0f6a24e81dce69f5 | |
parent | 73baa527100a311a3ab4e056b138ff5fc0e63700 (diff) | |
download | qtlocation-mapboxgl-3835ed556b03a7246d238e1ee1a52f3aac29ec98.tar.gz |
6402 update offline metadata android (#6456)
* [android] - update offline metadata jni integration
* extract offline utils for reuse
* add test activity for update metadata
* fix requested signature changes
9 files changed, 377 insertions, 31 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java index 22cb034f08..f52f20083a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java @@ -130,6 +130,26 @@ public class OfflineRegion { } /** + * This callback receives an asynchronous response containing the newly update + * OfflineMetadata in the database, or an error message otherwise. + */ + public interface OfflineRegionUpdateMetadataCallback { + /** + * Receives the newly update offline region metadata. + * + * @param metadata the offline metadata to u[date + */ + void onUpdate(byte[] metadata); + + /** + * Receives the error message. + * + * @param error the error message to be shown + */ + void onError(String error); + } + + /** * A region is either inactive (not downloading, but previously-downloaded * resources are available for use), or active (resources are being downloaded * or will be downloaded, if necessary, when network access is available). @@ -341,6 +361,43 @@ public class OfflineRegion { }); } + /** + * Update an offline region metadata from the database. + * <p> + * When the operation is complete or encounters an error, the given callback will be + * executed on the main thread. + * </p> + * <p> + * After you call this method, you may not call any additional methods on this object. + * </p> + * + * @param callback the callback to be invoked + */ + public void updateMetadata(@NonNull final byte[] bytes, @NonNull final OfflineRegionUpdateMetadataCallback callback) { + updateOfflineRegionMetadata(bytes, new OfflineRegionUpdateMetadataCallback() { + @Override + public void onUpdate(final byte[] metadata) { + getHandler().post(new Runnable() { + @Override + public void run() { + mMetadata = metadata; + callback.onUpdate(metadata); + } + }); + } + + @Override + public void onError(final String error) { + getHandler().post(new Runnable() { + @Override + public void run() { + callback.onError(error); + } + }); + } + }); + } + @Override protected void finalize() { try { @@ -369,4 +426,6 @@ public class OfflineRegion { private native void deleteOfflineRegion( OfflineRegionDeleteCallback deleteCallback); + private native void updateOfflineRegionMetadata(byte[] metadata, OfflineRegionUpdateMetadataCallback callback); + } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 9949132875..6b164283cf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -244,6 +244,14 @@ android:value="@string/category_offline" /> </activity> <activity + android:name=".activity.offline.UpdateMetadataActivity" + android:description="@string/description_update_metadata" + android:label="@string/activity_update_metadata"> + <meta-data + android:name="@string/category" + android:value="@string/category_offline" /> + </activity> + <activity android:name=".activity.imagegenerator.SnapshotActivity" android:description="@string/description_snapshot" android:label="@string/activity_snapshot"> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java index 139d5ae0b5..d3c66a22ad 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java @@ -29,8 +29,7 @@ import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.model.other.OfflineDownloadRegionDialog; import com.mapbox.mapboxsdk.testapp.model.other.OfflineListRegionsDialog; - -import org.json.JSONObject; +import com.mapbox.mapboxsdk.testapp.utils.OfflineUtils; import java.util.ArrayList; @@ -188,7 +187,7 @@ public class OfflineActivity extends AppCompatActivity // Get regions info ArrayList<String> offlineRegionsNames = new ArrayList<>(); for (OfflineRegion offlineRegion : offlineRegions) { - offlineRegionsNames.add(getRegionName(offlineRegion)); + offlineRegionsNames.add(OfflineUtils.convertRegionName(offlineRegion.getMetadata())); } // Create args @@ -205,22 +204,6 @@ public class OfflineActivity extends AppCompatActivity public void onError(String error) { Log.e(LOG_TAG, "Error: " + error); } - - private String getRegionName(OfflineRegion offlineRegion) { - String regionName; - - try { - byte[] metadata = offlineRegion.getMetadata(); - String json = new String(metadata, JSON_CHARSET); - JSONObject jsonObject = new JSONObject(json); - regionName = jsonObject.getString(JSON_FIELD_REGION_NAME); - } catch (Exception exception) { - Log.e(LOG_TAG, "Failed to decode metadata: " + exception.getMessage()); - regionName = "Region " + offlineRegion.getID(); - } - - return regionName; - } }); } @@ -249,16 +232,7 @@ public class OfflineActivity extends AppCompatActivity styleUrl, bounds, minZoom, maxZoom, pixelRatio); // Sample way of encoding metadata from a JSONObject - byte[] metadata; - try { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(JSON_FIELD_REGION_NAME, regionName); - String json = jsonObject.toString(); - metadata = json.getBytes(JSON_CHARSET); - } catch (Exception exception) { - Log.e(LOG_TAG, "Failed to encode metadata: " + exception.getMessage()); - metadata = null; - } + byte[] metadata = OfflineUtils.convertRegionName(regionName); // Create region offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java new file mode 100644 index 0000000000..0530f1c157 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java @@ -0,0 +1,186 @@ +package com.mapbox.mapboxsdk.testapp.activity.offline; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.InputType; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.offline.OfflineManager; +import com.mapbox.mapboxsdk.offline.OfflineRegion; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.OfflineUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Test activity showing integration of updating metadata of an OfflineRegion. + */ +public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { + + private OfflineRegionMetadataAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_metadata_update); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + } + + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter = new OfflineRegionMetadataAdapter(this)); + listView.setEmptyView(findViewById(android.R.id.empty)); + listView.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final OfflineRegion region = adapter.getItem(position); + String metadata = OfflineUtils.convertRegionName(region.getMetadata()); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Rename metadata"); + + final EditText input = new EditText(this); + input.setText(metadata); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setSelection(metadata.length()); + builder.setView(input); + + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + updateMetadata(region, OfflineUtils.convertRegionName(input.getText().toString())); + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } + + private void updateMetadata(OfflineRegion region, byte[] metadata) { + region.updateMetadata(metadata, new OfflineRegion.OfflineRegionUpdateMetadataCallback() { + @Override + public void onUpdate(byte[] metadata) { + adapter.notifyDataSetChanged(); + } + + @Override + public void onError(String error) { + Toast.makeText(UpdateMetadataActivity.this, "Region metadata update failed with " + error, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + loadOfflineRegions(); + } + + private void loadOfflineRegions() { + OfflineManager.getInstance(this).listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() { + @Override + public void onList(OfflineRegion[] offlineRegions) { + if (offlineRegions != null && offlineRegions.length > 0) { + adapter.setOfflineRegions(Arrays.asList(offlineRegions)); + } + } + + @Override + public void onError(String error) { + Toast.makeText(UpdateMetadataActivity.this, "Error loading regions " + error, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private static class OfflineRegionMetadataAdapter extends BaseAdapter { + + private Context context; + private List<OfflineRegion> offlineRegions; + + OfflineRegionMetadataAdapter(Context ctx) { + context = ctx; + offlineRegions = new ArrayList<>(); + } + + void setOfflineRegions(List<OfflineRegion> offlineRegions) { + this.offlineRegions = offlineRegions; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return offlineRegions.size(); + } + + @Override + public OfflineRegion getItem(int position) { + return offlineRegions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + + if (convertView == null) { + holder = new ViewHolder(); + convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false); + holder.text = (TextView) convertView.findViewById(android.R.id.text1); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + holder.text.setText(OfflineUtils.convertRegionName(getItem(position).getMetadata())); + return convertView; + } + + static class ViewHolder { + TextView text; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java new file mode 100644 index 0000000000..ee4ae039ee --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.testapp.utils; + +import android.support.annotation.NonNull; +import android.util.Log; + +import org.json.JSONObject; + +import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_CHARSET; +import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_FIELD_REGION_NAME; + +public class OfflineUtils { + + public static String convertRegionName(@NonNull byte[] metadata) { + try { + String json = new String(metadata, JSON_CHARSET); + JSONObject jsonObject = new JSONObject(json); + return jsonObject.getString(JSON_FIELD_REGION_NAME); + } catch (Exception exception) { + return null; + } + } + + public static byte[] convertRegionName(String regionName) { + byte[] metadata = null; + try { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(JSON_FIELD_REGION_NAME, regionName); + String json = jsonObject.toString(); + metadata = json.getBytes(JSON_CHARSET); + } catch (Exception exception) { + Log.e("OfflineUtils", "Failed to encode metadata: " + exception.getMessage()); + } + return metadata; + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml new file mode 100644 index 0000000000..067a52dae4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:background="@color/primary" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> + + <ListView + android:id="@+id/listView" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <TextView + android:id="@android:id/empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:text="No Results" + android:textSize="24sp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml index 52c30bebde..ff63df4ddd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml @@ -53,6 +53,7 @@ <string name="activity_map_padding">Map Padding</string> <string name="activity_debug_mode">Debug Mode</string> <string name="activity_offline">Offline Map</string> + <string name="activity_update_metadata">Update metadata Map</string> <string name="activity_minmax_zoom">Min/Max Zoom</string> <string name="activity_viewpager">ViewPager</string> <string name="activity_runtime_style">Runtime Style</string> @@ -88,6 +89,7 @@ <string name="description_map_padding">Map Padding example</string> <string name="description_debug_mode">Debug Mode</string> <string name="description_offline">Offline Map example</string> + <string name="description_update_metadata">Update metadata example</string> <string name="description_animated_marker">Animate the position change of a Marker</string> <string name="description_polyline">Add a polyline to a map</string> <string name="description_polygon">Add a polygon to a map</string> diff --git a/platform/android/scripts/generate-test-code.js b/platform/android/scripts/generate-test-code.js index 4c4a0b4cf8..780ec59957 100644 --- a/platform/android/scripts/generate-test-code.js +++ b/platform/android/scripts/generate-test-code.js @@ -14,7 +14,7 @@ global.camelize = function (str) { } -const excludeActivities = ["CarDrivingActivity","MyLocationTrackingModeActivity","MyLocationToggleActivity","MyLocationTintActivity","MyLocationDrawableActivity","DoubleMapActivity", "LocationPickerActivity","GeoJsonClusteringActivity","RuntimeStyleTestActivity", "AnimatedMarkerActivity", "ViewPagerActivity","MapFragmentActivity","SupportMapFragmentActivity","SnapshotActivity","NavigationDrawerActivity", "QueryRenderedFeaturesBoxHighlightActivity"]; +const excludeActivities = ["UpdateMetadataActivity","CarDrivingActivity","MyLocationTrackingModeActivity","MyLocationToggleActivity","MyLocationTintActivity","MyLocationDrawableActivity","DoubleMapActivity", "LocationPickerActivity","GeoJsonClusteringActivity","RuntimeStyleTestActivity", "AnimatedMarkerActivity", "ViewPagerActivity","MapFragmentActivity","SupportMapFragmentActivity","SnapshotActivity","NavigationDrawerActivity", "QueryRenderedFeaturesBoxHighlightActivity"]; const appBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity'; const testBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/gen'; const subPackages = fs.readdirSync(appBasePath); diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 03e66c5895..62d4ea1eed 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -137,6 +137,9 @@ jni::jfieldID* offlineRegionDefinitionPixelRatioId = nullptr; jni::jmethodID* createOnCreateMethodId = nullptr; jni::jmethodID* createOnErrorMethodId = nullptr; +jni::jmethodID* updateMetadataOnUpdateMethodId = nullptr; +jni::jmethodID* updateMetadataOnErrorMethodId = nullptr; + jni::jmethodID* offlineRegionObserveronStatusChangedId = nullptr; jni::jmethodID* offlineRegionObserveronErrorId = nullptr; jni::jmethodID* offlineRegionObserveronLimitId = nullptr; @@ -1670,6 +1673,52 @@ void deleteOfflineRegion(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobject }); } +void updateOfflineRegionMetadata(JNIEnv *env, jni::jobject* offlineRegion_, jni::jarray<jbyte>* metadata_, jni::jobject* updateCallback) { + mbgl::Log::Debug(mbgl::Event::JNI, "updateOfflineRegionMetadata"); + + // Offline region + mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); + + // File source + jni::jobject* jmanager = jni::GetField<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); + jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); + mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr); + + // Id conversion + int64_t id = offlineRegion->getID(); + + // Metadata + mbgl::OfflineRegionMetadata metadata; + if (metadata_ != nullptr) { + metadata = metadata_from_java(env, *metadata_); + } + + // Makes sure the objects don't get GC'ed + updateCallback = jni::NewGlobalRef(*env, updateCallback).release(); + + // Launch updateCallback + defaultFileSource->updateOfflineMetadata(id, metadata, [updateCallback] (std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionMetadata> data) mutable { + // Reattach, the callback comes from a different thread + JNIEnv *env2; + jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); + if (renderDetach) { + mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); + } + + if (error) { + std::string message = mbgl::util::toString(error); + jni::CallMethod<void>(*env2, updateCallback, *updateMetadataOnErrorMethodId, std_string_to_jstring(env2, message)); + } else if (data) { + jni::jarray<jbyte>* jmetadata = metadata_from_native(env2, *data); + jni::CallMethod<void>(*env2, updateCallback, *updateMetadataOnUpdateMethodId, jmetadata); + } + + // Delete global refs and detach when we're done + jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(updateCallback)); + detach_jni_thread(theJVM, &env2, renderDetach); + }); +} + // Offline calls end } @@ -1900,7 +1949,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { MAKE_NATIVE_METHOD(setOfflineRegionObserver, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver;)V"), MAKE_NATIVE_METHOD(setOfflineRegionDownloadState, "(I)V"), MAKE_NATIVE_METHOD(getOfflineRegionStatus, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback;)V"), - MAKE_NATIVE_METHOD(deleteOfflineRegion, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback;)V") + MAKE_NATIVE_METHOD(deleteOfflineRegion, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback;)V"), + MAKE_NATIVE_METHOD(updateOfflineRegionMetadata, "([BLcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback;)V") ); // This needs to be updated once we support more than one type of region definition @@ -1943,6 +1993,10 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { offlineRegionDeleteOnDeleteId = &jni::GetMethodID(env, *offlineRegionDeleteCallbackClass, "onDelete", "()V"); offlineRegionDeleteOnErrorId = &jni::GetMethodID(env, *offlineRegionDeleteCallbackClass, "onError", "(Ljava/lang/String;)V"); + jni::jclass* offlineRegionUpdateMetadataCallbackClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"); + updateMetadataOnUpdateMethodId = &jni::GetMethodID(env, *offlineRegionUpdateMetadataCallbackClass, "onUpdate", "([B)V"); + updateMetadataOnErrorMethodId = &jni::GetMethodID(env, *offlineRegionUpdateMetadataCallbackClass, "onError", "(Ljava/lang/String;)V"); + // Offline end char release[PROP_VALUE_MAX] = ""; |