[android] jni high level binding refactor

This commit is contained in:
Ivo van Dongen
2017-02-16 16:16:47 +02:00
committed by Ivo van Dongen
parent 7d9018093a
commit 465c949153
62 changed files with 3710 additions and 2847 deletions

View File

@@ -30,6 +30,8 @@ public class Marker extends Annotation {
private LatLng position;
private String snippet;
private Icon icon;
//Redundantly stored for JNI access
private String iconId;
private String title;
private InfoWindow infoWindow;
@@ -51,24 +53,19 @@ public class Marker extends Annotation {
* @param baseMarkerOptions The builder used to construct the Marker.
*/
public Marker(BaseMarkerOptions baseMarkerOptions) {
position = baseMarkerOptions.position;
snippet = baseMarkerOptions.snippet;
icon = baseMarkerOptions.icon;
title = baseMarkerOptions.title;
this(baseMarkerOptions.position, baseMarkerOptions.icon, baseMarkerOptions.title, baseMarkerOptions.snippet);
}
Marker(BaseMarkerViewOptions baseMarkerViewOptions) {
position = baseMarkerViewOptions.position;
snippet = baseMarkerViewOptions.snippet;
icon = baseMarkerViewOptions.icon;
title = baseMarkerViewOptions.title;
this(baseMarkerViewOptions.position, baseMarkerViewOptions.icon,
baseMarkerViewOptions.title, baseMarkerViewOptions.snippet);
}
Marker(LatLng position, Icon icon, String title, String snippet) {
this.position = position;
this.icon = icon;
this.title = title;
this.snippet = snippet;
setIcon(icon);
}
/**
@@ -148,6 +145,7 @@ public class Marker extends Annotation {
*/
public void setIcon(@Nullable Icon icon) {
this.icon = icon;
this.iconId = icon != null ? icon.getId() : null;
MapboxMap map = getMapboxMap();
if (map != null) {
map.updateMarker(this);

View File

@@ -22,7 +22,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -203,8 +202,6 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
nativeMapView.setAccessToken(Mapbox.getAccessToken());
if (savedInstanceState == null) {
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapLoadEvent());
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
@@ -409,11 +406,6 @@ public class MapView extends FrameLayout {
return;
}
// stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242
if (TextUtils.isEmpty(nativeMapView.getAccessToken())) {
nativeMapView.setAccessToken(Mapbox.getAccessToken());
}
nativeMapView.setStyleUrl(url);
}

View File

@@ -16,7 +16,6 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
@@ -866,10 +865,6 @@ public final class MapboxMap {
private void setStyleUrl(@NonNull MapboxMapOptions options) {
String style = options.getStyle();
if (!TextUtils.isEmpty(style)) {
// stopgap for https://github.com/mapbox/mapbox-gl-native/issues/6242
if (TextUtils.isEmpty(nativeMapView.getAccessToken())) {
nativeMapView.setAccessToken(Mapbox.getAccessToken());
}
setStyleUrl(style);
}
}

View File

@@ -1,16 +1,11 @@
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 com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.storage.Resource;
import com.mapbox.mapboxsdk.storage.FileSource;
import java.io.File;
@@ -30,11 +25,13 @@ public class OfflineManager {
System.loadLibrary("mapbox-gl");
}
// Default database name
private static final String DATABASE_NAME = "mbgl-offline.db";
// Holds the pointer to JNI DefaultFileSource
private long mDefaultFileSourcePtr = 0;
// Native peer pointer
private long nativePtr;
// Reference to the file source to keep it alive for the
// lifetime of this object
private final FileSource fileSource;
// Makes sure callbacks come back to the main thread
private Handler handler;
@@ -82,94 +79,17 @@ public class OfflineManager {
void onError(String error);
}
/**
* This callback allows implementors to transform URLs before they are requested
* from the internet. This can be used add or remove custom parameters, or reroute
* certain requests to other servers or endpoints.
*/
public interface ResourceTransformCallback {
/**
* Called whenever a URL needs to be transformed.
*
* @param kind The kind of URL to be transformed.
* @param offlineRegions The original URL to be transformed.
* @return A URL that will now be downloaded.
*/
String onURL(@Resource.Kind int kind, String url);
}
/*
* Constructors
* Constructor
*/
private OfflineManager(Context context) {
// Get a pointer to the DefaultFileSource instance
String cachePath = getDatabasePath(context) + File.separator + DATABASE_NAME;
String apkPath = context.getPackageCodePath();
mDefaultFileSourcePtr = sharedDefaultFileSource(cachePath, apkPath);
setAccessToken(mDefaultFileSourcePtr, Mapbox.getAccessToken());
this.fileSource = FileSource.getInstance(context);
initialize(fileSource);
// Delete any existing previous ambient cache database
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 exception) {
Timber.e("Failed to read the package metadata: ", exception);
} catch (Exception exception) {
Timber.e("Failed to read the storage key: ", exception);
}
String databasePath = null;
if (setStorageExternal && isExternalStorageReadable()) {
try {
// Try getting the external storage path
databasePath = context.getExternalFilesDir(null).getAbsolutePath();
} catch (NullPointerException exception) {
Timber.e("Failed to obtain the external storage path: ", exception);
}
}
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 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
* (or WRITE_EXTERNAL_STORAGE) for API level < 18 in your app Manifest.
* <p>
* Code from https://developer.android.com/guide/topics/data/data-storage.html#filesExternal
* </p>
*
* @return true if external storage is readable
*/
public static boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
Timber.w("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() {
@@ -215,7 +135,8 @@ public class OfflineManager {
* @param callback the callback to be invoked
*/
public void listOfflineRegions(@NonNull final ListOfflineRegionsCallback callback) {
listOfflineRegions(mDefaultFileSourcePtr, new ListOfflineRegionsCallback() {
listOfflineRegions(fileSource, new ListOfflineRegionsCallback() {
@Override
public void onList(final OfflineRegion[] offlineRegions) {
getHandler().post(new Runnable() {
@@ -259,7 +180,8 @@ public class OfflineManager {
@NonNull byte[] metadata,
@NonNull final CreateOfflineRegionCallback callback) {
createOfflineRegion(mDefaultFileSourcePtr, definition, metadata, new CreateOfflineRegionCallback() {
createOfflineRegion(fileSource, definition, metadata, new CreateOfflineRegionCallback() {
@Override
public void onCreate(final OfflineRegion offlineRegion) {
getHandler().post(new Runnable() {
@@ -282,48 +204,20 @@ public class OfflineManager {
});
}
/**
* Sets a callback for transforming URLs requested from the internet
* <p>
* The callback will be executed on the main thread once for every requested URL.
* </p>
*
* @param callback the callback to be invoked
*/
public void setResourceTransform(@NonNull final ResourceTransformCallback callback) {
setResourceTransform(mDefaultFileSourcePtr, callback);
}
/*
* Changing or bypassing this limit without permission from Mapbox is prohibited
* by the Mapbox Terms of Service.
*/
public void setOfflineMapboxTileCountLimit(long limit) {
setOfflineMapboxTileCountLimit(mDefaultFileSourcePtr, limit);
}
public native void setOfflineMapboxTileCountLimit(long limit);
private native void initialize(FileSource fileSource);
/*
* Native methods
*/
private native long sharedDefaultFileSource(
String cachePath, String assetRoot);
@Override
protected native void finalize() throws Throwable;
private native void setAccessToken(long defaultFileSourcePtr, String accessToken);
private native void listOfflineRegions(FileSource fileSource, ListOfflineRegionsCallback callback);
private native String getAccessToken(long defaultFileSourcePtr);
private native void listOfflineRegions(
long defaultFileSourcePtr, ListOfflineRegionsCallback callback);
private native void createOfflineRegion(
long defaultFileSourcePtr, OfflineRegionDefinition definition,
byte[] metadata, CreateOfflineRegionCallback callback);
private native void setResourceTransform(
long defaultFileSourcePtr, ResourceTransformCallback callback);
private native void setOfflineMapboxTileCountLimit(
long defaultFileSourcePtr, long limit);
private native void createOfflineRegion(FileSource fileSource, OfflineRegionDefinition definition,
byte[] metadata, CreateOfflineRegionCallback callback);
}

View File

@@ -5,7 +5,7 @@ import android.os.Looper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import timber.log.Timber;
import com.mapbox.mapboxsdk.storage.FileSource;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -25,22 +25,25 @@ public class OfflineRegion {
System.loadLibrary("mapbox-gl");
}
// Parent OfflineManager
private OfflineManager offlineManager;
// Members
private long mId = 0;
private OfflineRegionDefinition mDefinition = null;
// Holds the pointer to JNI OfflineRegion
private long nativePtr;
// Holds a reference to the FileSource to keep it alive
private FileSource fileSource;
//Region id
private long id;
private OfflineRegionDefinition definition;
/**
* Arbitrary binary region metadata. The contents are opaque to the SDK implementation;
* it just stores and retrieves a byte[]. Check the `OfflineActivity` in the TestApp
* for a sample implementation that uses JSON to store an offline region name.
*/
private byte[] mMetadata = null;
// Holds the pointer to JNI OfflineRegion
private long mOfflineRegionPtr = 0;
private byte[] metadata;
// Makes sure callbacks come back to the main thread
private Handler handler;
@@ -197,19 +200,22 @@ public class OfflineRegion {
if (state == STATE_ACTIVE) {
return true;
}
if (isDeliveringInactiveMessages()) {
return true;
}
return false;
return isDeliveringInactiveMessages();
}
/*
/**
* Constructor
*
* For JNI use only, to create a new offline region, use
* {@link OfflineManager#createOfflineRegion} instead.
*/
private OfflineRegion() {
// For JNI use only, to create a new offline region, use
// OfflineManager.createOfflineRegion() instead.
private OfflineRegion(long offlineRegionPtr, FileSource fileSource, long id,
OfflineRegionDefinition definition, byte[] metadata) {
this.fileSource = fileSource;
this.id = id;
this.definition = definition;
this.metadata = metadata;
initialize(offlineRegionPtr, fileSource);
}
/*
@@ -217,15 +223,15 @@ public class OfflineRegion {
*/
public long getID() {
return mId;
return id;
}
public OfflineRegionDefinition getDefinition() {
return mDefinition;
return definition;
}
public byte[] getMetadata() {
return mMetadata;
return metadata;
}
private Handler getHandler() {
@@ -383,7 +389,7 @@ public class OfflineRegion {
getHandler().post(new Runnable() {
@Override
public void run() {
mMetadata = metadata;
OfflineRegion.this.metadata = metadata;
callback.onUpdate(metadata);
}
});
@@ -401,33 +407,18 @@ public class OfflineRegion {
});
}
private native void initialize(long offlineRegionPtr, FileSource fileSource);
@Override
protected void finalize() {
try {
super.finalize();
destroyOfflineRegion();
} catch (Throwable throwable) {
Timber.e("Failed to finalize OfflineRegion: " + throwable.getMessage());
}
}
protected native void finalize();
/*
* Native methods
*/
private native void setOfflineRegionObserver(OfflineRegionObserver callback);
private native void destroyOfflineRegion();
private native void setOfflineRegionDownloadState(@DownloadState int offlineRegionDownloadState);
private native void setOfflineRegionObserver(
OfflineRegionObserver observerCallback);
private native void getOfflineRegionStatus(OfflineRegionStatusCallback callback);
private native void setOfflineRegionDownloadState(
@DownloadState int offlineRegionDownloadState);
private native void getOfflineRegionStatus(
OfflineRegionStatusCallback statusCallback);
private native void deleteOfflineRegion(
OfflineRegionDeleteCallback deleteCallback);
private native void deleteOfflineRegion(OfflineRegionDeleteCallback callback);
private native void updateOfflineRegionMetadata(byte[] metadata, OfflineRegionUpdateMetadataCallback callback);

View File

@@ -25,25 +25,23 @@ public class OfflineRegionError {
public static final String REASON_OTHER = "REASON_OTHER";
@ErrorReason
private String reason;
private final String reason;
/**
* /* An error message from the request handler, e.g. a server message or a system message
* /* informing the user about the reason for the failure.
*/
private String message;
private final String message;
/*
* Constructors
*/
// Constructors
private OfflineRegionError() {
private OfflineRegionError(String reason, String message) {
// For JNI use only
this.reason = reason;
this.message = message;
}
/*
* Getters
*/
// Getters
@ErrorReason
public String getReason() {

View File

@@ -56,24 +56,29 @@ public class OfflineRegionStatus {
/*
* Use setObserver(OfflineRegionObserver observer) to obtain a OfflineRegionStatus object.
*
* For JNI use only
*/
private OfflineRegionStatus() {
// For JNI use only
private OfflineRegionStatus(int downloadState, long completedResourceCount,
long completedResourceSize, long completedTileCount,
long completedTileSize, long requiredResourceCount,
boolean requiredResourceCountIsPrecise) {
this.downloadState = downloadState;
this.completedResourceCount = completedResourceCount;
this.completedResourceSize = completedResourceSize;
this.completedTileCount = completedTileCount;
this.completedTileSize = completedTileSize;
this.requiredResourceCount = requiredResourceCount;
this.requiredResourceCountIsPrecise = requiredResourceCountIsPrecise;
}
/*
/**
* Is the region complete?
*/
public boolean isComplete() {
return (completedResourceCount == requiredResourceCount);
}
/*
* Getters
*/
@OfflineRegion.DownloadState
public int getDownloadState() {
return downloadState;

View File

@@ -21,16 +21,18 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
private double maxZoom;
private float pixelRatio;
/*
* Constructors
/**
* Constructor
*
* @param styleURL the style
* @param bounds the bounds
* @param minZoom min zoom
* @param maxZoom max zoom
* @param pixelRatio pixel ratio of the device
*/
private OfflineTilePyramidRegionDefinition() {
// For JNI use only
}
public OfflineTilePyramidRegionDefinition(
String styleURL, LatLngBounds bounds, double minZoom, double maxZoom, float pixelRatio) {
// Note: Also used in JNI
this.styleURL = styleURL;
this.bounds = bounds;
this.minZoom = minZoom;

View File

@@ -0,0 +1,139 @@
package com.mapbox.mapboxsdk.storage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import java.lang.ref.WeakReference;
import timber.log.Timber;
/**
* Holds a central reference to the core's DefaultFileSource for as long as
* there are active mapviews / offline managers
*/
public class FileSource {
/**
* This callback allows implementors to transform URLs before they are requested
* from the internet. This can be used add or remove custom parameters, or reroute
* certain requests to other servers or endpoints.
*/
public interface ResourceTransformCallback {
/**
* Called whenever a URL needs to be transformed.
*
* @param kind The kind of URL to be transformed.
* @return A URL that will now be downloaded.
*/
String onURL(@Resource.Kind int kind, String url);
}
// Use weak reference to avoid blocking GC on this reference.
// Should only block when mapview / Offline manager instances
// are alive
private static WeakReference<FileSource> INSTANCE;
public static synchronized FileSource getInstance(Context context) {
if (INSTANCE == null || INSTANCE.get() == null) {
String cachePath = getCachePath(context);
String apkPath = context.getPackageCodePath();
INSTANCE = new WeakReference<>(new FileSource(cachePath, apkPath));
}
return INSTANCE.get();
}
public static String getCachePath(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 exception) {
Timber.e("Failed to read the package metadata: ", exception);
} catch (Exception exception) {
Timber.e("Failed to read the storage key: ", exception);
}
String cachePath = null;
if (setStorageExternal && isExternalStorageReadable()) {
try {
// Try getting the external storage path
cachePath = context.getExternalFilesDir(null).getAbsolutePath();
} catch (NullPointerException exception) {
Timber.e("Failed to obtain the external storage path: ", exception);
}
}
if (cachePath == null) {
// Default to internal storage
cachePath = context.getFilesDir().getAbsolutePath();
}
return cachePath;
}
/**
* Checks if external storage is available to at least read. In order for this to work, make
* sure you include &lt;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /&gt;
* (or WRITE_EXTERNAL_STORAGE) for API level &lt; 18 in your app Manifest.
* <p>
* Code from https://developer.android.com/guide/topics/data/data-storage.html#filesExternal
* </p>
*
* @return true if external storage is readable
*/
public static boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
Timber.w("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 long nativePtr;
private FileSource(String cachePath, String apkPath) {
initialize(Mapbox.getAccessToken(), cachePath, apkPath);
}
public native void setAccessToken(@NonNull String accessToken);
public native String getAccessToken();
public native void setApiBaseUrl(String baseUrl);
/**
* Sets a callback for transforming URLs requested from the internet
* <p>
* The callback will be executed on the main thread once for every requested URL.
* </p>
*
* @param callback the callback to be invoked
*/
public native void setResourceTransform(@NonNull final ResourceTransformCallback callback);
private native void initialize(String accessToken, String cachePath, String apkPath);
@Override
protected native void finalize() throws Throwable;
}

View File

@@ -328,6 +328,17 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
android:name=".activity.offline.DeleteRegionActivity"
android:description="@string/description_offline_region_delete"
android:label="@string/activity_offline_region_delete">
<meta-data
android:name="@string/category"
android:value="@string/category_offline"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
android:name=".activity.imagegenerator.SnapshotActivity"
android:description="@string/description_snapshot"

View File

@@ -0,0 +1,168 @@
package com.mapbox.mapboxsdk.testapp.activity.offline;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
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 deleting an OfflineRegion.
*/
public class DeleteRegionActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
private OfflineRegionAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offline_region_delete);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter = new OfflineRegionAdapter(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("Delete region");
final TextView input = new TextView(this);
input.setText(metadata);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
delete(region);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
private void delete(OfflineRegion region) {
region.delete(new OfflineRegion.OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
Toast.makeText(
DeleteRegionActivity.this,
"Region deleted",
Toast.LENGTH_SHORT
).show();
loadOfflineRegions();
}
@Override
public void onError(String error) {
Toast.makeText(
DeleteRegionActivity.this,
"Region deletion 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(DeleteRegionActivity.this, "Error loading regions " + error, Toast.LENGTH_LONG).show();
}
});
}
private static class OfflineRegionAdapter extends BaseAdapter {
private Context context;
private List<OfflineRegion> offlineRegions;
OfflineRegionAdapter(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;
}
}
}

View File

@@ -260,6 +260,7 @@ public class OfflineActivity extends AppCompatActivity
if (status.isComplete()) {
// Download complete
endProgress("Region downloaded successfully.");
offlineRegion.setObserver(null);
return;
} else if (status.isRequiredResourceCountPrecise()) {
// Switch to determinate state
@@ -277,6 +278,7 @@ public class OfflineActivity extends AppCompatActivity
public void onError(OfflineRegionError error) {
Timber.e("onError reason: " + error.getReason());
Timber.e("onError message: " + error.getMessage());
offlineRegion.setObserver(null);
}
@Override

View File

@@ -0,0 +1,21 @@
<?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">
<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>

View File

@@ -33,6 +33,7 @@
<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_offline_region_delete">Delete region</string>
<string name="activity_minmax_zoom">Min/Max Zoom</string>
<string name="activity_viewpager">ViewPager</string>
<string name="activity_runtime_style">Runtime Style</string>
@@ -76,6 +77,7 @@
<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_offline_region_delete">Delete region 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>

View File

@@ -15,7 +15,7 @@ if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR
set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_SHARED_LINKER_FLAGS}")
endif()
mason_use(jni.hpp VERSION 2.0.0-1 HEADER_ONLY)
mason_use(jni.hpp VERSION 3.0.0 HEADER_ONLY)
mason_use(libzip VERSION 1.1.3)
mason_use(nunicode VERSION 1.7.1)
mason_use(sqlite VERSION 3.14.2)
@@ -49,8 +49,6 @@ macro(mbgl_platform_core)
PRIVATE platform/default/default_file_source.cpp
PRIVATE platform/default/local_file_source.cpp
PRIVATE platform/default/online_file_source.cpp
PRIVATE platform/android/src/default_file_source.cpp
PRIVATE platform/android/src/default_file_source.hpp
# Offline
# PRIVATE include/mbgl/storage/offline.hpp
@@ -137,6 +135,10 @@ macro(mbgl_platform_core)
platform/android/src/style/functions/interval_stops.cpp
platform/android/src/style/functions/interval_stops.hpp
# FileSource holder
platform/android/src/file_source.cpp
platform/android/src/file_source.hpp
# Connectivity
platform/android/src/connectivity_listener.cpp
platform/android/src/connectivity_listener.hpp
@@ -145,6 +147,46 @@ macro(mbgl_platform_core)
platform/android/src/native_map_view.cpp
platform/android/src/native_map_view.hpp
# Java core classes
platform/android/src/java/util.cpp
platform/android/src/java/util.hpp
# Graphics
platform/android/src/graphics/pointf.cpp
platform/android/src/graphics/pointf.hpp
platform/android/src/graphics/rectf.cpp
platform/android/src/graphics/rectf.hpp
# Geometry
platform/android/src/geometry/feature.cpp
platform/android/src/geometry/feature.hpp
platform/android/src/geometry/lat_lng.cpp
platform/android/src/geometry/lat_lng.hpp
platform/android/src/geometry/lat_lng_bounds.cpp
platform/android/src/geometry/lat_lng_bounds.hpp
platform/android/src/geometry/projected_meters.cpp
platform/android/src/geometry/projected_meters.hpp
# Annotation
platform/android/src/annotation/marker.cpp
platform/android/src/annotation/marker.hpp
platform/android/src/annotation/polygon.cpp
platform/android/src/annotation/polygon.hpp
platform/android/src/annotation/polyline.cpp
platform/android/src/annotation/polyline.hpp
# Offline
platform/android/src/offline/offline_manager.cpp
platform/android/src/offline/offline_manager.hpp
platform/android/src/offline/offline_region.cpp
platform/android/src/offline/offline_region.hpp
platform/android/src/offline/offline_region_definition.cpp
platform/android/src/offline/offline_region_definition.hpp
platform/android/src/offline/offline_region_error.cpp
platform/android/src/offline/offline_region_error.hpp
platform/android/src/offline/offline_region_status.cpp
platform/android/src/offline/offline_region_status.hpp
# Main jni bindings
platform/android/src/attach_env.cpp
platform/android/src/attach_env.hpp

View File

@@ -0,0 +1,31 @@
#include "marker.hpp"
namespace mbgl {
namespace android {
jni::Class<Marker> Marker::javaClass;
mbgl::Point<double> Marker::getPosition(jni::JNIEnv& env, jni::Object<Marker> marker) {
static auto positionField = Marker::javaClass.GetField<jni::Object<LatLng>>(env, "position");
auto jPosition = marker.Get(env, positionField);
auto position = LatLng::getGeometry(env, jPosition);
jni::DeleteLocalRef(env, jPosition);
return position;
}
std::string Marker::getIconId(jni::JNIEnv& env, jni::Object<Marker> marker) {
static auto iconIdField = Marker::javaClass.GetField<jni::String>(env, "iconId");
auto jIconId = marker.Get(env, iconIdField);
auto iconId = jni::Make<std::string>(env, jIconId);
jni::DeleteLocalRef(env, jIconId);
return iconId;
}
void Marker::registerNative(jni::JNIEnv& env) {
// Lookup the class
Marker::javaClass = *jni::Class<Marker>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,30 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include <string>
#include "../geometry/lat_lng.hpp"
namespace mbgl {
namespace android {
class Marker : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Marker"; };
static jni::Class<Marker> javaClass;
static mbgl::Point<double> getPosition(jni::JNIEnv&, jni::Object<Marker>);
static std::string getIconId(jni::JNIEnv&, jni::Object<Marker>);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,42 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include "../geometry/lat_lng.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
class MultiPoint : protected mbgl::util::noncopyable {
protected:
template <class Geometry>
static Geometry toGeometry(JNIEnv& env, jni::Object<java::util::List> pointsList) {
NullCheck(env, &pointsList);
auto jarray = java::util::List::toArray<LatLng>(env, pointsList);
NullCheck(env, &jarray);
std::size_t size = jarray.Length(env);
Geometry geometry;
geometry.reserve(size);
for (std::size_t i = 0; i < size; i++) {
auto latLng = jarray.Get(env, i);
NullCheck(env, &latLng);
geometry.push_back(LatLng::getGeometry(env, latLng));
jni::DeleteLocalRef(env, latLng);
}
jni::DeleteLocalRef(env, jarray);
return geometry;
}
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,49 @@
#include "polygon.hpp"
#include "../conversion/color.hpp"
namespace mbgl {
namespace android {
jni::Class<Polygon> Polygon::javaClass;
mbgl::FillAnnotation Polygon::toAnnotation(jni::JNIEnv& env, jni::Object<Polygon> polygon) {
auto points = Polygon::getPoints(env, polygon);
mbgl::FillAnnotation annotation { mbgl::Polygon<double> { MultiPoint::toGeometry<mbgl::LinearRing<double>>(env, points) } };
annotation.opacity = { Polygon::getOpacity(env, polygon) };
annotation.color = { Polygon::getFillColor(env, polygon) };
annotation.outlineColor = { Polygon::getOutlineColor(env, polygon) };
jni::DeleteLocalRef(env, points);
return annotation;
}
jni::Object<java::util::List> Polygon::getPoints(jni::JNIEnv& env, jni::Object<Polygon> polygon) {
static auto field = Polygon::javaClass.GetField<jni::Object<java::util::List>>(env, "points");
return polygon.Get(env, field);
}
float Polygon::getOpacity(jni::JNIEnv& env, jni::Object<Polygon> polygon) {
static auto field = Polygon::javaClass.GetField<float>(env, "alpha");
return polygon.Get(env, field);
}
mbgl::Color Polygon::getFillColor(jni::JNIEnv& env, jni::Object<Polygon> polygon) {
static auto field = Polygon::javaClass.GetField<int>(env, "fillColor");
return *conversion::convert<mbgl::Color, int>(env, polygon.Get(env, field));
}
mbgl::Color Polygon::getOutlineColor(jni::JNIEnv& env, jni::Object<Polygon> polygon) {
static auto field = Polygon::javaClass.GetField<int>(env, "strokeColor");
return *conversion::convert<mbgl::Color, int>(env, polygon.Get(env, field));
}
void Polygon::registerNative(jni::JNIEnv& env) {
Polygon::javaClass = *jni::Class<Polygon>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,40 @@
#pragma once
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/color.hpp>
#include <string>
#include "multi_point.hpp"
#include "../geometry/lat_lng.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
class Polygon : private MultiPoint {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polygon"; };
static jni::Class<Polygon> javaClass;
static mbgl::FillAnnotation toAnnotation(jni::JNIEnv&, jni::Object<Polygon>);
static void registerNative(jni::JNIEnv&);
private:
static jni::Object<java::util::List> getPoints(jni::JNIEnv&, jni::Object<Polygon>);
static float getOpacity(jni::JNIEnv&, jni::Object<Polygon>);
static mbgl::Color getFillColor(jni::JNIEnv&, jni::Object<Polygon>);
static mbgl::Color getOutlineColor(jni::JNIEnv&, jni::Object<Polygon>);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,49 @@
#include "polyline.hpp"
#include "../conversion/color.hpp"
namespace mbgl {
namespace android {
jni::Class<Polyline> Polyline::javaClass;
mbgl::LineAnnotation Polyline::toAnnotation(jni::JNIEnv& env, jni::Object<Polyline> polyline) {
auto points = Polyline::getPoints(env, polyline);
mbgl::LineAnnotation annotation { MultiPoint::toGeometry<mbgl::LineString<double>>(env, points) };
annotation.opacity = { Polyline::getOpacity(env, polyline) };
annotation.color = { Polyline::getColor(env, polyline) };
annotation.width = { Polyline::getWidth(env, polyline) };
jni::DeleteLocalRef(env, points);
return annotation;
}
jni::Object<java::util::List> Polyline::getPoints(jni::JNIEnv& env, jni::Object<Polyline> polyline) {
static auto field = Polyline::javaClass.GetField<jni::Object<java::util::List>>(env, "points");
return polyline.Get(env, field);
}
float Polyline::getOpacity(jni::JNIEnv& env, jni::Object<Polyline> polyline) {
static auto field = Polyline::javaClass.GetField<float>(env, "alpha");
return polyline.Get(env, field);
}
mbgl::Color Polyline::getColor(jni::JNIEnv& env, jni::Object<Polyline> polyline) {
static auto field = Polyline::javaClass.GetField<int>(env, "color");
return *conversion::convert<mbgl::Color, int>(env, polyline.Get(env, field));
}
float Polyline::getWidth(jni::JNIEnv& env, jni::Object<Polyline> polyline) {
static auto field = Polyline::javaClass.GetField<float>(env, "width");
return polyline.Get(env, field);
}
void Polyline::registerNative(jni::JNIEnv& env) {
Polyline::javaClass = *jni::Class<Polyline>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,40 @@
#pragma once
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/color.hpp>
#include <string>
#include "multi_point.hpp"
#include "../geometry/lat_lng.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
class Polyline : private MultiPoint {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polyline"; };
static jni::Class<Polyline> javaClass;
static mbgl::LineAnnotation toAnnotation(jni::JNIEnv&, jni::Object<Polyline>);
static void registerNative(jni::JNIEnv&);
private:
static jni::Object<java::util::List> getPoints(jni::JNIEnv&, jni::Object<Polyline>);
static float getOpacity(jni::JNIEnv&, jni::Object<Polyline>);
static mbgl::Color getColor(jni::JNIEnv&, jni::Object<Polyline>);
static float getWidth(jni::JNIEnv&, jni::Object<Polyline>);
};
} // namespace android
} // namespace mbgl

View File

@@ -38,6 +38,19 @@ inline std::vector<std::string> toVector(JNIEnv& env, jni::jarray<jni::jobject>&
return vector;
}
inline std::vector<std::string> toVector(JNIEnv& env, jni::Array<jni::String> array) {
std::vector<std::string> vector;
std::size_t len = array.Length(env);
for (std::size_t i = 0; i < len; i++) {
jni::String jstr = array.Get(env, i);
vector.push_back(*convert<std::string, jni::String>(env, jstr));
jni::DeleteLocalRef(env, jstr);
}
return vector;
}
}
}
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "conversion.hpp"
#include <mbgl/util/color.hpp>
namespace mbgl {
namespace android {
namespace conversion {
template <>
struct Converter<mbgl::Color, int> {
Result<mbgl::Color> operator()(jni::JNIEnv&, const int& color) const {
float r = (color >> 16) & 0xFF;
float g = (color >> 8) & 0xFF;
float b = (color) & 0xFF;
float a = (color >> 24) & 0xFF;
return { mbgl::Color( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ) };
}
};
} // namespace conversion
} // namespace style
} // namespace mbgl

View File

@@ -1,20 +0,0 @@
#include "default_file_source.hpp"
#include <mbgl/util/logging.hpp>
#include <cassert>
namespace mbgl {
namespace android {
DefaultFileSource& defaultFileSource(const std::string& cachePath_, const std::string& assetRoot_) {
static auto cachePath = cachePath_;
assert(cachePath == cachePath_);
static auto assetRoot = assetRoot_;
assert(assetRoot == assetRoot_);
static DefaultFileSource defaultFileSource{ cachePath, assetRoot };
return defaultFileSource;
}
} // namespace android
} // namespace mbgl

View File

@@ -1,12 +0,0 @@
#pragma once
#include <mbgl/storage/default_file_source.hpp>
namespace mbgl {
namespace android {
DefaultFileSource& defaultFileSource(const std::string& cachePath = ":memory:",
const std::string& assetRoot = ".");
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,106 @@
#include "file_source.hpp"
#include <mbgl/util/logging.hpp>
#include <string>
#include "jni/generic_global_ref_deleter.hpp"
namespace mbgl {
namespace android {
// FileSource //
FileSource::FileSource(jni::JNIEnv& _env, jni::String accessToken, jni::String _cachePath, jni::String _apkPath) {
// Create a core default file source
fileSource = std::make_unique<mbgl::DefaultFileSource>(
jni::Make<std::string>(_env, _cachePath) + "/mbgl-offline.db",
jni::Make<std::string>(_env, _apkPath));
// Set access token
fileSource->setAccessToken(jni::Make<std::string>(_env, accessToken));
}
FileSource::~FileSource() {
}
jni::String FileSource::getAccessToken(jni::JNIEnv& env) {
return jni::Make<jni::String>(env, fileSource->getAccessToken());
}
void FileSource::setAccessToken(jni::JNIEnv& env, jni::String token) {
fileSource->setAccessToken(jni::Make<std::string>(env, token));
}
void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) {
fileSource->setAPIBaseURL(jni::Make<std::string>(env, url));
}
void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) {
if (transformCallback) {
// Launch transformCallback
fileSource->setResourceTransform([
// Capture the ResourceTransformCallback object as a managed global into
// the lambda. It is released automatically when we're setting a new ResourceTransform in
// a subsequent call.
// Note: we're converting it to shared_ptr because this lambda is converted to a std::function,
// which requires copyability of its captured variables.
callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter()),
env
](mbgl::Resource::Kind kind, std::string&& url_) {
return FileSource::ResourceTransformCallback::onURL(const_cast<jni::JNIEnv&>(env), jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_);
});
} else {
// Reset the callback
fileSource->setResourceTransform(nullptr);
}
}
jni::Class<FileSource> FileSource::javaClass;
FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) {
static auto field = FileSource::javaClass.GetField<jlong>(env, "nativePtr");
return reinterpret_cast<FileSource *>(jFileSource.Get(env, field));
}
mbgl::DefaultFileSource& FileSource::getDefaultFileSource(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) {
FileSource* fileSource = FileSource::getNativePeer(env, jFileSource);
assert(fileSource != nullptr);
return *fileSource->fileSource;
}
void FileSource::registerNative(jni::JNIEnv& env) {
//Register classes
FileSource::javaClass = *jni::Class<FileSource>::Find(env).NewGlobalRef(env).release();
FileSource::ResourceTransformCallback::javaClass = *jni::Class<FileSource::ResourceTransformCallback>::Find(env).NewGlobalRef(env).release();
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<FileSource>(
env, FileSource::javaClass, "nativePtr",
std::make_unique<FileSource, JNIEnv&, jni::String, jni::String, jni::String>,
"initialize",
"finalize",
METHOD(&FileSource::getAccessToken, "getAccessToken"),
METHOD(&FileSource::setAccessToken, "setAccessToken"),
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
METHOD(&FileSource::setResourceTransform, "setResourceTransform")
);
}
// FileSource::ResourceTransformCallback //
jni::Class<FileSource::ResourceTransformCallback> FileSource::ResourceTransformCallback::javaClass;
std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> callback, int kind, std::string url_) {
static auto method = FileSource::ResourceTransformCallback::javaClass.GetMethod<jni::String (jni::jint, jni::String)>(env, "onURL");
auto url = jni::Make<jni::String>(env, url_);
url = callback.Call(env, method, kind, url);
return jni::Make<std::string>(env, url);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,53 @@
#pragma once
#include <mbgl/storage/default_file_source.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
/**
* Peer class for the Android FileSource holder. Ensures that a single DefaultFileSource is used
*/
class FileSource {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource"; };
struct ResourceTransformCallback {
static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource$ResourceTransformCallback"; }
static std::string onURL(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>, int, std::string);
static jni::Class<ResourceTransformCallback> javaClass;
};
FileSource(jni::JNIEnv&, jni::String, jni::String, jni::String);
~FileSource();
jni::String getAccessToken(jni::JNIEnv&);
void setAccessToken(jni::JNIEnv&, jni::String);
void setAPIBaseUrl(jni::JNIEnv&, jni::String);
void setResourceTransform(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>);
static jni::Class<FileSource> javaClass;
static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>);
static mbgl::DefaultFileSource& getDefaultFileSource(jni::JNIEnv&, jni::Object<FileSource>);
static void registerNative(jni::JNIEnv&);
private:
std::unique_ptr<mbgl::DefaultFileSource> fileSource;
};
} // namespace android
} // namespace mbgl

View File

@@ -3,6 +3,7 @@
#include "../../conversion/constant.hpp"
#include "../../conversion/conversion.hpp"
#include "geometry.hpp"
#include "../../gson/json_object.hpp"
#include <mbgl/util/feature.hpp>
#include <mapbox/variant.hpp>
@@ -10,6 +11,7 @@
#include <jni/jni.hpp>
#include "../../jni/local_object.hpp"
#include "../feature.hpp"
#include <string>
#include <array>
@@ -168,39 +170,45 @@ struct Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>> {
template <>
struct Converter<jni::jobject*, mbgl::Feature> {
Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const {
static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release();
static jni::jmethodID* fromGeometry = &jni::GetStaticMethodID(env, *javaClass, "fromGeometry", "(Lcom/mapbox/services/commons/geojson/Geometry;Lcom/google/gson/JsonObject;Ljava/lang/String;)Lcom/mapbox/services/commons/geojson/Feature;");
struct Converter<jni::Object<Feature>, mbgl::Feature> {
Result<jni::Object<Feature>> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const {
// Convert Id
FeatureIdVisitor idEvaluator;
std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : "";
jni::LocalObject<jni::jobject> jid = jni::NewLocalObject(env, *convert<jni::jobject*>(env, id));
auto jid = jni::Make<jni::String>(env, id);
// Convert properties
jni::LocalObject<jni::jobject> properties = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.properties));
auto properties = jni::Object<JsonObject>(*convert<jni::jobject*>(env, value.properties));
// Convert geometry
jni::LocalObject<jni::jobject> geometry = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.geometry));
auto geometry = jni::Object<Geometry>(*convert<jni::jobject*>(env, value.geometry));
// Create feature
return {reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometry, geometry.get(), properties.get(), jid.get()))};
auto feature = Feature::fromGeometry(env, geometry, properties, jid);
//Cleanup
jni::DeleteLocalRef(env, jid);
jni::DeleteLocalRef(env, geometry);
jni::DeleteLocalRef(env, properties);
return feature;
}
};
template <>
struct Converter<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>> {
Result<jni::jarray<jni::jobject>*> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const {
static jni::jclass* featureClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release();
jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *featureClass);
struct Converter<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>> {
Result<jni::Array<jni::Object<Feature>>> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const {
auto features = jni::Array<jni::Object<Feature>>::New(env, value.size(), Feature::javaClass);
for(size_t i = 0; i < value.size(); i = i + 1) {
jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, mbgl::Feature>(env, value.at(i)));
jni::SetObjectArrayElement(env, jarray, i, converted.get());
auto converted = *convert<jni::Object<Feature>, mbgl::Feature>(env, value.at(i));
features.Set(env, i, converted);
jni::DeleteLocalRef(env, converted);
}
return {&jarray};
return {features};
}
};

View File

@@ -0,0 +1,20 @@
#include "feature.hpp"
namespace mbgl {
namespace android {
jni::Object<Feature> Feature::fromGeometry(jni::JNIEnv& env, jni::Object<Geometry> geometry, jni::Object<JsonObject> properties, jni::String id) {
static auto method = Feature::javaClass.GetStaticMethod<jni::Object<Feature> (jni::Object<Geometry>, jni::Object<JsonObject>, jni::String)>(env, "fromGeometry");
return Feature::javaClass.Call(env, method, geometry, properties, id);
}
void Feature::registerNative(jni::JNIEnv& env) {
// Lookup the class
Feature::javaClass = *jni::Class<Feature>::Find(env).NewGlobalRef(env).release();
}
jni::Class<Feature> Feature::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
#include "geometry.hpp"
#include "../gson/json_object.hpp"
namespace mbgl {
namespace android {
class Feature : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; };
static jni::Object<Feature> fromGeometry(jni::JNIEnv&, jni::Object<Geometry>, jni::Object<JsonObject>, jni::String);
static jni::Class<Feature> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
namespace mbgl {
namespace android {
class Geometry : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; };
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#include "lat_lng.hpp"
namespace mbgl {
namespace android {
jni::Object<LatLng> LatLng::New(jni::JNIEnv& env, double latitude, double longitude) {
static auto constructor = LatLng::javaClass.GetConstructor<double, double>(env);
return LatLng::javaClass.New(env, constructor, latitude, longitude);
}
mbgl::Point<double> LatLng::getGeometry(jni::JNIEnv& env, jni::Object<LatLng> latLng) {
static auto latitudeField = LatLng::javaClass.GetField<jni::jdouble>(env, "latitude");
static auto longitudeField = LatLng::javaClass.GetField<jni::jdouble>(env, "longitude");
return mbgl::Point<double>(latLng.Get(env, longitudeField), latLng.Get(env, latitudeField));
}
mbgl::LatLng LatLng::getLatLng(jni::JNIEnv& env, jni::Object<LatLng> latLng) {
auto point = LatLng::getGeometry(env, latLng);
return mbgl::LatLng(point.y, point.x);
}
void LatLng::registerNative(jni::JNIEnv& env) {
// Lookup the class
LatLng::javaClass = *jni::Class<LatLng>::Find(env).NewGlobalRef(env).release();
}
jni::Class<LatLng> LatLng::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class LatLng : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLng"; };
static jni::Object<LatLng> New(jni::JNIEnv&, double, double);
static mbgl::Point<double> getGeometry(jni::JNIEnv&, jni::Object<LatLng>);
static mbgl::LatLng getLatLng(jni::JNIEnv&, jni::Object<LatLng>);
static jni::Class<LatLng> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#include "lat_lng_bounds.hpp"
namespace mbgl {
namespace android {
jni::Object<LatLngBounds> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds bounds) {
static auto constructor = LatLngBounds::javaClass.GetConstructor<double, double, double, double>(env);
return LatLngBounds::javaClass.New(env, constructor, bounds.north(), bounds.east(), bounds.south(), bounds.west());
}
mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> bounds) {
static auto swLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLatSouth");
static auto swLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonWest");
static auto neLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLatNorth");
static auto neLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonEast");
return mbgl::LatLngBounds::hull(
{ bounds.Get(env, swLat), bounds.Get(env, swLon) },
{ bounds.Get(env, neLat), bounds.Get(env, neLon) }
);
}
void LatLngBounds::registerNative(jni::JNIEnv& env) {
// Lookup the class
LatLngBounds::javaClass = *jni::Class<LatLngBounds>::Find(env).NewGlobalRef(env).release();
}
jni::Class<LatLngBounds> LatLngBounds::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class LatLngBounds : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngBounds"; };
static jni::Object<LatLngBounds> New(jni::JNIEnv&, mbgl::LatLngBounds);
static mbgl::LatLngBounds getLatLngBounds(jni::JNIEnv&, jni::Object<LatLngBounds>);
static jni::Class<LatLngBounds> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#include "projected_meters.hpp"
namespace mbgl {
namespace android {
jni::Object<ProjectedMeters> ProjectedMeters::New(jni::JNIEnv& env, double northing, double easting) {
static auto constructor = ProjectedMeters::javaClass.GetConstructor<double, double>(env);
return ProjectedMeters::javaClass.New(env, constructor, northing, easting);
}
void ProjectedMeters::registerNative(jni::JNIEnv& env) {
// Lookup the class
ProjectedMeters::javaClass = *jni::Class<ProjectedMeters>::Find(env).NewGlobalRef(env).release();
}
jni::Class<ProjectedMeters> ProjectedMeters::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class ProjectedMeters : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/ProjectedMeters"; };
static jni::Object<ProjectedMeters> New(jni::JNIEnv&, double, double);
static jni::Class<ProjectedMeters> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#include "pointf.hpp"
namespace mbgl {
namespace android {
jni::Object<PointF> PointF::New(jni::JNIEnv& env, float x, float y) {
static auto constructor = PointF::javaClass.GetConstructor<float, float>(env);
return PointF::javaClass.New(env, constructor, x, y);
}
void PointF::registerNative(jni::JNIEnv& env) {
// Lookup the class
PointF::javaClass = *jni::Class<PointF>::Find(env).NewGlobalRef(env).release();
}
jni::Class<PointF> PointF::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,25 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class PointF : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "android/graphics/PointF"; };
static jni::Object<PointF> New(jni::JNIEnv&, float, float);
static jni::Class<PointF> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,35 @@
#include "rectf.hpp"
namespace mbgl {
namespace android {
float RectF::getLeft(jni::JNIEnv& env, jni::Object<RectF> rectf) {
static auto field = RectF::javaClass.GetField<float>(env, "left");
return rectf.Get(env, field);
}
float RectF::getTop(jni::JNIEnv& env, jni::Object<RectF> rectf) {
static auto field = RectF::javaClass.GetField<float>(env, "top");
return rectf.Get(env, field);
}
float RectF::getRight(jni::JNIEnv& env, jni::Object<RectF> rectf) {
static auto field = RectF::javaClass.GetField<float>(env, "right");
return rectf.Get(env, field);
}
float RectF::getBottom(jni::JNIEnv& env, jni::Object<RectF> rectf) {
static auto field = RectF::javaClass.GetField<float>(env, "bottom");
return rectf.Get(env, field);
}
void RectF::registerNative(jni::JNIEnv& env) {
// Lookup the class
RectF::javaClass = *jni::Class<RectF>::Find(env).NewGlobalRef(env).release();
}
jni::Class<RectF> RectF::javaClass;
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class RectF : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "android/graphics/RectF"; };
static float getLeft(jni::JNIEnv&, jni::Object<RectF>);
static float getTop(jni::JNIEnv&, jni::Object<RectF>);
static float getRight(jni::JNIEnv&, jni::Object<RectF>);
static float getBottom(jni::JNIEnv&, jni::Object<RectF>);
static jni::Class<RectF> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
namespace mbgl {
namespace android {
class JsonObject : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/google/gson/JsonObject"; };
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,18 @@
#include "util.hpp"
namespace mbgl {
namespace android {
namespace java {
namespace util {
jni::Class<List> List::javaClass;
void registerNative(jni::JNIEnv& env) {
List::javaClass = *jni::Class<List>::Find(env).NewGlobalRef(env).release();
}
} // namespace util
} // namespace java
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace java {
namespace util {
class List : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/List"; };
template<class T>
static jni::Array<jni::Object<T>> toArray(jni::JNIEnv& env, jni::Object<List> list) {
static auto toArray = List::javaClass.GetMethod<jni::Array<jni::Object<>> ()>(env, "toArray");
return (jni::Array<jni::Object<T>>) list.Call(env, toArray);
};
static jni::Class<List> javaClass;
};
void registerNative(jni::JNIEnv&);
} // namespace util
} // namespace java
} // namespace android
} // namespace mbgl

File diff suppressed because it is too large Load Diff

View File

@@ -14,15 +14,9 @@ extern JavaVM* theJVM;
extern std::string cachePath;
extern std::string dataPath;
extern std::string apkPath;
extern std::string androidRelease;
extern jmethodID onInvalidateId;
extern jmethodID onMapChangedId;
extern jmethodID onFpsChangedId;
extern jmethodID onSnapshotReadyId;
extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
extern void registerNatives(JavaVM* vm);

View File

@@ -0,0 +1,22 @@
#pragma once
#include <jni/jni.hpp>
#include "../attach_env.hpp"
namespace mbgl {
namespace android {
// A deleter that doesn't retain an JNIEnv handle but instead tries to attach the JVM. This means
// it can be used on any thread to delete a global ref.
struct GenericGlobalRefDeleter {
void operator()(jni::jobject* p) const {
if (p) {
auto env = AttachEnv();
env->DeleteGlobalRef(jni::Unwrap(p));
}
}
};
} // namespace android
} // namespace mbgl

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,265 @@
#pragma once
#include <mbgl/map/backend.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/map/backend.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include "file_source.hpp"
#include "annotation/marker.hpp"
#include "annotation/polygon.hpp"
#include "annotation/polyline.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
#include "geometry/feature.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/projected_meters.hpp"
#include "style/layers/layers.hpp"
#include "style/sources/sources.hpp"
#include <string>
#include <jni.h>
#include <android/native_window.h>
#include <EGL/egl.h>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class NativeMapView : public mbgl::View, public mbgl::Backend {
class NativeMapView : public View, public Backend {
public:
NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory);
static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; };
static jni::Class<NativeMapView> javaClass;
static void registerNative(jni::JNIEnv&);
NativeMapView(jni::JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jlong);
virtual ~NativeMapView();
mbgl::Size getFramebufferSize() const;
void updateViewBinding();
// mbgl::View //
void bind() override;
void invalidate() override;
// mbgl::Backend //
void invalidate() override;
void notifyMapChange(mbgl::MapChange) override;
mbgl::Map &getMap();
mbgl::DefaultFileSource &getFileSource();
// JNI //
void initializeDisplay();
void terminateDisplay();
void destroy(jni::JNIEnv&);
void initializeContext();
void terminateContext();
void render(jni::JNIEnv&);
void createSurface(ANativeWindow *window);
void destroySurface();
void update(jni::JNIEnv&);
void render();
void resizeView(jni::JNIEnv&, int, int);
void enableFps(bool enable);
void updateFps();
void resizeFramebuffer(jni::JNIEnv&, int, int);
void resizeView(int width, int height);
void resizeFramebuffer(int width, int height);
mbgl::EdgeInsets getInsets() { return insets;}
void setInsets(mbgl::EdgeInsets insets_);
void initializeDisplay(jni::JNIEnv&);
void scheduleTakeSnapshot();
void terminateDisplay(jni::JNIEnv&);
void initializeContext(jni::JNIEnv&);
void terminateContext(jni::JNIEnv&);
void createSurface(jni::JNIEnv&, jni::Object<>);
void destroySurface(jni::JNIEnv&);
jni::String getStyleUrl(jni::JNIEnv&);
void setStyleUrl(jni::JNIEnv&, jni::String);
jni::String getStyleJson(jni::JNIEnv&);
void setStyleJson(jni::JNIEnv&, jni::String);
void cancelTransitions(jni::JNIEnv&);
void setGestureInProgress(jni::JNIEnv&, jni::jboolean);
void moveBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble);
void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, jni::jboolean);
void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble);
jni::Object<LatLng> getLatLng(JNIEnv&);
void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
void setReachability(jni::JNIEnv&, jni::jboolean);
void resetPosition(jni::JNIEnv&);
jni::jdouble getPitch(jni::JNIEnv&);
void setPitch(jni::JNIEnv&, jni::jdouble, jni::jlong);
void scaleBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
void setScale(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
jni::jdouble getScale(jni::JNIEnv&);
void setZoom(jni::JNIEnv&, jni::jdouble, jni::jlong);
jni::jdouble getZoom(jni::JNIEnv&);
void resetZoom(jni::JNIEnv&);
void setMinZoom(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMinZoom(jni::JNIEnv&);
void setMaxZoom(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMaxZoom(jni::JNIEnv&);
void rotateBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
void setBearing(jni::JNIEnv&, jni::jdouble, jni::jlong);
void setBearingXY(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
jni::jdouble getBearing(jni::JNIEnv&);
void resetNorth(jni::JNIEnv&);
void setVisibleCoordinateBounds(JNIEnv&, jni::Array<jni::Object<LatLng>>, jni::Object<RectF>, jni::jdouble, jni::jlong);
void setContentPadding(JNIEnv&, double, double, double, double);
void scheduleSnapshot(jni::JNIEnv&);
void enableFps(jni::JNIEnv&, jni::jboolean enable);
jni::Array<jni::jdouble> getCameraValues(jni::JNIEnv&);
void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String);
jni::Array<jni::jlong> addMarkers(jni::JNIEnv&, jni::Array<jni::Object<Marker>>);
void onLowMemory(JNIEnv& env);
void setDebug(JNIEnv&, jni::jboolean);
void cycleDebugOptions(JNIEnv&);
jni::jboolean getDebug(JNIEnv&);
jni::jboolean isFullyLoaded(JNIEnv&);
jni::jdouble getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble, jni::jdouble);
jni::Object<ProjectedMeters> projectedMetersForLatLng(JNIEnv&, jni::jdouble, jni::jdouble);
jni::Object<PointF> pixelForLatLng(JNIEnv&, jdouble, jdouble);
jni::Object<LatLng> latLngForProjectedMeters(JNIEnv&, jdouble, jdouble);
jni::Object<LatLng> latLngForPixel(JNIEnv&, jfloat, jfloat);
jni::Array<jlong> addPolylines(JNIEnv&, jni::Array<jni::Object<Polyline>>);
jni::Array<jlong> addPolygons(JNIEnv&, jni::Array<jni::Object<Polygon>>);
void updatePolyline(JNIEnv&, jlong, jni::Object<Polyline>);
void updatePolygon(JNIEnv&, jlong, jni::Object<Polygon>);
void removeAnnotations(JNIEnv&, jni::Array<jlong>);
void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>);
jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String);
jni::jlong getTransitionDuration(JNIEnv&);
void setTransitionDuration(JNIEnv&, jni::jlong);
jni::jlong getTransitionDelay(JNIEnv&);
void setTransitionDelay(JNIEnv&, jni::jlong);
jni::Array<jlong> queryPointAnnotations(JNIEnv&, jni::Object<RectF>);
jni::Array<jni::Object<Feature>> queryRenderedFeaturesForPoint(JNIEnv&, jni::jfloat, jni::jfloat, jni::Array<jni::String>);
jni::Array<jni::Object<Feature>> queryRenderedFeaturesForBox(JNIEnv&, jni::jfloat, jni::jfloat, jni::jfloat, jni::jfloat, jni::Array<jni::String>);
jni::Object<Layer> getLayer(JNIEnv&, jni::String);
void addLayer(JNIEnv&, jlong, jni::String);
void removeLayerById(JNIEnv&, jni::String);
void removeLayer(JNIEnv&, jlong);
jni::Object<Source> getSource(JNIEnv&, jni::String);
void addSource(JNIEnv&, jni::jlong);
void removeSourceById(JNIEnv&, jni::String);
void removeSource(JNIEnv&, jlong);
void addImage(JNIEnv&, jni::String, jni::jint, jni::jint, jni::jfloat, jni::Array<jbyte>);
void removeImage(JNIEnv&, jni::String);
protected:
// mbgl::Backend //
void activate() override;
void deactivate() override;
private:
void _initializeDisplay();
void _terminateDisplay();
void _initializeContext();
void _terminateContext();
void _createSurface(ANativeWindow*);
void _destroySurface();
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
void updateViewBinding();
mbgl::Size getFramebufferSize() const;
void updateFps();
private:
JavaVM *vm = nullptr;
JNIEnv *env = nullptr;
jweak obj = nullptr;
jni::UniqueWeakObject<NativeMapView> javaPeer;
std::string styleUrl;
std::string apiKey;
ANativeWindow *window = nullptr;
EGLConfig config = nullptr;
EGLint format = -1;
EGLDisplay oldDisplay = EGL_NO_DISPLAY;
EGLSurface oldReadSurface = EGL_NO_SURFACE;
EGLSurface oldDrawSurface = EGL_NO_SURFACE;
@@ -75,15 +269,11 @@ private:
EGLSurface surface = EGL_NO_SURFACE;
EGLContext context = EGL_NO_CONTEXT;
EGLConfig config = nullptr;
EGLint format = -1;
std::string styleUrl;
std::string apiKey;
bool firstTime = false;
float pixelRatio;
bool fpsEnabled = false;
bool snapshot = false;
bool firstRender = true;
double fps = 0.0;
int width = 0;
@@ -96,12 +286,12 @@ private:
size_t totalMemory = 0;
// Ensure these are initialised last
mbgl::DefaultFileSource& fileSource;
std::shared_ptr<mbgl::ThreadPool> threadPool;
std::unique_ptr<mbgl::Map> map;
mbgl::EdgeInsets insets;
unsigned active = 0;
};
}
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,164 @@
#include "offline_manager.hpp"
#include <mbgl/util/string.hpp>
#include "../attach_env.hpp"
#include "../jni/generic_global_ref_deleter.hpp"
namespace mbgl {
namespace android {
// OfflineManager //
OfflineManager::OfflineManager(jni::JNIEnv& env, jni::Object<FileSource> jFileSource)
: fileSource(mbgl::android::FileSource::getDefaultFileSource(env, jFileSource)) {
}
OfflineManager::~OfflineManager() {}
void OfflineManager::setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit) {
fileSource.setOfflineMapboxTileCountLimit(limit);
}
void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, jni::Object<FileSource> jFileSource_, jni::Object<ListOfflineRegionsCallback> callback_) {
// list regions
fileSource.listOfflineRegions([
//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::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()),
jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter())
](std::exception_ptr error, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) mutable {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
if (error) {
OfflineManager::ListOfflineRegionsCallback::onError(*env, jni::Object<ListOfflineRegionsCallback>(*callback), error);
} else if (regions) {
OfflineManager::ListOfflineRegionsCallback::onList(*env, jni::Object<FileSource>(*jFileSource), jni::Object<ListOfflineRegionsCallback>(*callback), std::move(regions));
}
});
}
void OfflineManager::createOfflineRegion(jni::JNIEnv& env_,
jni::Object<FileSource> jFileSource_,
jni::Object<OfflineRegionDefinition> definition_,
jni::Array<jni::jbyte> metadata_,
jni::Object<CreateOfflineRegionCallback> callback_) {
// Convert
// XXX hardcoded cast for now as we only support OfflineTilePyramidRegionDefinition
auto definition = OfflineTilePyramidRegionDefinition::getDefinition(env_, jni::Object<OfflineTilePyramidRegionDefinition>(*definition_));
mbgl::OfflineRegionMetadata metadata;
if (metadata_) {
metadata = OfflineRegion::metadata(env_, metadata_);
}
// Create region
fileSource.createOfflineRegion(definition, metadata, [
//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::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()),
jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter())
](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegion> region) mutable {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
if (error) {
OfflineManager::CreateOfflineRegionCallback::onError(*env, jni::Object<CreateOfflineRegionCallback>(*callback), error);
} else if (region) {
OfflineManager::CreateOfflineRegionCallback::onCreate(
*env,
jni::Object<FileSource>(*jFileSource),
jni::Object<CreateOfflineRegionCallback>(*callback), std::move(region)
);
}
});
}
jni::Class<OfflineManager> OfflineManager::javaClass;
void OfflineManager::registerNative(jni::JNIEnv& env) {
OfflineManager::ListOfflineRegionsCallback::registerNative(env);
OfflineManager::CreateOfflineRegionCallback::registerNative(env);
javaClass = *jni::Class<OfflineManager>::Find(env).NewGlobalRef(env).release();
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
jni::RegisterNativePeer<OfflineManager>( env, javaClass, "nativePtr",
std::make_unique<OfflineManager, JNIEnv&, jni::Object<FileSource>>,
"initialize",
"finalize",
METHOD(&OfflineManager::setOfflineMapboxTileCountLimit, "setOfflineMapboxTileCountLimit"),
METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"),
METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"));
}
// OfflineManager::ListOfflineRegionsCallback //
void OfflineManager::ListOfflineRegionsCallback::onError(jni::JNIEnv& env,
jni::Object<OfflineManager::ListOfflineRegionsCallback> callback,
std::exception_ptr error) {
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
std::string message = mbgl::util::toString(error);
callback.Call(env, method, jni::Make<jni::String>(env, message));
}
void OfflineManager::ListOfflineRegionsCallback::onList(jni::JNIEnv& env,
jni::Object<FileSource> jFileSource,
jni::Object<OfflineManager::ListOfflineRegionsCallback> callback,
mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) {
//Convert the regions to java peer objects
std::size_t index = 0;
auto jregions = jni::Array<jni::Object<OfflineRegion>>::New(env, regions->size(), OfflineRegion::javaClass);
for (auto& region : *regions) {
auto jregion = OfflineRegion::New(env, jFileSource, std::move(region));
jregions.Set(env, index, jregion);
jni::DeleteLocalRef(env, jregion);
index++;
}
// Trigger callback
static auto method = javaClass.GetMethod<void (jni::Array<jni::Object<OfflineRegion>>)>(env, "onList");
callback.Call(env, method, jregions);
jni::DeleteLocalRef(env, jregions);
}
jni::Class<OfflineManager::ListOfflineRegionsCallback> OfflineManager::ListOfflineRegionsCallback::javaClass;
void OfflineManager::ListOfflineRegionsCallback::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineManager::ListOfflineRegionsCallback>::Find(env).NewGlobalRef(env).release();
}
// OfflineManager::CreateOfflineRegionCallback //
void OfflineManager::CreateOfflineRegionCallback::onError(jni::JNIEnv& env,
jni::Object<OfflineManager::CreateOfflineRegionCallback> callback,
std::exception_ptr error) {
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
std::string message = mbgl::util::toString(error);
callback.Call(env, method, jni::Make<jni::String>(env, message));
}
void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env,
jni::Object<FileSource> jFileSource,
jni::Object<OfflineManager::CreateOfflineRegionCallback> callback,
mbgl::optional<mbgl::OfflineRegion> region) {
//Convert the region to java peer object
auto jregion = OfflineRegion::New(env, jFileSource, std::move(*region));
// Trigger callback
static auto method = javaClass.GetMethod<void (jni::Object<OfflineRegion>)>(env, "onCreate");
callback.Call(env, method, jregion);
jni::DeleteLocalRef(env, jregion);
}
jni::Class<OfflineManager::CreateOfflineRegionCallback> OfflineManager::CreateOfflineRegionCallback::javaClass;
void OfflineManager::CreateOfflineRegionCallback::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineManager::CreateOfflineRegionCallback>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,75 @@
#pragma once
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/offline.hpp>
#include <jni/jni.hpp>
#include "../file_source.hpp"
#include "offline_region.hpp"
#include "offline_region_definition.hpp"
namespace mbgl {
namespace android {
class OfflineManager {
public:
class ListOfflineRegionsCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback";}
static void onError(jni::JNIEnv&, jni::Object<OfflineManager::ListOfflineRegionsCallback>, std::exception_ptr);
static void onList(jni::JNIEnv&,
jni::Object<FileSource>,
jni::Object<OfflineManager::ListOfflineRegionsCallback>,
mbgl::optional<std::vector<mbgl::OfflineRegion>>);
static jni::Class<OfflineManager::ListOfflineRegionsCallback> javaClass;
static void registerNative(jni::JNIEnv&);
};
class CreateOfflineRegionCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback"; }
static void onError(jni::JNIEnv&, jni::Object<OfflineManager::CreateOfflineRegionCallback>, std::exception_ptr);
static void onCreate(jni::JNIEnv&,
jni::Object<FileSource>,
jni::Object<OfflineManager::CreateOfflineRegionCallback>,
mbgl::optional<mbgl::OfflineRegion>);
static jni::Class<OfflineManager::CreateOfflineRegionCallback> javaClass;
static void registerNative(jni::JNIEnv&);
};
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; };
static jni::Class<OfflineManager> javaClass;
static void registerNative(jni::JNIEnv&);
OfflineManager(jni::JNIEnv&, jni::Object<FileSource>);
~OfflineManager();
void setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit);
void listOfflineRegions(jni::JNIEnv&, jni::Object<FileSource>, jni::Object<ListOfflineRegionsCallback> callback);
void createOfflineRegion(jni::JNIEnv&,
jni::Object<FileSource> jFileSource_,
jni::Object<OfflineRegionDefinition> definition,
jni::Array<jni::jbyte> metadata,
jni::Object<OfflineManager::CreateOfflineRegionCallback> callback);
private:
mbgl::DefaultFileSource& fileSource;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,308 @@
#include "offline_region.hpp"
#include <mbgl/util/logging.hpp>
#include <mbgl/util/string.hpp>
#include "offline_region_definition.hpp"
#include "offline_region_error.hpp"
#include "offline_region_status.hpp"
#include "../attach_env.hpp"
#include "../jni/generic_global_ref_deleter.hpp"
namespace mbgl {
namespace android {
// OfflineRegion //
OfflineRegion::OfflineRegion(jni::JNIEnv& env, jni::jlong offlineRegionPtr, jni::Object<FileSource> jFileSource)
: region(reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr)),
fileSource(mbgl::android::FileSource::getDefaultFileSource(env, jFileSource)) {}
OfflineRegion::~OfflineRegion() {}
void OfflineRegion::setOfflineRegionObserver(jni::JNIEnv& env_, jni::Object<OfflineRegion::OfflineRegionObserver> callback) {
// Define the observer
class Observer : public mbgl::OfflineRegionObserver {
public:
Observer(jni::UniqueObject<OfflineRegion::OfflineRegionObserver>&& callback_)
//TODO add a generic deleter for jni::Object
: callback(callback_.release()->Get()) {
}
~Observer() override {
android::UniqueEnv env = android::AttachEnv();
env->DeleteGlobalRef(Unwrap(*callback));
}
void statusChanged(mbgl::OfflineRegionStatus status) override {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
// Status object
auto jStatus = OfflineRegionStatus::New(*env, status);
// Call
static auto method = OfflineRegion::OfflineRegionObserver::javaClass
.GetMethod<void (jni::Object<OfflineRegionStatus>)>(*env, "onStatusChanged");
callback.Call(*env, method, jStatus);
// Delete references
jni::DeleteLocalRef(*env, jStatus);
}
void responseError(mbgl::Response::Error error) override {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
// Error object
auto jError = OfflineRegionError::New(*env, error);
// Call
static auto method = OfflineRegion::OfflineRegionObserver::javaClass
.GetMethod<void (jni::Object<mbgl::android::OfflineRegionError>)>(*env, "onError");
callback.Call(*env, method, jError);
// Delete references
jni::DeleteLocalRef(*env, jError);
}
void mapboxTileCountLimitExceeded(uint64_t limit) override {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
// Send limit
static auto method = OfflineRegion::OfflineRegionObserver::javaClass
.GetMethod<void (jni::jlong)>(*env, "mapboxTileCountLimitExceeded");
callback.Call(*env, method, jlong(limit));
}
jni::Object<OfflineRegion::OfflineRegionObserver> callback;
};
// Set the observer
fileSource.setOfflineRegionObserver(*region, std::make_unique<Observer>(callback.NewGlobalRef(env_)));
}
void OfflineRegion::setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint jState) {
// State
mbgl::OfflineRegionDownloadState state;
switch (jState) {
case 0:
state = mbgl::OfflineRegionDownloadState::Inactive;
break;
case 1:
state = mbgl::OfflineRegionDownloadState::Active;
break;
default:
mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active).");
return;
}
fileSource.setOfflineRegionDownloadState(*region, state);
}
void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, jni::Object<OfflineRegionStatusCallback> callback_) {
fileSource.getOfflineRegionStatus(*region, [
//Ensure the object is not gc'd in the meanwhile
callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter())
](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionStatus> status) mutable {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
if (error) {
OfflineRegionStatusCallback::onError(*env, jni::Object<OfflineRegionStatusCallback>(*callback), error);
} else if (status) {
OfflineRegionStatusCallback::onStatus(*env, jni::Object<OfflineRegionStatusCallback>(*callback), std::move(status));
}
});
}
void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, jni::Object<OfflineRegionDeleteCallback> callback_) {
// Delete
fileSource.deleteOfflineRegion(std::move(*region), [
//Ensure the object is not gc'd in the meanwhile
callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter())
](std::exception_ptr error) mutable {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
if (error) {
OfflineRegionDeleteCallback::onError(*env, jni::Object<OfflineRegionDeleteCallback>(*callback), error);
} else {
OfflineRegionDeleteCallback::onDelete(*env, jni::Object<OfflineRegionDeleteCallback>(*callback));
}
});
}
void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, jni::Array<jni::jbyte> jMetadata, jni::Object<OfflineRegionUpdateMetadataCallback> callback_) {
// Convert
auto metadata = OfflineRegion::metadata(env_, jMetadata);
fileSource.updateOfflineMetadata(region->getID(), metadata, [
//Ensure the object is not gc'd in the meanwhile
callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter())
](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionMetadata> data) mutable {
// Reattach, the callback comes from a different thread
android::UniqueEnv env = android::AttachEnv();
if (error) {
OfflineRegionUpdateMetadataCallback::onError(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), error);
} else if (data) {
OfflineRegionUpdateMetadataCallback::onUpdate(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), std::move(data));
}
});
}
jni::Object<OfflineRegion> OfflineRegion::New(jni::JNIEnv& env, jni::Object<FileSource> jFileSource, mbgl::OfflineRegion region) {
// Definition
auto definition = jni::Object<OfflineRegionDefinition>(*OfflineTilePyramidRegionDefinition::New(env, region.getDefinition()));
// Metadata
auto metadata = OfflineRegion::metadata(env, region.getMetadata());
// Create region java object
static auto constructor = OfflineRegion::javaClass.GetConstructor<jni::jlong, jni::Object<FileSource>, jni::jlong, jni::Object<OfflineRegionDefinition>, jni::Array<jni::jbyte>>(env);
auto jregion = OfflineRegion::javaClass.New(env, constructor,
reinterpret_cast<jni::jlong>(new mbgl::OfflineRegion(std::move(region))), //Copy a region to the heap
jFileSource, jni::jlong(region.getID()), definition, metadata);
//Delete references
jni::DeleteLocalRef(env, definition);
jni::DeleteLocalRef(env, metadata);
return jregion;
}
jni::Array<jni::jbyte> OfflineRegion::metadata(jni::JNIEnv& env, mbgl::OfflineRegionMetadata metadata_) {
std::vector<jni::jbyte> convertedMetadata(metadata_.begin(), metadata_.end());
std::size_t length = static_cast<std::size_t>(convertedMetadata.size());
auto metadata = jni::Array<jni::jbyte>::New(env, length);
metadata.SetRegion<std::vector<jni::jbyte>>(env, 0, convertedMetadata);
return metadata;
}
mbgl::OfflineRegionMetadata OfflineRegion::metadata(jni::JNIEnv& env, jni::Array<jni::jbyte> metadata_) {
std::size_t length = metadata_.Length(env);
auto metadata_tmp = std::vector<jni::jbyte>();
metadata_tmp.resize(length);
metadata_.GetRegion<std::vector<jni::jbyte>>(env, 0, metadata_tmp);
auto metadata = std::vector<uint8_t>(metadata_tmp.begin(), metadata_tmp.end());
return metadata;
}
jni::Class<OfflineRegion> OfflineRegion::javaClass;
void OfflineRegion::registerNative(jni::JNIEnv& env) {
OfflineRegion::OfflineRegionObserver::registerNative(env);
OfflineRegion::OfflineRegionStatusCallback::registerNative(env);
OfflineRegion::OfflineRegionDeleteCallback::registerNative(env);
OfflineRegion::OfflineRegionUpdateMetadataCallback::registerNative(env);
javaClass = *jni::Class<OfflineRegion>::Find(env).NewGlobalRef(env).release();
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
jni::RegisterNativePeer<OfflineRegion>( env, javaClass, "nativePtr",
std::make_unique<OfflineRegion, JNIEnv&, jni::jlong, jni::Object<FileSource>>,
"initialize",
"finalize",
METHOD(&OfflineRegion::setOfflineRegionObserver, "setOfflineRegionObserver"),
METHOD(&OfflineRegion::setOfflineRegionDownloadState, "setOfflineRegionDownloadState"),
METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"),
METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"),
METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata")
);
}
// OfflineRegionObserver //
jni::Class<OfflineRegion::OfflineRegionObserver> OfflineRegion::OfflineRegionObserver::javaClass;
void OfflineRegion::OfflineRegionObserver::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegion::OfflineRegionObserver>::Find(env).NewGlobalRef(env).release();
}
// OfflineRegionStatusCallback //
jni::Class<OfflineRegion::OfflineRegionStatusCallback> OfflineRegion::OfflineRegionStatusCallback::javaClass;
void OfflineRegion::OfflineRegionStatusCallback::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionStatusCallback>::Find(env).NewGlobalRef(env).release();
}
void OfflineRegion::OfflineRegionStatusCallback::onError(jni::JNIEnv& env,
jni::Object<OfflineRegion::OfflineRegionStatusCallback> callback,
std::exception_ptr error) {
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
std::string message = mbgl::util::toString(error);
callback.Call(env, method, jni::Make<jni::String>(env, message));
}
void OfflineRegion::OfflineRegionStatusCallback::onStatus(jni::JNIEnv& env,
jni::Object<OfflineRegion::OfflineRegionStatusCallback> callback,
mbgl::optional<mbgl::OfflineRegionStatus> status) {
//Convert to java peer object
auto jStatus = OfflineRegionStatus::New(env, std::move(*status));
// Trigger callback
static auto method = javaClass.GetMethod<void (jni::Object<OfflineRegionStatus>)>(env, "onStatus");
callback.Call(env, method, jStatus);
jni::DeleteLocalRef(env, jStatus);
}
// OfflineRegionDeleteCallback //
jni::Class<OfflineRegion::OfflineRegionDeleteCallback> OfflineRegion::OfflineRegionDeleteCallback::javaClass;
void OfflineRegion::OfflineRegionDeleteCallback::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionDeleteCallback>::Find(env).NewGlobalRef(env).release();
}
void OfflineRegion::OfflineRegionDeleteCallback::onError(jni::JNIEnv& env,
jni::Object<OfflineRegion::OfflineRegionDeleteCallback> callback,
std::exception_ptr error) {
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
std::string message = mbgl::util::toString(error);
callback.Call(env, method, jni::Make<jni::String>(env, message));
}
void OfflineRegion::OfflineRegionDeleteCallback::onDelete(jni::JNIEnv& env, jni::Object<OfflineRegion::OfflineRegionDeleteCallback> callback) {
// Trigger callback
static auto method = javaClass.GetMethod<void ()>(env, "onDelete");
callback.Call(env, method);
}
// OfflineRegionUpdateMetadataCallback //
jni::Class<OfflineRegion::OfflineRegionUpdateMetadataCallback> OfflineRegion::OfflineRegionUpdateMetadataCallback::javaClass;
void OfflineRegion::OfflineRegionUpdateMetadataCallback::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionUpdateMetadataCallback>::Find(env).NewGlobalRef(env).release();
}
void OfflineRegion::OfflineRegionUpdateMetadataCallback::onError(jni::JNIEnv& env,
jni::Object<OfflineRegion::OfflineRegionUpdateMetadataCallback> callback,
std::exception_ptr error) {
static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError");
std::string message = mbgl::util::toString(error);
callback.Call(env, method, jni::Make<jni::String>(env, message));
}
void OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& env,
jni::Object<OfflineRegion::OfflineRegionUpdateMetadataCallback> callback,
mbgl::optional<mbgl::OfflineRegionMetadata> metadata) {
//Convert to java peer object
auto jMetadata = OfflineRegion::metadata(env, std::move(*metadata));
// Trigger callback
static auto method = javaClass.GetMethod<void (jni::Array<jni::jbyte>)>(env, "onUpdate");
callback.Call(env, method, jMetadata);
jni::DeleteLocalRef(env, jMetadata);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,99 @@
#pragma once
#include <mbgl/storage/offline.hpp>
#include <jni/jni.hpp>
#include "../file_source.hpp"
#include <memory>
namespace mbgl {
namespace android {
class OfflineRegion {
public:
class OfflineRegionObserver {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver"; };
static jni::Class<OfflineRegionObserver> javaClass;
static void registerNative(jni::JNIEnv&);
};
class OfflineRegionStatusCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback"; };
static void onError(jni::JNIEnv&, jni::Object<OfflineRegionStatusCallback>, std::exception_ptr);
static void onStatus(jni::JNIEnv&,
jni::Object<OfflineRegionStatusCallback>,
mbgl::optional<mbgl::OfflineRegionStatus>);
static jni::Class<OfflineRegionStatusCallback> javaClass;
static void registerNative(jni::JNIEnv&);
};
class OfflineRegionDeleteCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback"; };
static void onError(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>, std::exception_ptr);
static void onDelete(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>);
static jni::Class<OfflineRegionDeleteCallback> javaClass;
static void registerNative(jni::JNIEnv&);
};
class OfflineRegionUpdateMetadataCallback {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"; };
static void onError(jni::JNIEnv&, jni::Object<OfflineRegionUpdateMetadataCallback>, std::exception_ptr);
static void onUpdate(jni::JNIEnv&,
jni::Object<OfflineRegionUpdateMetadataCallback>,
mbgl::optional<mbgl::OfflineRegionMetadata>);
static jni::Class<OfflineRegionUpdateMetadataCallback> javaClass;
static void registerNative(jni::JNIEnv&);
};
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion"; };
OfflineRegion(jni::JNIEnv&, jni::jlong, jni::Object<FileSource>);
~OfflineRegion();
void setOfflineRegionObserver(jni::JNIEnv&, jni::Object<OfflineRegion::OfflineRegionObserver>);
void setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint);
void getOfflineRegionStatus(jni::JNIEnv&, jni::Object<OfflineRegion::OfflineRegionStatusCallback>);
void deleteOfflineRegion(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>);
void updateOfflineRegionMetadata(jni::JNIEnv&, jni::Array<jni::jbyte>, jni::Object<OfflineRegionUpdateMetadataCallback>);
static jni::Object<OfflineRegion> New(jni::JNIEnv&, jni::Object<FileSource>, mbgl::OfflineRegion);
static jni::Array<jni::jbyte> metadata(jni::JNIEnv&, mbgl::OfflineRegionMetadata);
static mbgl::OfflineRegionMetadata metadata(jni::JNIEnv&, jni::Array<jni::jbyte>);
static jni::Class<OfflineRegion> javaClass;
static void registerNative(jni::JNIEnv&);
private:
std::unique_ptr<mbgl::OfflineRegion> region;
mbgl::DefaultFileSource& fileSource;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,69 @@
#include "offline_region_definition.hpp"
#include "../geometry/lat_lng_bounds.hpp"
namespace mbgl {
namespace android {
// OfflineRegionDefinition //
jni::Class<OfflineRegionDefinition> OfflineRegionDefinition::javaClass;
void OfflineRegionDefinition::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionDefinition>::Find(env).NewGlobalRef(env).release();
}
// OfflineTilePyramidRegionDefinition //
jni::Object<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::New(jni::JNIEnv& env, mbgl::OfflineTilePyramidRegionDefinition definition) {
//Convert objects
auto styleURL = jni::Make<jni::String>(env, definition.styleURL);
auto bounds = LatLngBounds::New(env, definition.bounds);
static auto constructor = javaClass.GetConstructor<jni::String, jni::Object<LatLngBounds>, jni::jdouble, jni::jdouble, jni::jfloat>(env);
auto jdefinition = javaClass.New(env, constructor, styleURL, bounds, definition.minZoom, definition.maxZoom, definition.pixelRatio);
//Delete References
jni::DeleteLocalRef(env, styleURL);
jni::DeleteLocalRef(env, bounds);
return jdefinition;
}
mbgl::OfflineTilePyramidRegionDefinition OfflineTilePyramidRegionDefinition::getDefinition(jni::JNIEnv& env, jni::Object<OfflineTilePyramidRegionDefinition> jDefinition) {
// Field references
static auto styleURLF = javaClass.GetField<jni::String>(env, "styleURL");
static auto boundsF = javaClass.GetField<jni::Object<LatLngBounds>>(env, "bounds");
static auto minZoomF = javaClass.GetField<jni::jdouble>(env, "minZoom");
static auto maxZoomF = javaClass.GetField<jni::jdouble>(env, "maxZoom");
static auto pixelRatioF = javaClass.GetField<jni::jfloat>(env, "pixelRatio");
// Get objects
auto jStyleURL = jDefinition.Get(env, styleURLF);
auto jBounds = jDefinition.Get(env, boundsF);
// Create definition
mbgl::OfflineTilePyramidRegionDefinition definition(
jni::Make<std::string>(env, jStyleURL),
LatLngBounds::getLatLngBounds(env, jBounds),
jDefinition.Get(env, minZoomF),
jDefinition.Get(env, maxZoomF),
jDefinition.Get(env, pixelRatioF)
);
// Delete references
jni::DeleteLocalRef(env, jStyleURL);
jni::DeleteLocalRef(env, jBounds);
return definition;
}
jni::Class<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::javaClass;
void OfflineTilePyramidRegionDefinition::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineTilePyramidRegionDefinition>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,34 @@
#pragma once
#include <mbgl/storage/offline.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class OfflineRegionDefinition {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionDefinition"; };
static jni::Class<OfflineRegionDefinition> javaClass;
static void registerNative(jni::JNIEnv&);
};
class OfflineTilePyramidRegionDefinition: public OfflineRegionDefinition {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition"; };
static jni::Object<OfflineTilePyramidRegionDefinition> New(jni::JNIEnv&, mbgl::OfflineTilePyramidRegionDefinition);
static mbgl::OfflineTilePyramidRegionDefinition getDefinition(jni::JNIEnv&, jni::Object<OfflineTilePyramidRegionDefinition>);
static jni::Class<OfflineTilePyramidRegionDefinition> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,53 @@
#include "offline_region_error.hpp"
namespace mbgl {
namespace android {
jni::Object<OfflineRegionError> OfflineRegionError::New(jni::JNIEnv& env, mbgl::Response::Error error) {
// Handle the value of reason independently of the underlying int value
std::string reason;
switch(error.reason) {
case mbgl::Response::Error::Reason::Success:
reason = "REASON_SUCCESS";
break;
case mbgl::Response::Error::Reason::NotFound:
reason = "REASON_NOT_FOUND";
break;
case mbgl::Response::Error::Reason::Server:
reason = "REASON_SERVER";
break;
case mbgl::Response::Error::Reason::Connection:
reason = "REASON_CONNECTION";
break;
case mbgl::Response::Error::Reason::RateLimit:
reason = "REASON_RATE_LIMIT";
break;
case mbgl::Response::Error::Reason::Other:
reason = "REASON_OTHER";
break;
}
// Convert
auto jReason = jni::Make<jni::String>(env, reason);
auto jMessage = jni::Make<jni::String>(env, error.message);
// Create java object
static auto constructor = javaClass.GetConstructor<jni::String, jni::String>(env);
auto jError = javaClass.New(env, constructor, jReason, jMessage);
// Delete references
jni::DeleteLocalRef(env, jReason);
jni::DeleteLocalRef(env, jMessage);
return jError;
}
jni::Class<OfflineRegionError> OfflineRegionError::javaClass;
void OfflineRegionError::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionError>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include <mbgl/storage/offline.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class OfflineRegionError {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionError"; };
static jni::Object<OfflineRegionError> New(jni::JNIEnv&, mbgl::Response::Error);
static jni::Class<OfflineRegionError> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,39 @@
#include "offline_region_status.hpp"
namespace mbgl {
namespace android {
jni::Object<OfflineRegionStatus> OfflineRegionStatus::New(jni::JNIEnv& env, mbgl::OfflineRegionStatus status) {
// Convert to jint
jint downloadState;
switch(status.downloadState) {
case mbgl::OfflineRegionDownloadState::Inactive:
downloadState = 0;
break;
case mbgl::OfflineRegionDownloadState::Active:
downloadState = 1;
break;
}
// Create java object
static auto constructor = javaClass.GetConstructor<jint, jlong, jlong, jlong, jlong, jlong, jboolean>(env);
return javaClass.New(env, constructor,
downloadState,
jlong(status.completedResourceCount),
jlong(status.completedResourceSize),
jlong(status.completedTileCount),
jlong(status.completedTileSize),
jlong(status.requiredResourceCount),
jboolean(status.requiredResourceCountIsPrecise)
);
}
jni::Class<OfflineRegionStatus> OfflineRegionStatus::javaClass;
void OfflineRegionStatus::registerNative(jni::JNIEnv& env) {
javaClass = *jni::Class<OfflineRegionStatus>::Find(env).NewGlobalRef(env).release();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include <mbgl/storage/offline.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class OfflineRegionStatus {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionStatus"; };
static jni::Object<OfflineRegionStatus> New(jni::JNIEnv&, mbgl::OfflineRegionStatus status);
static jni::Class<OfflineRegionStatus> javaClass;
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -4,7 +4,6 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/optional.hpp>
#include <jni/jni.hpp>