From b28ed88992b2d798fc563365a382455ede2b233e Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 13 Feb 2023 11:13:27 +0100 Subject: [PATCH 01/17] Initial lite mode implementation --- .../gms/maps/mapbox/CameraUpdateFactory.kt | 117 +++- .../org/microg/gms/maps/mapbox/GoogleMap.kt | 48 +- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 528 ++++++++++++++++++ .../org/microg/gms/maps/mapbox/MapFragment.kt | 45 +- .../org/microg/gms/maps/mapbox/MapView.kt | 30 +- .../org/microg/gms/maps/mapbox/Projection.kt | 44 ++ .../gms/maps/mapbox/model/BitmapDescriptor.kt | 10 + .../mapbox/model/BitmapDescriptorFactory.kt | 11 +- .../microg/gms/maps/mapbox/model/Circle.kt | 179 +++--- .../gms/maps/mapbox/model/InfoWindow.kt | 4 +- .../microg/gms/maps/mapbox/model/Marker.kt | 273 ++++++--- .../microg/gms/maps/mapbox/model/Markup.kt | 2 +- .../microg/gms/maps/mapbox/model/Polygon.kt | 176 ++++-- .../microg/gms/maps/mapbox/model/Polyline.kt | 94 ++-- 14 files changed, 1263 insertions(+), 298 deletions(-) create mode 100644 play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt index 675429b90..c45c454af 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt @@ -17,11 +17,13 @@ package org.microg.gms.maps.mapbox import android.graphics.Point +import android.graphics.PointF import android.os.Parcel import android.util.Log import com.google.android.gms.dynamic.IObjectWrapper import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.internal.ICameraUpdateFactoryDelegate +import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLngBounds @@ -32,29 +34,40 @@ import org.microg.gms.maps.mapbox.utils.toMapbox class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { - override fun zoomIn(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomIn()) - override fun zoomOut(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomOut()) + override fun zoomIn(): IObjectWrapper = ObjectWrapper.wrap(ZoomByCameraUpdate(1f)) + override fun zoomOut(): IObjectWrapper = ObjectWrapper.wrap(ZoomByCameraUpdate(-1f)) - override fun zoomTo(zoom: Float): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.zoomTo(zoom.toDouble() - 1.0)) + override fun zoomTo(zoom: Float): IObjectWrapper = ObjectWrapper.wrap(ZoomToCameraUpdate(zoom)) override fun zoomBy(zoomDelta: Float): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta.toDouble())) + ObjectWrapper.wrap(ZoomByCameraUpdate(zoomDelta)).also { + Log.d(TAG, "zoomBy") + } override fun zoomByWithFocus(zoomDelta: Float, x: Int, y: Int): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta.toDouble(), Point(x, y))) + ObjectWrapper.wrap(ZoomByWithFocusCameraUpdate(zoomDelta, x, y)).also { + Log.d(TAG, "zoomByWithFocus") + } override fun newCameraPosition(cameraPosition: CameraPosition): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.newCameraPosition(cameraPosition.toMapbox())) + ObjectWrapper.wrap(NewCameraPositionCameraUpdate(cameraPosition)).also { + Log.d(TAG, "newCameraPosition") + } override fun newLatLng(latLng: LatLng): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.newLatLng(latLng.toMapbox())) + ObjectWrapper.wrap(NewLatLngCameraUpdate(latLng)).also { + Log.d(TAG, "newLatLng") + } override fun newLatLngZoom(latLng: LatLng, zoom: Float): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.newLatLngZoom(latLng.toMapbox(), zoom.toDouble() - 1.0)) + ObjectWrapper.wrap(NewLatLngZoomCameraUpdate(latLng, zoom)).also { + Log.d(TAG, "newLatLngZoom") + } override fun newLatLngBounds(bounds: LatLngBounds, padding: Int): IObjectWrapper = - ObjectWrapper.wrap(CameraUpdateFactory.newLatLngBounds(bounds.toMapbox(), padding)) + ObjectWrapper.wrap(NewLatLngBoundsCameraUpdate(bounds, padding)).also { + Log.d(TAG, "newLatLngBounds") + } override fun scrollBy(x: Float, y: Float): IObjectWrapper { Log.d(TAG, "unimplemented Method: scrollBy") @@ -62,7 +75,9 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { } override fun newLatLngBoundsWithSize(bounds: LatLngBounds, width: Int, height: Int, padding: Int): IObjectWrapper = - ObjectWrapper.wrap(CameraBoundsWithSizeUpdate(bounds.toMapbox(), width, height, padding)) + ObjectWrapper.wrap(NewLatLngBoundsWithSizeCameraUpdate(bounds, width, height, padding)).also { + Log.d(TAG, "newLatLngBoundsWithSize") + } override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = if (super.onTransact(code, data, reply, flags)) { @@ -71,9 +86,11 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false } - private inner class NoCameraUpdate : CameraUpdate { + private inner class NoCameraUpdate : CameraUpdate, GmsCameraUpdate { override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = mapboxMap.cameraPosition + + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = map.cameraPosition } companion object { @@ -81,4 +98,80 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { } } +interface GmsCameraUpdate { + fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition +} + +class ZoomToCameraUpdate(private val zoom: Float) : GmsCameraUpdate, CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).zoom(zoom).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.zoomTo(zoom.toDouble() - 1.0).getCameraPosition(mapboxMap) + +} + +class ZoomByCameraUpdate(private val delta: Float) : GmsCameraUpdate, CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).zoom(map.cameraPosition.zoom + delta).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.zoomBy(delta.toDouble()).getCameraPosition(mapboxMap) + +} + +class ZoomByWithFocusCameraUpdate(private val delta: Float, private val x: Int, private val y: Int) : GmsCameraUpdate, + CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).zoom(map.cameraPosition.zoom + delta) + .target(map.projection.fromScreenLocation(ObjectWrapper.wrap(PointF(x.toFloat(), y.toFloat())))).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.zoomBy(delta.toDouble(), Point(x, y)).getCameraPosition(mapboxMap) +} + +class NewCameraPositionCameraUpdate(private val cameraPosition: CameraPosition) : GmsCameraUpdate, CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = this.cameraPosition + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition = + this.cameraPosition.toMapbox() +} + +class NewLatLngCameraUpdate(private val latLng: LatLng) : GmsCameraUpdate, CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).target(latLng).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.newLatLng(latLng.toMapbox()).getCameraPosition(mapboxMap) +} + +class NewLatLngZoomCameraUpdate(private val latLng: LatLng, private val zoom: Float) : GmsCameraUpdate, CameraUpdate { + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).target(latLng).zoom(zoom).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.newLatLngZoom(latLng.toMapbox(), zoom - 1.0).getCameraPosition(mapboxMap) +} + +class NewLatLngBoundsCameraUpdate(private val bounds: LatLngBounds, private val padding: Int) : GmsCameraUpdate, + CameraUpdate { + + // TODO + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).target(bounds.center).zoom(4f).build() + + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraUpdateFactory.newLatLngBounds(bounds.toMapbox(), padding).getCameraPosition(mapboxMap) +} + +class NewLatLngBoundsWithSizeCameraUpdate( + private val bounds: LatLngBounds, private val width: Int, private val height: Int, private val padding: Int +) : GmsCameraUpdate, CameraUpdate { + + // TODO + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + CameraPosition.Builder(map.cameraPosition).target(bounds.center).zoom(4f).build() + override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = + CameraBoundsWithSizeUpdate(bounds.toMapbox(), width, height, padding).getCameraPosition(mapboxMap) +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index 297dc873a..8da855d4e 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -78,6 +78,14 @@ fun runOnMainLooper(method: () -> Unit) { } } +fun getStyleUriByMapType(mapType: Int) = when (mapType) { + MAP_TYPE_SATELLITE -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" + MAP_TYPE_TERRAIN -> "mapbox://styles/mapbox/outdoors-v12" + MAP_TYPE_HYBRID -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" + //MAP_TYPE_NONE, MAP_TYPE_NORMAL, + else -> "mapbox://styles/microg/cjui4020201oo1fmca7yuwbor" +} + class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() { val view: FrameLayout @@ -290,6 +298,13 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } else { fill.update(fillManager) } + + val lineManager = lineManager + if (lineManager == null) { + pendingLines.addAll(fill.strokes) + } else { + for (stroke in fill.strokes) stroke.update(lineManager) + } } return fill } @@ -373,13 +388,11 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } // TODO: Serve map styles locally - when (storedMapType) { - MAP_TYPE_SATELLITE -> map?.setStyle(Style.Builder().fromUri("mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi"), update) - MAP_TYPE_TERRAIN -> map?.setStyle(Style.Builder().fromUri("mapbox://styles/mapbox/outdoors-v12"), update) - MAP_TYPE_HYBRID -> map?.setStyle(Style.Builder().fromUri("mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi"), update) - //MAP_TYPE_NONE, MAP_TYPE_NORMAL, - else -> map?.setStyle(Style.Builder().fromUrl("mapbox://styles/microg/cjui4020201oo1fmca7yuwbor"), update) - } + map?.setStyle( + Style.Builder().fromUri( + getStyleUriByMapType(storedMapType) + ), update + ) map?.let { BitmapDescriptorFactoryImpl.registerMap(it) } @@ -449,29 +462,14 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) initializedCallbackList.add(it.getMapReadyCallback()) } - override fun getProjection(): IProjectionDelegate? = map?.projection?.let { + override fun getProjection(): IProjectionDelegate = map?.projection?.let { val experiment = try { map?.cameraPosition?.tilt == 0.0 && map?.cameraPosition?.bearing == 0.0 } catch (e: Exception) { Log.w(TAG, e); false } ProjectionImpl(it, experiment) - } ?: object : IProjectionDelegate.Stub() { // dummy projection if map not initialized - override fun fromScreenLocation(obj: IObjectWrapper?): LatLng { - Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate fromScreenLocation") - return LatLng(0.0, 0.0) - } - - override fun toScreenLocation(latLng: LatLng?): IObjectWrapper { - Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate toScreenLocation") - return ObjectWrapper.wrap(Point(0, 0)) - } - - override fun getVisibleRegion(): VisibleRegion { - Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate getVisibleRegion") - return VisibleRegion(LatLngBounds(LatLng(0.0, 0.0), LatLng(0.0, 0.0))) - } - } + } ?: DummyProjection() override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) { cameraChangeListener = listener @@ -515,7 +513,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) override fun getTestingHelper(): IObjectWrapper? { Log.d(TAG, "unimplemented Method: getTestingHelper") - return null + return ObjectWrapper.wrap(null) } override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt new file mode 100644 index 000000000..2b3b94c21 --- /dev/null +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -0,0 +1,528 @@ +package org.microg.gms.maps.mapbox + +import android.content.Context +import android.graphics.PointF +import android.location.Location +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.DisplayMetrics +import android.util.Log +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.annotation.UiThread +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper +import com.google.android.gms.dynamic.unwrap +import com.google.android.gms.maps.GoogleMapOptions +import com.google.android.gms.maps.internal.* +import com.google.android.gms.maps.model.* +import com.google.android.gms.maps.model.internal.* +import com.mapbox.mapboxsdk.Mapbox +import com.mapbox.mapboxsdk.WellKnownTileServer +import com.mapbox.mapboxsdk.maps.Style +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter +import com.mapbox.mapboxsdk.style.layers.FillLayer +import com.mapbox.mapboxsdk.style.layers.LineLayer +import com.mapbox.mapboxsdk.style.layers.Property +import com.mapbox.mapboxsdk.style.layers.PropertyFactory +import com.mapbox.mapboxsdk.style.layers.SymbolLayer +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource +import org.microg.gms.maps.mapbox.model.* +import org.microg.gms.maps.mapbox.utils.MapContext +import org.microg.gms.maps.mapbox.utils.toGms +import org.microg.gms.maps.mapbox.utils.toMapbox +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.max + +class MetaSnapshot(val snapshot: MapSnapshot, val cameraPosition: CameraPosition, val width: Int, val height: Int) + +class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() { + + private val mapContext = MapContext(context) + + internal val view: FrameLayout = FrameLayout(mapContext) + val map: ImageView + + val dpiFactor: Float + get() = mapContext.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT + + private var created = false + + private var cameraPosition = options.camera + private var mapType: Int = options.mapType + + private var currentSnapshotter: MapSnapshotter? = null + + private var lastSnapshot: MetaSnapshot? = null + + private val afterNextDrawCallback = mutableListOf<() -> Unit>() + private var cameraChangeListener: IOnCameraChangeListener? = null + + internal val markers: MutableList = mutableListOf() + internal val polygons: MutableList = mutableListOf() + internal val polylines: MutableList = mutableListOf() + internal val circles: MutableList = mutableListOf() + + private var nextObjectId = 0 + + private var showWatermark = true + + private val updatePosted = AtomicBoolean(false) + + init { + map = ImageView(mapContext).apply { + layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) + } + + view.addView(map) + + view.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> postUpdateSnapshot() } + + BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) + } + + override fun onCreate(savedInstanceState: Bundle?) { + if (!created) { + + Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY, WellKnownTileServer.Mapbox) + + if (savedInstanceState?.containsKey(BUNDLE_CAMERA_POSITION) == true) { + cameraPosition = savedInstanceState.getParcelable(BUNDLE_CAMERA_POSITION) + } + + postUpdateSnapshot() + + created = true + } + } + + internal fun postUpdateSnapshot() { + if (updatePosted.compareAndSet(false, true)) { + Handler(Looper.getMainLooper()).post { + updatePosted.set(false) + updateSnapshot() + } + } + } + + @UiThread + private fun updateSnapshot() { + + val cameraPosition = cameraPosition + val width = max(map.width, 1) + val height = max(map.height, 1) + + val styleBuilder = Style.Builder().fromUri(getStyleUriByMapType(mapType)) + + // Add visible polygons (before polylines, so that they are drawn below their strokes) + for (polygon in polygons.filter { it.isVisible }) { + styleBuilder.withLayer( + FillLayer("l${polygon.id}", polygon.id).withProperties( + PropertyFactory.fillColor(polygon.fillColor) + ) + ).withSource( + GeoJsonSource(polygon.id, polygon.annotationOptions.geometry) + ) + } + + // Add visible polylines + for (polyline in polylines.filter { it.isVisible }) { + styleBuilder.withLayer( + LineLayer("l${polyline.id}", polyline.id).withProperties( + PropertyFactory.lineWidth(polyline.width), + PropertyFactory.lineColor(polyline.color), + PropertyFactory.lineCap(Property.LINE_CAP_ROUND) + ) + ).withSource( + GeoJsonSource(polyline.id, polyline.annotationOptions.geometry) + ) + } + + // Add circles + for (circle in circles.filter { it.isVisible }) { + styleBuilder.withLayer(FillLayer("l${circle.id}c", circle.id).withProperties( + PropertyFactory.fillColor(circle.fillColor) + )).withSource(GeoJsonSource(circle.id, circle.annotationOptions.geometry)) + + styleBuilder.withLayer(LineLayer("l${circle.id}s", "${circle.id}s").withProperties( + PropertyFactory.lineWidth(circle.strokeWidth), + PropertyFactory.lineColor(circle.strokeColor), + PropertyFactory.lineCap(Property.LINE_CAP_ROUND) + )).withSource(GeoJsonSource("${circle.id}s", circle.line.annotationOptions.geometry)) + } + + // Add markers + BitmapDescriptorFactoryImpl.put(styleBuilder) + for (marker in markers.filter { it.isVisible }) { + val layer = SymbolLayer("l${marker.id}", marker.id).withProperties( + PropertyFactory.symbolSortKey(marker.zIndex), + PropertyFactory.iconAllowOverlap(true) + ) + marker.icon?.applyTo(layer, marker.anchor, dpiFactor) + styleBuilder.withLayer(layer).withSource( + GeoJsonSource(marker.id, marker.annotationOptions.geometry) + ) + } + + val snapshotter = MapSnapshotter( + mapContext, MapSnapshotter.Options(width, height) + .withCameraPosition(this@LiteGoogleMapImpl.cameraPosition.toMapbox()) + .withStyleBuilder(styleBuilder) + .withLogo(showWatermark) + ) + + synchronized(this) { + this.currentSnapshotter?.cancel() + this.currentSnapshotter = snapshotter + } + + snapshotter.start { + + val cameraPositionChanged = !cameraPosition.equals(lastSnapshot?.cameraPosition) + + lastSnapshot = MetaSnapshot(it, cameraPosition, width, height) + map.setImageBitmap(it.bitmap) + + for (callback in afterNextDrawCallback) callback() + afterNextDrawCallback.clear() + + if (cameraPositionChanged) { + // Notify apps that new projection is now available + cameraChangeListener?.onCameraChange(cameraPosition) + } + + synchronized(this) { + this.currentSnapshotter = null + } + + } + } + + fun getMapAsync(callback: IOnMapReadyCallback) { + if (lastSnapshot == null) { + Log.d(TAG, "Invoking callback instantly, as a snapshot is ready") + callback.onMapReady(this) + } else { + Log.d(TAG, "Delay callback invocation, as snapshot has not been rendered yet") + afterNextDrawCallback.add { callback.onMapReady(this) } + } + } + + override fun getCameraPosition(): CameraPosition = cameraPosition + + override fun getMaxZoomLevel() = 21f + + override fun getMinZoomLevel() = 1f + + override fun moveCamera(cameraUpdate: IObjectWrapper?): Unit = cameraUpdate.unwrap()?.let { + cameraPosition = it.getCameraPosition(this) + + postUpdateSnapshot() + } ?: Unit + + override fun animateCamera(cameraUpdate: IObjectWrapper?) = moveCamera(cameraUpdate) + + override fun animateCameraWithCallback(cameraUpdate: IObjectWrapper?, callback: ICancelableCallback?) { + moveCamera(cameraUpdate) + Log.d(TAG, "animateCameraWithCallback: animation not possible in lite mode, invoking callback instantly") + callback?.onFinish() + } + + override fun animateCameraWithDurationAndCallback( + cameraUpdate: IObjectWrapper?, duration: Int, callback: ICancelableCallback? + ) = animateCameraWithCallback(cameraUpdate, callback) + + override fun stopAnimation() { + Log.d(TAG, "stopAnimation: animation not possible in lite mode") + } + + override fun addPolyline(options: PolylineOptions): IPolylineDelegate { + return LitePolylineImpl(this, "polyline${nextObjectId++}", options).also { polylines.add(it) } + } + + override fun addPolygon(options: PolygonOptions): IPolygonDelegate { + return LitePolygonImpl( + "polygon${nextObjectId++}", options, this + ).also { + polygons.add(it) + polylines.addAll(it.strokes) + postUpdateSnapshot() + } + } + + override fun addMarker(options: MarkerOptions): IMarkerDelegate { + return LiteMarkerImpl("marker${nextObjectId++}", options, this).also { + markers.add(it) + postUpdateSnapshot() + } + } + + override fun addGroundOverlay(options: GroundOverlayOptions?): IGroundOverlayDelegate? { + Log.d(TAG, "addGroundOverlay: not supported in lite mode") + return null + } + + override fun addTileOverlay(options: TileOverlayOptions?): ITileOverlayDelegate? { + Log.d(TAG, "addTileOverlay: not supported in lite mode") + return null + } + + override fun clear() { + polylines.clear() + polygons.clear() + markers.clear() + circles.clear() + postUpdateSnapshot() + } + + override fun getMapType(): Int { + return mapType + } + + override fun setMapType(type: Int) { + mapType = type + postUpdateSnapshot() + } + + override fun isTrafficEnabled(): Boolean { + Log.d(TAG, "isTrafficEnabled: traffic not supported in lite mode") + return false + } + + override fun setTrafficEnabled(traffic: Boolean) { + Log.d(TAG, "setTrafficEnabled: traffic not supported in lite mode") + } + + override fun isIndoorEnabled(): Boolean { + Log.d(TAG, "isIndoorEnabled: indoor not supported in lite mode") + return false + } + + override fun setIndoorEnabled(indoor: Boolean) { + Log.d(TAG, "setIndoorEnabled: indoor not supported in lite mode") + } + + override fun isMyLocationEnabled(): Boolean { + TODO("Not yet implemented") + } + + override fun setMyLocationEnabled(myLocation: Boolean) { + TODO("Not yet implemented") + } + + override fun getMyLocation(): Location? { + Log.d(TAG, "unimplemented Method: getMyLocation") + return null + } + + override fun setLocationSource(locationSource: ILocationSourceDelegate?) { + Log.d(TAG, "unimplemented Method: setLocationSource") + } + + override fun getUiSettings(): IUiSettingsDelegate { + Log.d(TAG, "UI settings have no effect") + return UiSettingsCache() + } + + /** + * Gets a projection snapshot. This means that, in accordance to the docs, the projection object + * will represent the map as it is seen at the point in time that the projection is queried, and + * not updated later on. + */ + override fun getProjection(): IProjectionDelegate = lastSnapshot?.let { LiteProjection(it) } ?: DummyProjection() + + override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) { + cameraChangeListener = listener + } + + override fun setOnMapClickListener(listener: IOnMapClickListener?) { + // TODO + view.setOnClickListener { + listener?.onMapClick( + lastSnapshot?.snapshot?.latLngForPixel(PointF(0f, 0f))?.toGms() ?: LatLng(0.0, 0.0) + ) + } + } + + override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) { + view.setOnLongClickListener { + listener?.onMapLongClick(lastSnapshot?.snapshot?.latLngForPixel(PointF(0f, 0f))?.toGms() ?: LatLng(0.0, 0.0)) + listener != null + } + } + + override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) { + // TODO("Not yet implemented") + } + + override fun setOnMarkerDragListener(listener: IOnMarkerDragListener?) { + Log.d(TAG, "setOnMarkerDragListener: marker drag is not supported in lite mode") + } + + override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { + // TODO("Not yet implemented") + } + + override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) { + // TODO("Not yet implemented") + } + + override fun getTestingHelper(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getTestingHelper") + return ObjectWrapper.wrap(null) + } + + override fun addCircle(options: CircleOptions): ICircleDelegate { + return LiteCircleImpl(this, "circle${nextObjectId++}", options).also { circles.add(it) } + } + + override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) { + TODO("Not yet implemented") + } + + override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) { + TODO("Not yet implemented") + } + + override fun snapshot(callback: ISnapshotReadyCallback?, bitmap: IObjectWrapper?) { + val lastSnapshot = lastSnapshot + if (lastSnapshot == null) { + afterNextDrawCallback.add { + callback?.onBitmapWrappedReady(ObjectWrapper.wrap(this@LiteGoogleMapImpl.lastSnapshot!!.snapshot.bitmap)) + } + } else { + callback?.onBitmapWrappedReady(ObjectWrapper.wrap(lastSnapshot.snapshot.bitmap)) + } + } + + override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { + Log.d(TAG, "setPadding: not implemented") + } + + override fun isBuildingsEnabled(): Boolean { + Log.d(TAG, "isBuildingsEnabled: never enabled in light mode") + return false + } + + override fun setBuildingsEnabled(buildings: Boolean) { + Log.d(TAG, "setBuildingsEnabled: cannot be enabled in light mode") + } + + override fun setOnMapLoadedCallback(callback: IOnMapLoadedCallback?) = callback?.let { onMapLoadedCallback -> + if (lastSnapshot != null) { + Log.d(TAG, "Invoking map loaded callback instantly, as a snapshot is ready") + onMapLoadedCallback.onMapLoaded() + } + else { + Log.d(TAG, "Delaying map loaded callback, as snapshot has not been taken yet") + afterNextDrawCallback.add { onMapLoadedCallback.onMapLoaded() } + } + Unit + } ?: Unit + + override fun setWatermarkEnabled(watermark: Boolean) { + showWatermark = watermark + } + + override fun onResume() { + } + + override fun onPause() { + synchronized(this) { + currentSnapshotter?.cancel() + currentSnapshotter = null + } + } + + override fun onDestroy() { + view.removeView(map) + } + + override fun onLowMemory() { + } + + override fun useViewLifecycleWhenInFragment(): Boolean { + Log.d(TAG, "unimplemented Method: useViewLifecycleWhenInFragment") + return false + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putParcelable(BUNDLE_CAMERA_POSITION, cameraPosition) + } + + override fun setContentDescription(desc: String?) { + view.contentDescription = desc + } + + override fun onEnterAmbient(bundle: Bundle?) { + Log.d(TAG, "unimplemented Method: onEnterAmbient") + } + + override fun onExitAmbient() { + Log.d(TAG, "unimplemented Method: onExitAmbient") + } + + override fun setInfoWindowLongClickListener(listener: IOnInfoWindowLongClickListener?) { + // TODO("Not yet implemented") + } + + override fun setInfoWindowCloseListener(listener: IOnInfoWindowCloseListener?) { + // TODO("Not yet implemented") + } + + override fun setCircleClickListener(listener: IOnCircleClickListener?) { + // TODO("Not yet implemented") + } + + override fun setMapStyle(options: MapStyleOptions?): Boolean { + Log.d(TAG, "setMapStyle options: " + options?.getJson()) + return true + } + + override fun setMinZoomPreference(minZoom: Float) { + Log.d(TAG, "setMinZoomPreference: no interactivity in lite mode") + } + + override fun setMaxZoomPreference(maxZoom: Float) { + Log.d(TAG, "setMaxZoomPreference: no interactivity in lite mode") + } + + override fun resetMinMaxZoomPreference() { + Log.d(TAG, "resetMinMaxZoomPreference: no interactivity in lite mode") + } + + override fun setLatLngBoundsForCameraTarget(bounds: LatLngBounds?) { + Log.d(TAG, "setLatLngBoundsForCameraTarget: no interactivity in lite mode") + } + + override fun setCameraMoveStartedListener(listener: IOnCameraMoveStartedListener?) { + Log.d(TAG, "setCameraMoveStartedListener: event not supported in lite mode") + } + + override fun setCameraMoveListener(listener: IOnCameraMoveListener?) { + Log.d(TAG, "setCameraMoveListener: event not supported in lite mode") + + } + + override fun setCameraMoveCanceledListener(listener: IOnCameraMoveCanceledListener?) { + Log.d(TAG, "setCameraMoveCanceledListener: event not supported in lite mode") + } + + override fun setCameraIdleListener(listener: IOnCameraIdleListener?) { + Log.d(TAG, "setCameraIdleListener: event not supported in lite mode") + } + + override fun onStart() { + } + + override fun onStop() { + } + + companion object { + private val TAG = "GmsMapLite" + private val BUNDLE_CAMERA_POSITION = "camera" + } +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index daf4bfb92..69135ab29 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -32,12 +32,18 @@ import com.google.android.gms.maps.internal.IOnMapReadyCallback class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stub() { - private var map: GoogleMapImpl? = null + private var map: IGoogleMapDelegate? = null private var options: GoogleMapOptions? = null override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle?) { this.options = options - map?.options = options + map?.apply { + if (this is GoogleMapImpl) { + this.options = options + } else if (this is LiteGoogleMapImpl) { + this.options = options + } + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -47,7 +53,11 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu if (options == null) { options = GoogleMapOptions() } - map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) + if (options?.liteMode == true) { + map = LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) + } else { + map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) + } } override fun onCreateView(layoutInflater: IObjectWrapper, container: IObjectWrapper, savedInstanceState: Bundle?): IObjectWrapper { @@ -56,13 +66,25 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu } Log.d(TAG, "onCreateView: ${options?.camera?.target}") if (map == null) { - map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) + map = if (options?.liteMode == true) { + LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) + } else { + GoogleMapImpl(activity, options ?: GoogleMapOptions()) + } + } + map!!.apply { + onCreate(savedInstanceState) + + val view = when (this) { + is GoogleMapImpl -> this.view + is LiteGoogleMapImpl -> this.view + else -> null + } + + val parent = view?.parent as ViewGroup? + parent?.removeView(view) + return ObjectWrapper.wrap(view) } - map!!.onCreate(savedInstanceState) - val view = map!!.view - val parent = view.parent as ViewGroup? - parent?.removeView(view) - return ObjectWrapper.wrap(view) } override fun getMap(): IGoogleMapDelegate? = map @@ -74,7 +96,10 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu override fun onPause() = map?.onPause() ?: Unit override fun onLowMemory() = map?.onLowMemory() ?: Unit override fun isReady(): Boolean = this.map != null - override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit + override fun getMapAsync(callback: IOnMapReadyCallback) = map?.let { + if (it is GoogleMapImpl) it.getMapAsync(callback) + else if (it is LiteGoogleMapImpl) it.getMapAsync(callback) + } ?: Unit override fun onDestroyView() { map?.onDestroy() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt index bef3cd83c..4ce82b13e 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt @@ -30,12 +30,17 @@ import com.google.android.gms.maps.internal.IOnMapReadyCallback class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { private val options: GoogleMapOptions = options ?: GoogleMapOptions() - private var map: GoogleMapImpl? = null + private var map: IGoogleMapDelegate? = null override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "onCreate: ${options?.camera?.target}") - map = GoogleMapImpl(context, options) - map!!.onCreate(savedInstanceState) + map = if (options.liteMode) { + LiteGoogleMapImpl(context, options) + } else { + GoogleMapImpl(context, options) + }.apply { + this.onCreate(savedInstanceState) + } } override fun getMap(): IGoogleMapDelegate? = map @@ -53,8 +58,23 @@ class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IM override fun onLowMemory() = map?.onLowMemory() ?: Unit override fun onSaveInstanceState(outState: Bundle) = map?.onSaveInstanceState(outState) ?: Unit - override fun getView(): IObjectWrapper = ObjectWrapper.wrap(map?.view) - override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit + override fun getView(): IObjectWrapper = ObjectWrapper.wrap( + map?.let { + when (it) { + is GoogleMapImpl -> it.view + is LiteGoogleMapImpl -> it.view + else -> null + } + } + ) + + override fun getMapAsync(callback: IOnMapReadyCallback) = map?.let { + when (it) { + is GoogleMapImpl -> it.getMapAsync(callback) + is LiteGoogleMapImpl -> it.getMapAsync(callback) + else -> null + } + } ?: Unit override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = if (super.onTransact(code, data, reply, flags)) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt index d4ed42f91..42898cf27 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt @@ -26,6 +26,7 @@ import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.VisibleRegion import com.mapbox.mapboxsdk.maps.Projection import com.google.android.gms.dynamic.unwrap +import com.google.android.gms.maps.model.LatLngBounds import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox import kotlin.math.roundToInt @@ -78,3 +79,46 @@ class ProjectionImpl(private val projection: Projection, private val withoutTilt private val TAG = "GmsMapProjection" } } + +class LiteProjection(private val snapshot: MetaSnapshot) : IProjectionDelegate.Stub() { + + private fun fromScreenLocation(point: Point?): LatLng = + point?.let { snapshot.snapshot.latLngForPixel(PointF(point)).toGms() } ?: LatLng(0.0, 0.0) + + override fun fromScreenLocation(obj: IObjectWrapper?): LatLng = fromScreenLocation(obj.unwrap()) + + override fun toScreenLocation(latLng: LatLng?): IObjectWrapper = + ObjectWrapper.wrap(snapshot.snapshot.pixelForLatLng(latLng?.toMapbox()).let { + Point(it.x.roundToInt(), it.y.roundToInt()) + }) + + override fun getVisibleRegion(): VisibleRegion { + val nearLeft = fromScreenLocation(Point(0, snapshot.height)) + val nearRight = fromScreenLocation(Point(snapshot.width, snapshot.height)) + val farLeft = fromScreenLocation(Point(0, 0)) + val farRight = fromScreenLocation(Point(snapshot.width, 0)) + + return VisibleRegion(nearLeft, nearRight, farLeft, farRight, LatLngBounds(nearLeft, farRight)) + } +} + +class DummyProjection : IProjectionDelegate.Stub() { + override fun fromScreenLocation(obj: IObjectWrapper?): LatLng { + Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate fromScreenLocation") + return LatLng(0.0, 0.0) + } + + override fun toScreenLocation(latLng: LatLng?): IObjectWrapper { + Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate toScreenLocation") + return ObjectWrapper.wrap(Point(0, 0)) + } + + override fun getVisibleRegion(): VisibleRegion { + Log.d(TAG, "Map not initialized when calling getProjection(). Cannot calculate getVisibleRegion") + return VisibleRegion(LatLngBounds(LatLng(0.0, 0.0), LatLng(0.0, 0.0))) + } + + companion object { + private val TAG = "GmsMapDummyProjection" + } +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptor.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptor.kt index f0dc0f882..48b16f754 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptor.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptor.kt @@ -22,6 +22,8 @@ import android.util.Log import com.mapbox.mapboxsdk.plugins.annotation.Symbol import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_TOP_LEFT +import com.mapbox.mapboxsdk.style.layers.PropertyFactory +import com.mapbox.mapboxsdk.style.layers.SymbolLayer import com.mapbox.mapboxsdk.utils.ColorUtils open class BitmapDescriptorImpl(private val id: String, internal val size: FloatArray) { @@ -34,6 +36,14 @@ open class BitmapDescriptorImpl(private val id: String, internal val size: Float symbol.iconOffset = PointF(-anchor[0] * size[0] / dpiFactor, -anchor[1] * size[1] / dpiFactor) symbol.iconImage = id } + + open fun applyTo(symbolLayer: SymbolLayer, anchor: FloatArray, dpiFactor: Float) { + symbolLayer.withProperties( + PropertyFactory.iconAnchor(ICON_ANCHOR_TOP_LEFT), + PropertyFactory.iconOffset(arrayOf(-anchor[0] * size[0] / dpiFactor, -anchor[1] * size[1] / dpiFactor)), + PropertyFactory.iconImage(id) + ) + } } class ColorBitmapDescriptorImpl(id: String, size: FloatArray, val hue: Float) : BitmapDescriptorImpl(id, size) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt index f4f517481..465230041 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt @@ -18,14 +18,13 @@ package org.microg.gms.maps.mapbox.model import android.content.res.Resources import android.graphics.* -import android.os.Handler -import android.os.Looper import android.os.Parcel import android.util.Log import com.google.android.gms.dynamic.IObjectWrapper import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate import com.mapbox.mapboxsdk.maps.MapboxMap +import com.mapbox.mapboxsdk.maps.Style import org.microg.gms.maps.mapbox.R import org.microg.gms.maps.mapbox.runOnMainLooper @@ -57,6 +56,14 @@ object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { // TODO: cleanup bitmaps? } + fun put(style: Style.Builder) { + synchronized(bitmaps) { + for (bitmap in bitmaps) { + style.withImage(bitmap.key, bitmap.value) + } + } + } + fun bitmapSize(id: String): FloatArray = bitmaps[id]?.let { floatArrayOf(it.width.toFloat(), it.height.toFloat()) } ?: floatArrayOf(0f, 0f) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt index d0aa04dab..984cef9aa 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt @@ -32,6 +32,7 @@ import com.mapbox.turf.TurfMeasurement import com.mapbox.turf.TurfMeta import com.mapbox.turf.TurfTransformation import org.microg.gms.maps.mapbox.GoogleMapImpl +import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import com.google.android.gms.maps.model.CircleOptions as GmsCircleOptions val NORTH_POLE: Point = Point.fromLngLat(0.0, 90.0) @@ -42,15 +43,18 @@ val SOUTH_POLE: Point = Point.fromLngLat(0.0, -90.0) */ const val CIRCLE_POLYGON_STEPS = 256 -class CircleImpl(private val map: GoogleMapImpl, private val id: String, options: GmsCircleOptions) : ICircleDelegate.Stub(), Markup { - private var center: LatLng = options.center - private var radius: Double = options.radius // in meters - private var strokeWidth: Float = options.strokeWidth - private var strokeColor: Int = options.strokeColor - private var fillColor: Int = options.fillColor - private var visible: Boolean = options.isVisible - private var clickable: Boolean = options.isClickable - private var tag: Any? = null +abstract class AbstractCircleImpl( + private val id: String, options: GmsCircleOptions, private val dpiFactor: Function0 +) : ICircleDelegate.Stub() { + + internal var center: LatLng = options.center + internal var radius: Double = options.radius // in meters + internal var strokeWidth: Float = options.strokeWidth + internal var strokeColor: Int = options.strokeColor + internal var fillColor: Int = options.fillColor + internal var visible: Boolean = options.isVisible + internal var clickable: Boolean = options.isClickable + internal var tag: Any? = null internal val line: Markup = object : Markup { override var annotation: Line? = null @@ -60,16 +64,14 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options LineString.fromLngLats( makeOutlineLatLngs() ) - ).withLineWidth(strokeWidth / map.dpiFactor) + ).withLineWidth(strokeWidth / dpiFactor()) .withLineColor(ColorUtils.colorToRgbaString(strokeColor)) .withLineOpacity(if (visible) 1f else 0f) - override val removed: Boolean = false + override var removed: Boolean = false } - override var annotation: Fill? = null - override var removed: Boolean = false - override val annotationOptions: FillOptions + val annotationOptions: FillOptions get() = FillOptions() .withGeometry(makePolygon()) @@ -77,15 +79,17 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options .withFillOutlineColor(ColorUtils.colorToRgbaString(strokeColor)) .withFillOpacity(if (visible && !wrapsAroundPoles()) 1f else 0f) - private fun makePolygon() = TurfTransformation.circle( + internal abstract fun update() + + internal fun makePolygon() = TurfTransformation.circle( Point.fromLngLat(center.longitude, center.latitude), radius, CIRCLE_POLYGON_STEPS, TurfConstants.UNIT_METERS ) /** - * Google's "map renderer is unable to draw the circle fill if - * the circle encompasses either the North or South pole". + * Google's "map renderer is unable to draw the circle fill if the circle encompasses + * either the North or South pole" (though it does so incorrectly anyway) */ - private fun wrapsAroundPoles() = Point.fromLngLat(center.longitude, center.latitude).let { + internal fun wrapsAroundPoles() = Point.fromLngLat(center.longitude, center.latitude).let { TurfMeasurement.distance( it, NORTH_POLE ) * 1000 < radius || TurfMeasurement.distance( @@ -93,7 +97,7 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options ) * 1000 < radius } - private fun makeOutlineLatLngs(): MutableList { + internal fun makeOutlineLatLngs(): MutableList { val pointList = TurfMeta.coordAll( makePolygon(), wrapsAroundPoles() ) @@ -135,67 +139,40 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options return pointList } - private fun updateLatLngs() { - val polygon = makePolygon() - - // Extracts points from generated polygon in expected format - annotation?.latLngs = FillOptions().withGeometry(polygon).latLngs - - line.annotation?.latLngs = makeOutlineLatLngs().map { point -> - com.mapbox.mapboxsdk.geometry.LatLng( - point.latitude(), - point.longitude() - ) - } - - if (wrapsAroundPoles()) { - annotation?.fillOpacity = 0f - } - } - - override fun remove() { - removed = true - map.fillManager?.let { update(it) } - } - override fun getId(): String = id override fun setCenter(center: LatLng) { this.center = center - updateLatLngs() - map.fillManager?.let { update(it) } + update() + } override fun getCenter(): LatLng = center override fun setRadius(radius: Double) { this.radius = radius - updateLatLngs() - map.fillManager?.let { update(it) } + update() } override fun getRadius(): Double = radius override fun setStrokeWidth(width: Float) { this.strokeWidth = width - line.annotation?.lineWidth = width / map.dpiFactor - map.lineManager?.let { line.update(it) } + update() } override fun getStrokeWidth(): Float = strokeWidth override fun setStrokeColor(color: Int) { this.strokeColor = color - line.annotation?.setLineColor(color) - map.lineManager?.let { line.update(it) } + update() } override fun getStrokeColor(): Int = strokeColor override fun setFillColor(color: Int) { this.fillColor = color - annotation?.setFillColor(color) - map.fillManager?.let { update(it) } + update() } override fun getFillColor(): Int = fillColor @@ -211,8 +188,7 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options override fun setVisible(visible: Boolean) { this.visible = visible - annotation?.fillOpacity = if (visible && !wrapsAroundPoles()) 1f else 0f - map.fillManager?.let { update(it) } + update() } override fun isVisible(): Boolean = visible @@ -229,20 +205,6 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options return clickable } - override fun update(manager: AnnotationManager<*, Fill, FillOptions, *, *, *>) { - synchronized(this) { - val id = annotation?.id - if (removed && id != null) { - map.circles.remove(id) - } - super.update(manager) - val annotation = annotation - if (annotation != null && id == null) { - map.circles[annotation.id] = this - } - } - } - override fun setStrokePattern(pattern: IObjectWrapper?) { Log.d(TAG, "unimplemented method: set stroke pattern") } @@ -274,13 +236,86 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options } override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = - if (super.onTransact(code, data, reply, flags)) { - true - } else { - Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false + if (super.onTransact(code, data, reply, flags)) { + true + } else { + Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false + } + + companion object { + val TAG = "GmsMapAbstractCircle" + } +} + +class CircleImpl(private val map: GoogleMapImpl, private val id: String, options: GmsCircleOptions) : + AbstractCircleImpl(id, options, { map.dpiFactor }), Markup { + + + + override var annotation: Fill? = null + override var removed: Boolean = false + + override fun update() { + val polygon = makePolygon() + + // Extracts points from generated polygon in expected format + annotation?.let { + it.latLngs = FillOptions().withGeometry(polygon).latLngs + it.setFillColor(fillColor) + it.fillOpacity = if (visible && !wrapsAroundPoles()) 1f else 0f + } + + line.annotation?.let { + it.latLngs = makeOutlineLatLngs().map { point -> + com.mapbox.mapboxsdk.geometry.LatLng( + point.latitude(), + point.longitude() + ) + } + + it.lineWidth = strokeWidth / map.dpiFactor + it.setLineColor(strokeColor) + } + + map.fillManager?.let { update(it) } + map.lineManager?.let { line.update(it) } + } + + override fun remove() { + removed = true + line.removed = true + map.fillManager?.let { update(it) } + map.lineManager?.let { line.update(it) } + } + + + override fun update(manager: AnnotationManager<*, Fill, FillOptions, *, *, *>) { + synchronized(this) { + val id = annotation?.id + if (removed && id != null) { + map.circles.remove(id) + } + super.update(manager) + val annotation = annotation + if (annotation != null && id == null) { + map.circles[annotation.id] = this } + } + } companion object { val TAG = "GmsMapCircle" } +} + +class LiteCircleImpl(private val map: LiteGoogleMapImpl, id: String, options: GmsCircleOptions) : + AbstractCircleImpl(id, options, { map.dpiFactor }) { + override fun update() { + map.postUpdateSnapshot() + } + + override fun remove() { + map.circles.remove(this) + } + } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt index c5f00abbd..2918b428c 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt @@ -117,9 +117,9 @@ class InfoWindow internal constructor( val height = iconDimensions?.get(1) ?: 0f view.x = - coordinates.x - view.measuredWidth / 2f + sin(Math.toRadians(marker.rotation.toDouble())).toFloat() * width * marker.getInfoWindowAnchor()[0] + coordinates.x - view.measuredWidth / 2f + sin(Math.toRadians(marker.rotation.toDouble())).toFloat() * width * marker.infoWindowAnchor[0] view.y = coordinates.y - view.measuredHeight - max( - height * cos(Math.toRadians(marker.rotation.toDouble())).toFloat() * marker.getInfoWindowAnchor()[1], 0f + height * cos(Math.toRadians(marker.rotation.toDouble())).toFloat() * marker.infoWindowAnchor[1], 0f ) } } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt index 0d11c4c12..9753a8bb3 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt @@ -28,44 +28,142 @@ import com.mapbox.mapboxsdk.plugins.annotation.Symbol import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.google.android.gms.dynamic.unwrap import org.microg.gms.maps.mapbox.GoogleMapImpl +import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox -class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options: MarkerOptions) : IMarkerDelegate.Stub(), Markup { - private var position: LatLng = options.position - private var visible: Boolean = options.isVisible - private var rotation: Float = options.rotation - private var anchor: FloatArray = floatArrayOf(options.anchorU, options.anchorV) - private var infoWindowAnchor: FloatArray? = null - private var icon: BitmapDescriptorImpl? = options.icon?.remoteObject.unwrap() - private var alpha: Float = options.alpha - private var title: String? = options.title - private var snippet: String? = options.snippet - private var zIndex: Float = options.zIndex - private var draggable: Boolean = options.isDraggable - private var tag: IObjectWrapper? = null - - private var infoWindowShown = false - - override var annotation: Symbol? = null - override var removed: Boolean = false - override val annotationOptions: SymbolOptions +abstract class AbstractMarkerImpl( + private val id: String, options: MarkerOptions, private val dpiFactor: Function0 +) : IMarkerDelegate.Stub() { + + internal var position: LatLng = options.position + internal var visible: Boolean = options.isVisible + internal var anchor: FloatArray = floatArrayOf(options.anchorU, options.anchorV) + internal var infoWindowAnchor: FloatArray = floatArrayOf(0.5f, 1f) + internal var icon: BitmapDescriptorImpl? = options.icon?.remoteObject.unwrap() + internal var alpha: Float = options.alpha + internal var title: String? = options.title + internal var snippet: String? = options.snippet + internal var zIndex: Float = options.zIndex + internal var tag: IObjectWrapper? = null + internal open var draggable = false + + val annotationOptions: SymbolOptions get() { val symbolOptions = SymbolOptions() - .withIconOpacity(if (visible) alpha else 0f) - .withIconRotate(rotation) - .withSymbolSortKey(zIndex) - .withDraggable(draggable) + .withIconOpacity(if (visible) alpha else 0f) + .withIconRotate(rotation) + .withSymbolSortKey(zIndex) + .withDraggable(draggable) position.let { symbolOptions.withLatLng(it.toMapbox()) } - icon?.applyTo(symbolOptions, anchor, map.dpiFactor) + icon?.applyTo(symbolOptions, anchor, dpiFactor()) return symbolOptions } + internal abstract fun update() + + override fun getId(): String = id + + override fun setPosition(position: LatLng?) { + this.position = position ?: return + update() + } + + override fun getPosition(): LatLng = position + + override fun setIcon(obj: IObjectWrapper?) { + obj.unwrap()?.let { icon -> + this.icon = icon + update() + } + } + + override fun setVisible(visible: Boolean) { + this.visible = visible + update() + } + + override fun setTitle(title: String?) { + this.title = title + update() + } + + override fun getTitle(): String? = title + + override fun getSnippet(): String? = snippet + + override fun isVisible(): Boolean = visible + + override fun setAnchor(x: Float, y: Float) { + anchor = floatArrayOf(x, y) + update() + } + + override fun setAlpha(alpha: Float) { + this.alpha = alpha + update() + } + + override fun getAlpha(): Float = alpha + + override fun setZIndex(zIndex: Float) { + this.zIndex = zIndex + update() + } + + override fun getZIndex(): Float = zIndex + + override fun setTag(obj: IObjectWrapper?) { + this.tag = obj + } + + override fun getTag(): IObjectWrapper? = tag ?: ObjectWrapper.wrap(null) + + override fun setSnippet(snippet: String?) { + this.snippet = snippet + } + + override fun equalsRemote(other: IMarkerDelegate?): Boolean = equals(other) + + override fun hashCodeRemote(): Int = hashCode() + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + if (super.onTransact(code, data, reply, flags)) { + true + } else { + Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false + } + + companion object { + private val TAG = "GmsMapAbstractMarker" + } +} + +class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options: MarkerOptions) : + AbstractMarkerImpl(id, options, { map.dpiFactor }), Markup { + + internal var rotation: Float = options.rotation + override var draggable: Boolean = options.isDraggable + + override var annotation: Symbol? = null + override var removed: Boolean = false + override fun remove() { removed = true map.symbolManager?.let { update(it) } } + override fun update() { + annotation?.let { + it.latLng = position.toMapbox() + it.isDraggable = draggable + it.iconOpacity = if (visible) alpha else 0f + it.symbolSortKey = zIndex + icon?.applyTo(it, anchor, map.dpiFactor) + } + map.symbolManager?.let { update(it) } + } + override fun update(manager: AnnotationManager<*, Symbol, SymbolOptions, *, *, *>) { synchronized(this) { val id = annotation?.id @@ -80,12 +178,8 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options } } - override fun getId(): String = id - override fun setPosition(position: LatLng?) { - this.position = position ?: return - annotation?.latLng = position.toMapbox() - map.symbolManager?.let { update(it) } + super.setPosition(position) map.currentInfoWindow?.update() } @@ -98,29 +192,22 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options map.currentInfoWindow?.update() } - override fun getPosition(): LatLng = position - override fun setTitle(title: String?) { - this.title = title + super.setTitle(title) map.currentInfoWindow?.let { if (it.marker == this) it.close() } } - override fun getTitle(): String? = title - override fun setSnippet(snippet: String?) { - this.snippet = snippet + super.setSnippet(snippet) map.currentInfoWindow?.let { if (it.marker == this) it.close() } } - override fun getSnippet(): String? = snippet - override fun setDraggable(draggable: Boolean) { this.draggable = draggable - annotation?.isDraggable = draggable map.symbolManager?.let { update(it) } } @@ -145,22 +232,12 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options return map.currentInfoWindow?.marker == this } - override fun setVisible(visible: Boolean) { - this.visible = visible - annotation?.iconOpacity = if (visible) alpha else 0f - map.symbolManager?.let { update(it) } - } - - override fun isVisible(): Boolean = visible - override fun equals(other: Any?): Boolean { if (this === other) return true if (other is IMarkerDelegate) return other.id == id return false } - override fun equalsRemote(other: IMarkerDelegate?): Boolean = equals(other) - override fun hashCode(): Int { return id.hashCode() } @@ -169,23 +246,9 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options return "$id ($title)" } - override fun hashCodeRemote(): Int = hashCode() - - override fun setIcon(obj: IObjectWrapper?) { - obj.unwrap()?.let { icon -> - this.icon = icon - annotation?.let { icon.applyTo(it, anchor, map.dpiFactor) } - } - map.symbolManager?.let { update(it) } - } - override fun setAnchor(x: Float, y: Float) { - anchor = floatArrayOf(x, y) - annotation?.let { icon?.applyTo(it, anchor, map.dpiFactor) } - map.symbolManager?.let { update(it) } - if (infoWindowAnchor == null) { - map.currentInfoWindow?.update() - } + super.setAnchor(x, y) + map.currentInfoWindow?.update() } override fun setFlat(flat: Boolean) { @@ -211,37 +274,18 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options map.currentInfoWindow?.update() } - internal fun getInfoWindowAnchor() = infoWindowAnchor ?: floatArrayOf(0.5f, 1f) - override fun setAlpha(alpha: Float) { - this.alpha = alpha - annotation?.iconOpacity = if (visible) alpha else 0f + super.setAlpha(alpha) map.symbolManager?.let { update(it) } } - override fun getAlpha(): Float = alpha - override fun setZIndex(zIndex: Float) { - this.zIndex = zIndex - annotation?.symbolSortKey = zIndex + super.setZIndex(zIndex) map.symbolManager?.let { update(it) } } override fun getZIndex(): Float = zIndex - override fun setTag(obj: IObjectWrapper?) { - this.tag = obj - } - - override fun getTag(): IObjectWrapper = tag ?: ObjectWrapper.wrap(null) - - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = - if (super.onTransact(code, data, reply, flags)) { - true - } else { - Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false - } - fun getIconDimensions(): FloatArray? { return icon?.size } @@ -250,3 +294,62 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options private val TAG = "GmsMapMarker" } } + +class LiteMarkerImpl(id: String, options: MarkerOptions, private val map: LiteGoogleMapImpl) : + AbstractMarkerImpl(id, options, { map.dpiFactor }) { + override fun remove() { + map.markers.remove(this) + map.postUpdateSnapshot() + } + + override fun update() { + map.postUpdateSnapshot() + } + + override fun setDraggable(drag: Boolean) { + Log.d(TAG, "setDraggable: not available in lite mode") + } + + override fun isDraggable(): Boolean { + Log.d(TAG, "isDraggable: markers are never draggable in lite mode") + return false + } + + override fun showInfoWindow() { + TODO("Not yet implemented") + } + + override fun hideInfoWindow() { + TODO("Not yet implemented") + } + + override fun isInfoWindowShown(): Boolean { + TODO("Not yet implemented") + } + + override fun setFlat(flat: Boolean) { + Log.d(TAG, "setFlat: not available in lite mode") + } + + override fun isFlat(): Boolean { + Log.d(TAG, "isFlat: markers in lite mode can never be flat") + return false + } + + override fun setRotation(rotation: Float) { + Log.d(TAG, "setRotation: not available in lite mode") + } + + override fun getRotation(): Float { + Log.d(TAG, "setRotation: markers in lite mode can never be rotated") + return 0f + } + + override fun setInfoWindowAnchor(x: Float, y: Float) { + TODO("Not yet implemented") + } + + companion object { + private val TAG = "GmsMapMarkerLite" + } +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt index c737afccd..179c5a0b0 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt @@ -24,7 +24,7 @@ import com.mapbox.mapboxsdk.plugins.annotation.Options interface Markup, S : Options> { var annotation: T? val annotationOptions: S - val removed: Boolean + var removed: Boolean fun update(manager: AnnotationManager<*, T, S, *, *, *>) { synchronized(this) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt index c0de5468e..bdc4d3fef 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt @@ -7,68 +7,63 @@ package org.microg.gms.maps.mapbox.model import android.os.Parcel import android.util.Log +import androidx.annotation.CallSuper import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.PatternItem import com.google.android.gms.maps.model.PolygonOptions import com.google.android.gms.maps.model.PolylineOptions import com.google.android.gms.maps.model.internal.IPolygonDelegate -import com.mapbox.mapboxsdk.plugins.annotation.AnnotationManager import com.mapbox.mapboxsdk.plugins.annotation.Fill import com.mapbox.mapboxsdk.plugins.annotation.FillOptions import com.mapbox.mapboxsdk.utils.ColorUtils import org.microg.gms.maps.mapbox.GoogleMapImpl +import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox import org.microg.gms.utils.warnOnTransactionIssues -class PolygonImpl(private val map: GoogleMapImpl, private val id: String, options: PolygonOptions) : IPolygonDelegate.Stub(), Markup { - private var points = ArrayList(options.points.orEmpty()) - private var holes: List> = ArrayList(options.holes.map { ArrayList(it.orEmpty()) }) - private var fillColor = options.fillColor - private var strokeColor = options.strokeColor - private var strokeWidth = options.strokeWidth - private var strokeJointType = options.strokeJointType - private var strokePattern = ArrayList(options.strokePattern.orEmpty()) - private var visible: Boolean = options.isVisible - private var clickable: Boolean = options.isClickable - private var tag: IObjectWrapper? = null - - private var strokes = (listOf(PolylineImpl(map, "$id-stroke-main", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(points))) - + holes.mapIndexed { idx, it -> PolylineImpl(map, "$id-stroke-hole-$idx", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it)) }).toMutableList() - - override var annotation: Fill? = null - override var removed: Boolean = false - override val annotationOptions: FillOptions +abstract class AbstractPolygonImpl(private val id: String, options: PolygonOptions) : IPolygonDelegate.Stub() { + internal var points = ArrayList(options.points.orEmpty()) + internal var holes: List> = ArrayList(options.holes.map { ArrayList(it.orEmpty()) }) + internal var fillColor = options.fillColor + internal var strokeColor = options.strokeColor + internal var strokeWidth = options.strokeWidth + internal var strokeJointType = options.strokeJointType + internal var strokePattern = ArrayList(options.strokePattern.orEmpty()) + internal var visible: Boolean = options.isVisible + internal var clickable: Boolean = options.isClickable + internal var tag: IObjectWrapper? = null + + val annotationOptions: FillOptions get() = FillOptions() - .withLatLngs(mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } })) - .withFillColor(ColorUtils.colorToRgbaString(fillColor)) - .withFillOpacity(if (visible) 1f else 0f) + .withLatLngs(mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } })) + .withFillColor(ColorUtils.colorToRgbaString(fillColor)) + .withFillOpacity(if (visible) 1f else 0f) - override fun remove() { - removed = true - map.fillManager?.let { update(it) } - strokes.forEach { it.remove() } - } + internal abstract val strokes: MutableList - override fun update(manager: AnnotationManager<*, Fill, FillOptions, *, *, *>) { - super.update(manager) - map.lineManager?.let { lineManager -> strokes.forEach { it.update(lineManager) } } + internal abstract fun update() + + @CallSuper + override fun remove() { + for (stroke in strokes) stroke.remove() } override fun getId(): String = id override fun setPoints(points: List) { this.points = ArrayList(points) - annotation?.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } }) - map.fillManager?.let { update(it) } - strokes[0].points = points + strokes[0].setPoints(points) + update() } override fun getPoints(): List = points + internal abstract fun addPolyline(id: String, options: PolylineOptions) + override fun setHoles(holes: List?) { this.holes = if (holes == null) emptyList() else ArrayList(holes.mapNotNull { if (it is List<*>) it.mapNotNull { if (it is LatLng) it else null }.let { if (it.isNotEmpty()) it else null } else null }) - annotation?.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(this.holes.map { it.map { it.toMapbox() } }) while (strokes.size > this.holes.size + 1) { val last = strokes.last() last.remove() @@ -77,34 +72,40 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option strokes.forEachIndexed { idx, it -> if (idx > 0) it.points = this.holes[idx - 1] } if (this.holes.size + 1 > strokes.size) { try { - strokes.addAll(this.holes.subList(strokes.size, this.holes.size - 1).mapIndexed { idx, it -> PolylineImpl(map, "$id-stroke-hole-${strokes.size + idx}", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it)) }) + this.holes.subList(strokes.size, this.holes.size - 1).mapIndexed { idx, it -> + addPolyline( + "$id-stroke-hole-${strokes.size + idx}", + PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it) + ) + } } catch (e: Exception) { Log.w(TAG, e) } } - map.fillManager?.let { update(it) } } - override fun getHoles(): List = holes + override fun getHoles(): List> = holes + override fun setStrokeWidth(width: Float) { - this.strokeWidth = width - strokes.forEach { it.width = width } + strokeWidth = width + strokes.forEach { it.setWidth(width) } + update() } override fun getStrokeWidth(): Float = strokeWidth override fun setStrokeColor(color: Int) { - this.strokeColor = color - strokes.forEach { it.color = color } + strokeColor = color + strokes.forEach { it.setColor(color) } + update() } override fun getStrokeColor(): Int = strokeColor override fun setFillColor(color: Int) { - this.fillColor = color - annotation?.setFillColor(color) - map.fillManager?.let { update(it) } + fillColor = color + update() } override fun getFillColor(): Int = fillColor @@ -119,9 +120,8 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option } override fun setVisible(visible: Boolean) { - this.visible = visible - annotation?.fillOpacity = if (visible) 1f else 0f - map.fillManager?.let { update(it) } + isVisible = visible + update() } override fun isVisible(): Boolean = visible @@ -135,22 +135,20 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option return false } - override fun equalsRemote(other: IPolygonDelegate?): Boolean = equals(other) - - override fun hashCodeRemote(): Int = hashCode() - override fun setClickable(click: Boolean) { clickable = click } override fun setStrokeJointType(type: Int) { strokeJointType = type + update() } override fun getStrokeJointType(): Int = strokeJointType override fun setStrokePattern(items: MutableList?) { strokePattern = ArrayList(items.orEmpty()) + update() } override fun getStrokePattern(): MutableList = strokePattern @@ -159,7 +157,11 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option tag = obj } - override fun getTag(): IObjectWrapper? = tag + override fun getTag(): IObjectWrapper = tag ?: ObjectWrapper.wrap(null) + + override fun equalsRemote(other: IPolygonDelegate?): Boolean = equals(other) + + override fun hashCodeRemote(): Int = hashCode() override fun hashCode(): Int { return id.hashCode() @@ -169,6 +171,48 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option return id } + companion object { + private val TAG = "GmsMapAbstractPolygon" + } +} + +class PolygonImpl(private val map: GoogleMapImpl, id: String, options: PolygonOptions) : + AbstractPolygonImpl(id, options), Markup { + + + override val strokes = (listOf( + PolylineImpl( + map, "$id-stroke-main", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(points) + ) + ) + holes.mapIndexed { idx, it -> + PolylineImpl( + map, "$id-stroke-hole-$idx", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it) + ) + }).toMutableList() + + override var annotation: Fill? = null + override var removed: Boolean = false + + override fun remove() { + removed = true + map.fillManager?.let { update(it) } + super.remove() + } + + override fun update() { + annotation?.let { + it.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } }) + it.setFillColor(fillColor) + it.fillOpacity = if (visible) 1f else 0f + it.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(this.holes.map { it.map { it.toMapbox() } }) + } + map.fillManager?.let { update(it) } + } + + override fun addPolyline(id: String, options: PolylineOptions) { + strokes.add(PolylineImpl(map, id, options)) + } + override fun equals(other: Any?): Boolean { if (other is PolygonImpl) { return other.id == id @@ -182,3 +226,31 @@ class PolygonImpl(private val map: GoogleMapImpl, private val id: String, option private val TAG = "GmsMapPolygon" } } + +class LitePolygonImpl(id: String, options: PolygonOptions, private val map: LiteGoogleMapImpl) : AbstractPolygonImpl(id, options) { + + override val strokes: MutableList = (listOf( + LitePolylineImpl( + map, "$id-stroke-main", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(points) + ) + ) + holes.mapIndexed { idx, it -> + LitePolylineImpl( + map, "$id-stroke-hole-$idx", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it) + ) + }).toMutableList() + + + override fun remove() { + super.remove() + map.polygons.remove(this) + map.postUpdateSnapshot() + } + + override fun update() { + map.postUpdateSnapshot() + } + + override fun addPolyline(id: String, options: PolylineOptions) { + strokes.add(LitePolylineImpl(map, id, options)) + } +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt index f9a8f91b9..52c6d0a16 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt @@ -24,51 +24,44 @@ import com.mapbox.mapboxsdk.plugins.annotation.Line import com.mapbox.mapboxsdk.plugins.annotation.LineOptions import com.mapbox.mapboxsdk.utils.ColorUtils import org.microg.gms.maps.mapbox.GoogleMapImpl +import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox import com.google.android.gms.maps.model.PolylineOptions as GmsLineOptions -class PolylineImpl(private val map: GoogleMapImpl, private val id: String, options: GmsLineOptions) : IPolylineDelegate.Stub(), Markup { - private var points = ArrayList(options.points) - private var width = options.width - private var color = options.color - private var visible: Boolean = options.isVisible +abstract class AbstractPolylineImpl(private val id: String, options: GmsLineOptions, private val dpiFactor: Function0) : IPolylineDelegate.Stub() { + internal var points: List = ArrayList(options.points) + internal var width = options.width + internal var color = options.color + internal var visible: Boolean = options.isVisible - override var annotation: Line? = null - override var removed: Boolean = false - override val annotationOptions: LineOptions + val annotationOptions: LineOptions get() = LineOptions() - .withLatLngs(points.map { it.toMapbox() }) - .withLineWidth(width / map.dpiFactor) - .withLineColor(ColorUtils.colorToRgbaString(color)) - .withLineOpacity(if (visible) 1f else 0f) + .withLatLngs(points.map { it.toMapbox() }) + .withLineWidth(width / dpiFactor.invoke()) + .withLineColor(ColorUtils.colorToRgbaString(color)) + .withLineOpacity(if (visible) 1f else 0f) - override fun remove() { - removed = true - map.lineManager?.let { update(it) } - } + internal abstract fun update() override fun getId(): String = id override fun setPoints(points: List) { this.points = ArrayList(points) - annotation?.latLngs = points.map { it.toMapbox() } - map.lineManager?.let { update(it) } + update() } override fun getPoints(): List = points override fun setWidth(width: Float) { this.width = width - annotation?.lineWidth = width / map.dpiFactor - map.lineManager?.let { update(it) } + update() } override fun getWidth(): Float = width override fun setColor(color: Int) { this.color = color - annotation?.setLineColor(color) - map.lineManager?.let { update(it) } + update() } override fun getColor(): Int = color @@ -84,8 +77,7 @@ class PolylineImpl(private val map: GoogleMapImpl, private val id: String, optio override fun setVisible(visible: Boolean) { this.visible = visible - annotation?.lineOpacity = if (visible) 1f else 0f - map.lineManager?.let { update(it) } + update() } override fun isVisible(): Boolean = visible @@ -111,6 +103,39 @@ class PolylineImpl(private val map: GoogleMapImpl, private val id: String, optio return id } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + if (super.onTransact(code, data, reply, flags)) { + true + } else { + Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false + } + + companion object { + const val TAG = "GmsPolylineAbstract" + } +} + +class PolylineImpl(private val map: GoogleMapImpl, id: String, options: GmsLineOptions) : + AbstractPolylineImpl(id, options, { map.dpiFactor }), Markup { + + override var annotation: Line? = null + override var removed: Boolean = false + + override fun remove() { + removed = true + map.lineManager?.let { update(it) } + } + + override fun update() { + annotation?.apply { + latLngs = points.map { it.toMapbox() } + lineWidth = width / map.dpiFactor + setLineColor(color) + lineOpacity = if (visible) 1f else 0f + } + map.lineManager?.let { update(it) } + } + override fun equals(other: Any?): Boolean { if (other is PolylineImpl) { return other.id == id @@ -118,14 +143,19 @@ class PolylineImpl(private val map: GoogleMapImpl, private val id: String, optio return false } - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = - if (super.onTransact(code, data, reply, flags)) { - true - } else { - Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false - } - companion object { private val TAG = "GmsMapPolyline" } -} \ No newline at end of file +} + +class LitePolylineImpl(private val map: LiteGoogleMapImpl, id: String, options: GmsLineOptions) : + AbstractPolylineImpl(id, options, { map.dpiFactor }) { + override fun remove() { + map.polylines.remove(this) + map.postUpdateSnapshot() + } + + override fun update() { + map.postUpdateSnapshot() + } +} -- GitLab From 51913f9b479abd2c1f1a2486db178fea8f23cb17 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 21 Feb 2023 11:49:13 +0100 Subject: [PATCH 02/17] Fix lite mode enabling all the time --- .../java/com/google/android/gms/maps/GoogleMapOptions.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/play-services-api/src/main/java/com/google/android/gms/maps/GoogleMapOptions.java b/play-services-api/src/main/java/com/google/android/gms/maps/GoogleMapOptions.java index 405f31d11..346660534 100644 --- a/play-services-api/src/main/java/com/google/android/gms/maps/GoogleMapOptions.java +++ b/play-services-api/src/main/java/com/google/android/gms/maps/GoogleMapOptions.java @@ -46,7 +46,7 @@ public final class GoogleMapOptions extends AutoSafeParcelable { @SafeParceled(11) private boolean rotateGesturesEnabled = true; @SafeParceled(12) - private boolean liteMode = false; + private int liteMode = 0; @SafeParceled(14) private boolean mapToobarEnabled = false; @SafeParceled(15) @@ -79,8 +79,9 @@ public final class GoogleMapOptions extends AutoSafeParcelable { return boundsForCamera; } - public Boolean getLiteMode() { - return liteMode; + public boolean getLiteMode() { + // Is encoded as `-1` if null, `0` if false, `1` if true. The default is false. + return liteMode == 1; } public Boolean getMapToolbarEnabled() { -- GitLab From 0b76d265d30631ee911738b72505b40dffdf9a2f Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 21 Feb 2023 12:05:55 +0100 Subject: [PATCH 03/17] Use DPI ratio on lite mode map --- .../kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 2b3b94c21..06807750f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -36,6 +36,7 @@ import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.max +import kotlin.math.roundToInt class MetaSnapshot(val snapshot: MapSnapshot, val cameraPosition: CameraPosition, val width: Int, val height: Int) @@ -168,10 +169,14 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog } val snapshotter = MapSnapshotter( - mapContext, MapSnapshotter.Options(width, height) + mapContext, MapSnapshotter.Options( + (width / dpiFactor).roundToInt(), + (height / dpiFactor).roundToInt() + ) .withCameraPosition(this@LiteGoogleMapImpl.cameraPosition.toMapbox()) .withStyleBuilder(styleBuilder) .withLogo(showWatermark) + .withPixelRatio(dpiFactor) ) synchronized(this) { -- GitLab From b93af3b5d8071101ddb503aedbef26f7d63481f2 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 21 Feb 2023 13:37:38 +0100 Subject: [PATCH 04/17] Lite mode camera bounds update --- .../maps/mapbox/CameraBoundsWithSizeUpdate.kt | 7 ++++++- .../gms/maps/mapbox/CameraUpdateFactory.kt | 20 +++---------------- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 20 ++++++++++++++++++- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt index c31bf2238..fc094a263 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt @@ -17,16 +17,21 @@ package org.microg.gms.maps.mapbox import android.util.Log +import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.camera.CameraUpdate import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.maps.MapboxMap import java.util.* -internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: Int, val height: Int, val padding: IntArray) : CameraUpdate { +internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: Int, val height: Int, val padding: IntArray) : GmsCameraUpdate, CameraUpdate { constructor(bounds: LatLngBounds, width: Int, height: Int, paddingLeft: Int, paddingTop: Int = paddingLeft, paddingRight: Int = paddingLeft, paddingBottom: Int = paddingTop) : this(bounds, width, height, intArrayOf(paddingLeft, paddingTop, paddingRight, paddingBottom)) {} + override fun getCameraPosition(map: IGoogleMapDelegate): com.google.android.gms.maps.model.CameraPosition { + throw UnsupportedOperationException() + } + override fun getCameraPosition(map: MapboxMap): CameraPosition? { val padding = this.padding.clone() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt index c45c454af..b2e4c94f3 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt @@ -75,7 +75,7 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { } override fun newLatLngBoundsWithSize(bounds: LatLngBounds, width: Int, height: Int, padding: Int): IObjectWrapper = - ObjectWrapper.wrap(NewLatLngBoundsWithSizeCameraUpdate(bounds, width, height, padding)).also { + ObjectWrapper.wrap(CameraBoundsWithSizeUpdate(bounds.toMapbox(), width, height, padding)).also { Log.d(TAG, "newLatLngBoundsWithSize") } @@ -153,25 +153,11 @@ class NewLatLngZoomCameraUpdate(private val latLng: LatLng, private val zoom: Fl CameraUpdateFactory.newLatLngZoom(latLng.toMapbox(), zoom - 1.0).getCameraPosition(mapboxMap) } -class NewLatLngBoundsCameraUpdate(private val bounds: LatLngBounds, private val padding: Int) : GmsCameraUpdate, +class NewLatLngBoundsCameraUpdate(internal val bounds: LatLngBounds, internal val padding: Int) : GmsCameraUpdate, CameraUpdate { - // TODO - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = - CameraPosition.Builder(map.cameraPosition).target(bounds.center).zoom(4f).build() + override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = throw UnsupportedOperationException() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = CameraUpdateFactory.newLatLngBounds(bounds.toMapbox(), padding).getCameraPosition(mapboxMap) -} - -class NewLatLngBoundsWithSizeCameraUpdate( - private val bounds: LatLngBounds, private val width: Int, private val height: Int, private val padding: Int -) : GmsCameraUpdate, CameraUpdate { - - // TODO - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = - CameraPosition.Builder(map.cameraPosition).target(bounds.center).zoom(4f).build() - - override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = - CameraBoundsWithSizeUpdate(bounds.toMapbox(), width, height, padding).getCameraPosition(mapboxMap) } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 06807750f..62d61917f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -53,6 +53,8 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog private var created = false private var cameraPosition = options.camera + private var cameraBounds: com.mapbox.mapboxsdk.geometry.LatLngBounds? = null + private var mapType: Int = options.mapType private var currentSnapshotter: MapSnapshotter? = null @@ -174,6 +176,10 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog (height / dpiFactor).roundToInt() ) .withCameraPosition(this@LiteGoogleMapImpl.cameraPosition.toMapbox()) + .apply { + // if camera bounds are set, overwrite camera position + cameraBounds?.let { withRegion(it) } + } .withStyleBuilder(styleBuilder) .withLogo(showWatermark) .withPixelRatio(dpiFactor) @@ -223,7 +229,19 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog override fun getMinZoomLevel() = 1f override fun moveCamera(cameraUpdate: IObjectWrapper?): Unit = cameraUpdate.unwrap()?.let { - cameraPosition = it.getCameraPosition(this) + when (it) { + is NewLatLngBoundsCameraUpdate -> cameraBounds = it.bounds.toMapbox() + + is CameraBoundsWithSizeUpdate -> { + // We don't handle the bound's size, so potentially our map displays less context than it should + cameraBounds = it.bounds + } + + else -> { + cameraPosition = it.getCameraPosition(this) + cameraBounds = null + } + } postUpdateSnapshot() } ?: Unit -- GitLab From 1b245231b6899755f163887d8b97e75161893bb5 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 21 Feb 2023 14:01:29 +0100 Subject: [PATCH 05/17] Lite mode map click position Uphold last touch position --- .../kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 62d61917f..87b918056 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -61,6 +61,8 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog private var lastSnapshot: MetaSnapshot? = null + private var lastTouchPosition = PointF(0f, 0f) + private val afterNextDrawCallback = mutableListOf<() -> Unit>() private var cameraChangeListener: IOnCameraChangeListener? = null @@ -85,6 +87,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog view.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> postUpdateSnapshot() } BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) + + // noinspection ClickableViewAccessibility; touch listener only has side effects + map.setOnTouchListener { _, event -> lastTouchPosition = PointF(event.x, event.y); false } } override fun onCreate(savedInstanceState: Bundle?) { @@ -365,14 +370,14 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog // TODO view.setOnClickListener { listener?.onMapClick( - lastSnapshot?.snapshot?.latLngForPixel(PointF(0f, 0f))?.toGms() ?: LatLng(0.0, 0.0) + lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) ) } } override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) { view.setOnLongClickListener { - listener?.onMapLongClick(lastSnapshot?.snapshot?.latLngForPixel(PointF(0f, 0f))?.toGms() ?: LatLng(0.0, 0.0)) + listener?.onMapLongClick(lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0)) listener != null } } -- GitLab From 5108267c60ce93b80a944243de0f08e919ec4c25 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 21 Feb 2023 14:07:30 +0100 Subject: [PATCH 06/17] Improved storage of camera bounds --- .../org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 87b918056..4e3e6e932 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -38,7 +38,13 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.max import kotlin.math.roundToInt -class MetaSnapshot(val snapshot: MapSnapshot, val cameraPosition: CameraPosition, val width: Int, val height: Int) +class MetaSnapshot( + val snapshot: MapSnapshot, + val cameraPosition: CameraPosition, + val cameraBounds: com.mapbox.mapboxsdk.geometry.LatLngBounds?, + val width: Int, + val height: Int +) class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() { @@ -99,6 +105,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog if (savedInstanceState?.containsKey(BUNDLE_CAMERA_POSITION) == true) { cameraPosition = savedInstanceState.getParcelable(BUNDLE_CAMERA_POSITION) + cameraBounds = savedInstanceState.getParcelable(BUNDLE_CAMERA_BOUNDS) } postUpdateSnapshot() @@ -197,9 +204,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog snapshotter.start { - val cameraPositionChanged = !cameraPosition.equals(lastSnapshot?.cameraPosition) + val cameraPositionChanged = cameraPosition != lastSnapshot?.cameraPosition || (cameraBounds != lastSnapshot?.cameraBounds) - lastSnapshot = MetaSnapshot(it, cameraPosition, width, height) + lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, width, height) map.setImageBitmap(it.bitmap) for (callback in afterNextDrawCallback) callback() @@ -367,7 +374,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog } override fun setOnMapClickListener(listener: IOnMapClickListener?) { - // TODO view.setOnClickListener { listener?.onMapClick( lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) @@ -479,6 +485,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog override fun onSaveInstanceState(outState: Bundle) { outState.putParcelable(BUNDLE_CAMERA_POSITION, cameraPosition) + outState.putParcelable(BUNDLE_CAMERA_BOUNDS, cameraBounds) } override fun setContentDescription(desc: String?) { @@ -552,5 +559,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog companion object { private val TAG = "GmsMapLite" private val BUNDLE_CAMERA_POSITION = "camera" + private val BUNDLE_CAMERA_BOUNDS = "cameraBounds" } } \ No newline at end of file -- GitLab From 96c4bc070ad9aefd78f009e739a74eedb5babbfd Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 23 Feb 2023 14:36:34 +0100 Subject: [PATCH 07/17] Lite mode info window --- .../gms/maps/mapbox/AbstractGoogleMap.kt | 42 ++++++++++ .../org/microg/gms/maps/mapbox/GoogleMap.kt | 36 +------- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 36 ++++---- .../gms/maps/mapbox/model/InfoWindow.kt | 18 ++-- .../microg/gms/maps/mapbox/model/Marker.kt | 82 +++++++------------ 5 files changed, 103 insertions(+), 111 deletions(-) create mode 100644 play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt new file mode 100644 index 000000000..3aeb71e9a --- /dev/null +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -0,0 +1,42 @@ +package org.microg.gms.maps.mapbox + +import android.content.Context +import android.util.DisplayMetrics +import com.google.android.gms.maps.internal.* +import org.microg.gms.maps.mapbox.model.AbstractMarkerImpl +import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter +import org.microg.gms.maps.mapbox.model.InfoWindow +import org.microg.gms.maps.mapbox.utils.MapContext + +abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { + + internal val mapContext = MapContext(context) + + val dpiFactor: Float + get() = mapContext.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT + + internal var currentInfoWindow: InfoWindow? = null + internal var infoWindowAdapter: IInfoWindowAdapter = DefaultInfoWindowAdapter(mapContext) + internal var onInfoWindowClickListener: IOnInfoWindowClickListener? = null + internal var onInfoWindowLongClickListener: IOnInfoWindowLongClickListener? = null + internal var onInfoWindowCloseListener: IOnInfoWindowCloseListener? = null + + internal abstract fun showInfoWindow(marker: AbstractMarkerImpl): Boolean + + override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { + onInfoWindowClickListener = listener + } + + override fun setInfoWindowLongClickListener(listener: IOnInfoWindowLongClickListener) { + onInfoWindowLongClickListener = listener + } + + override fun setInfoWindowCloseListener(listener: IOnInfoWindowCloseListener) { + onInfoWindowCloseListener = listener + } + + override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) { + infoWindowAdapter = adapter ?: DefaultInfoWindowAdapter(mapContext) + } + +} \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index 8da855d4e..ae36fcdb6 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -86,13 +86,11 @@ fun getStyleUriByMapType(mapType: Int) = when (mapType) { else -> "mapbox://styles/microg/cjui4020201oo1fmca7yuwbor" } -class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() { +class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { val view: FrameLayout var map: MapboxMap? = null private set - val dpiFactor: Float - get() = context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT private var mapView: MapView? = null private var created = false @@ -113,13 +111,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) private var markerDragListener: IOnMarkerDragListener? = null private var circleClickListener: IOnCircleClickListener? = null - private var infoWindowAdapter: IInfoWindowAdapter = DefaultInfoWindowAdapter(MapContext(context)) - internal var onInfoWindowClickListener: IOnInfoWindowClickListener? = null - internal var onInfoWindowLongClickListener: IOnInfoWindowLongClickListener? = null - internal var onInfoWindowCloseListener: IOnInfoWindowCloseListener? = null - - var currentInfoWindow: InfoWindow? = null - var lineManager: LineManager? = null val pendingLines = mutableSetOf>() var lineId = 0L @@ -142,7 +133,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) var locationEnabled: Boolean = false init { - val mapContext = MapContext(context) BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext, context)) runOnMainLooper { @@ -439,7 +429,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) Log.w(TAG, e) locationEnabled = false } - Unit } } @@ -495,22 +484,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) circleClickListener = listener } - override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { - onInfoWindowClickListener = listener - } - - override fun setInfoWindowLongClickListener(listener: IOnInfoWindowLongClickListener) { - onInfoWindowLongClickListener = listener - } - - override fun setInfoWindowCloseListener(listener: IOnInfoWindowCloseListener) { - onInfoWindowCloseListener = listener - } - - override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) { - infoWindowAdapter = adapter ?: DefaultInfoWindowAdapter(MapContext(context)) - } - override fun getTestingHelper(): IObjectWrapper? { Log.d(TAG, "unimplemented Method: getTestingHelper") return ObjectWrapper.wrap(null) @@ -602,7 +575,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) override fun onCreate(savedInstanceState: Bundle?) { if (!created) { Log.d(TAG, "create"); - val mapView = MapView(MapContext(context)) + val mapView = MapView(mapContext) this.mapView = mapView view.addView(mapView) mapView.onCreate(savedInstanceState?.toMapbox()) @@ -784,7 +757,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) pendingMarkers.forEach { it.update(symbolManager) } pendingMarkers.clear() - val mapContext = MapContext(context) map.locationComponent.apply { activateLocationComponent(LocationComponentActivationOptions.builder(mapContext, it) .useSpecializedLocationLayer(true) @@ -811,8 +783,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } } - internal fun showInfoWindow(marker: MarkerImpl): Boolean { - infoWindowAdapter.getInfoWindowViewFor(marker, MapContext(context))?.let { infoView -> + override fun showInfoWindow(marker: AbstractMarkerImpl): Boolean { + infoWindowAdapter.getInfoWindowViewFor(marker, mapContext)?.let { infoView -> currentInfoWindow?.close() currentInfoWindow = InfoWindow(infoView, this, marker).also { infoWindow -> mapView?.let { infoWindow.open(it) } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 4e3e6e932..b29ded858 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -46,16 +46,11 @@ class MetaSnapshot( val height: Int ) -class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() { - - private val mapContext = MapContext(context) +class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { internal val view: FrameLayout = FrameLayout(mapContext) val map: ImageView - val dpiFactor: Float - get() = mapContext.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT - private var created = false private var cameraPosition = options.camera @@ -217,6 +212,8 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog cameraChangeListener?.onCameraChange(cameraPosition) } + currentInfoWindow?.update() + synchronized(this) { this.currentSnapshotter = null } @@ -396,14 +393,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog Log.d(TAG, "setOnMarkerDragListener: marker drag is not supported in lite mode") } - override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { - // TODO("Not yet implemented") - } - - override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) { - // TODO("Not yet implemented") - } - override fun getTestingHelper(): IObjectWrapper { Log.d(TAG, "unimplemented Method: getTestingHelper") return ObjectWrapper.wrap(null) @@ -461,6 +450,17 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog showWatermark = watermark } + override fun showInfoWindow(marker: AbstractMarkerImpl): Boolean { + infoWindowAdapter.getInfoWindowViewFor(marker, mapContext)?.let { infoView -> + currentInfoWindow?.close() + currentInfoWindow = InfoWindow(infoView, this, marker).also { infoWindow -> + infoWindow.open(view) + } + return true + } + return false + } + override fun onResume() { } @@ -500,14 +500,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : IGoog Log.d(TAG, "unimplemented Method: onExitAmbient") } - override fun setInfoWindowLongClickListener(listener: IOnInfoWindowLongClickListener?) { - // TODO("Not yet implemented") - } - - override fun setInfoWindowCloseListener(listener: IOnInfoWindowCloseListener?) { - // TODO("Not yet implemented") - } - override fun setCircleClickListener(listener: IOnCircleClickListener?) { // TODO("Not yet implemented") } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt index 2918b428c..8cc1973a0 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt @@ -1,5 +1,6 @@ package org.microg.gms.maps.mapbox.model +import android.graphics.Point import android.graphics.PointF import android.view.LayoutInflater import android.view.View @@ -13,7 +14,7 @@ import com.google.android.gms.dynamic.unwrap import com.google.android.gms.maps.internal.IInfoWindowAdapter import com.google.android.gms.maps.model.internal.IMarkerDelegate import com.mapbox.android.gestures.Utils -import com.mapbox.mapboxsdk.maps.MapView +import org.microg.gms.maps.mapbox.AbstractGoogleMap import org.microg.gms.maps.mapbox.GoogleMapImpl import org.microg.gms.maps.mapbox.R import org.microg.gms.maps.mapbox.utils.MapContext @@ -60,7 +61,7 @@ fun IInfoWindowAdapter.getInfoWindowViewFor(marker: IMarkerDelegate, mapContext: } class InfoWindow internal constructor( - private val view: View, private val map: GoogleMapImpl, internal val marker: MarkerImpl + private val view: View, private val map: AbstractGoogleMap, internal val marker: AbstractMarkerImpl ) { private var coordinates: PointF = PointF(0f, 0f) var isVisible = false @@ -75,7 +76,7 @@ class InfoWindow internal constructor( } } - fun open(mapView: MapView) { + fun open(mapView: FrameLayout) { val layoutParams: FrameLayout.LayoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT ) @@ -108,8 +109,15 @@ class InfoWindow internal constructor( * Updates the position of the displayed view. */ fun update() { - map.map?.projection?.toScreenLocation(marker.position.toMapbox())?.let { - coordinates = it + + if (map is GoogleMapImpl) { + map.map?.projection?.toScreenLocation(marker.position.toMapbox())?.let { + coordinates = it + } + } else { + map.projection.toScreenLocation(marker.position)?.let { + coordinates = PointF(it.unwrap()!!) + } } val iconDimensions = marker.getIconDimensions() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt index 9753a8bb3..8f205c04f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt @@ -27,12 +27,13 @@ import com.mapbox.mapboxsdk.plugins.annotation.AnnotationManager import com.mapbox.mapboxsdk.plugins.annotation.Symbol import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.google.android.gms.dynamic.unwrap +import org.microg.gms.maps.mapbox.AbstractGoogleMap import org.microg.gms.maps.mapbox.GoogleMapImpl import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox abstract class AbstractMarkerImpl( - private val id: String, options: MarkerOptions, private val dpiFactor: Function0 + private val id: String, options: MarkerOptions, private val map: AbstractGoogleMap ) : IMarkerDelegate.Stub() { internal var position: LatLng = options.position @@ -56,7 +57,7 @@ abstract class AbstractMarkerImpl( .withDraggable(draggable) position.let { symbolOptions.withLatLng(it.toMapbox()) } - icon?.applyTo(symbolOptions, anchor, dpiFactor()) + icon?.applyTo(symbolOptions, anchor, map.dpiFactor) return symbolOptions } @@ -113,6 +114,29 @@ abstract class AbstractMarkerImpl( override fun getZIndex(): Float = zIndex + fun getIconDimensions(): FloatArray? { + return icon?.size + } + + override fun showInfoWindow() { + if (isInfoWindowShown) { + // Per docs, don't call `onWindowClose` if info window is re-opened programmatically + map.currentInfoWindow?.close(silent = true) + } + map.showInfoWindow(this) + } + + override fun hideInfoWindow() { + if (isInfoWindowShown) { + map.currentInfoWindow?.close() + map.currentInfoWindow = null + } + } + + override fun isInfoWindowShown(): Boolean { + return map.currentInfoWindow?.marker == this + } + override fun setTag(obj: IObjectWrapper?) { this.tag = obj } @@ -140,7 +164,7 @@ abstract class AbstractMarkerImpl( } class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options: MarkerOptions) : - AbstractMarkerImpl(id, options, { map.dpiFactor }), Markup { + AbstractMarkerImpl(id, options, map), Markup { internal var rotation: Float = options.rotation override var draggable: Boolean = options.isDraggable @@ -213,25 +237,6 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options override fun isDraggable(): Boolean = draggable - override fun showInfoWindow() { - if (isInfoWindowShown) { - // Per docs, don't call `onWindowClose` if info window is re-opened programmatically - map.currentInfoWindow?.close(silent = true) - } - map.showInfoWindow(this) - } - - override fun hideInfoWindow() { - if (isInfoWindowShown) { - map.currentInfoWindow?.close() - map.currentInfoWindow = null - } - } - - override fun isInfoWindowShown(): Boolean { - return map.currentInfoWindow?.marker == this - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (other is IMarkerDelegate) return other.id == id @@ -274,29 +279,13 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options map.currentInfoWindow?.update() } - override fun setAlpha(alpha: Float) { - super.setAlpha(alpha) - map.symbolManager?.let { update(it) } - } - - override fun setZIndex(zIndex: Float) { - super.setZIndex(zIndex) - map.symbolManager?.let { update(it) } - } - - override fun getZIndex(): Float = zIndex - - fun getIconDimensions(): FloatArray? { - return icon?.size - } - companion object { private val TAG = "GmsMapMarker" } } class LiteMarkerImpl(id: String, options: MarkerOptions, private val map: LiteGoogleMapImpl) : - AbstractMarkerImpl(id, options, { map.dpiFactor }) { + AbstractMarkerImpl(id, options, map) { override fun remove() { map.markers.remove(this) map.postUpdateSnapshot() @@ -315,18 +304,6 @@ class LiteMarkerImpl(id: String, options: MarkerOptions, private val map: LiteGo return false } - override fun showInfoWindow() { - TODO("Not yet implemented") - } - - override fun hideInfoWindow() { - TODO("Not yet implemented") - } - - override fun isInfoWindowShown(): Boolean { - TODO("Not yet implemented") - } - override fun setFlat(flat: Boolean) { Log.d(TAG, "setFlat: not available in lite mode") } @@ -346,7 +323,8 @@ class LiteMarkerImpl(id: String, options: MarkerOptions, private val map: LiteGo } override fun setInfoWindowAnchor(x: Float, y: Float) { - TODO("Not yet implemented") + infoWindowAnchor = floatArrayOf(x, y) + map.currentInfoWindow?.update() } companion object { -- GitLab From 1a31c22404b2f456b9a66d3018ddfc79cb0e1b70 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 23 Feb 2023 18:38:43 +0100 Subject: [PATCH 08/17] Click listeners for circle and marker --- .../gms/maps/mapbox/AbstractGoogleMap.kt | 21 ++++ .../org/microg/gms/maps/mapbox/GoogleMap.kt | 20 ---- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 102 ++++++++++++------ .../microg/gms/maps/mapbox/model/Circle.kt | 18 ++-- .../gms/maps/mapbox/utils/typeConverter.kt | 5 + 5 files changed, 108 insertions(+), 58 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 3aeb71e9a..4a5971e28 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -21,6 +21,12 @@ abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { internal var onInfoWindowLongClickListener: IOnInfoWindowLongClickListener? = null internal var onInfoWindowCloseListener: IOnInfoWindowCloseListener? = null + internal var mapClickListener: IOnMapClickListener? = null + internal var mapLongClickListener: IOnMapLongClickListener? = null + internal var markerClickListener: IOnMarkerClickListener? = null + internal var circleClickListener: IOnCircleClickListener? = null + + internal abstract fun showInfoWindow(marker: AbstractMarkerImpl): Boolean override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { @@ -39,4 +45,19 @@ abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { infoWindowAdapter = adapter ?: DefaultInfoWindowAdapter(mapContext) } + override fun setOnMapClickListener(listener: IOnMapClickListener?) { + mapClickListener = listener + } + + override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) { + mapLongClickListener = listener + } + + override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) { + markerClickListener = listener + } + + override fun setCircleClickListener(listener: IOnCircleClickListener?) { + circleClickListener = listener + } } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index ae36fcdb6..b7ac5e29a 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -105,11 +105,7 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG private var cameraMoveCanceledListener: IOnCameraMoveCanceledListener? = null private var cameraMoveStartedListener: IOnCameraMoveStartedListener? = null private var cameraIdleListener: IOnCameraIdleListener? = null - private var mapClickListener: IOnMapClickListener? = null - private var mapLongClickListener: IOnMapLongClickListener? = null - private var markerClickListener: IOnMarkerClickListener? = null private var markerDragListener: IOnMarkerDragListener? = null - private var circleClickListener: IOnCircleClickListener? = null var lineManager: LineManager? = null val pendingLines = mutableSetOf>() @@ -464,26 +460,10 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG cameraChangeListener = listener } - override fun setOnMapClickListener(listener: IOnMapClickListener?) { - mapClickListener = listener - } - - override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) { - mapLongClickListener = listener - } - - override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) { - markerClickListener = listener - } - override fun setOnMarkerDragListener(listener: IOnMarkerDragListener?) { markerDragListener = listener } - override fun setCircleClickListener(listener: IOnCircleClickListener?) { - circleClickListener = listener - } - override fun getTestingHelper(): IObjectWrapper? { Log.d(TAG, "unimplemented Method: getTestingHelper") return ObjectWrapper.wrap(null) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index b29ded858..c85539ea3 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -30,10 +30,13 @@ import com.mapbox.mapboxsdk.style.layers.Property import com.mapbox.mapboxsdk.style.layers.PropertyFactory import com.mapbox.mapboxsdk.style.layers.SymbolLayer import com.mapbox.mapboxsdk.style.sources.GeoJsonSource +import com.mapbox.turf.TurfConstants.UNIT_METERS +import com.mapbox.turf.TurfMeasurement import org.microg.gms.maps.mapbox.model.* import org.microg.gms.maps.mapbox.utils.MapContext import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox +import org.microg.gms.maps.mapbox.utils.toPoint import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.max import kotlin.math.roundToInt @@ -91,6 +94,67 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr // noinspection ClickableViewAccessibility; touch listener only has side effects map.setOnTouchListener { _, event -> lastTouchPosition = PointF(event.x, event.y); false } + + view.setOnClickListener { + + lastSnapshot?.snapshot?.let { snapshot -> + // Calculate marker hitboxes + for (marker in markers.filter { it.isVisible }) { + + marker.getIconDimensions()?.let { iconDimensions -> // consider only markers with icon + val anchorPoint = snapshot.pixelForLatLng(marker.position.toMapbox()) + + val leftX = anchorPoint.x - marker.anchor[0] * iconDimensions[0] + val topY = anchorPoint.y - marker.anchor[1] * iconDimensions[1] + + if (lastTouchPosition.x >= leftX && lastTouchPosition.x <= leftX + iconDimensions[0] + && lastTouchPosition.y >= topY && lastTouchPosition.y <= topY + iconDimensions[1]) { + // Marker was clicked + if (markerClickListener?.onMarkerClick(marker) == true) { + currentInfoWindow?.close() + currentInfoWindow = null + return@setOnClickListener + } else if (showInfoWindow(marker)) { + return@setOnClickListener + } + } + } + } + + currentInfoWindow?.close() + currentInfoWindow = null + + // Test if circle was clicked + for (circle in circles.filter { it.isVisible && it.isClickable }) { + Log.d(TAG, "last touch ${lastTouchPosition.x}, ${lastTouchPosition.y}, turf ${TurfMeasurement.distance( + circle.center.toPoint(), + snapshot.latLngForPixel(lastTouchPosition).toPoint(), + UNIT_METERS + )}, radius ${circle.radius}") + if (TurfMeasurement.distance( + circle.center.toPoint(), + snapshot.latLngForPixel(lastTouchPosition).toPoint(), + UNIT_METERS + ) <= circle.radius) { + // Circle was clicked + circleClickListener?.onCircleClick(circle) + return@setOnClickListener + } + } + + mapClickListener?.onMapClick( + snapshot.latLngForPixel(lastTouchPosition).toGms() + ) + } + + + } + view.setOnLongClickListener { + mapLongClickListener?.onMapLongClick( + lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) + ) + mapLongClickListener != null + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -122,8 +186,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr private fun updateSnapshot() { val cameraPosition = cameraPosition - val width = max(map.width, 1) - val height = max(map.height, 1) val styleBuilder = Style.Builder().fromUri(getStyleUriByMapType(mapType)) @@ -177,11 +239,14 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr ) } + val pixelWidth = map.width + val pixelHeight = map.height + + val dpiWidth = max(pixelWidth / dpiFactor, 1f).roundToInt() + val dpiHeight = max(pixelHeight / dpiFactor, 1f).roundToInt() + val snapshotter = MapSnapshotter( - mapContext, MapSnapshotter.Options( - (width / dpiFactor).roundToInt(), - (height / dpiFactor).roundToInt() - ) + mapContext, MapSnapshotter.Options(dpiWidth, dpiHeight) .withCameraPosition(this@LiteGoogleMapImpl.cameraPosition.toMapbox()) .apply { // if camera bounds are set, overwrite camera position @@ -201,7 +266,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr val cameraPositionChanged = cameraPosition != lastSnapshot?.cameraPosition || (cameraBounds != lastSnapshot?.cameraBounds) - lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, width, height) + lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight) map.setImageBitmap(it.bitmap) for (callback in afterNextDrawCallback) callback() @@ -370,25 +435,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr cameraChangeListener = listener } - override fun setOnMapClickListener(listener: IOnMapClickListener?) { - view.setOnClickListener { - listener?.onMapClick( - lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) - ) - } - } - - override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) { - view.setOnLongClickListener { - listener?.onMapLongClick(lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0)) - listener != null - } - } - - override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) { - // TODO("Not yet implemented") - } - override fun setOnMarkerDragListener(listener: IOnMarkerDragListener?) { Log.d(TAG, "setOnMarkerDragListener: marker drag is not supported in lite mode") } @@ -500,10 +546,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr Log.d(TAG, "unimplemented Method: onExitAmbient") } - override fun setCircleClickListener(listener: IOnCircleClickListener?) { - // TODO("Not yet implemented") - } - override fun setMapStyle(options: MapStyleOptions?): Boolean { Log.d(TAG, "setMapStyle options: " + options?.getJson()) return true diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt index 984cef9aa..5cb5655b9 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt @@ -28,11 +28,13 @@ import com.mapbox.geojson.Point import com.mapbox.mapboxsdk.plugins.annotation.* import com.mapbox.mapboxsdk.utils.ColorUtils import com.mapbox.turf.TurfConstants +import com.mapbox.turf.TurfConstants.UNIT_METERS import com.mapbox.turf.TurfMeasurement import com.mapbox.turf.TurfMeta import com.mapbox.turf.TurfTransformation import org.microg.gms.maps.mapbox.GoogleMapImpl import org.microg.gms.maps.mapbox.LiteGoogleMapImpl +import org.microg.gms.maps.mapbox.utils.toPoint import com.google.android.gms.maps.model.CircleOptions as GmsCircleOptions val NORTH_POLE: Point = Point.fromLngLat(0.0, 90.0) @@ -89,12 +91,12 @@ abstract class AbstractCircleImpl( * Google's "map renderer is unable to draw the circle fill if the circle encompasses * either the North or South pole" (though it does so incorrectly anyway) */ - internal fun wrapsAroundPoles() = Point.fromLngLat(center.longitude, center.latitude).let { + internal fun wrapsAroundPoles() = center.toPoint().let { TurfMeasurement.distance( - it, NORTH_POLE - ) * 1000 < radius || TurfMeasurement.distance( - it, SOUTH_POLE - ) * 1000 < radius + it, NORTH_POLE, UNIT_METERS + ) < radius || TurfMeasurement.distance( + it, SOUTH_POLE, UNIT_METERS + ) < radius } internal fun makeOutlineLatLngs(): MutableList { @@ -105,9 +107,9 @@ abstract class AbstractCircleImpl( // We modify our lines such to match the way Mapbox / MapLibre draws them. // This results in a small gap somewhere in the line, but avoids an incorrect horizontal line. - val centerPoint = Point.fromLngLat(center.longitude, center.latitude) + val centerPoint = center.toPoint() - if (!centerPoint.equals(NORTH_POLE) && TurfMeasurement.distance(centerPoint, NORTH_POLE) * 1000 < radius) { + if (!centerPoint.equals(NORTH_POLE) && TurfMeasurement.distance(centerPoint, NORTH_POLE, UNIT_METERS) < radius) { // Wraps around North Pole for (i in 0 until pointList.size) { // We want to have the north-most points at the start and end @@ -121,7 +123,7 @@ abstract class AbstractCircleImpl( } } - if (!centerPoint.equals(SOUTH_POLE) && TurfMeasurement.distance(centerPoint, SOUTH_POLE) * 1000 < radius) { + if (!centerPoint.equals(SOUTH_POLE) && TurfMeasurement.distance(centerPoint, SOUTH_POLE, UNIT_METERS) < radius) { // Wraps around South Pole for (i in 0 until pointList.size) { // We want to have the south-most points at the start and end diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt index 9664aea6f..aefa0d1cb 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt @@ -18,6 +18,7 @@ package org.microg.gms.maps.mapbox.utils import android.os.Bundle import com.google.android.gms.maps.internal.ICancelableCallback +import com.mapbox.geojson.Point import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.geometry.LatLngBounds @@ -31,6 +32,8 @@ import com.google.android.gms.maps.model.VisibleRegion as GmsVisibleRegion fun GmsLatLng.toMapbox(): LatLng = LatLng(latitude, longitude) +fun GmsLatLng.toPoint() = Point.fromLngLat(latitude, longitude) + fun GmsLatLngBounds.toMapbox(): LatLngBounds = LatLngBounds.from(this.northeast.latitude, this.northeast.longitude, this.southwest.latitude, this.southwest.longitude) @@ -68,6 +71,8 @@ fun Bundle.toMapbox(): Bundle { fun LatLng.toGms(): GmsLatLng = GmsLatLng(latitude, longitude) +fun LatLng.toPoint(): Point = Point.fromLngLat(latitude, longitude) + fun LatLngBounds.toGms(): GmsLatLngBounds = GmsLatLngBounds(southWest.toGms(), northEast.toGms()) fun CameraPosition.toGms(): GmsCameraPosition = -- GitLab From 3687110352854b0cc3cda70afb22f02a6f4d5174 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Thu, 23 Feb 2023 18:48:06 +0100 Subject: [PATCH 09/17] Fix projection and circle clicks for dpi factor != 1 Workaround upstream issue, pending ticket creation. --- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 28 +++++++++++++------ .../org/microg/gms/maps/mapbox/Projection.kt | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index c85539ea3..cf8068526 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -46,8 +46,15 @@ class MetaSnapshot( val cameraPosition: CameraPosition, val cameraBounds: com.mapbox.mapboxsdk.geometry.LatLngBounds?, val width: Int, - val height: Int -) + val height: Int, + val dpi: Float +) { + fun latLngForPixelFixed(point: PointF) = snapshot.latLngForPixel( + PointF( + point.x / dpi, point.y / dpi + ) + ) +} class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { @@ -97,12 +104,12 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr view.setOnClickListener { - lastSnapshot?.snapshot?.let { snapshot -> + lastSnapshot?.let { meta -> // Calculate marker hitboxes for (marker in markers.filter { it.isVisible }) { marker.getIconDimensions()?.let { iconDimensions -> // consider only markers with icon - val anchorPoint = snapshot.pixelForLatLng(marker.position.toMapbox()) + val anchorPoint = meta.snapshot.pixelForLatLng(marker.position.toMapbox()) val leftX = anchorPoint.x - marker.anchor[0] * iconDimensions[0] val topY = anchorPoint.y - marker.anchor[1] * iconDimensions[1] @@ -128,12 +135,12 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr for (circle in circles.filter { it.isVisible && it.isClickable }) { Log.d(TAG, "last touch ${lastTouchPosition.x}, ${lastTouchPosition.y}, turf ${TurfMeasurement.distance( circle.center.toPoint(), - snapshot.latLngForPixel(lastTouchPosition).toPoint(), + meta.latLngForPixelFixed(lastTouchPosition).toPoint(), UNIT_METERS )}, radius ${circle.radius}") if (TurfMeasurement.distance( circle.center.toPoint(), - snapshot.latLngForPixel(lastTouchPosition).toPoint(), + meta.latLngForPixelFixed(lastTouchPosition).toPoint(), UNIT_METERS ) <= circle.radius) { // Circle was clicked @@ -143,7 +150,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } mapClickListener?.onMapClick( - snapshot.latLngForPixel(lastTouchPosition).toGms() + meta.latLngForPixelFixed(lastTouchPosition).toGms() ) } @@ -151,7 +158,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } view.setOnLongClickListener { mapLongClickListener?.onMapLongClick( - lastSnapshot?.snapshot?.latLngForPixel(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) + lastSnapshot?.latLngForPixelFixed(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) ) mapLongClickListener != null } @@ -239,6 +246,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr ) } + val cameraBounds = cameraBounds + val dpi = dpiFactor + val pixelWidth = map.width val pixelHeight = map.height @@ -266,7 +276,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr val cameraPositionChanged = cameraPosition != lastSnapshot?.cameraPosition || (cameraBounds != lastSnapshot?.cameraBounds) - lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight) + lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight, dpiFactor) map.setImageBitmap(it.bitmap) for (callback in afterNextDrawCallback) callback() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt index 42898cf27..0cf8dae63 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt @@ -83,7 +83,7 @@ class ProjectionImpl(private val projection: Projection, private val withoutTilt class LiteProjection(private val snapshot: MetaSnapshot) : IProjectionDelegate.Stub() { private fun fromScreenLocation(point: Point?): LatLng = - point?.let { snapshot.snapshot.latLngForPixel(PointF(point)).toGms() } ?: LatLng(0.0, 0.0) + point?.let { snapshot.latLngForPixelFixed(PointF(point)).toGms() } ?: LatLng(0.0, 0.0) override fun fromScreenLocation(obj: IObjectWrapper?): LatLng = fromScreenLocation(obj.unwrap()) -- GitLab From 5aeae94ceeb18cc5e583376183f89ea81fdfa809 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 27 Feb 2023 20:21:27 +0100 Subject: [PATCH 10/17] Implement my location support in lite mode --- .../gms/maps/mapbox/AbstractGoogleMap.kt | 23 ++++ .../org/microg/gms/maps/mapbox/GoogleMap.kt | 19 --- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 118 ++++++++++++------ .../src/main/res/drawable/location_dot.xml | 5 + 4 files changed, 111 insertions(+), 54 deletions(-) create mode 100644 play-services-maps-core-mapbox/src/main/res/drawable/location_dot.xml diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 4a5971e28..11be09038 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -1,7 +1,9 @@ package org.microg.gms.maps.mapbox import android.content.Context +import android.location.Location import android.util.DisplayMetrics +import android.util.Log import com.google.android.gms.maps.internal.* import org.microg.gms.maps.mapbox.model.AbstractMarkerImpl import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter @@ -60,4 +62,25 @@ abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { override fun setCircleClickListener(listener: IOnCircleClickListener?) { circleClickListener = listener } + + override fun getMyLocation(): Location? { + Log.d(TAG, "unimplemented Method: getMyLocation") + return null + } + + override fun setLocationSource(locationSource: ILocationSourceDelegate?) { + Log.d(TAG, "unimplemented Method: setLocationSource") + } + + override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) { + Log.d(TAG, "unimplemented Method: setOnMyLocationChangeListener") + } + + override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) { + Log.d(TAG, "unimplemented Method: setOnMyLocationButtonClickListener") + } + + companion object { + val TAG = "GmsMapAbstract" + } } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index b7ac5e29a..bdb1a9e00 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -428,15 +428,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG } } - override fun getMyLocation(): Location? { - Log.d(TAG, "unimplemented Method: getMyLocation") - return null - } - - override fun setLocationSource(locationSource: ILocationSourceDelegate?) { - Log.d(TAG, "unimplemented Method: setLocationSource") - } - override fun setContentDescription(desc: String?) { mapView?.contentDescription = desc } @@ -469,16 +460,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG return ObjectWrapper.wrap(null) } - override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) { - Log.d(TAG, "unimplemented Method: setOnMyLocationChangeListener") - - } - - override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) { - Log.d(TAG, "unimplemented Method: setOnMyLocationButtonClickListener") - - } - override fun snapshot(callback: ISnapshotReadyCallback, bitmap: IObjectWrapper?) { val map = map if (map == null) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index cf8068526..72df9d426 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -1,17 +1,19 @@ package org.microg.gms.maps.mapbox +import android.Manifest import android.content.Context +import android.content.pm.PackageManager import android.graphics.PointF import android.location.Location import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.DisplayMetrics import android.util.Log import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout import android.widget.ImageView import androidx.annotation.UiThread +import androidx.core.app.ActivityCompat import com.google.android.gms.dynamic.IObjectWrapper import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.dynamic.unwrap @@ -21,19 +23,16 @@ import com.google.android.gms.maps.model.* import com.google.android.gms.maps.model.internal.* import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.WellKnownTileServer +import com.mapbox.mapboxsdk.location.engine.* import com.mapbox.mapboxsdk.maps.Style +import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions import com.mapbox.mapboxsdk.snapshotter.MapSnapshot import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter -import com.mapbox.mapboxsdk.style.layers.FillLayer -import com.mapbox.mapboxsdk.style.layers.LineLayer -import com.mapbox.mapboxsdk.style.layers.Property -import com.mapbox.mapboxsdk.style.layers.PropertyFactory -import com.mapbox.mapboxsdk.style.layers.SymbolLayer +import com.mapbox.mapboxsdk.style.layers.* import com.mapbox.mapboxsdk.style.sources.GeoJsonSource import com.mapbox.turf.TurfConstants.UNIT_METERS import com.mapbox.turf.TurfMeasurement import org.microg.gms.maps.mapbox.model.* -import org.microg.gms.maps.mapbox.utils.MapContext import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox import org.microg.gms.maps.mapbox.utils.toPoint @@ -41,6 +40,10 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.max import kotlin.math.roundToInt +// From com.mapbox.mapboxsdk.location.LocationComponent +const val DEFAULT_INTERVAL_MILLIS = 1000L +const val DEFAULT_FASTEST_INTERVAL_MILLIS = 1000L + class MetaSnapshot( val snapshot: MapSnapshot, val cameraPosition: CameraPosition, @@ -77,6 +80,21 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr private val afterNextDrawCallback = mutableListOf<() -> Unit>() private var cameraChangeListener: IOnCameraChangeListener? = null + private var myLocationEnabled = false + private var myLocation: Location? = null + private var locationEngineProvider: LocationEngine = LocationEngineProvider.getBestLocationEngine(mapContext) + private val locationListener = object : LocationEngineCallback { + override fun onSuccess(result: LocationEngineResult?) { + this@LiteGoogleMapImpl.myLocation = result?.lastLocation + postUpdateSnapshot() + } + + override fun onFailure(exception: Exception) { + // same behavior as MapLibre's LocationComponent + Log.e(TAG, "Failed to obtain location update", exception) + } + } + internal val markers: MutableList = mutableListOf() internal val polygons: MutableList = mutableListOf() internal val polylines: MutableList = mutableListOf() @@ -193,6 +211,12 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr private fun updateSnapshot() { val cameraPosition = cameraPosition + val dpi = dpiFactor + + val cameraBounds = cameraBounds + + val pixelWidth = map.width + val pixelHeight = map.height val styleBuilder = Style.Builder().fromUri(getStyleUriByMapType(mapType)) @@ -240,20 +264,34 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr PropertyFactory.symbolSortKey(marker.zIndex), PropertyFactory.iconAllowOverlap(true) ) - marker.icon?.applyTo(layer, marker.anchor, dpiFactor) + marker.icon?.applyTo(layer, marker.anchor, dpi) styleBuilder.withLayer(layer).withSource( GeoJsonSource(marker.id, marker.annotationOptions.geometry) ) } - val cameraBounds = cameraBounds - val dpi = dpiFactor - - val pixelWidth = map.width - val pixelHeight = map.height + // Add location overlay + if (myLocationEnabled) myLocation?.let { + val indicator = mapContext.getDrawable(R.drawable.location_dot)!! + styleBuilder.withImage("locationIndicator", indicator) + val layer = SymbolLayer("location", "locationSource").withProperties( + PropertyFactory.iconAllowOverlap(true), + PropertyFactory.iconImage("locationIndicator"), + PropertyFactory.iconAnchor(Property.ICON_ANCHOR_TOP_LEFT), + PropertyFactory.iconOffset(arrayOf( + 0.5f * indicator.minimumWidth / dpi, 0.5f * indicator.minimumHeight / dpi + )) + ) + styleBuilder.withLayer(layer).withSource( + GeoJsonSource( + "locationSource", + SymbolOptions().withLatLng(com.mapbox.mapboxsdk.geometry.LatLng(it.latitude, it.longitude)).geometry + ) + ) + } - val dpiWidth = max(pixelWidth / dpiFactor, 1f).roundToInt() - val dpiHeight = max(pixelHeight / dpiFactor, 1f).roundToInt() + val dpiWidth = max(pixelWidth / dpi, 1f).roundToInt() + val dpiHeight = max(pixelHeight / dpi, 1f).roundToInt() val snapshotter = MapSnapshotter( mapContext, MapSnapshotter.Options(dpiWidth, dpiHeight) @@ -264,7 +302,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } .withStyleBuilder(styleBuilder) .withLogo(showWatermark) - .withPixelRatio(dpiFactor) + .withPixelRatio(dpi) ) synchronized(this) { @@ -276,7 +314,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr val cameraPositionChanged = cameraPosition != lastSnapshot?.cameraPosition || (cameraBounds != lastSnapshot?.cameraBounds) - lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight, dpiFactor) + lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight, dpi) map.setImageBitmap(it.bitmap) for (callback in afterNextDrawCallback) callback() @@ -412,21 +450,39 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr Log.d(TAG, "setIndoorEnabled: indoor not supported in lite mode") } - override fun isMyLocationEnabled(): Boolean { - TODO("Not yet implemented") - } + override fun isMyLocationEnabled(): Boolean = myLocationEnabled override fun setMyLocationEnabled(myLocation: Boolean) { - TODO("Not yet implemented") - } + if (!myLocationEnabled && myLocation) { + activateLocationProvider() + } else if (myLocationEnabled && !myLocation) { + deactivateLocationProvider() + } // else situation is unchanged + myLocationEnabled = myLocation + } + + private fun activateLocationProvider() { + // Activate only if sufficient permissions + if (ActivityCompat.checkSelfPermission( + mapContext, Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission( + mapContext, Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + locationEngineProvider.requestLocationUpdates( + LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) + .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) + .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) + .build(), locationListener, Looper.getMainLooper() + ) - override fun getMyLocation(): Location? { - Log.d(TAG, "unimplemented Method: getMyLocation") - return null + } else { + Log.w(TAG, "Called setMyLocationEnabled(true) without sufficient permissions. Not showing location.") + } } - override fun setLocationSource(locationSource: ILocationSourceDelegate?) { - Log.d(TAG, "unimplemented Method: setLocationSource") + private fun deactivateLocationProvider() { + locationEngineProvider.removeLocationUpdates(locationListener) } override fun getUiSettings(): IUiSettingsDelegate { @@ -458,14 +514,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr return LiteCircleImpl(this, "circle${nextObjectId++}", options).also { circles.add(it) } } - override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) { - TODO("Not yet implemented") - } - - override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) { - TODO("Not yet implemented") - } - override fun snapshot(callback: ISnapshotReadyCallback?, bitmap: IObjectWrapper?) { val lastSnapshot = lastSnapshot if (lastSnapshot == null) { diff --git a/play-services-maps-core-mapbox/src/main/res/drawable/location_dot.xml b/play-services-maps-core-mapbox/src/main/res/drawable/location_dot.xml new file mode 100644 index 000000000..af953b62b --- /dev/null +++ b/play-services-maps-core-mapbox/src/main/res/drawable/location_dot.xml @@ -0,0 +1,5 @@ + + + + -- GitLab From d2dd2085f642300b1283641ed90d466d5b6bb896 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Mon, 27 Feb 2023 20:27:35 +0100 Subject: [PATCH 11/17] Lite mode location provider lifecycle awareness --- .../src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 72df9d426..eafe48ee9 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -566,6 +566,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } override fun onResume() { + if (myLocationEnabled) activateLocationProvider() } override fun onPause() { @@ -573,6 +574,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr currentSnapshotter?.cancel() currentSnapshotter = null } + deactivateLocationProvider() } override fun onDestroy() { -- GitLab From e0380a67366905f512ccdce2fcd86d3c9ee31388 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 28 Feb 2023 10:50:34 +0100 Subject: [PATCH 12/17] Implement `isClickable` as in docs https://developers.google.com/maps/documentation/android-sdk/events#disabling_click_events_in_lite_mode --- .../main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index eafe48ee9..a5b6bb2cd 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -9,6 +9,7 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log +import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout import android.widget.ImageView @@ -122,6 +123,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr view.setOnClickListener { + // Test if clickable + if ((view.parent as View?)?.isClickable == false) return@setOnClickListener + lastSnapshot?.let { meta -> // Calculate marker hitboxes for (marker in markers.filter { it.isVisible }) { -- GitLab From 9a65ead48ad4634874eb76f4b553610945778fcc Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 28 Feb 2023 14:26:46 +0100 Subject: [PATCH 13/17] Lite mode padding --- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 23 ++++++++++++++----- .../org/microg/gms/maps/mapbox/Projection.kt | 16 +++++++------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index a5b6bb2cd..8b2b7c67a 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -51,6 +51,8 @@ class MetaSnapshot( val cameraBounds: com.mapbox.mapboxsdk.geometry.LatLngBounds?, val width: Int, val height: Int, + val paddingRight: Int, + val paddingTop: Int, val dpi: Float ) { fun latLngForPixelFixed(point: PointF) = snapshot.latLngForPixel( @@ -114,14 +116,20 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr view.addView(map) - view.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> postUpdateSnapshot() } + view.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> + postUpdateSnapshot() + currentInfoWindow?.update() + } BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) // noinspection ClickableViewAccessibility; touch listener only has side effects - map.setOnTouchListener { _, event -> lastTouchPosition = PointF(event.x, event.y); false } + map.setOnTouchListener { _, event -> + lastTouchPosition = PointF(event.x + map.paddingLeft, event.y + map.paddingTop) + false + } - view.setOnClickListener { + map.setOnClickListener { // Test if clickable if ((view.parent as View?)?.isClickable == false) return@setOnClickListener @@ -178,7 +186,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } - view.setOnLongClickListener { + map.setOnLongClickListener { mapLongClickListener?.onMapLongClick( lastSnapshot?.latLngForPixelFixed(lastTouchPosition)?.toGms() ?: LatLng(0.0, 0.0) ) @@ -318,7 +326,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr val cameraPositionChanged = cameraPosition != lastSnapshot?.cameraPosition || (cameraBounds != lastSnapshot?.cameraBounds) - lastSnapshot = MetaSnapshot(it, cameraPosition, cameraBounds, pixelWidth, pixelHeight, dpi) + lastSnapshot = MetaSnapshot( + it, cameraPosition, cameraBounds, pixelWidth, pixelHeight, view.paddingRight, view.paddingTop, dpi + ) map.setImageBitmap(it.bitmap) for (callback in afterNextDrawCallback) callback() @@ -530,7 +540,8 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { - Log.d(TAG, "setPadding: not implemented") + view.setPadding(left, top, right, bottom) + postUpdateSnapshot() } override fun isBuildingsEnabled(): Boolean { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt index 0cf8dae63..0077c3e88 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/Projection.kt @@ -82,21 +82,23 @@ class ProjectionImpl(private val projection: Projection, private val withoutTilt class LiteProjection(private val snapshot: MetaSnapshot) : IProjectionDelegate.Stub() { - private fun fromScreenLocation(point: Point?): LatLng = + private fun fromScreenLocationAfterPadding(point: Point?): LatLng = point?.let { snapshot.latLngForPixelFixed(PointF(point)).toGms() } ?: LatLng(0.0, 0.0) - override fun fromScreenLocation(obj: IObjectWrapper?): LatLng = fromScreenLocation(obj.unwrap()) + override fun fromScreenLocation(obj: IObjectWrapper?): LatLng = fromScreenLocationAfterPadding(obj.unwrap()?.let { + Point((it.x - snapshot.paddingRight), (it.y - snapshot.paddingRight)) + }) override fun toScreenLocation(latLng: LatLng?): IObjectWrapper = ObjectWrapper.wrap(snapshot.snapshot.pixelForLatLng(latLng?.toMapbox()).let { - Point(it.x.roundToInt(), it.y.roundToInt()) + Point(it.x.roundToInt() + snapshot.paddingRight, it.y.roundToInt() + snapshot.paddingTop) }) override fun getVisibleRegion(): VisibleRegion { - val nearLeft = fromScreenLocation(Point(0, snapshot.height)) - val nearRight = fromScreenLocation(Point(snapshot.width, snapshot.height)) - val farLeft = fromScreenLocation(Point(0, 0)) - val farRight = fromScreenLocation(Point(snapshot.width, 0)) + val nearLeft = fromScreenLocationAfterPadding(Point(0, snapshot.height)) + val nearRight = fromScreenLocationAfterPadding(Point(snapshot.width, snapshot.height)) + val farLeft = fromScreenLocationAfterPadding(Point(0, 0)) + val farRight = fromScreenLocationAfterPadding(Point(snapshot.width, 0)) return VisibleRegion(nearLeft, nearRight, farLeft, farRight, LatLngBounds(nearLeft, farRight)) } -- GitLab From 243251fa52b2d16af992cb441ddce1825890db27 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 28 Feb 2023 14:40:38 +0100 Subject: [PATCH 14/17] Lite mode default click listener: open geo: URI --- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 8b2b7c67a..5a2ba2167 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -1,10 +1,14 @@ package org.microg.gms.maps.mapbox import android.Manifest +import android.content.ActivityNotFoundException import android.content.Context +import android.content.Intent +import android.content.Intent.ACTION_VIEW import android.content.pm.PackageManager import android.graphics.PointF import android.location.Location +import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Looper @@ -179,9 +183,23 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr } } - mapClickListener?.onMapClick( - meta.latLngForPixelFixed(lastTouchPosition).toGms() - ) + val clickedPosition = meta.latLngForPixelFixed(lastTouchPosition) + val clickListenerConsumedClick = mapClickListener?.let { + it.onMapClick(clickedPosition.toGms()) + true + } ?: false + + if (clickListenerConsumedClick) return@setOnClickListener + + // else open external map at clicked location + val intent = + Intent(ACTION_VIEW, Uri.parse("geo:${clickedPosition.latitude},${clickedPosition.longitude}")) + + try { + context.startActivity(intent) + } catch (e: ActivityNotFoundException) { + Log.e(TAG, "No compatible mapping application installed. Not handling click.") + } } -- GitLab From 685a65e17c071662aeaf92192763a427424c771f Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 28 Feb 2023 15:22:42 +0100 Subject: [PATCH 15/17] Move unimplemented methods to abstract class --- .../gms/maps/mapbox/AbstractGoogleMap.kt | 49 +++++++++++++++++++ .../org/microg/gms/maps/mapbox/GoogleMap.kt | 46 ----------------- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 18 ------- 3 files changed, 49 insertions(+), 64 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 11be09038..9e7ea641d 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -2,8 +2,11 @@ package org.microg.gms.maps.mapbox import android.content.Context import android.location.Location +import android.os.Bundle import android.util.DisplayMetrics import android.util.Log +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.internal.* import org.microg.gms.maps.mapbox.model.AbstractMarkerImpl import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter @@ -80,6 +83,52 @@ abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { Log.d(TAG, "unimplemented Method: setOnMyLocationButtonClickListener") } + override fun getTestingHelper(): IObjectWrapper { + Log.d(TAG, "unimplemented Method: getTestingHelper") + return ObjectWrapper.wrap(null) + } + + override fun isBuildingsEnabled(): Boolean { + Log.d(TAG, "unimplemented Method: isBuildingsEnabled") + return false + } + + override fun setBuildingsEnabled(buildings: Boolean) { + Log.d(TAG, "unimplemented Method: setBuildingsEnabled") + } + + override fun useViewLifecycleWhenInFragment(): Boolean { + Log.d(TAG, "unimplemented Method: useViewLifecycleWhenInFragment") + return false + } + + override fun onEnterAmbient(bundle: Bundle?) { + Log.d(TAG, "unimplemented Method: onEnterAmbient") + } + + override fun onExitAmbient() { + Log.d(TAG, "unimplemented Method: onExitAmbient") + } + + override fun isTrafficEnabled(): Boolean { + Log.d(TAG, "unimplemented Method: isTrafficEnabled") + return false + } + + override fun setTrafficEnabled(traffic: Boolean) { + Log.d(TAG, "unimplemented Method: setTrafficEnabled") + + } + + override fun isIndoorEnabled(): Boolean { + Log.d(TAG, "unimplemented Method: isIndoorEnabled") + return false + } + + override fun setIndoorEnabled(indoor: Boolean) { + Log.d(TAG, "unimplemented Method: setIndoorEnabled") + } + companion object { val TAG = "GmsMapAbstract" } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index bdb1a9e00..b227c57be 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -388,26 +388,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG it.uiSettings.isLogoEnabled = watermark } - override fun isTrafficEnabled(): Boolean { - Log.d(TAG, "unimplemented Method: isTrafficEnabled") - return false - } - - override fun setTrafficEnabled(traffic: Boolean) { - Log.d(TAG, "unimplemented Method: setTrafficEnabled") - - } - - override fun isIndoorEnabled(): Boolean { - Log.d(TAG, "unimplemented Method: isIndoorEnabled") - return false - } - - override fun setIndoorEnabled(indoor: Boolean) { - Log.d(TAG, "unimplemented Method: setIndoorEnabled") - - } - override fun isMyLocationEnabled(): Boolean { return locationEnabled } @@ -455,11 +435,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG markerDragListener = listener } - override fun getTestingHelper(): IObjectWrapper? { - Log.d(TAG, "unimplemented Method: getTestingHelper") - return ObjectWrapper.wrap(null) - } - override fun snapshot(callback: ISnapshotReadyCallback, bitmap: IObjectWrapper?) { val map = map if (map == null) { @@ -488,15 +463,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG map.uiSettings.setAttributionMargins(left + ninetyTwoDp, top + fourDp, right + fourDp, bottom + fourDp) } - override fun isBuildingsEnabled(): Boolean { - Log.d(TAG, "unimplemented Method: isBuildingsEnabled") - return false - } - - override fun setBuildingsEnabled(buildings: Boolean) { - Log.d(TAG, "unimplemented Method: setBuildingsEnabled") - } - override fun setOnMapLoadedCallback(callback: IOnMapLoadedCallback?) { if (callback != null) { synchronized(mapLock) { @@ -755,11 +721,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG return false } - override fun useViewLifecycleWhenInFragment(): Boolean { - Log.d(TAG, "unimplemented Method: useViewLifecycleWhenInFragment") - return false - } - override fun onResume() = mapView?.onResume() ?: Unit override fun onPause() = mapView?.onPause() ?: Unit override fun onDestroy() { @@ -803,13 +764,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG mapView?.onStop() } - override fun onEnterAmbient(bundle: Bundle?) { - Log.d(TAG, "unimplemented Method: onEnterAmbient") - } - - override fun onExitAmbient() { - Log.d(TAG, "unimplemented Method: onExitAmbient") - } override fun onLowMemory() = mapView?.onLowMemory() ?: Unit override fun onSaveInstanceState(outState: Bundle) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 5a2ba2167..13082dea9 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -537,11 +537,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr Log.d(TAG, "setOnMarkerDragListener: marker drag is not supported in lite mode") } - override fun getTestingHelper(): IObjectWrapper { - Log.d(TAG, "unimplemented Method: getTestingHelper") - return ObjectWrapper.wrap(null) - } - override fun addCircle(options: CircleOptions): ICircleDelegate { return LiteCircleImpl(this, "circle${nextObjectId++}", options).also { circles.add(it) } } @@ -617,11 +612,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr override fun onLowMemory() { } - override fun useViewLifecycleWhenInFragment(): Boolean { - Log.d(TAG, "unimplemented Method: useViewLifecycleWhenInFragment") - return false - } - override fun onSaveInstanceState(outState: Bundle) { outState.putParcelable(BUNDLE_CAMERA_POSITION, cameraPosition) outState.putParcelable(BUNDLE_CAMERA_BOUNDS, cameraBounds) @@ -631,14 +621,6 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr view.contentDescription = desc } - override fun onEnterAmbient(bundle: Bundle?) { - Log.d(TAG, "unimplemented Method: onEnterAmbient") - } - - override fun onExitAmbient() { - Log.d(TAG, "unimplemented Method: onExitAmbient") - } - override fun setMapStyle(options: MapStyleOptions?): Boolean { Log.d(TAG, "setMapStyle options: " + options?.getJson()) return true -- GitLab From 43878bb42e7c17418592ba105028b1510050e560 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Tue, 28 Feb 2023 15:24:46 +0100 Subject: [PATCH 16/17] Move `getStyleUriByMapType` to abstract class --- .../org/microg/gms/maps/mapbox/AbstractGoogleMap.kt | 9 +++++++++ .../main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 9e7ea641d..058df35f5 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -8,11 +8,20 @@ import android.util.Log import com.google.android.gms.dynamic.IObjectWrapper import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.internal.* +import org.microg.gms.maps.MapsConstants import org.microg.gms.maps.mapbox.model.AbstractMarkerImpl import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.utils.MapContext +fun getStyleUriByMapType(mapType: Int) = when (mapType) { + MapsConstants.MAP_TYPE_SATELLITE -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" + MapsConstants.MAP_TYPE_TERRAIN -> "mapbox://styles/mapbox/outdoors-v12" + MapsConstants.MAP_TYPE_HYBRID -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" + //MAP_TYPE_NONE, MAP_TYPE_NORMAL, + else -> "mapbox://styles/microg/cjui4020201oo1fmca7yuwbor" +} + abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { internal val mapContext = MapContext(context) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index b227c57be..ef58d3dbc 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -78,14 +78,6 @@ fun runOnMainLooper(method: () -> Unit) { } } -fun getStyleUriByMapType(mapType: Int) = when (mapType) { - MAP_TYPE_SATELLITE -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" - MAP_TYPE_TERRAIN -> "mapbox://styles/mapbox/outdoors-v12" - MAP_TYPE_HYBRID -> "mapbox://styles/microg/cjxgloted25ap1ct4uex7m6hi" - //MAP_TYPE_NONE, MAP_TYPE_NORMAL, - else -> "mapbox://styles/microg/cjui4020201oo1fmca7yuwbor" -} - class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { val view: FrameLayout -- GitLab From f102ecaf38bd53841da96f0cd1b72c32c1ccd018 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Wed, 8 Mar 2023 12:15:10 +0100 Subject: [PATCH 17/17] Apply review --- .../gms/maps/mapbox/AbstractGoogleMap.kt | 4 +- .../maps/mapbox/CameraBoundsWithSizeUpdate.kt | 8 ++-- .../gms/maps/mapbox/CameraUpdateFactory.kt | 40 ++++++++++--------- .../org/microg/gms/maps/mapbox/GoogleMap.kt | 7 +--- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 27 ++++--------- .../microg/gms/maps/mapbox/model/Circle.kt | 24 +++++------ .../gms/maps/mapbox/model/InfoWindow.kt | 2 +- .../microg/gms/maps/mapbox/model/Marker.kt | 6 +-- .../microg/gms/maps/mapbox/model/Polygon.kt | 20 +++++----- .../microg/gms/maps/mapbox/model/Polyline.kt | 14 +++---- 10 files changed, 69 insertions(+), 83 deletions(-) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 058df35f5..57f09515f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -9,7 +9,7 @@ import com.google.android.gms.dynamic.IObjectWrapper import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.maps.internal.* import org.microg.gms.maps.MapsConstants -import org.microg.gms.maps.mapbox.model.AbstractMarkerImpl +import org.microg.gms.maps.mapbox.model.AbstractMarker import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.utils.MapContext @@ -41,7 +41,7 @@ abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { internal var circleClickListener: IOnCircleClickListener? = null - internal abstract fun showInfoWindow(marker: AbstractMarkerImpl): Boolean + internal abstract fun showInfoWindow(marker: AbstractMarker): Boolean override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) { onInfoWindowClickListener = listener diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt index fc094a263..6d97b262f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt @@ -24,13 +24,13 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.maps.MapboxMap import java.util.* -internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: Int, val height: Int, val padding: IntArray) : GmsCameraUpdate, CameraUpdate { +internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: Int, val height: Int, val padding: IntArray) : LiteModeCameraUpdate, CameraUpdate { constructor(bounds: LatLngBounds, width: Int, height: Int, paddingLeft: Int, paddingTop: Int = paddingLeft, paddingRight: Int = paddingLeft, paddingBottom: Int = paddingTop) : this(bounds, width, height, intArrayOf(paddingLeft, paddingTop, paddingRight, paddingBottom)) {} - override fun getCameraPosition(map: IGoogleMapDelegate): com.google.android.gms.maps.model.CameraPosition { - throw UnsupportedOperationException() - } + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate) = null + + override fun getLiteModeCameraBounds() = bounds override fun getCameraPosition(map: MapboxMap): CameraPosition? { val padding = this.padding.clone() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt index b2e4c94f3..d5cd86d49 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt @@ -86,11 +86,11 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false } - private inner class NoCameraUpdate : CameraUpdate, GmsCameraUpdate { + private inner class NoCameraUpdate : CameraUpdate, LiteModeCameraUpdate { override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = mapboxMap.cameraPosition - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = map.cameraPosition + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = map.cameraPosition } companion object { @@ -98,12 +98,14 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { } } -interface GmsCameraUpdate { - fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition +interface LiteModeCameraUpdate { + fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition? + + fun getLiteModeCameraBounds(): com.mapbox.mapboxsdk.geometry.LatLngBounds? = null } -class ZoomToCameraUpdate(private val zoom: Float) : GmsCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = +class ZoomToCameraUpdate(private val zoom: Float) : LiteModeCameraUpdate, CameraUpdate { + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = CameraPosition.Builder(map.cameraPosition).zoom(zoom).build() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = @@ -111,8 +113,8 @@ class ZoomToCameraUpdate(private val zoom: Float) : GmsCameraUpdate, CameraUpdat } -class ZoomByCameraUpdate(private val delta: Float) : GmsCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = +class ZoomByCameraUpdate(private val delta: Float) : LiteModeCameraUpdate, CameraUpdate { + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = CameraPosition.Builder(map.cameraPosition).zoom(map.cameraPosition.zoom + delta).build() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = @@ -120,9 +122,9 @@ class ZoomByCameraUpdate(private val delta: Float) : GmsCameraUpdate, CameraUpda } -class ZoomByWithFocusCameraUpdate(private val delta: Float, private val x: Int, private val y: Int) : GmsCameraUpdate, +class ZoomByWithFocusCameraUpdate(private val delta: Float, private val x: Int, private val y: Int) : LiteModeCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = CameraPosition.Builder(map.cameraPosition).zoom(map.cameraPosition.zoom + delta) .target(map.projection.fromScreenLocation(ObjectWrapper.wrap(PointF(x.toFloat(), y.toFloat())))).build() @@ -130,33 +132,35 @@ class ZoomByWithFocusCameraUpdate(private val delta: Float, private val x: Int, CameraUpdateFactory.zoomBy(delta.toDouble(), Point(x, y)).getCameraPosition(mapboxMap) } -class NewCameraPositionCameraUpdate(private val cameraPosition: CameraPosition) : GmsCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = this.cameraPosition +class NewCameraPositionCameraUpdate(private val cameraPosition: CameraPosition) : LiteModeCameraUpdate, CameraUpdate { + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = this.cameraPosition override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition = this.cameraPosition.toMapbox() } -class NewLatLngCameraUpdate(private val latLng: LatLng) : GmsCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = +class NewLatLngCameraUpdate(private val latLng: LatLng) : LiteModeCameraUpdate, CameraUpdate { + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = CameraPosition.Builder(map.cameraPosition).target(latLng).build() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = CameraUpdateFactory.newLatLng(latLng.toMapbox()).getCameraPosition(mapboxMap) } -class NewLatLngZoomCameraUpdate(private val latLng: LatLng, private val zoom: Float) : GmsCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = +class NewLatLngZoomCameraUpdate(private val latLng: LatLng, private val zoom: Float) : LiteModeCameraUpdate, CameraUpdate { + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition = CameraPosition.Builder(map.cameraPosition).target(latLng).zoom(zoom).build() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = CameraUpdateFactory.newLatLngZoom(latLng.toMapbox(), zoom - 1.0).getCameraPosition(mapboxMap) } -class NewLatLngBoundsCameraUpdate(internal val bounds: LatLngBounds, internal val padding: Int) : GmsCameraUpdate, +class NewLatLngBoundsCameraUpdate(private val bounds: LatLngBounds, internal val padding: Int) : LiteModeCameraUpdate, CameraUpdate { - override fun getCameraPosition(map: IGoogleMapDelegate): CameraPosition = throw UnsupportedOperationException() + override fun getLiteModeCameraPosition(map: IGoogleMapDelegate): CameraPosition? = null + + override fun getLiteModeCameraBounds() = bounds.toMapbox() override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? = CameraUpdateFactory.newLatLngBounds(bounds.toMapbox(), padding).getCameraPosition(mapboxMap) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index ef58d3dbc..7e33f0854 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -18,12 +18,9 @@ package org.microg.gms.maps.mapbox import android.annotation.SuppressLint import android.content.Context -import android.graphics.Point -import android.location.Location import android.os.* import androidx.annotation.IdRes import androidx.annotation.Keep -import android.util.DisplayMetrics import android.util.Log import android.view.Gravity import android.view.View @@ -54,14 +51,12 @@ import com.mapbox.mapboxsdk.plugins.annotation.Annotation import com.mapbox.mapboxsdk.style.layers.Property.LINE_CAP_ROUND import com.google.android.gms.dynamic.unwrap import com.mapbox.mapboxsdk.WellKnownTileServer -import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.model.getInfoWindowViewFor import com.mapbox.mapboxsdk.camera.CameraUpdateFactory import com.mapbox.mapboxsdk.maps.OnMapReadyCallback import org.microg.gms.maps.MapsConstants.* import org.microg.gms.maps.mapbox.model.* -import org.microg.gms.maps.mapbox.utils.MapContext import org.microg.gms.maps.mapbox.utils.MultiArchLoader import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox @@ -702,7 +697,7 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG } } - override fun showInfoWindow(marker: AbstractMarkerImpl): Boolean { + override fun showInfoWindow(marker: AbstractMarker): Boolean { infoWindowAdapter.getInfoWindowViewFor(marker, mapContext)?.let { infoView -> currentInfoWindow?.close() currentInfoWindow = InfoWindow(infoView, this, marker).also { infoWindow -> diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 13082dea9..c7499947a 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -73,7 +73,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr private var created = false - private var cameraPosition = options.camera + private var cameraPosition: CameraPosition = options.camera private var cameraBounds: com.mapbox.mapboxsdk.geometry.LatLngBounds? = null private var mapType: Int = options.mapType @@ -171,12 +171,12 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr circle.center.toPoint(), meta.latLngForPixelFixed(lastTouchPosition).toPoint(), UNIT_METERS - )}, radius ${circle.radius}") + )}, radius ${circle.radiusInMeters}") if (TurfMeasurement.distance( circle.center.toPoint(), meta.latLngForPixelFixed(lastTouchPosition).toPoint(), UNIT_METERS - ) <= circle.radius) { + ) <= circle.radiusInMeters) { // Circle was clicked circleClickListener?.onCircleClick(circle) return@setOnClickListener @@ -218,7 +218,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY, WellKnownTileServer.Mapbox) if (savedInstanceState?.containsKey(BUNDLE_CAMERA_POSITION) == true) { - cameraPosition = savedInstanceState.getParcelable(BUNDLE_CAMERA_POSITION) + cameraPosition = savedInstanceState.getParcelable(BUNDLE_CAMERA_POSITION)!! cameraBounds = savedInstanceState.getParcelable(BUNDLE_CAMERA_BOUNDS) } @@ -382,20 +382,9 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr override fun getMinZoomLevel() = 1f - override fun moveCamera(cameraUpdate: IObjectWrapper?): Unit = cameraUpdate.unwrap()?.let { - when (it) { - is NewLatLngBoundsCameraUpdate -> cameraBounds = it.bounds.toMapbox() - - is CameraBoundsWithSizeUpdate -> { - // We don't handle the bound's size, so potentially our map displays less context than it should - cameraBounds = it.bounds - } - - else -> { - cameraPosition = it.getCameraPosition(this) - cameraBounds = null - } - } + override fun moveCamera(cameraUpdate: IObjectWrapper?): Unit = cameraUpdate.unwrap()?.let { + cameraPosition = it.getLiteModeCameraPosition(this) ?: cameraPosition + cameraBounds = it.getLiteModeCameraBounds() postUpdateSnapshot() } ?: Unit @@ -582,7 +571,7 @@ class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : Abstr showWatermark = watermark } - override fun showInfoWindow(marker: AbstractMarkerImpl): Boolean { + override fun showInfoWindow(marker: AbstractMarker): Boolean { infoWindowAdapter.getInfoWindowViewFor(marker, mapContext)?.let { infoView -> currentInfoWindow?.close() currentInfoWindow = InfoWindow(infoView, this, marker).also { infoWindow -> diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt index 5cb5655b9..bbb6aaab2 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt @@ -45,12 +45,12 @@ val SOUTH_POLE: Point = Point.fromLngLat(0.0, -90.0) */ const val CIRCLE_POLYGON_STEPS = 256 -abstract class AbstractCircleImpl( +abstract class AbstractCircle( private val id: String, options: GmsCircleOptions, private val dpiFactor: Function0 ) : ICircleDelegate.Stub() { internal var center: LatLng = options.center - internal var radius: Double = options.radius // in meters + internal var radiusInMeters: Double = options.radius // unlike MapLibre's circles, which only work with pixel radii internal var strokeWidth: Float = options.strokeWidth internal var strokeColor: Int = options.strokeColor internal var fillColor: Int = options.fillColor @@ -84,7 +84,7 @@ abstract class AbstractCircleImpl( internal abstract fun update() internal fun makePolygon() = TurfTransformation.circle( - Point.fromLngLat(center.longitude, center.latitude), radius, CIRCLE_POLYGON_STEPS, TurfConstants.UNIT_METERS + Point.fromLngLat(center.longitude, center.latitude), radiusInMeters, CIRCLE_POLYGON_STEPS, TurfConstants.UNIT_METERS ) /** @@ -94,9 +94,9 @@ abstract class AbstractCircleImpl( internal fun wrapsAroundPoles() = center.toPoint().let { TurfMeasurement.distance( it, NORTH_POLE, UNIT_METERS - ) < radius || TurfMeasurement.distance( + ) < radiusInMeters || TurfMeasurement.distance( it, SOUTH_POLE, UNIT_METERS - ) < radius + ) < radiusInMeters } internal fun makeOutlineLatLngs(): MutableList { @@ -109,7 +109,7 @@ abstract class AbstractCircleImpl( val centerPoint = center.toPoint() - if (!centerPoint.equals(NORTH_POLE) && TurfMeasurement.distance(centerPoint, NORTH_POLE, UNIT_METERS) < radius) { + if (!centerPoint.equals(NORTH_POLE) && TurfMeasurement.distance(centerPoint, NORTH_POLE, UNIT_METERS) < radiusInMeters) { // Wraps around North Pole for (i in 0 until pointList.size) { // We want to have the north-most points at the start and end @@ -123,7 +123,7 @@ abstract class AbstractCircleImpl( } } - if (!centerPoint.equals(SOUTH_POLE) && TurfMeasurement.distance(centerPoint, SOUTH_POLE, UNIT_METERS) < radius) { + if (!centerPoint.equals(SOUTH_POLE) && TurfMeasurement.distance(centerPoint, SOUTH_POLE, UNIT_METERS) < radiusInMeters) { // Wraps around South Pole for (i in 0 until pointList.size) { // We want to have the south-most points at the start and end @@ -152,11 +152,11 @@ abstract class AbstractCircleImpl( override fun getCenter(): LatLng = center override fun setRadius(radius: Double) { - this.radius = radius + this.radiusInMeters = radius update() } - override fun getRadius(): Double = radius + override fun getRadius(): Double = radiusInMeters override fun setStrokeWidth(width: Float) { this.strokeWidth = width @@ -250,9 +250,7 @@ abstract class AbstractCircleImpl( } class CircleImpl(private val map: GoogleMapImpl, private val id: String, options: GmsCircleOptions) : - AbstractCircleImpl(id, options, { map.dpiFactor }), Markup { - - + AbstractCircle(id, options, { map.dpiFactor }), Markup { override var annotation: Fill? = null override var removed: Boolean = false @@ -311,7 +309,7 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options } class LiteCircleImpl(private val map: LiteGoogleMapImpl, id: String, options: GmsCircleOptions) : - AbstractCircleImpl(id, options, { map.dpiFactor }) { + AbstractCircle(id, options, { map.dpiFactor }) { override fun update() { map.postUpdateSnapshot() } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt index 8cc1973a0..91b41f117 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/InfoWindow.kt @@ -61,7 +61,7 @@ fun IInfoWindowAdapter.getInfoWindowViewFor(marker: IMarkerDelegate, mapContext: } class InfoWindow internal constructor( - private val view: View, private val map: AbstractGoogleMap, internal val marker: AbstractMarkerImpl + private val view: View, private val map: AbstractGoogleMap, internal val marker: AbstractMarker ) { private var coordinates: PointF = PointF(0f, 0f) var isVisible = false diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt index 8f205c04f..a1423b6ca 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Marker.kt @@ -32,7 +32,7 @@ import org.microg.gms.maps.mapbox.GoogleMapImpl import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox -abstract class AbstractMarkerImpl( +abstract class AbstractMarker( private val id: String, options: MarkerOptions, private val map: AbstractGoogleMap ) : IMarkerDelegate.Stub() { @@ -164,7 +164,7 @@ abstract class AbstractMarkerImpl( } class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options: MarkerOptions) : - AbstractMarkerImpl(id, options, map), Markup { + AbstractMarker(id, options, map), Markup { internal var rotation: Float = options.rotation override var draggable: Boolean = options.isDraggable @@ -285,7 +285,7 @@ class MarkerImpl(private val map: GoogleMapImpl, private val id: String, options } class LiteMarkerImpl(id: String, options: MarkerOptions, private val map: LiteGoogleMapImpl) : - AbstractMarkerImpl(id, options, map) { + AbstractMarker(id, options, map) { override fun remove() { map.markers.remove(this) map.postUpdateSnapshot() diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt index bdc4d3fef..f6f700538 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt @@ -23,7 +23,7 @@ import org.microg.gms.maps.mapbox.LiteGoogleMapImpl import org.microg.gms.maps.mapbox.utils.toMapbox import org.microg.gms.utils.warnOnTransactionIssues -abstract class AbstractPolygonImpl(private val id: String, options: PolygonOptions) : IPolygonDelegate.Stub() { +abstract class AbstractPolygon(private val id: String, options: PolygonOptions) : IPolygonDelegate.Stub() { internal var points = ArrayList(options.points.orEmpty()) internal var holes: List> = ArrayList(options.holes.map { ArrayList(it.orEmpty()) }) internal var fillColor = options.fillColor @@ -167,6 +167,13 @@ abstract class AbstractPolygonImpl(private val id: String, options: PolygonOptio return id.hashCode() } + override fun equals(other: Any?): Boolean { + if (other is AbstractPolygon) { + return other.id == id + } + return false + } + override fun toString(): String { return id } @@ -177,7 +184,7 @@ abstract class AbstractPolygonImpl(private val id: String, options: PolygonOptio } class PolygonImpl(private val map: GoogleMapImpl, id: String, options: PolygonOptions) : - AbstractPolygonImpl(id, options), Markup { + AbstractPolygon(id, options), Markup { override val strokes = (listOf( @@ -213,13 +220,6 @@ class PolygonImpl(private val map: GoogleMapImpl, id: String, options: PolygonOp strokes.add(PolylineImpl(map, id, options)) } - override fun equals(other: Any?): Boolean { - if (other is PolygonImpl) { - return other.id == id - } - return false - } - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = warnOnTransactionIssues(code, reply, flags) { super.onTransact(code, data, reply, flags) } companion object { @@ -227,7 +227,7 @@ class PolygonImpl(private val map: GoogleMapImpl, id: String, options: PolygonOp } } -class LitePolygonImpl(id: String, options: PolygonOptions, private val map: LiteGoogleMapImpl) : AbstractPolygonImpl(id, options) { +class LitePolygonImpl(id: String, options: PolygonOptions, private val map: LiteGoogleMapImpl) : AbstractPolygon(id, options) { override val strokes: MutableList = (listOf( LitePolylineImpl( diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt index 52c6d0a16..2e16ddb16 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt @@ -99,6 +99,13 @@ abstract class AbstractPolylineImpl(private val id: String, options: GmsLineOpti return id.hashCode() } + override fun equals(other: Any?): Boolean { + if (other is AbstractPolylineImpl) { + return other.id == id + } + return false + } + override fun toString(): String { return id } @@ -136,13 +143,6 @@ class PolylineImpl(private val map: GoogleMapImpl, id: String, options: GmsLineO map.lineManager?.let { update(it) } } - override fun equals(other: Any?): Boolean { - if (other is PolylineImpl) { - return other.id == id - } - return false - } - companion object { private val TAG = "GmsMapPolyline" } -- GitLab