Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[android] migrate to new Android flutter plugin architecture #488

Merged
merged 1 commit into from
Feb 12, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions android/src/main/java/com/mapbox/mapboxgl/GlobalMethodHandler.java
Original file line number Diff line number Diff line change
@@ -5,6 +5,10 @@
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mapbox.mapboxgl.models.OfflineRegionData;
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -15,6 +19,7 @@
import java.io.InputStream;
import java.io.OutputStream;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
@@ -23,10 +28,22 @@ class GlobalMethodHandler implements MethodChannel.MethodCallHandler {
private static final String TAG = GlobalMethodHandler.class.getSimpleName();
private static final String DATABASE_NAME = "mbgl-offline.db";
private static final int BUFFER_SIZE = 1024 * 2;
private final PluginRegistry.Registrar registrar;

GlobalMethodHandler(PluginRegistry.Registrar registrar) {
@Nullable
private PluginRegistry.Registrar registrar;
@Nullable
private FlutterPlugin.FlutterAssets flutterAssets;
@NonNull
private final Context context;

GlobalMethodHandler(@NonNull PluginRegistry.Registrar registrar) {
this.registrar = registrar;
this.context = registrar.activeContext();
}

GlobalMethodHandler(@NonNull Context context, @NonNull FlutterPlugin.FlutterAssets assets) {
this.context = context;
this.flutterAssets = assets;
}

@Override
@@ -58,7 +75,7 @@ public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
}

private void installOfflineMapTiles(String tilesDb) {
final File dest = new File(registrar.activeContext().getFilesDir(), DATABASE_NAME);
final File dest = new File(context.getFilesDir(), DATABASE_NAME);
try (InputStream input = openTilesDbFile(tilesDb);
OutputStream output = new FileOutputStream(dest)) {
copy(input, output);
@@ -71,7 +88,14 @@ private InputStream openTilesDbFile(String tilesDb) throws IOException {
if (tilesDb.startsWith("/")) { // Absolute path.
return new FileInputStream(new File(tilesDb));
} else {
final String assetKey = registrar.lookupKeyForAsset(tilesDb);
String assetKey;
if (registrar != null) {
assetKey = registrar.lookupKeyForAsset(tilesDb);
} else if(flutterAssets != null) {
assetKey = flutterAssets.getAssetFilePathByName(tilesDb);
} else {
throw new IllegalStateException();
}
return registrar.activeContext().getAssets().open(assetKey);
}
}
@@ -84,7 +108,7 @@ private String extractAccessToken(MethodCall methodCall, String fallbackValue) {
return fallbackValue;
}

private static int copy(InputStream input, OutputStream output) throws IOException {
private static void copy(InputStream input, OutputStream output) throws IOException {
final byte[] buffer = new byte[BUFFER_SIZE];
final BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
final BufferedOutputStream out = new BufferedOutputStream(output, BUFFER_SIZE);
@@ -108,6 +132,5 @@ private static int copy(InputStream input, OutputStream output) throws IOExcepti
Log.e(TAG, e.getMessage(), e);
}
}
return count;
}
}
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.Style;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry;

import java.util.concurrent.atomic.AtomicInteger;
@@ -30,9 +31,9 @@ class MapboxMapBuilder implements MapboxMapOptionsSink {
private String styleString = Style.MAPBOX_STREETS;

MapboxMapController build(
int id, Context context, AtomicInteger state, PluginRegistry.Registrar registrar, String accessToken) {
int id, Context context, BinaryMessenger messenger, MapboxMapsPlugin.LifecycleProvider lifecycleProvider, String accessToken) {
final MapboxMapController controller =
new MapboxMapController(id, context, state, registrar, options, accessToken, styleString);
new MapboxMapController(id, context, messenger, lifecycleProvider, options, accessToken, styleString);
controller.init();
controller.setMyLocationEnabled(myLocationEnabled);
controller.setMyLocationTrackingMode(myLocationTrackingMode);
181 changes: 70 additions & 111 deletions android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
Original file line number Diff line number Diff line change
@@ -5,12 +5,10 @@
package com.mapbox.mapboxgl;

import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
@@ -19,12 +17,14 @@
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import androidx.annotation.NonNull;
import android.util.Log;
import android.view.Gravity;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
@@ -38,63 +38,57 @@
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;

import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.LatLngQuad;
import com.mapbox.mapboxsdk.geometry.VisibleRegion;
import com.mapbox.mapboxsdk.location.LocationComponent;
import com.mapbox.mapboxsdk.location.LocationComponentOptions;
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
import com.mapbox.mapboxsdk.location.OnCameraTrackingChangedListener;
import com.mapbox.mapboxsdk.location.modes.CameraMode;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.Projection;
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.plugins.annotation.Annotation;
import com.mapbox.mapboxsdk.plugins.annotation.Circle;
import com.mapbox.mapboxsdk.plugins.annotation.CircleManager;
import com.mapbox.mapboxsdk.plugins.annotation.Fill;
import com.mapbox.mapboxsdk.plugins.annotation.FillManager;
import com.mapbox.mapboxsdk.plugins.annotation.Line;
import com.mapbox.mapboxsdk.plugins.annotation.LineManager;
import com.mapbox.mapboxsdk.plugins.annotation.OnAnnotationClickListener;
import com.mapbox.mapboxsdk.plugins.annotation.Symbol;
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager;
import com.mapbox.mapboxsdk.plugins.annotation.Line;
import com.mapbox.mapboxsdk.plugins.annotation.LineManager;
import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions;
import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.platform.PlatformView;
import com.mapbox.mapboxsdk.style.layers.RasterLayer;
import com.mapbox.mapboxsdk.style.sources.ImageSource;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static com.mapbox.mapboxgl.MapboxMapsPlugin.CREATED;
import static com.mapbox.mapboxgl.MapboxMapsPlugin.DESTROYED;
import static com.mapbox.mapboxgl.MapboxMapsPlugin.PAUSED;
import static com.mapbox.mapboxgl.MapboxMapsPlugin.RESUMED;
import static com.mapbox.mapboxgl.MapboxMapsPlugin.STARTED;
import static com.mapbox.mapboxgl.MapboxMapsPlugin.STOPPED;

import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin;
import com.mapbox.mapboxsdk.style.layers.RasterLayer;
import com.mapbox.mapboxsdk.style.sources.ImageSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;

/**
* Controller of a single MapboxMaps MapView instance.
*/
@SuppressLint("MissingPermission")
final class MapboxMapController
implements Application.ActivityLifecycleCallbacks,
implements DefaultLifecycleObserver,
MapboxMap.OnCameraIdleListener,
MapboxMap.OnCameraMoveListener,
MapboxMap.OnCameraMoveStartedListener,
@@ -112,10 +106,9 @@ final class MapboxMapController
PlatformView {
private static final String TAG = "MapboxMapController";
private final int id;
private final AtomicInteger activityState;
private final MethodChannel methodChannel;
private final PluginRegistry.Registrar registrar;
private final MapView mapView;
private final MapboxMapsPlugin.LifecycleProvider lifecycleProvider;
private MapView mapView;
private MapboxMap mapboxMap;
private final Map<String, SymbolController> symbols;
private final Map<String, LineController> lines;
@@ -132,7 +125,6 @@ final class MapboxMapController
private boolean disposed = false;
private final float density;
private MethodChannel.Result mapReadyResult;
private final int registrarActivityHashCode;
private final Context context;
private final String styleStringInitial;
private LocationComponent locationComponent = null;
@@ -144,27 +136,24 @@ final class MapboxMapController
MapboxMapController(
int id,
Context context,
AtomicInteger activityState,
PluginRegistry.Registrar registrar,
BinaryMessenger messenger,
MapboxMapsPlugin.LifecycleProvider lifecycleProvider,
MapboxMapOptions options,
String accessToken,
String styleStringInitial) {
MapBoxUtils.getMapbox(context, accessToken);
this.id = id;
this.context = context;
this.activityState = activityState;
this.registrar = registrar;
this.styleStringInitial = styleStringInitial;
this.mapView = new MapView(context, options);
this.symbols = new HashMap<>();
this.lines = new HashMap<>();
this.circles = new HashMap<>();
this.fills = new HashMap<>();
this.density = context.getResources().getDisplayMetrics().density;
methodChannel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/mapbox_maps_" + id);
this.lifecycleProvider = lifecycleProvider;
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/mapbox_maps_" + id);
methodChannel.setMethodCallHandler(this);
this.registrarActivityHashCode = registrar.activity().hashCode();
}

@Override
@@ -173,43 +162,7 @@ public View getView() {
}

void init() {
switch (activityState.get()) {
case STOPPED:
mapView.onCreate(null);
mapView.onStart();
mapView.onResume();
mapView.onPause();
mapView.onStop();
break;
case PAUSED:
mapView.onCreate(null);
mapView.onStart();
mapView.onResume();
mapView.onPause();
break;
case RESUMED:
mapView.onCreate(null);
mapView.onStart();
mapView.onResume();
break;
case STARTED:
mapView.onCreate(null);
mapView.onStart();
break;
case CREATED:
mapView.onCreate(null);
break;
case DESTROYED:
mapboxMap.removeOnCameraIdleListener(this);
mapboxMap.removeOnCameraMoveStartedListener(this);
mapboxMap.removeOnCameraMoveListener(this);
mapView.onDestroy();
break;
default:
throw new IllegalArgumentException(
"Cannot interpret " + activityState.get() + " as an activity state");
}
registrar.activity().getApplication().registerActivityLifecycleCallbacks(this);
lifecycleProvider.getLifecycle().addObserver(this);
mapView.getMapAsync(this);
}

@@ -325,8 +278,7 @@ public void setStyleString(String styleString) {
!styleString.startsWith("https://")&&
!styleString.startsWith("mapbox://")) {
// We are assuming that the style will be loaded from an asset here.
AssetManager assetManager = registrar.context().getAssets();
String key = registrar.lookupKeyForAsset(styleString);
String key = MapboxMapsPlugin.flutterAssets.getAssetFilePathByName(styleString);
mapboxMap.setStyle(new Style.Builder().fromUri("asset://" + key), onStyleLoadedCallback);
} else {
mapboxMap.setStyle(new Style.Builder().fromUrl(styleString), onStyleLoadedCallback);
@@ -387,7 +339,9 @@ private void onUserLocationUpdate(Location location){
userLocation.put("altitude", location.getAltitude());
userLocation.put("bearing", location.getBearing());
userLocation.put("horizontalAccuracy", location.getAccuracy());
userLocation.put("verticalAccuracy", (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? location.getVerticalAccuracyMeters() : null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
userLocation.put("verticalAccuracy", (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? location.getVerticalAccuracyMeters() : null);
}
userLocation.put("timestamp", location.getTime());

final Map<String, Object> arguments = new HashMap<>(1);
@@ -997,10 +951,23 @@ public boolean onMapLongClick(@NonNull LatLng point) {

@Override
public void dispose() {
if (disposed || registrar.activity() == null) {
if (disposed) {
return;
}
disposed = true;
methodChannel.setMethodCallHandler(null);
destroyMapViewIfNecessary();
Lifecycle lifecycle = lifecycleProvider.getLifecycle();
if (lifecycle != null) {
lifecycle.removeObserver(this);
}
}

private void destroyMapViewIfNecessary() {
if (mapView == null) {
return;
}

if (locationComponent != null) {
locationComponent.setLocationComponentEnabled(false);
}
@@ -1017,29 +984,30 @@ public void dispose() {
fillManager.onDestroy();
}
stopListeningForLocationUpdates();

mapView.onDestroy();
registrar.activity().getApplication().unregisterActivityLifecycleCallbacks(this);
mapView = null;
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onCreate(@NonNull LifecycleOwner owner) {
if (disposed) {
return;
}
mapView.onCreate(savedInstanceState);
mapView.onCreate(null);
}

@Override
public void onActivityStarted(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onStart(@NonNull LifecycleOwner owner) {
if (disposed) {
return;
}
mapView.onStart();
}

@Override
public void onActivityResumed(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onResume(@NonNull LifecycleOwner owner) {
if (disposed) {
return;
}
mapView.onResume();
@@ -1049,36 +1017,28 @@ public void onActivityResumed(Activity activity) {
}

@Override
public void onActivityPaused(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onPause(@NonNull LifecycleOwner owner) {
if (disposed) {
return;
}
mapView.onPause();
stopListeningForLocationUpdates();
mapView.onResume();
}

@Override
public void onActivityStopped(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onStop(@NonNull LifecycleOwner owner) {
if (disposed) {
return;
}
mapView.onStop();
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
public void onDestroy(@NonNull LifecycleOwner owner) {
owner.getLifecycle().removeObserver(this);
if (disposed) {
return;
}
mapView.onSaveInstanceState(outState);
}

@Override
public void onActivityDestroyed(Activity activity) {
if (disposed || activity.hashCode() != registrarActivityHashCode) {
return;
}
mapView.onDestroy();
destroyMapViewIfNecessary();
}

// MapboxMapOptionsSink methods
@@ -1279,8 +1239,7 @@ private int checkSelfPermission(String permission) {
* @return
*/
private Bitmap getScaledImage(String imageId, float density) {
AssetManager assetManager = registrar.context().getAssets();
AssetFileDescriptor assetFileDescriptor = null;
AssetFileDescriptor assetFileDescriptor;

// Split image path into parts.
List<String> imagePathList = Arrays.asList(imageId.split("/"));
@@ -1293,7 +1252,7 @@ private Bitmap getScaledImage(String imageId, float density) {
String assetPath;
if (i == 1) {
// If density is 1.0x then simply take the default asset path
assetPath = registrar.lookupKeyForAsset(imageId);
assetPath = MapboxMapsPlugin.flutterAssets.getAssetFilePathByName(imageId);
} else {
// Build a resolution aware asset path as follows:
// <directory asset>/<ratio>/<image name>
@@ -1306,7 +1265,7 @@ private Bitmap getScaledImage(String imageId, float density) {
stringBuilder.append(((float) i) + "x");
stringBuilder.append("/");
stringBuilder.append(imagePathList.get(imagePathList.size()-1));
assetPath = registrar.lookupKeyForAsset(stringBuilder.toString());
assetPath = MapboxMapsPlugin.flutterAssets.getAssetFilePathByName(stringBuilder.toString());
}
// Build up a list of resolution aware asset paths.
assetPathList.add(assetPath);
@@ -1317,7 +1276,7 @@ private Bitmap getScaledImage(String imageId, float density) {
for (String assetPath : assetPathList) {
try {
// Read path (throws exception if doesn't exist).
assetFileDescriptor = assetManager.openFd(assetPath);
assetFileDescriptor = mapView.getContext().getAssets().openFd(assetPath);
InputStream assetStream = assetFileDescriptor.createInputStream();
bitmap = BitmapFactory.decodeStream(assetStream);
assetFileDescriptor.close(); // Close for memory
20 changes: 9 additions & 11 deletions android/src/main/java/com/mapbox/mapboxgl/MapboxMapFactory.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package com.mapbox.mapboxgl;

import static io.flutter.plugin.common.PluginRegistry.Registrar;

import android.content.Context;

import com.mapbox.mapboxsdk.camera.CameraPosition;

import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class MapboxMapFactory extends PlatformViewFactory {

private final AtomicInteger mActivityState;
private final Registrar mPluginRegistrar;
private final BinaryMessenger messenger;
private final MapboxMapsPlugin.LifecycleProvider lifecycleProvider;

public MapboxMapFactory(AtomicInteger state, Registrar registrar) {
public MapboxMapFactory(BinaryMessenger messenger, MapboxMapsPlugin.LifecycleProvider lifecycleProvider) {
super(StandardMessageCodec.INSTANCE);
mActivityState = state;
mPluginRegistrar = registrar;
this.messenger = messenger;
this.lifecycleProvider = lifecycleProvider;
}

@Override
@@ -34,6 +32,6 @@ public PlatformView create(Context context, int id, Object args) {
CameraPosition position = Convert.toCameraPosition(params.get("initialCameraPosition"));
builder.setInitialCameraPosition(position);
}
return builder.build(id, context, mActivityState, mPluginRegistrar, (String) params.get("accessToken"));
return builder.build(id, context, messenger, lifecycleProvider, (String) params.get("accessToken"));
}
}
230 changes: 173 additions & 57 deletions android/src/main/java/com/mapbox/mapboxgl/MapboxMapsPlugin.java
Original file line number Diff line number Diff line change
@@ -8,8 +8,16 @@
import android.app.Application;
import android.os.Bundle;

import java.util.concurrent.atomic.AtomicInteger;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry.Registrar;

@@ -19,87 +27,195 @@
* the map. A Texture drawn using MapboxMap bitmap snapshots can then be shown instead of the
* overlay.
*/
public class MapboxMapsPlugin implements Application.ActivityLifecycleCallbacks {
static final int CREATED = 1;
static final int STARTED = 2;
static final int RESUMED = 3;
static final int PAUSED = 4;
static final int STOPPED = 5;
static final int DESTROYED = 6;
private final AtomicInteger state = new AtomicInteger(0);
private final int registrarActivityHashCode;
public class MapboxMapsPlugin implements FlutterPlugin, ActivityAware {

public static void registerWith(Registrar registrar) {
if (registrar.activity() == null) {
// When a background flutter view tries to register the plugin, the registrar has no activity.
// We stop the registration process as this plugin is foreground only.
return;
}
final MapboxMapsPlugin plugin = new MapboxMapsPlugin(registrar);
registrar.activity().getApplication().registerActivityLifecycleCallbacks(plugin);
registrar
.platformViewRegistry()
.registerViewFactory(
"plugins.flutter.io/mapbox_gl", new MapboxMapFactory(plugin.state, registrar));
private static final String VIEW_TYPE = "plugins.flutter.io/mapbox_gl";

MethodChannel methodChannel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/mapbox_gl");
methodChannel.setMethodCallHandler(new GlobalMethodHandler(registrar));
static FlutterAssets flutterAssets;
private Lifecycle lifecycle;

public MapboxMapsPlugin() {
// no-op
}

// New Plugin APIs

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
state.set(CREATED);
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
flutterAssets = binding.getFlutterAssets();

MethodChannel methodChannel = new MethodChannel(binding.getBinaryMessenger(), "plugins.flutter.io/mapbox_gl");
methodChannel.setMethodCallHandler(
new GlobalMethodHandler(
binding.getApplicationContext(),
binding.getFlutterAssets()
)
);

binding
.getPlatformViewRegistry()
.registerViewFactory(
"plugins.flutter.io/mapbox_gl", new MapboxMapFactory(binding.getBinaryMessenger(), new LifecycleProvider() {
@Nullable
@Override
public Lifecycle getLifecycle() {
return lifecycle;
}
}));
}

@Override
public void onActivityStarted(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
state.set(STARTED);
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
// no-op
}

@Override
public void onActivityResumed(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
state.set(RESUMED);
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding);
}

@Override
public void onActivityPaused(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
state.set(PAUSED);
public void onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity();
}

@Override
public void onActivityStopped(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
state.set(STOPPED);
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
public void onDetachedFromActivity() {
lifecycle = null;
}

@Override
public void onActivityDestroyed(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
// Old Plugin APIs

public static void registerWith(Registrar registrar) {
final Activity activity = registrar.activity();
if (activity == null) {
// When a background flutter view tries to register the plugin, the registrar has no activity.
// We stop the registration process as this plugin is foreground only.
return;
}
state.set(DESTROYED);
if (activity instanceof LifecycleOwner) {
registrar
.platformViewRegistry()
.registerViewFactory(
VIEW_TYPE,
new MapboxMapFactory(
registrar.messenger(),
new LifecycleProvider() {
@Override
public Lifecycle getLifecycle() {
return ((LifecycleOwner) activity).getLifecycle();
}
}));
} else {
registrar
.platformViewRegistry()
.registerViewFactory(
VIEW_TYPE,
new MapboxMapFactory(registrar.messenger(), new ProxyLifecycleProvider(activity)));
}

MethodChannel methodChannel = new MethodChannel(
registrar.messenger(),
"plugins.flutter.io/mapbox_gl"
);
methodChannel.setMethodCallHandler(new GlobalMethodHandler(registrar));
}

private MapboxMapsPlugin(Registrar registrar) {
this.registrarActivityHashCode = registrar.activity().hashCode();
private static final class ProxyLifecycleProvider
implements Application.ActivityLifecycleCallbacks, LifecycleOwner, LifecycleProvider {

private final LifecycleRegistry lifecycle = new LifecycleRegistry(this);
private final int registrarActivityHashCode;

private ProxyLifecycleProvider(Activity activity) {
this.registrarActivityHashCode = activity.hashCode();
activity.getApplication().registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}

@Override
public void onActivityStarted(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
}

@Override
public void onActivityResumed(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}

@Override
public void onActivityPaused(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}

@Override
public void onActivityStopped(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}

@Override
public void onActivityDestroyed(Activity activity) {
if (activity.hashCode() != registrarActivityHashCode) {
return;
}
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}

@NonNull
@Override
public Lifecycle getLifecycle() {
return lifecycle;
}
}

interface LifecycleProvider {
@Nullable
Lifecycle getLifecycle();
}

/** Provides a static method for extracting lifecycle objects from Flutter plugin bindings. */
public static class FlutterLifecycleAdapter {

/**
* Returns the lifecycle object for the activity a plugin is bound to.
*
* <p>Returns null if the Flutter engine version does not include the lifecycle extraction code.
* (this probably means the Flutter engine version is too old).
*/
@NonNull
public static Lifecycle getActivityLifecycle(
@NonNull ActivityPluginBinding activityPluginBinding) {
HiddenLifecycleReference reference =
(HiddenLifecycleReference) activityPluginBinding.getLifecycle();
return reference.getLifecycle();
}
}
}
25 changes: 12 additions & 13 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mapbox.mapboxglexample">

<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="mapbox_gl_example"
android:icon="@mipmap/ic_launcher">

<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
@@ -37,6 +25,17 @@
</intent-filter>
</activity>

<activity
android:name=".EmbeddingV1Activity"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>

<meta-data
android:name="flutterEmbedding"
android:value="2" />

</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.mapbox.mapboxglexample;

import android.os.Bundle;

import com.mapbox.mapboxgl.MapboxMapsPlugin;

import io.flutter.app.FlutterActivity;

public class EmbeddingV1Activity extends FlutterActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MapboxMapsPlugin.registerWith(registrarFor("com.mapbox.mapboxgl.MapboxMapsPlugin"));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
package com.mapbox.mapboxglexample;

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.embedding.android.FlutterActivity;

public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}

}