mirror of https://github.com/YTVanced/VancedMicroG
593 lines
20 KiB
Java
593 lines
20 KiB
Java
/*
|
|
* Copyright 2013-2015 microG Project Team
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.microg.gms.maps;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.graphics.Bitmap;
|
|
import android.location.Criteria;
|
|
import android.location.Location;
|
|
import android.location.LocationListener;
|
|
import android.location.LocationManager;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Parcel;
|
|
import android.os.RemoteException;
|
|
import android.support.v4.app.ActivityCompat;
|
|
import android.support.v4.content.ContextCompat;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
|
|
import com.google.android.gms.dynamic.IObjectWrapper;
|
|
import com.google.android.gms.dynamic.ObjectWrapper;
|
|
import com.google.android.gms.maps.GoogleMapOptions;
|
|
import com.google.android.gms.maps.internal.ICancelableCallback;
|
|
import com.google.android.gms.maps.internal.IGoogleMapDelegate;
|
|
import com.google.android.gms.maps.internal.IInfoWindowAdapter;
|
|
import com.google.android.gms.maps.internal.ILocationSourceDelegate;
|
|
import com.google.android.gms.maps.internal.IOnCameraChangeListener;
|
|
import com.google.android.gms.maps.internal.IOnInfoWindowClickListener;
|
|
import com.google.android.gms.maps.internal.IOnMapClickListener;
|
|
import com.google.android.gms.maps.internal.IOnMapLoadedCallback;
|
|
import com.google.android.gms.maps.internal.IOnMapLongClickListener;
|
|
import com.google.android.gms.maps.internal.IOnMarkerClickListener;
|
|
import com.google.android.gms.maps.internal.IOnMarkerDragListener;
|
|
import com.google.android.gms.maps.internal.IOnMyLocationButtonClickListener;
|
|
import com.google.android.gms.maps.internal.IOnMyLocationChangeListener;
|
|
import com.google.android.gms.maps.internal.IProjectionDelegate;
|
|
import com.google.android.gms.maps.internal.ISnapshotReadyCallback;
|
|
import com.google.android.gms.maps.internal.IUiSettingsDelegate;
|
|
import com.google.android.gms.maps.model.CameraPosition;
|
|
import com.google.android.gms.maps.model.CircleOptions;
|
|
import com.google.android.gms.maps.model.GroundOverlayOptions;
|
|
import com.google.android.gms.maps.model.MarkerOptions;
|
|
import com.google.android.gms.maps.model.PolygonOptions;
|
|
import com.google.android.gms.maps.model.PolylineOptions;
|
|
import com.google.android.gms.maps.model.TileOverlayOptions;
|
|
import com.google.android.gms.maps.model.internal.ICircleDelegate;
|
|
import com.google.android.gms.maps.model.internal.IGroundOverlayDelegate;
|
|
import com.google.android.gms.maps.model.internal.IMarkerDelegate;
|
|
import com.google.android.gms.maps.model.internal.IPolygonDelegate;
|
|
import com.google.android.gms.maps.model.internal.IPolylineDelegate;
|
|
import com.google.android.gms.maps.model.internal.ITileOverlayDelegate;
|
|
|
|
import org.microg.gms.maps.camera.CameraUpdate;
|
|
import org.microg.gms.maps.camera.MapPositionCameraUpdate;
|
|
import org.microg.gms.maps.markup.CircleImpl;
|
|
import org.microg.gms.maps.markup.GroundOverlayImpl;
|
|
import org.microg.gms.maps.markup.MarkerImpl;
|
|
import org.microg.gms.maps.markup.Markup;
|
|
import org.microg.gms.maps.markup.PolygonImpl;
|
|
import org.microg.gms.maps.markup.PolylineImpl;
|
|
import org.microg.gms.maps.markup.TileOverlayImpl;
|
|
|
|
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
|
|
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|
|
|
public class GoogleMapImpl extends IGoogleMapDelegate.Stub
|
|
implements UiSettingsImpl.UiSettingsListener, Markup.MarkupListener, BackendMap.CameraUpdateListener {
|
|
private static final String TAG = "GmsMapImpl";
|
|
|
|
private final GoogleMapOptions options;
|
|
private final Context context;
|
|
private final BackendMap backendMap;
|
|
private final UiSettingsImpl uiSettings;
|
|
private final ProjectionImpl projection;
|
|
|
|
private int markerCounter = 0;
|
|
private int circleCounter = 0;
|
|
private int polylineCounter = 0;
|
|
private int polygonCounter = 0;
|
|
|
|
private IOnMarkerClickListener onMarkerClickListener;
|
|
private IOnMarkerDragListener onMarkerDragListener;
|
|
private IOnCameraChangeListener onCameraChangeListener;
|
|
private IOnMyLocationChangeListener onMyLocationChangeListener;
|
|
|
|
private Criteria criteria;
|
|
private LocationListener listener = new LocationListener() {
|
|
@Override
|
|
public void onLocationChanged(Location location) {
|
|
// TODO: Actually do my location overlay
|
|
if (onMyLocationChangeListener != null && location != null) {
|
|
try {
|
|
onMyLocationChangeListener.onMyLocationChanged(ObjectWrapper.wrap(location));
|
|
} catch (RemoteException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
}
|
|
|
|
@Override
|
|
public void onProviderEnabled(String provider) {
|
|
}
|
|
|
|
@Override
|
|
public void onProviderDisabled(String provider) {
|
|
}
|
|
};
|
|
|
|
private GoogleMapImpl(Context context, GoogleMapOptions options) {
|
|
this.context = context;
|
|
Context appContext = context;
|
|
if (appContext.getApplicationContext() != null)
|
|
appContext = appContext.getApplicationContext();
|
|
Context wrappedContext = ApplicationContextWrapper.gmsContextWithAttachedApplicationContext(appContext);
|
|
backendMap = new BackendMap(wrappedContext, this);
|
|
uiSettings = new UiSettingsImpl(this);
|
|
projection = new ProjectionImpl(backendMap.getViewport());
|
|
this.options = options;
|
|
|
|
criteria = new Criteria();
|
|
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
|
|
criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
|
|
|
|
if (options != null) initFromOptions();
|
|
}
|
|
|
|
public synchronized static GoogleMapImpl create(Context context, GoogleMapOptions options) {
|
|
return new GoogleMapImpl(context, options);
|
|
}
|
|
|
|
private void initFromOptions() {
|
|
try {
|
|
uiSettings.setCompassEnabled(options.isCompassEnabled());
|
|
uiSettings.setRotateGesturesEnabled(options.isRotateGesturesEnabled());
|
|
uiSettings.setTiltGesturesEnabled(options.isTiltGesturesEnabled());
|
|
uiSettings.setScrollGesturesEnabled(options.isScrollGesturesEnabled());
|
|
uiSettings.setZoomControlsEnabled(options.isZoomControlsEnabled());
|
|
uiSettings.setZoomGesturesEnabled(options.isZoomGesturesEnabled());
|
|
if (options.getCamera() != null) {
|
|
backendMap.applyCameraUpdate(MapPositionCameraUpdate.directMapPosition(GmsMapsTypeHelper.fromCameraPosition(options.getCamera())));
|
|
}
|
|
} catch (RemoteException e) {
|
|
// Never happens: not remote
|
|
}
|
|
}
|
|
|
|
public void onDestroy() {
|
|
backendMap.destroy();
|
|
}
|
|
|
|
public void onResume() {
|
|
backendMap.onResume();
|
|
}
|
|
|
|
public void onPause() {
|
|
backendMap.onPause();
|
|
}
|
|
|
|
public View getView() {
|
|
return backendMap.getView();
|
|
}
|
|
|
|
private String getNextMarkerId() {
|
|
return "m" + markerCounter++;
|
|
}
|
|
|
|
private String getNextCircleId() {
|
|
return "c" + circleCounter++;
|
|
}
|
|
|
|
private String getNextPolylineId() {
|
|
return "l" + polylineCounter++;
|
|
}
|
|
|
|
private String getNextPolygonId() {
|
|
return "p" + polygonCounter++;
|
|
}
|
|
|
|
/*
|
|
Camera
|
|
*/
|
|
|
|
@Override
|
|
public CameraPosition getCameraPosition() throws RemoteException {
|
|
return GmsMapsTypeHelper.toCameraPosition(backendMap.getMapPosition());
|
|
}
|
|
|
|
@Override
|
|
public float getMaxZoomLevel() throws RemoteException {
|
|
return (float) backendMap.getViewport().limitScale(Double.MIN_VALUE);
|
|
}
|
|
|
|
@Override
|
|
public float getMinZoomLevel() throws RemoteException {
|
|
return (float) backendMap.getViewport().limitScale(Double.MAX_VALUE);
|
|
}
|
|
|
|
@Override
|
|
public void moveCamera(IObjectWrapper cameraUpdate) throws RemoteException {
|
|
CameraUpdate camUpdate = (CameraUpdate) ObjectWrapper.unwrap(cameraUpdate);
|
|
backendMap.applyCameraUpdate(camUpdate);
|
|
}
|
|
|
|
@Override
|
|
public void animateCamera(IObjectWrapper cameraUpdate) throws RemoteException {
|
|
CameraUpdate camUpdate = (CameraUpdate) ObjectWrapper.unwrap(cameraUpdate);
|
|
backendMap.applyCameraUpdateAnimated(camUpdate, 1000);
|
|
}
|
|
|
|
@Override
|
|
public void animateCameraWithCallback(IObjectWrapper cameraUpdate, ICancelableCallback callback)
|
|
throws RemoteException {
|
|
CameraUpdate camUpdate = (CameraUpdate) ObjectWrapper.unwrap(cameraUpdate);
|
|
backendMap.applyCameraUpdateAnimated(camUpdate, 1000);
|
|
}
|
|
|
|
@Override
|
|
public void animateCameraWithDurationAndCallback(IObjectWrapper cameraUpdate, int duration,
|
|
ICancelableCallback callback) throws RemoteException {
|
|
CameraUpdate camUpdate = (CameraUpdate) ObjectWrapper.unwrap(cameraUpdate);
|
|
backendMap.applyCameraUpdateAnimated(camUpdate, duration);
|
|
}
|
|
|
|
@Override
|
|
public IProjectionDelegate getProjection() throws RemoteException {
|
|
return projection;
|
|
}
|
|
|
|
@Override
|
|
public void stopAnimation() throws RemoteException {
|
|
backendMap.stopAnimation();
|
|
}
|
|
|
|
@Override
|
|
public void onCameraUpdate(CameraPosition cameraPosition) {
|
|
if (onCameraChangeListener != null) {
|
|
try {
|
|
onCameraChangeListener.onCameraChange(cameraPosition);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Markers, polylines, polygons, overlays, etc
|
|
*/
|
|
|
|
@Override
|
|
public ICircleDelegate addCircle(CircleOptions options) throws RemoteException {
|
|
return backendMap.add(new CircleImpl(getNextCircleId(), options, this));
|
|
}
|
|
|
|
@Override
|
|
public IPolylineDelegate addPolyline(PolylineOptions options) throws RemoteException {
|
|
return backendMap.add(new PolylineImpl(getNextPolylineId(), options, this));
|
|
}
|
|
|
|
@Override
|
|
public IPolygonDelegate addPolygon(PolygonOptions options) throws RemoteException {
|
|
return backendMap.add(new PolygonImpl(getNextPolygonId(), options, this));
|
|
}
|
|
|
|
@Override
|
|
public IMarkerDelegate addMarker(MarkerOptions options) throws RemoteException {
|
|
return backendMap.add(new MarkerImpl(getNextMarkerId(), options, this));
|
|
}
|
|
|
|
@Override
|
|
public IGroundOverlayDelegate addGroundOverlay(GroundOverlayOptions options)
|
|
throws RemoteException {
|
|
Log.d(TAG, "not yet usable: addGroundOverlay");
|
|
return new GroundOverlayImpl(options); // TODO
|
|
}
|
|
|
|
@Override
|
|
public ITileOverlayDelegate addTileOverlay(TileOverlayOptions options) throws RemoteException {
|
|
Log.d(TAG, "not yet usable: addTileOverlay");
|
|
return new TileOverlayImpl(); // TODO
|
|
}
|
|
|
|
@Override
|
|
public void setInfoWindowAdapter(IInfoWindowAdapter adapter) throws RemoteException {
|
|
Log.d(TAG, "not yet usable: setInfoWindowAdapter");
|
|
}
|
|
|
|
@Override
|
|
public void clear() throws RemoteException {
|
|
backendMap.clear();
|
|
markerCounter = 0;
|
|
circleCounter = 0;
|
|
polylineCounter = 0;
|
|
polygonCounter = 0;
|
|
}
|
|
|
|
@Override
|
|
public void update(Markup markup) {
|
|
backendMap.update(markup);
|
|
}
|
|
|
|
@Override
|
|
public void remove(Markup markup) {
|
|
backendMap.remove(markup);
|
|
}
|
|
|
|
@Override
|
|
public boolean onClick(Markup markup) {
|
|
if (markup instanceof IMarkerDelegate) {
|
|
if (onMarkerClickListener != null) {
|
|
try {
|
|
if (onMarkerClickListener.onMarkerClick((IMarkerDelegate) markup))
|
|
return true;
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
// TODO: open InfoWindow
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void onDragStart(Markup markup) {
|
|
backendMap.setScrollGesturesEnabled(false);
|
|
backendMap.setRotateGesturesEnabled(false);
|
|
backendMap.setTiltGesturesEnabled(false);
|
|
backendMap.setZoomGesturesEnabled(false);
|
|
if (markup instanceof IMarkerDelegate) {
|
|
if (onMarkerDragListener != null) {
|
|
try {
|
|
onMarkerDragListener.onMarkerDragStart((IMarkerDelegate) markup);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDragStop(Markup markup) {
|
|
try {
|
|
backendMap.setScrollGesturesEnabled(uiSettings.isScrollGesturesEnabled());
|
|
backendMap.setRotateGesturesEnabled(uiSettings.isRotateGesturesEnabled());
|
|
backendMap.setTiltGesturesEnabled(uiSettings.isTiltGesturesEnabled());
|
|
backendMap.setZoomGesturesEnabled(uiSettings.isZoomGesturesEnabled());
|
|
} catch (RemoteException e) {
|
|
// Never happens, is local.
|
|
}
|
|
if (markup instanceof IMarkerDelegate) {
|
|
if (onMarkerDragListener != null) {
|
|
try {
|
|
onMarkerDragListener.onMarkerDragEnd((IMarkerDelegate) markup);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDragProgress(Markup markup) {
|
|
if (markup instanceof IMarkerDelegate) {
|
|
if (onMarkerDragListener != null) {
|
|
try {
|
|
onMarkerDragListener.onMarkerDrag((IMarkerDelegate) markup);
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Map options
|
|
*/
|
|
|
|
@Override
|
|
public int getMapType() throws RemoteException {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void setMapType(int type) throws RemoteException {
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean isTrafficEnabled() throws RemoteException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void setTrafficEnabled(boolean traffic) throws RemoteException {
|
|
Log.w(TAG, "Traffic not yet supported");
|
|
}
|
|
|
|
@Override
|
|
public boolean isIndoorEnabled() throws RemoteException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void setIndoorEnabled(boolean indoor) throws RemoteException {
|
|
Log.w(TAG, "Indoor not yet supported");
|
|
}
|
|
|
|
@Override
|
|
public boolean isMyLocationEnabled() throws RemoteException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void setMyLocationEnabled(boolean myLocation) throws RemoteException {
|
|
Log.w(TAG, "MyLocation not yet supported");
|
|
boolean hasPermission = ContextCompat.checkSelfPermission(context, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
|
|
|| ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED;
|
|
if (!hasPermission) {
|
|
if (context instanceof Activity)
|
|
ActivityCompat.requestPermissions((Activity) context, new String[]{ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}, -1);
|
|
return;
|
|
}
|
|
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
|
if (myLocation) {
|
|
locationManager.requestLocationUpdates(5000, 10, criteria, listener, Looper.getMainLooper());
|
|
} else {
|
|
locationManager.removeUpdates(listener);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isBuildingsEnabled() throws RemoteException {
|
|
return backendMap.hasBuilding();
|
|
}
|
|
|
|
@Override
|
|
public void setBuildingsEnabled(boolean buildingsEnabled) throws RemoteException {
|
|
backendMap.setBuildings(buildingsEnabled);
|
|
}
|
|
|
|
/*
|
|
Ui Settings
|
|
*/
|
|
|
|
@Override
|
|
public IUiSettingsDelegate getUiSettings() throws RemoteException {
|
|
return uiSettings;
|
|
}
|
|
|
|
@Override
|
|
public void onUiSettingsChanged(UiSettingsImpl settings) throws RemoteException {
|
|
if (settings.isCompassEnabled()) {
|
|
Log.w(TAG, "Compass not yet supported");
|
|
}
|
|
if (settings.isMyLocationButtonEnabled()) {
|
|
Log.w(TAG, "MyLocationButton not yet supported");
|
|
}
|
|
if (settings.isZoomControlsEnabled()) {
|
|
Log.w(TAG, "ZoomControls not yet supported");
|
|
}
|
|
backendMap.setScrollGesturesEnabled(settings.isScrollGesturesEnabled());
|
|
backendMap.setRotateGesturesEnabled(settings.isRotateGesturesEnabled());
|
|
backendMap.setTiltGesturesEnabled(settings.isTiltGesturesEnabled());
|
|
backendMap.setZoomGesturesEnabled(settings.isZoomGesturesEnabled());
|
|
}
|
|
|
|
/*
|
|
Listener and callback setters
|
|
*/
|
|
|
|
@Override
|
|
public void setOnCameraChangeListener(IOnCameraChangeListener listener) throws RemoteException {
|
|
Log.d(TAG, "setOnCameraChangeListener");
|
|
this.onCameraChangeListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void setOnMapClickListener(IOnMapClickListener listener) throws RemoteException {
|
|
Log.d(TAG, "setOnMapClickListener: not supported");
|
|
}
|
|
|
|
@Override
|
|
public void setOnMapLongClickListener(IOnMapLongClickListener listener) throws RemoteException {
|
|
Log.d(TAG, "setOnMapLongClickListener: not supported");
|
|
}
|
|
|
|
@Override
|
|
public void setOnMarkerClickListener(IOnMarkerClickListener listener) throws RemoteException {
|
|
Log.d(TAG, "setOnMarkerClickListener");
|
|
this.onMarkerClickListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void setOnMarkerDragListener(IOnMarkerDragListener listener) throws RemoteException {
|
|
Log.d(TAG, "setOnMarkerDragListener");
|
|
this.onMarkerDragListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void setOnInfoWindowClickListener(IOnInfoWindowClickListener listener)
|
|
throws RemoteException {
|
|
Log.d(TAG, "setOnInfoWindowClickListener: not supported");
|
|
}
|
|
|
|
@Override
|
|
public void setOnMyLocationChangeListener(IOnMyLocationChangeListener listener)
|
|
throws RemoteException {
|
|
Log.d(TAG, "setOnMyLocationChangeListener");
|
|
this.onMyLocationChangeListener = listener;
|
|
}
|
|
|
|
@Override
|
|
public void setOnMyLocationButtonClickListener(IOnMyLocationButtonClickListener listener)
|
|
throws RemoteException {
|
|
Log.d(TAG, "setOnMyLocationButtonClickListener: not supported");
|
|
}
|
|
|
|
@Override
|
|
public void setOnMapLoadedCallback(final IOnMapLoadedCallback callback) throws RemoteException {
|
|
Log.d(TAG, "setOnMapLoadedCallback");
|
|
new Handler(context.getMainLooper()).postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Log.d(TAG, "Announce map loaded");
|
|
try {
|
|
callback.onMapLoaded();
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, e);
|
|
}
|
|
}
|
|
}, 5000);
|
|
}
|
|
|
|
/*
|
|
Misc
|
|
*/
|
|
|
|
@Override
|
|
public IObjectWrapper getTestingHelper() throws RemoteException {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void snapshot(ISnapshotReadyCallback callback, IObjectWrapper bitmap)
|
|
throws RemoteException {
|
|
Bitmap b = (Bitmap) ObjectWrapper.unwrap(bitmap);
|
|
Log.d(TAG, "snapshot!: " + b);
|
|
backendMap.snapshot(b, callback);
|
|
}
|
|
|
|
@Override
|
|
public void setPadding(int left, int top, int right, int bottom) throws RemoteException {
|
|
getView().setPadding(left, top, right, bottom);
|
|
}
|
|
|
|
@Override
|
|
public Location getMyLocation() throws RemoteException {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void setLocationSource(ILocationSourceDelegate locationSource) throws RemoteException {
|
|
Log.d(TAG, "setLocationSource: " + locationSource);
|
|
}
|
|
|
|
@Override
|
|
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
|
if (super.onTransact(code, data, reply, flags)) return true;
|
|
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
|
|
return false;
|
|
}
|
|
}
|