package com.mapbox.mapboxsdk.maps; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.opengl.GLSurfaceView; import android.os.Build; import android.os.Bundle; 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.v4.util.LongSparseArray; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.mapboxsdk.MapStrictMode; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.offline.OfflineGeometryRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.utils.BitmapUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; /** *
* 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 implements NativeMapView.ViewCallback { private final MapCallback mapCallback = new MapCallback(); private final CopyOnWriteArrayList* You must call this method from the parent's Activity#onCreate(Bundle)} or * Fragment#onViewCreated(View, Bundle). *
* You must set a valid access token with {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} * before you call this method or an exception will be thrown. * * @param savedInstanceState Pass in the parent's savedInstanceState. * @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String) */ @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { TelemetryDefinition telemetry = Mapbox.getTelemetry(); if (telemetry != null) { telemetry.onAppUserTurnstileEvent(); } } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { this.savedInstanceState = savedInstanceState; } } private void initialiseDrawingSurface(MapboxMapOptions options) { String localFontFamily = options.getLocalIdeographFontFamily(); if (options.getTextureMode()) { TextureView textureView = new TextureView(getContext()); boolean translucentSurface = options.getTranslucentTextureSurface(); mapRenderer = new TextureViewMapRenderer(getContext(), textureView, localFontFamily, translucentSurface) { @Override protected void onSurfaceCreated(GL10 gl, EGLConfig config) { MapView.this.onSurfaceCreated(); super.onSurfaceCreated(gl, config); } }; addView(textureView, 0); } else { GLSurfaceView glSurfaceView = new GLSurfaceView(getContext()); glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, localFontFamily) { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { MapView.this.onSurfaceCreated(); super.onSurfaceCreated(gl, config); } }; addView(glSurfaceView, 0); } nativeMapView = new NativeMapView(getContext(), getPixelRatio(), this, mapRenderer); nativeMapView.addOnMapChangedListener(change -> { // dispatch events to external listeners if (!onMapChangedListeners.isEmpty()) { for (OnMapChangedListener onMapChangedListener : onMapChangedListeners) { onMapChangedListener.onMapChanged(change); } } }); nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } private void onSurfaceCreated() { hasSurface = true; post(new Runnable() { @Override public void run() { // Initialise only when not destroyed and only once if (!destroyed && mapboxMap == null) { MapView.this.initialiseMap(); mapboxMap.onStart(); } } }); } /** * You must call this method from the parent's Activity#onSaveInstanceState(Bundle) * or Fragment#onSaveInstanceState(Bundle). * * @param outState Pass in the parent's outState. */ @UiThread public void onSaveInstanceState(@NonNull Bundle outState) { if (mapboxMap != null) { outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true); mapboxMap.onSaveInstanceState(outState); } } /** * You must call this method from the parent's Activity#onStart() or Fragment#onStart() */ @UiThread public void onStart() { ConnectivityReceiver.instance(getContext()).activate(); FileSource.getInstance(getContext()).activate(); if (mapboxMap != null) { mapboxMap.onStart(); } if (mapRenderer != null) { mapRenderer.onStart(); } } /** * You must call this method from the parent's Activity#onResume() or Fragment#onResume(). */ @UiThread public void onResume() { if (mapRenderer != null) { mapRenderer.onResume(); } } /** * You must call this method from the parent's Activity#onPause() or Fragment#onPause(). */ @UiThread public void onPause() { if (mapRenderer != null) { mapRenderer.onPause(); } } /** * You must call this method from the parent's Activity#onStop() or Fragment#onStop(). */ @UiThread public void onStop() { if (mapboxMap != null) { // map was destroyed before it was started mapGestureDetector.cancelAnimators(); mapboxMap.onStop(); } if (mapRenderer != null) { mapRenderer.onStop(); } ConnectivityReceiver.instance(getContext()).deactivate(); FileSource.getInstance(getContext()).deactivate(); } /** * You must call this method from the parent's Activity#onDestroy() or Fragment#onDestroyView(). */ @UiThread public void onDestroy() { destroyed = true; onMapChangedListeners.clear(); mapCallback.clearOnMapReadyCallbacks(); if (mapboxMap != null) { mapboxMap.onDestroy(); } if (nativeMapView != null && hasSurface) { // null when destroying an activity programmatically mapbox-navigation-android/issues/503 nativeMapView.destroy(); nativeMapView = null; } if (mapRenderer != null) { mapRenderer.onDestroy(); } } /** * Returns if the map has been destroyed. ** This method can be used to determine if the result of an asynchronous operation should be set. *
* * @return true, if the map has been destroyed */ @UiThread public boolean isDestroyed() { return destroyed; } @Override public boolean onTouchEvent(MotionEvent event) { if (!isMapInitialized() || !isZoomButtonControllerInitialized() || !isGestureDetectorInitialized()) { return super.onTouchEvent(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) { if (!isGestureDetectorInitialized()) { return super.onGenericMotionEvent(event); } return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); } @Override public boolean onHoverEvent(MotionEvent event) { if (!isZoomButtonControllerInitialized()) { return super.onHoverEvent(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 Activity#onLowMemory() or Fragment#onLowMemory(). */ @UiThread public void onLowMemory() { if (nativeMapView != null) { nativeMapView.onLowMemory(); } } /** ** Loads a new map style from the specified URL. *
* {@code url} can take the following forms: *