diff --git a/res/drawable-xhdpi/maps_default_window.9.png b/res/drawable-xhdpi/maps_default_window.9.png new file mode 100644 index 00000000..57724d79 Binary files /dev/null and b/res/drawable-xhdpi/maps_default_window.9.png differ diff --git a/src/com/google/android/gms/maps/internal/IInfoWindowAdapter.aidl b/src/com/google/android/gms/maps/internal/IInfoWindowAdapter.aidl index 9f0ca6fb..28c2d91d 100644 --- a/src/com/google/android/gms/maps/internal/IInfoWindowAdapter.aidl +++ b/src/com/google/android/gms/maps/internal/IInfoWindowAdapter.aidl @@ -1,4 +1,9 @@ package com.google.android.gms.maps.internal; +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.maps.model.internal.IMarkerDelegate; + interface IInfoWindowAdapter { + IObjectWrapper getInfoWindow(IMarkerDelegate marker); + IObjectWrapper getInfoContents(IMarkerDelegate marker); } diff --git a/src/com/google/android/gms/maps/internal/IOnInfoWindowClickListener.aidl b/src/com/google/android/gms/maps/internal/IOnInfoWindowClickListener.aidl index 5f495438..b9301030 100644 --- a/src/com/google/android/gms/maps/internal/IOnInfoWindowClickListener.aidl +++ b/src/com/google/android/gms/maps/internal/IOnInfoWindowClickListener.aidl @@ -1,4 +1,7 @@ package com.google.android.gms.maps.internal; +import com.google.android.gms.maps.model.internal.IMarkerDelegate; + interface IOnInfoWindowClickListener { + void onInfoWindowClick(IMarkerDelegate marker); } diff --git a/src/org/microg/gms/maps/GoogleMapImpl.java b/src/org/microg/gms/maps/GoogleMapImpl.java index 425bdfb9..5c981163 100644 --- a/src/org/microg/gms/maps/GoogleMapImpl.java +++ b/src/org/microg/gms/maps/GoogleMapImpl.java @@ -25,7 +25,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; +import android.widget.RelativeLayout; import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.maps.GoogleMapOptions; @@ -49,44 +49,104 @@ public class GoogleMapImpl { private final ViewGroup view; private final GoogleMapOptions options; + private final Delegate delegate = new Delegate(); private final UiSettings uiSettings = new UiSettings(); private final Projection projection = new Projection(); - private MapView mapView; - private int mapType = 1; + + private MapView mapView; private Context context; + private InfoWindow infoWindow; + private int markerCounter = 0; + + private IOnCameraChangeListener cameraChangeListener; + private IOnMapClickListener mapClickListener; + private IOnMapLoadedCallback mapLoadedCallback; + private IOnMapLongClickListener mapLongClickListener; private IOnMarkerClickListener markerClickListener; - public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) { + private IOnMarkerDragListener markerDragListener; + private IOnInfoWindowClickListener infoWindowClickListener; + + private int mapType = 1; + private IInfoWindowAdapter infoWindowAdapter; + + public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) { context = inflater.getContext(); - this.view = new FrameLayout(context); - try { - mapView = (MapView) Class.forName("com.google.android.maps.MapView").getConstructor(Context.class, String.class).newInstance(context, null); - view.addView(mapView); - } catch (Exception e) { - Log.d(TAG, "Sorry, can't create legacy MapView"); - } + this.view = new RelativeLayout(context); + try { + mapView = (MapView) Class.forName("com.google.android.maps.MapView").getConstructor(Context.class, String.class).newInstance(context, null); + prepareMapView(); + view.addView(mapView); + } catch (Exception e) { + Log.d(TAG, "Sorry, can't create legacy MapView"); + } this.options = options; } + private void prepareMapView() { + mapView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.d(TAG, "onClick"); + IOnMapClickListener listener = mapClickListener; + if (listener != null) { + try { + // TODO: Handle LatLng right + listener.onMapClick(new LatLng(0, 0)); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } + } + }); + mapView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + Log.d(TAG, "onLongClick"); + IOnMapLongClickListener listener = mapLongClickListener; + if (listener != null) { + try { + // TODO: Handle LatLng right + listener.onMapLongClick(new LatLng(0, 0)); + } catch (RemoteException e) { + Log.w(TAG, e); + } + return true; + } + return false; + } + }); + } + public void onCreate(Bundle savedInstanceState) { - try { - delegate.animateCamera(new CameraUpdateFactoryImpl().newCameraPosition(options.getCamera())); - delegate.setMapType(options.getMapType()); - uiSettings.setCompassEnabled(options.isCompassEnabled()); - uiSettings.setZoomControlsEnabled(options.isZoomControlsEnabled()); - uiSettings.setRotateGesturesEnabled(options.isRotateGesturesEnabled()); - uiSettings.setScrollGesturesEnabled(options.isScrollGesturesEnabled()); - uiSettings.setTiltGesturesEnabled(options.isTiltGesturesEnabled()); - uiSettings.setZoomGesturesEnabled(options.isZoomGesturesEnabled()); - } catch (RemoteException ignored) { - // It's not remote... + if (options != null) { + try { + delegate.animateCamera(new CameraUpdateFactoryImpl().newCameraPosition(options.getCamera())); + delegate.setMapType(options.getMapType()); + uiSettings.setCompassEnabled(options.isCompassEnabled()); + uiSettings.setZoomControlsEnabled(options.isZoomControlsEnabled()); + uiSettings.setRotateGesturesEnabled(options.isRotateGesturesEnabled()); + uiSettings.setScrollGesturesEnabled(options.isScrollGesturesEnabled()); + uiSettings.setTiltGesturesEnabled(options.isTiltGesturesEnabled()); + uiSettings.setZoomGesturesEnabled(options.isZoomGesturesEnabled()); + } catch (RemoteException ignored) { + // It's not remote... + } } - } + if (savedInstanceState != null) { + savedInstanceState.setClassLoader(GoogleMapImpl.class.getClassLoader()); + mapView.onRestoreInstanceState(savedInstanceState); + } + } public IOnMarkerClickListener getMarkerClickListener() { return markerClickListener; } + public IOnInfoWindowClickListener getInfoWindowClickListener() { + return infoWindowClickListener; + } + public Context getContext() { return context; } @@ -108,8 +168,15 @@ public class GoogleMapImpl { } public void remove(MarkerImpl marker) { - mapView.getOverlays().remove(marker); - } + mapView.getOverlays().remove(marker.getOverlay()); + try { + if (infoWindow != null && infoWindow.getMarker().getId().equals(marker.getId())) { + hideInfoWindow(); + } + } catch (RemoteException e) { + // It's not remote... + } + } public void redraw() { mapView.postInvalidate(); @@ -120,6 +187,49 @@ public class GoogleMapImpl { } } + public void onSaveInstanceState(Bundle outState) { + mapView.onSaveInstanceState(outState); + } + + public void hideInfoWindow() { + if (infoWindow != null) { + mapView.getOverlays().remove(infoWindow); + infoWindow.destroy(); + } + infoWindow = null; + } + + public void showInfoWindow(final MarkerImpl marker) { + hideInfoWindow(); + InfoWindow window = new InfoWindow(context, this, marker); + if (infoWindowAdapter != null) { + try { + IObjectWrapper infoWindow = infoWindowAdapter.getInfoWindow(marker); + window.setWindow((View) ObjectWrapper.unwrap(infoWindow)); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } + if (!window.isComplete()) { + if (infoWindowAdapter != null) { + try { + IObjectWrapper contents = infoWindowAdapter.getInfoContents(marker); + window.setContent((View) ObjectWrapper.unwrap(contents)); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } + } + if (!window.isComplete()) { + window.buildDefault(); + } + if (window.isComplete()) { + infoWindow = window; + Log.d(TAG, "Showing info window " + infoWindow + " for marker " + marker); + mapView.getOverlays().add(infoWindow); + } + } + private class Delegate extends IGoogleMapDelegate.Stub { @Override public CameraPosition getCameraPosition() throws RemoteException { @@ -176,8 +286,10 @@ public class GoogleMapImpl { @Override public IMarkerDelegate addMarker(MarkerOptions options) throws RemoteException { - MarkerImpl marker = new MarkerImpl(options, GoogleMapImpl.this); - mapView.getOverlays().add(marker.getOverlay()); + MarkerImpl marker = new MarkerImpl("m" + markerCounter++, options, GoogleMapImpl.this); + if (infoWindow != null) mapView.getOverlays().remove(infoWindow); + mapView.getOverlays().add(marker.getOverlay()); + if (infoWindow != null) mapView.getOverlays().add(infoWindow); redraw(); return marker; } @@ -195,6 +307,7 @@ public class GoogleMapImpl { @Override public void clear() throws RemoteException { mapView.getOverlays().clear(); + hideInfoWindow(); redraw(); } @@ -265,38 +378,18 @@ public class GoogleMapImpl { @Override public void setOnCameraChangeListener(IOnCameraChangeListener listener) throws RemoteException { - - } + cameraChangeListener = listener; + } @Override public void setOnMapClickListener(final IOnMapClickListener listener) throws RemoteException { - mapView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - Log.d(TAG, "onMapClick:"); - listener.onMapClick(new LatLng(0, 0)); - } catch (RemoteException ignored) { - } - } - }); - } + mapClickListener = listener; + } @Override public void setOnMapLongClickListener(final IOnMapLongClickListener listener) throws RemoteException { - mapView.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - try { - Log.d(TAG, "onMapLongClick:"); - listener.onMapLongClick(new LatLng(0, 0)); - } catch (RemoteException e) { - return false; - } - return true; - } - }); - } + mapLongClickListener = listener; + } @Override public void setOnMarkerClickListener(IOnMarkerClickListener listener) throws RemoteException { @@ -305,18 +398,18 @@ public class GoogleMapImpl { @Override public void setOnMarkerDragListener(IOnMarkerDragListener listener) throws RemoteException { - - } + markerDragListener = listener; + } @Override public void setOnInfoWindowClickListener(IOnInfoWindowClickListener listener) throws RemoteException { - - } + infoWindowClickListener = listener; + } @Override public void setInfoWindowAdapter(IInfoWindowAdapter adapter) throws RemoteException { - - } + infoWindowAdapter = adapter; + } @Override public IObjectWrapper getTestingHelper() throws RemoteException { @@ -360,9 +453,9 @@ public class GoogleMapImpl { @Override public void setOnMapLoadedCallback(IOnMapLoadedCallback callback) throws RemoteException { - - } - } + mapLoadedCallback = callback; + } + } private class UiSettings extends IUiSettingsDelegate.Stub { diff --git a/src/org/microg/gms/maps/MapFragmentImpl.java b/src/org/microg/gms/maps/MapFragmentImpl.java index 82222099..ec5c2ed0 100644 --- a/src/org/microg/gms/maps/MapFragmentImpl.java +++ b/src/org/microg/gms/maps/MapFragmentImpl.java @@ -104,7 +104,7 @@ public class MapFragmentImpl extends IMapFragmentDelegate.Stub { @Override public void onSaveInstanceState(Bundle outState) throws RemoteException { - + myMap().onSaveInstanceState(outState); } @Override diff --git a/src/org/microg/gms/maps/camera/CameraUpdateFactoryImpl.java b/src/org/microg/gms/maps/camera/CameraUpdateFactoryImpl.java index 444520d0..b63660e5 100644 --- a/src/org/microg/gms/maps/camera/CameraUpdateFactoryImpl.java +++ b/src/org/microg/gms/maps/camera/CameraUpdateFactoryImpl.java @@ -100,6 +100,14 @@ public class CameraUpdateFactoryImpl extends ICameraUpdateFactoryDelegate.Stub { @Override public IObjectWrapper newCameraPosition(final CameraPosition cameraPosition) throws RemoteException { Log.d(TAG, "newCameraPosition"); + if (cameraPosition == null) { + return new ObjectWrapper(new CameraUpdate() { + @Override + public void update(GoogleMapImpl map) { + // Nothing + } + }); + } return newLatLngZoom(cameraPosition.target, cameraPosition.zoom); } diff --git a/src/org/microg/gms/maps/markup/InfoWindow.java b/src/org/microg/gms/maps/markup/InfoWindow.java new file mode 100644 index 00000000..6b05206f --- /dev/null +++ b/src/org/microg/gms/maps/markup/InfoWindow.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2014 μg 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.markup; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.google.android.gms.R; +import com.google.android.gms.maps.internal.IOnInfoWindowClickListener; +import com.google.android.gms.maps.model.internal.IMarkerDelegate; +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapView; +import com.google.android.maps.Overlay; +import org.microg.gms.maps.GoogleMapImpl; +import org.microg.gms.maps.ResourcesContainer; + +public class InfoWindow extends Overlay { + private static final String TAG = InfoWindow.class.getName(); + private Context context; + private View window; + private GoogleMapImpl map; + private MarkerImpl marker; + + public InfoWindow(Context context, final GoogleMapImpl map, final MarkerImpl marker) { + super(); + this.context = context; + this.map = map; + this.marker = marker; + } + + public void setWindow(View view) { + window = view; + if (window != null) { + window.measure(0, 0); + } + } + + public boolean isComplete() { + return window != null; + } + + public void setContent(View view) { + if (view == null) return; + setWindow(new DefaultWindow(view)); + } + + public void buildDefault() { + try { + if (marker.getTitle() != null) + setContent(new DefaultContent()); + } catch (RemoteException e) { + // Not remote... + } + } + + public void destroy() { + if (window instanceof DefaultWindow) { + ((DefaultWindow) window).removeAllViews(); + } + } + + public IMarkerDelegate getMarker() { + return marker; + } + + @Override + public void draw(Canvas canvas, MapView mapView, boolean shadow) { + if (window != null && marker.getHeight() != -1) { + try { + Point zero = mapView.getProjection().toPixels(new GeoPoint(0, 0), null); + Point point = mapView.getProjection().toPixels(marker.getPosition().toGeoPoint(), null); + point.offset(-zero.x, -zero.y); + point.offset(-window.getMeasuredWidth() / 2, -window.getMeasuredHeight() - marker.getHeight()); + Log.d(TAG, point.toString()); + window.layout(0, 0, window.getMeasuredWidth(), window.getMeasuredHeight()); + canvas.save(); + canvas.translate(point.x, point.y); + window.draw(canvas); + canvas.restore(); + } catch (RemoteException e) { + // This is not remote... + } + } + } + + @Override + public boolean onTap(GeoPoint p, MapView mapView) { + try { + IOnInfoWindowClickListener listener = map.getInfoWindowClickListener(); + if (listener != null) { + Point clickPoint = mapView.getProjection().toPixels(p, null); + Point markerPoint = mapView.getProjection().toPixels(marker.getPosition().toGeoPoint(), null); + Rect rect = new Rect(markerPoint.x - (window.getMeasuredWidth() / 2), + markerPoint.y - marker.getHeight() - window.getMeasuredHeight(), + markerPoint.x + (window.getMeasuredWidth() / 2), + markerPoint.y - marker.getHeight()); + if (rect.contains(clickPoint.x, clickPoint.y)) { + try { + listener.onInfoWindowClick(marker); + } catch (RemoteException e) { + Log.w(TAG, e); + } + return true; + } + } + } catch (RemoteException e) { + // This is not remote... + } + return false; + } + + private class DefaultWindow extends FrameLayout { + public DefaultWindow(View view) { + super(context); + addView(view); + setBackground(ResourcesContainer.get().getDrawable(R.drawable.maps_default_window)); + } + } + + private class DefaultContent extends LinearLayout { + public DefaultContent() { + super(context); + setOrientation(LinearLayout.VERTICAL); + try { + TextView title = new TextView(context); + title.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium_Inverse); + title.setText(marker.getTitle()); + addView(title); + if (marker.getSnippet() != null) { + TextView snippet = new TextView(context); + snippet.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Inverse); + snippet.setText(marker.getSnippet()); + addView(snippet); + } + } catch (RemoteException e) { + // ... + } + } + } +} diff --git a/src/org/microg/gms/maps/markup/MarkerImpl.java b/src/org/microg/gms/maps/markup/MarkerImpl.java index 2a9cf2f0..9ceb28d2 100644 --- a/src/org/microg/gms/maps/markup/MarkerImpl.java +++ b/src/org/microg/gms/maps/markup/MarkerImpl.java @@ -35,10 +35,11 @@ import org.microg.gms.maps.GoogleMapImpl; public class MarkerImpl extends IMarkerDelegate.Stub { private static final String TAG = MarkerImpl.class.getName(); + private final String id; + private float alpha; private boolean flat; private boolean draggable; - private String id = Integer.toHexString(hashCode()); private LatLng position; private float anchorU; private float anchorV; @@ -74,7 +75,7 @@ public class MarkerImpl extends IMarkerDelegate.Stub { } if (!result) { mapView.getController().animateTo(position.toGeoPoint()); - // TODO info window + map.showInfoWindow(MarkerImpl.this); } return true; } @@ -109,7 +110,8 @@ public class MarkerImpl extends IMarkerDelegate.Stub { } }; - public MarkerImpl(MarkerOptions options, GoogleMapImpl map) { + public MarkerImpl(String id, MarkerOptions options, GoogleMapImpl map) { + this.id = id; this.map = map; this.alpha = options.getAlpha(); this.draggable = options.isDraggable(); @@ -124,7 +126,7 @@ public class MarkerImpl extends IMarkerDelegate.Stub { this.icon = options.getIcon(); if (icon == null) icon = new BitmapDescriptor(new ObjectWrapper(new DefaultBitmapDescriptor(0))); - Log.d(TAG, "New: " + title + " @ " + position + ", " + icon); + Log.d(TAG, "New: " + id + " with title " + title + " @ " + position); } @Override @@ -267,4 +269,10 @@ public class MarkerImpl extends IMarkerDelegate.Stub { public Overlay getOverlay() { return overlay; } + + public int getHeight() { + Bitmap bitmap = icon.getBitmap(); + if (bitmap == null) return -1; + return bitmap.getHeight(); + } }