com.mapbox.mapboxsdk.maps.MapView Maven / Gradle / Ivy
package com.mapbox.mapboxsdk.maps;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.SurfaceTexture;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.app.AlertDialog;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
import com.mapbox.services.android.telemetry.MapboxEvent;
import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
*
* A {@code MapView} provides an embeddable map interface.
* You use this class to display map information and to manipulate the map contents from your application.
* You can center the map on a given coordinate, specify the size of the area you want to display,
* and style the features of the map to fit your application's use case.
*
*
* Use of {@code MapView} requires a Mapbox API access token.
* Obtain an access token on the Mapbox account page.
*
* Warning: Please note that you are responsible for getting permission to use the map data,
* and for ensuring your use adheres to the relevant terms of use.
*/
public class MapView extends FrameLayout {
private NativeMapView nativeMapView;
private boolean destroyed;
private boolean hasSurface;
private MapboxMap mapboxMap;
private MapCallback mapCallback;
private boolean onStartCalled;
private boolean onStopCalled;
private MapGestureDetector mapGestureDetector;
private MapKeyListener mapKeyListener;
private MapZoomButtonController mapZoomButtonController;
@UiThread
public MapView(@NonNull Context context) {
super(context);
initialise(context, MapboxMapOptions.createFromAttributes(context, null));
}
@UiThread
public MapView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialise(context, MapboxMapOptions.createFromAttributes(context, attrs));
}
@UiThread
public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialise(context, MapboxMapOptions.createFromAttributes(context, attrs));
}
@UiThread
public MapView(@NonNull Context context, @Nullable MapboxMapOptions options) {
super(context);
initialise(context, options == null ? MapboxMapOptions.createFromAttributes(context, null) : options);
}
private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) {
if (isInEditMode()) {
// in IDE, show preview map
LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this);
return;
}
// inflate view
View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this);
CompassView compassView = (CompassView) view.findViewById(R.id.compassView);
MyLocationView myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
ImageView attrView = (ImageView) view.findViewById(R.id.attributionView);
initalizeDrawingSurface(context, options);
// create native Map object
nativeMapView = new NativeMapView(this);
// callback for focal point invalidation
FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView);
// callback for registering touch listeners
RegisterTouchListener registerTouchListener = new RegisterTouchListener();
// callback for zooming in the camera
CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator();
// setup components for MapboxMap creation
Projection proj = new Projection(nativeMapView);
UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView));
TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator);
MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint);
MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer));
AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager);
Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings);
mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
registerTouchListener, annotations);
// user input
mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
mapZoomButtonController = new MapZoomButtonController(this, uiSettings, transform);
// inject widgets with MapboxMap
compassView.setMapboxMap(mapboxMap);
myLocationView.setMapboxMap(mapboxMap);
attrView.setOnClickListener(new AttributionOnClickListener(context, transform));
// Ensure this view is interactable
setClickable(true);
setLongClickable(true);
setFocusable(true);
setFocusableInTouchMode(true);
requestDisallowInterceptTouchEvent(true);
// allow onDraw invocation
setWillNotDraw(false);
// notify Map object about current connectivity state
nativeMapView.setReachability(ConnectivityReceiver.instance(context).isConnected(context));
// initialise MapboxMap
mapboxMap.initialise(context, options);
}
private void initalizeDrawingSurface(Context context, MapboxMapOptions options) {
if (options.getTextureMode()) {
TextureView textureView = new TextureView(context);
textureView.setSurfaceTextureListener(new SurfaceTextureListener());
addView(textureView, 0);
} else {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceView.getHolder().addCallback(new SurfaceCallback());
surfaceView.setVisibility(View.VISIBLE);
}
}
//
// Lifecycle events
//
/**
*
* You must call this method from the parent's {@link android.app.Activity#onCreate(Bundle)} or
* {@link android.app.Fragment#onCreate(Bundle)}.
*
* You must set a valid access token with {@link Mapbox#getInstance(Context, String)}) before you call this method
* or an exception will be thrown.
*
* @param savedInstanceState Pass in the parent's savedInstanceState.
* @see Mapbox#getInstance(Context, String)
*/
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
MapboxTelemetry.getInstance().pushEvent(MapboxEvent.buildMapLoadEvent());
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
mapboxMap.onRestoreInstanceState(savedInstanceState);
}
// Initialize EGL
nativeMapView.initializeDisplay();
nativeMapView.initializeContext();
addOnMapChangedListener(mapCallback = new MapCallback(mapboxMap));
}
/**
* You must call this method from the parent's {@link android.app.Activity#onSaveInstanceState(Bundle)}
* or {@link android.app.Fragment#onSaveInstanceState(Bundle)}.
*
* @param outState Pass in the parent's outState.
*/
@UiThread
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
mapboxMap.onSaveInstanceState(outState);
}
/**
* You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()}
*/
@UiThread
public void onStart() {
onStartCalled = true;
mapboxMap.onStart();
ConnectivityReceiver.instance(getContext()).activate();
}
/**
* You must call this method from the parent's {@link Activity#onResume()} or {@link Fragment#onResume()}.
*/
@UiThread
public void onResume() {
if (!onStartCalled) {
// TODO: 26/10/16, can be removed after 5.0.0 release
throw new IllegalStateException("MapView#onStart() was not called. "
+ "You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()}.");
}
}
/**
* You must call this method from the parent's {@link Activity#onPause()} or {@link Fragment#onPause()}.
*/
@UiThread
public void onPause() {
// replaced by onStop in v5.0.0, keep around for future development
}
/**
* You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}.
*/
@UiThread
public void onStop() {
onStopCalled = true;
mapboxMap.onStop();
ConnectivityReceiver.instance(getContext()).deactivate();
}
/**
* You must call this method from the parent's {@link Activity#onDestroy()} or {@link Fragment#onDestroy()}.
*/
@UiThread
public void onDestroy() {
if (!onStopCalled) {
// TODO: 26/10/16, can be removed after 5.0.0 release
throw new IllegalStateException("MapView#onStop() was not called. "
+ "You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}.");
}
destroyed = true;
nativeMapView.terminateContext();
nativeMapView.terminateDisplay();
nativeMapView.destroySurface();
nativeMapView.destroy();
nativeMapView = null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mapZoomButtonController.setVisible(true);
}
return mapGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return mapKeyListener.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
return mapKeyListener.onKeyLongPress(keyCode, event) || super.onKeyLongPress(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return mapKeyListener.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
return mapKeyListener.onTrackballEvent(event) || super.onTrackballEvent(event);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
mapZoomButtonController.setVisible(true);
return true;
case MotionEvent.ACTION_HOVER_EXIT:
mapZoomButtonController.setVisible(false);
return true;
default:
// We are not interested in this event
return false;
}
}
/**
* You must call this method from the parent's {@link Activity#onLowMemory()} or {@link Fragment#onLowMemory()}.
*/
@UiThread
public void onLowMemory() {
nativeMapView.onLowMemory();
}
// Called when debug mode is enabled to update a FPS counter
// Called via JNI from NativeMapView
// Forward to any listener
protected void onFpsChanged(final double fps) {
final MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener();
if (listener != null) {
post(new Runnable() {
@Override
public void run() {
listener.onFpsChanged(fps);
}
});
}
}
/**
*
* Loads a new map style from the specified URL.
*
* {@code url} can take the following forms:
*
* - {@code Style.*}: load one of the bundled styles in {@link Style}.
* - {@code mapbox://styles/
/