diff --git a/build.gradle b/build.gradle
index e49cbac140..6ca6e4cdc6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,11 +39,17 @@ buildscript {
ext.localProperties = new Properties()
try {
- ext.localProperties.load(rootProject.file('local.properties').newDataInputStream())
+ var stream = rootProject.file('local.properties').newDataInputStream()
+ ext.localProperties.load(stream)
+ stream.close()
} catch (ignored) {
// Ignore
}
+ ext.hasModule = (String name, boolean enabledByDefault) -> {
+ return ext.localProperties.getProperty("modules." + name, enabledByDefault.toString()).toBoolean()
+ }
+
repositories {
mavenCentral()
google()
@@ -111,6 +117,7 @@ subprojects {
repositories {
mavenCentral()
google()
+ if (hasModule("hms", false)) maven {url 'https://developer.huawei.com/repo/'}
}
}
diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle
index 8b97fa8cea..0112728623 100644
--- a/play-services-core/build.gradle
+++ b/play-services-core/build.gradle
@@ -6,13 +6,10 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
-def hasModule(String name, boolean enabledByDefault) {
- return localProperties.getProperty("modules." + name, enabledByDefault.toString()).toBoolean()
-}
-
configurations {
mapboxRuntimeOnly
vtmRuntimeOnly
+ if (hasModule("hms", true)) hmsRuntimeOnly
defaultRuntimeOnly
}
@@ -69,6 +66,7 @@ dependencies {
defaultRuntimeOnly project(':play-services-location-core-provider')
if (hasModule("nearby", true)) runtimeOnly project(':play-services-nearby-core-package')
+ if (hasModule("hms", false)) hmsRuntimeOnly project(':play-services-maps-core-hms')
// AndroidX UI
implementation "androidx.multidex:multidex:$multidexVersion"
@@ -149,6 +147,9 @@ android {
dimension 'target'
versionNameSuffix "-hw"
}
+ "hms" {
+ dimension 'maps'
+ }
"mapbox" {
dimension 'maps'
}
diff --git a/play-services-maps/core/hms/build.gradle b/play-services-maps/core/hms/build.gradle
new file mode 100644
index 0000000000..9ca2082e01
--- /dev/null
+++ b/play-services-maps/core/hms/build.gradle
@@ -0,0 +1,61 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+dependencies {
+ implementation project(':play-services-base-core')
+ implementation project(':play-services-maps')
+
+ implementation 'com.huawei.hms:maps:6.9.0.300'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
+}
+
+android {
+ namespace "org.microg.gms.maps.hms"
+
+ compileSdkVersion androidCompileSdk
+ buildToolsVersion "$androidBuildVersionTools"
+
+ defaultConfig {
+ versionName version
+ minSdkVersion androidMinSdk
+ targetSdkVersion androidTargetSdk
+ buildConfigField "String", "HMSMAP_KEY", "\"${localProperties.getProperty("hmsmap.key", "")}\""
+
+ ndk {
+ abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
+ }
+ }
+
+ buildFeatures {
+ buildConfig = true
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ lintOptions {
+ disable 'GradleCompatible'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = 1.8
+ }
+}
diff --git a/play-services-maps/core/hms/proguard-rules.pro b/play-services-maps/core/hms/proguard-rules.pro
new file mode 100644
index 0000000000..74a78e657e
--- /dev/null
+++ b/play-services-maps/core/hms/proguard-rules.pro
@@ -0,0 +1,9 @@
+-ignorewarnings
+-keepattributes *Annotation*
+-keepattributes Exceptions
+-keepattributes InnerClasses
+-keepattributes Signature
+-keepattributes SourceFile,LineNumberTable
+-keep class com.huawei.hianalytics.**{*;}
+-keep class com.huawei.updatesdk.**{*;}
+-keep class com.huawei.hms.**{*;}
\ No newline at end of file
diff --git a/play-services-maps/core/hms/src/main/AndroidManifest.xml b/play-services-maps/core/hms/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..1e55b5b68f
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/play-services-maps/core/hms/src/main/assets/.gitignore b/play-services-maps/core/hms/src/main/assets/.gitignore
new file mode 100644
index 0000000000..47e1b18ecd
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/assets/.gitignore
@@ -0,0 +1 @@
+agconnect-services.json
diff --git a/play-services-maps/core/hms/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/maps_dynamite/ModuleDescriptor.java b/play-services-maps/core/hms/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/maps_dynamite/ModuleDescriptor.java
new file mode 100644
index 0000000000..edfbe6c36a
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/maps_dynamite/ModuleDescriptor.java
@@ -0,0 +1,14 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.google.android.gms.dynamite.descriptors.com.google.android.gms.maps_dynamite;
+
+import androidx.annotation.Keep;
+
+@Keep
+public class ModuleDescriptor {
+ public static final String MODULE_ID = "com.google.android.gms.maps_dynamite";
+ public static final int MODULE_VERSION = 1;
+}
diff --git a/play-services-maps/core/hms/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/hms/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java
new file mode 100644
index 0000000000..a37ea464bb
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java
@@ -0,0 +1,79 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.google.android.gms.maps.internal;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Parcel;
+import android.os.RemoteException;
+import androidx.annotation.Keep;
+import android.util.Log;
+
+import com.google.android.gms.dynamic.IObjectWrapper;
+import com.google.android.gms.dynamic.ObjectWrapper;
+import com.google.android.gms.maps.GoogleMapOptions;
+import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate;
+
+import com.huawei.hms.maps.MapsInitializer;
+import org.microg.gms.maps.hms.CameraUpdateFactoryImpl;
+import org.microg.gms.maps.hms.MapFragmentImpl;
+import org.microg.gms.maps.hms.MapViewImpl;
+import org.microg.gms.maps.hms.model.BitmapDescriptorFactoryImpl;
+
+@Keep
+public class CreatorImpl extends ICreator.Stub {
+ private static final String TAG = "GmsMapCreator";
+
+ @Override
+ public void init(IObjectWrapper resources) {
+ initV2(resources, 0);
+ }
+
+ @Override
+ public IMapFragmentDelegate newMapFragmentDelegate(IObjectWrapper activity) {
+ return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class));
+ }
+
+ @Override
+ public IMapViewDelegate newMapViewDelegate(IObjectWrapper context, GoogleMapOptions options) {
+ return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), options);
+ }
+
+ @Override
+ public ICameraUpdateFactoryDelegate newCameraUpdateFactoryDelegate() {
+ return new CameraUpdateFactoryImpl();
+ }
+
+ @Override
+ public IBitmapDescriptorFactoryDelegate newBitmapDescriptorFactoryDelegate() {
+ return BitmapDescriptorFactoryImpl.INSTANCE;
+ }
+
+ @Override
+ public void initV2(IObjectWrapper resources, int flags) {
+ BitmapDescriptorFactoryImpl.INSTANCE.initialize(ObjectWrapper.unwrapTyped(resources, Resources.class));
+ //ResourcesContainer.set((Resources) ObjectWrapper.unwrap(resources));
+ Log.d(TAG, "initV2 " + flags);
+ }
+
+ @Override
+ public int getRendererType() throws RemoteException {
+ return 2;
+ }
+
+ @Override
+ public void logInitialization(IObjectWrapper context, int preferredRenderer) throws RemoteException {
+ Log.d(TAG, "HMS-based Map initialized (preferred renderer was " + preferredRenderer + ")");
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+ if (super.onTransact(code, data, reply, flags)) return true;
+ Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
+ return false;
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/CameraUpdateFactory.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/CameraUpdateFactory.kt
new file mode 100644
index 0000000000..7be84bda55
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/CameraUpdateFactory.kt
@@ -0,0 +1,71 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.graphics.Point
+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.model.CameraPosition
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.huawei.hms.maps.CameraUpdateFactory
+import org.microg.gms.maps.hms.utils.toHms
+import org.microg.gms.maps.hms.utils.toHmsZoom
+
+class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() {
+
+ override fun zoomIn(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomIn())
+ override fun zoomOut(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomOut())
+
+ override fun zoomTo(zoom: Float): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.zoomTo(toHmsZoom(zoom)))
+
+ override fun zoomBy(zoomDelta: Float): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta))
+
+ override fun zoomByWithFocus(zoomDelta: Float, x: Int, y: Int): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta, Point(x, y)))
+
+ override fun newCameraPosition(cameraPosition: CameraPosition): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.newCameraPosition(cameraPosition.toHms()))
+
+ override fun newLatLng(latLng: LatLng): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.newLatLng(latLng.toHms()))
+
+ override fun newLatLngZoom(latLng: LatLng, zoom: Float): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.newLatLngZoom(latLng.toHms(),
+ toHmsZoom(zoom)
+ ))
+
+ override fun newLatLngBounds(bounds: LatLngBounds, padding: Int): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.newLatLngBounds(bounds.toHms(), padding))
+
+ override fun scrollBy(x: Float, y: Float): IObjectWrapper {
+ Log.d(TAG, "scrollBy: $x, $y")
+ // gms map: A positive value moves the camera downwards
+ // hms map: A positive value moves the camera upwards
+ return ObjectWrapper.wrap(CameraUpdateFactory.scrollBy(x, -y))
+ }
+
+ override fun newLatLngBoundsWithSize(bounds: LatLngBounds, width: Int, height: Int, padding: Int): IObjectWrapper =
+ ObjectWrapper.wrap(CameraUpdateFactory.newLatLngBounds(bounds.toHms(), width, height, padding))
+
+ 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 = "GmsCameraUpdate"
+ }
+}
+
+
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/GoogleMap.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/GoogleMap.kt
new file mode 100644
index 0000000000..da1d0e2ef1
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/GoogleMap.kt
@@ -0,0 +1,768 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.location.Location
+import android.os.*
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.collection.LongSparseArray
+import com.google.android.gms.dynamic.IObjectWrapper
+import com.google.android.gms.dynamic.unwrap
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_HYBRID
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_SATELLITE
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_TERRAIN
+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.huawei.hms.maps.CameraUpdate
+import com.huawei.hms.maps.HuaweiMap
+import com.huawei.hms.maps.HuaweiMapOptions
+import com.huawei.hms.maps.MapView
+import com.huawei.hms.maps.MapsInitializer
+import com.huawei.hms.maps.internal.IOnIndoorStateChangeListener
+import com.huawei.hms.maps.internal.IOnInfoWindowCloseListener
+import com.huawei.hms.maps.internal.IOnInfoWindowLongClickListener
+import com.huawei.hms.maps.internal.IOnPoiClickListener
+import com.huawei.hms.maps.model.Marker
+import org.microg.gms.common.Constants
+import org.microg.gms.maps.hms.model.*
+import org.microg.gms.maps.hms.utils.*
+
+
+private fun LongSparseArray.values() = (0 until size()).mapNotNull { valueAt(it) }
+
+fun runOnMainLooper(method: () -> Unit) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ method()
+ } else {
+ Handler(Looper.getMainLooper()).post {
+ method()
+ }
+ }
+}
+
+class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() {
+
+ val view: FrameLayout
+ var map: HuaweiMap? = null
+ private set
+ val dpiFactor: Float
+ get() = context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
+
+ private var mapView: MapView? = null
+ private var created = false
+ private var initialized = false
+ private var loaded = false
+ private val mapLock = Object()
+
+ private val initializedCallbackList = mutableListOf()
+ private var loadedCallback: IOnMapLoadedCallback? = null
+ private var cameraChangeListener: IOnCameraChangeListener? = null
+ private var cameraMoveListener: IOnCameraMoveListener? = null
+ 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 val groundOverlays = mutableMapOf()
+ private val markers = mutableMapOf()
+ private val polylines = mutableMapOf()
+ private val polygons = mutableMapOf()
+ private val circles = mutableMapOf()
+ private val tileOverlays = mutableMapOf()
+
+ private var storedMapType: Int = options.mapType
+ val waitingCameraUpdates = mutableListOf()
+ var locationEnabled: Boolean = false
+
+ init {
+ val mapContext = MapContext(context)
+ BitmapDescriptorFactoryImpl.initialize(context.resources)
+ runOnMainLooper {
+ MapsInitializer.setApiKey(BuildConfig.HMSMAP_KEY)
+ }
+
+ this.view = object : FrameLayout(mapContext) {}
+ }
+
+ override fun getCameraPosition(): CameraPosition? = map?.cameraPosition?.toGms()
+ override fun getMaxZoomLevel(): Float = toHmsZoom(map?.maxZoomLevel ?: 18.toFloat())
+ override fun getMinZoomLevel(): Float = toHmsZoom(map?.minZoomLevel ?: 3.toFloat())
+
+ override fun moveCamera(cameraUpdate: IObjectWrapper?) {
+ val update = cameraUpdate.unwrap() ?: return
+ synchronized(mapLock) {
+ if (initialized) {
+ this.map?.moveCamera(update)
+ } else {
+ waitingCameraUpdates.add(update)
+ }
+ }
+ }
+
+ override fun animateCamera(cameraUpdate: IObjectWrapper?) {
+ val update = cameraUpdate.unwrap() ?: return
+ synchronized(mapLock) {
+ if (initialized) {
+ this.map?.animateCamera(update)
+ } else {
+ waitingCameraUpdates.add(update)
+ }
+ }
+ }
+
+ fun afterInitialized(runnable: () -> Unit) {
+ initializedCallbackList.add(object : IOnMapReadyCallback {
+ override fun onMapReady(map: IGoogleMapDelegate?) {
+ runnable()
+ }
+
+ override fun asBinder(): IBinder? = null
+ })
+ }
+
+ override fun animateCameraWithCallback(cameraUpdate: IObjectWrapper?, callback: ICancelableCallback?) {
+ val update = cameraUpdate.unwrap() ?: return
+ synchronized(mapLock) {
+ if (initialized) {
+ this.map?.animateCamera(update, callback?.toHms())
+ } else {
+ waitingCameraUpdates.add(update)
+ afterInitialized { callback?.onFinish() }
+ }
+ }
+ }
+
+ override fun animateCameraWithDurationAndCallback(cameraUpdate: IObjectWrapper?, duration: Int, callback: ICancelableCallback?) {
+ val update = cameraUpdate.unwrap() ?: return
+ synchronized(mapLock) {
+ if (initialized) {
+ this.map?.animateCamera(update, duration, callback?.toHms())
+ } else {
+ waitingCameraUpdates.add(update)
+ afterInitialized { callback?.onFinish() }
+ }
+ }
+ }
+
+ override fun stopAnimation() = map?.stopAnimation() ?: Unit
+
+ override fun setMapStyle(options: MapStyleOptions?): Boolean {
+ Log.d(TAG, "unimplemented Method: setMapStyle ${options?.getJson()}")
+ return true
+ }
+
+ override fun setMinZoomPreference(minZoom: Float) {
+ map?.setMinZoomPreference(toHmsZoom(minZoom))
+ }
+
+ override fun setMaxZoomPreference(maxZoom: Float) {
+ map?.setMaxZoomPreference(toHmsZoom(maxZoom))
+ }
+
+ override fun resetMinMaxZoomPreference() {
+ map?.setMinZoomPreference(3.toFloat())
+ map?.setMaxZoomPreference(18.toFloat())
+ }
+
+ override fun setLatLngBoundsForCameraTarget(bounds: LatLngBounds?) {
+ map?.setLatLngBoundsForCameraTarget(bounds?.toHms())
+ }
+
+ override fun addPolyline(options: PolylineOptions): IPolylineDelegate? {
+ val polyline = map?.addPolyline(options.toHms()) ?: return null
+ val polylineImpl = PolylineImpl(polyline, options)
+ polylines[polylineImpl.id] = polylineImpl
+ return polylineImpl
+ }
+
+ override fun addPolygon(options: PolygonOptions): IPolygonDelegate? {
+ val polygon = map?.addPolygon(options.toHms()) ?: return null
+ val polygonImpl = PolygonImpl(polygon)
+ polygons[polygonImpl.id] = polygonImpl
+ return polygonImpl
+ }
+
+ override fun addMarker(options: MarkerOptions): IMarkerDelegate? {
+ val marker = map?.addMarker(options.toHms()) ?: return null
+ val markerImpl = MarkerImpl(marker)
+ markers[markerImpl.id] = markerImpl
+ return markerImpl
+ }
+
+ override fun addGroundOverlay(options: GroundOverlayOptions): IGroundOverlayDelegate? {
+ Log.d(TAG, "Method: addGroundOverlay")
+ val groundOverlay = map?.addGroundOverlay(options.toHms()) ?: return null
+ val groundOverlayImpl = GroundOverlayImpl(groundOverlay)
+ groundOverlays[groundOverlayImpl.id] = groundOverlayImpl
+ return groundOverlayImpl
+ }
+
+ override fun addTileOverlay(options: TileOverlayOptions): ITileOverlayDelegate? {
+ Log.d(TAG, "Method: addTileOverlay")
+ val tileOverlay = map?.addTileOverlay(options.toHms()) ?: return null
+ val tileOverlayImpl = TileOverlayImpl(tileOverlay)
+ tileOverlays[tileOverlayImpl.id] = tileOverlayImpl
+ return tileOverlayImpl
+ }
+
+ override fun addCircle(options: CircleOptions): ICircleDelegate? {
+ val circle = map?.addCircle(options.toHms()) ?: return null
+ val circleImpl = CircleImpl(circle)
+ circles[circleImpl.id] = circleImpl
+ return circleImpl
+ }
+
+ override fun clear() {
+ map?.clear()
+ }
+
+
+ override fun getMapType(): Int {
+ return map?.mapType ?: storedMapType
+ }
+
+ override fun setMapType(type: Int) {
+ storedMapType = type
+ applyMapType()
+ }
+
+ fun applyMapType() {
+ // TODO: Serve map styles locally
+ when (storedMapType) {
+ MAP_TYPE_SATELLITE -> map?.mapType = HuaweiMap.MAP_TYPE_SATELLITE
+ MAP_TYPE_TERRAIN -> map?.mapType = HuaweiMap.MAP_TYPE_TERRAIN
+ MAP_TYPE_HYBRID -> map?.mapType = HuaweiMap.MAP_TYPE_HYBRID
+ //MAP_TYPE_NONE, MAP_TYPE_NORMAL,
+ else -> map?.mapType = HuaweiMap.MAP_TYPE_NORMAL
+ }
+ // map?.let { BitmapDescriptorFactoryImpl.registerMap(it) }
+ }
+
+ override fun isTrafficEnabled(): Boolean {
+ return map?.isTrafficEnabled ?: false
+ }
+
+ override fun setTrafficEnabled(traffic: Boolean) {
+ Log.d(TAG, "setTrafficEnabled")
+ map?.isTrafficEnabled = traffic
+ }
+
+ override fun isIndoorEnabled(): Boolean {
+ Log.d(TAG, "isIndoorEnabled")
+ return map?.isIndoorEnabled ?: false
+ }
+
+ override fun setIndoorEnabled(indoor: Boolean) {
+ Log.d(TAG, "setIndoorEnabled")
+ map?.isIndoorEnabled = indoor
+ }
+
+ override fun isMyLocationEnabled(): Boolean {
+ return map?.isMyLocationEnabled ?: false
+ }
+
+ override fun setMyLocationEnabled(myLocation: Boolean) {
+ map?.isMyLocationEnabled = myLocation
+ }
+
+ override fun getMyLocation(): Location? {
+ Log.d(TAG, "deprecated Method: getMyLocation")
+ return null
+ }
+
+ override fun setLocationSource(locationSource: ILocationSourceDelegate?) {
+ Log.d(TAG, "unimplemented Method: setLocationSource")
+ }
+
+ override fun setContentDescription(desc: String?) {
+ map?.setContentDescription(desc)
+ }
+
+ override fun getUiSettings(): IUiSettingsDelegate? = map?.uiSettings?.let { UiSettingsImpl(it) }
+
+ override fun getProjection(): IProjectionDelegate? = map?.projection?.let {
+ Log.d(TAG, "getProjection")
+ ProjectionImpl(it)
+ }
+
+ override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) {
+ Log.d(TAG, "setOnCameraChangeListener");
+ cameraChangeListener = listener
+ map?.setOnCameraIdleListener {
+ try {
+ cameraChangeListener?.onCameraChange(map?.cameraPosition?.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setOnCircleClickListener(listener: IOnCircleClickListener?) {
+ Log.d(TAG, "setOnCircleClickListener")
+ map?.setOnCircleClickListener { listener?.onCircleClick(circles[it.id]) }
+ }
+
+ override fun setOnGroundOverlayClickListener(listener: IOnGroundOverlayClickListener?) {
+ Log.d(TAG, "Method: setOnGroundOverlayClickListener")
+ map?.setOnGroundOverlayClickListener { listener?.onGroundOverlayClick(groundOverlays[it.id]) }
+ }
+
+ override fun setOnInfoWindowLongClickListener(listener: com.google.android.gms.maps.internal.IOnInfoWindowLongClickListener?) {
+ Log.d(TAG,"Not yet implemented setInfoWindowLongClickListener")
+ }
+
+ fun setOnIndoorStateChangeListener(listener: IOnIndoorStateChangeListener?) {
+ Log.d(TAG, "unimplemented Method: setOnIndoorStateChangeListener")
+ }
+
+ override fun setOnMapClickListener(listener: IOnMapClickListener?) {
+ mapClickListener = listener
+ map?.setOnMapClickListener { latlng ->
+ try {
+ mapClickListener?.onMapClick(latlng.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) {
+ mapLongClickListener = listener
+ map?.setOnMapLongClickListener { latlng ->
+ try {
+ mapLongClickListener?.onMapLongClick(latlng.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) {
+ map?.setOnMarkerClickListener { listener?.onMarkerClick(markers[it.id]) ?: false }
+ }
+
+ override fun setOnMarkerDragListener(listener: IOnMarkerDragListener?) {
+ map?.setOnMarkerDragListener(object : HuaweiMap.OnMarkerDragListener{
+ override fun onMarkerDragStart(p0: Marker?) {
+ listener?.onMarkerDragStart(markers[p0?.id])
+ }
+
+ override fun onMarkerDrag(p0: Marker?) {
+ listener?.onMarkerDrag(markers[p0?.id])
+ }
+
+ override fun onMarkerDragEnd(p0: Marker?) {
+ listener?.onMarkerDragEnd(markers[p0?.id])
+ }
+ })
+ }
+
+ override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) {
+ Log.d(TAG, "setOnInfoWindowClickListener")
+ map?.setOnInfoWindowClickListener { listener?.onInfoWindowClick(markers[it.id]) }
+ }
+
+ fun setOnInfoWindowCloseListener(listener: IOnInfoWindowCloseListener?) {
+ Log.d(TAG, "unimplemented Method: setOnInfoWindowCloseListener")
+ }
+
+ fun setOnInfoWindowLongClickListener(listener: IOnInfoWindowLongClickListener?) {
+ Log.d(TAG, "unimplemented Method: setOnInfoWindowLongClickListener")
+ }
+
+ override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) {
+ Log.d(TAG, "setInfoWindowAdapter")
+ map?.setInfoWindowAdapter(object : HuaweiMap.InfoWindowAdapter{
+ override fun getInfoContents(p0: Marker?): View? {
+ return adapter?.getInfoContents(markers[p0?.id]).unwrap()
+ }
+
+ override fun getInfoWindow(p0: Marker?): View? {
+ return adapter?.getInfoWindow(markers[p0?.id]).unwrap()
+ }
+
+ })
+ }
+
+ override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) {
+ Log.d(TAG, "deprecated Method: setOnMyLocationChangeListener")
+ }
+
+ override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) {
+ Log.d(TAG, "setOnMyLocationButtonClickListener")
+ map?.setOnMyLocationButtonClickListener { listener?.onMyLocationButtonClick() ?: false }
+ }
+
+ override fun setOnMyLocationClickListener(listener: IOnMyLocationClickListener?) {
+ Log.d(TAG, "setOnMyLocationClickListener")
+ map?.setOnMyLocationClickListener { listener?.onMyLocationClick(it) }
+ }
+
+ fun setOnPoiClickListener(listener: IOnPoiClickListener?) {
+ Log.d(TAG, "unimplemented Method: setOnPoiClickListener")
+ }
+
+ override fun setOnPolygonClickListener(listener: IOnPolygonClickListener?) {
+ Log.d(TAG, "setOnPolygonClickListener")
+ map?.setOnPolygonClickListener { listener?.onPolygonClick(polygons[it.id]) }
+ }
+
+ override fun setOnInfoWindowCloseListener(listener: com.google.android.gms.maps.internal.IOnInfoWindowCloseListener?) {
+ Log.d(TAG, "Not yet implemented setInfoWindowCloseListener")
+ }
+
+ override fun setOnPolylineClickListener(listener: IOnPolylineClickListener?) {
+ Log.d(TAG, "unimplemented Method: setOnPolylineClickListener")
+ map?.setOnPolylineClickListener { listener?.onPolylineClick(polylines[it.id]) }
+ }
+
+ override fun snapshot(callback: ISnapshotReadyCallback, bitmap: IObjectWrapper?) {
+ Log.d(TAG, "snapshot")
+ val hmsBitmap = bitmap.unwrap() ?: return
+ val hmsCallback = HuaweiMap.SnapshotReadyCallback { p0 -> callback.onBitmapReady(p0) }
+ map?.snapshot(hmsCallback, hmsBitmap)
+ }
+
+ override fun snapshotForTest(callback: ISnapshotReadyCallback) {
+ Log.d(TAG, "snapshotForTest")
+ val hmsCallback = HuaweiMap.SnapshotReadyCallback { p0 -> callback.onBitmapReady(p0) }
+ map?.snapshot(hmsCallback)
+ }
+
+ override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
+ Log.d(TAG, "setPadding: $left $top $right $bottom")
+ map?.setPadding(left, top, right, bottom)
+ }
+
+ override fun isBuildingsEnabled(): Boolean {
+ Log.d(TAG, "isBuildingsEnabled")
+ return map?.isBuildingsEnabled ?: true
+ }
+
+ override fun setBuildingsEnabled(buildings: Boolean) {
+ Log.d(TAG, "setBuildingsEnabled: $buildings")
+ map?.isBuildingsEnabled = buildings
+ }
+
+ override fun setOnMapLoadedCallback(callback: IOnMapLoadedCallback?) {
+ if (callback != null) {
+ synchronized(mapLock) {
+ if (loaded) {
+ Log.d(TAG, "Invoking callback instantly, as map is loaded")
+ try {
+ callback.onMapLoaded()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ } else {
+ Log.d(TAG, "Delay callback invocation, as map is not yet loaded")
+ loadedCallback = callback
+ }
+ }
+ } else {
+ loadedCallback = null
+ }
+ }
+
+ override fun setCameraMoveStartedListener(listener: IOnCameraMoveStartedListener?) {
+ Log.d(TAG, "setCameraMoveStartedListener")
+ cameraMoveStartedListener = listener
+ map?.setOnCameraMoveStartedListener {
+ try {
+ cameraMoveStartedListener?.onCameraMoveStarted(it)
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setCameraMoveListener(listener: IOnCameraMoveListener?) {
+ Log.d(TAG, "setCameraMoveListener")
+ cameraMoveListener = listener
+ map?.setOnCameraMoveListener {
+ try {
+ cameraMoveListener?.onCameraMove()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setCameraMoveCanceledListener(listener: IOnCameraMoveCanceledListener?) {
+ Log.d(TAG, "setCameraMoveCanceledListener")
+ cameraMoveCanceledListener = listener
+ map?.setOnCameraMoveCanceledListener {
+ try {
+ cameraMoveCanceledListener?.onCameraMoveCanceled()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun setCameraIdleListener(listener: IOnCameraIdleListener?) {
+ Log.d(TAG, "onCameraIdle: successful")
+ cameraIdleListener = listener
+ map?.setOnCameraIdleListener {
+ try {
+ cameraIdleListener?.onCameraIdle()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+
+ override fun getTestingHelper(): IObjectWrapper? {
+ Log.d(TAG, "unimplemented Method: getTestingHelper")
+ return null
+ }
+
+ override fun setWatermarkEnabled(watermark: Boolean) {
+ Log.d(TAG, "unimplemented Method: setWatermarkEnabled")
+ }
+
+ override fun useViewLifecycleWhenInFragment(): Boolean {
+ Log.d(TAG, "unimplemented Method: useViewLifecycleWhenInFragment")
+ return false
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ if (!created) {
+ Log.d(TAG, "create: ${context.packageName},\n$options")
+ val mapContext = MapContext(context)
+ MapsInitializer.initialize(mapContext)
+ val mapView = MapView(mapContext, options.toHms())
+ this.mapView = mapView
+ view.addView(mapView)
+ mapView.onCreate(savedInstanceState?.toHms())
+ view.viewTreeObserver.addOnGlobalLayoutListener {
+ if (!isFakeWatermark) {
+ fakeWatermark()
+ }
+ }
+ mapView.getMapAsync(this::initMap)
+
+ created = true
+ }
+ }
+
+ private var isFakeWatermark: Boolean = false
+ private fun fakeWatermark() {
+ Log.d(TAG_LOGO, "start")
+ try {
+ val view1 = view.getChildAt(0) as ViewGroup
+ val view2 = view1.getChildAt(0) as ViewGroup
+ val view4 = view2.getChildAt(1)
+ Log.d(TAG_LOGO, view4.toString())
+ if (view4 is LinearLayout) {
+ view4.visibility = View.GONE
+ isFakeWatermark = true
+ } else {
+ throw Exception("LinearLayout not found")
+ }
+ } catch (tr: Throwable) {
+ Log.d(TAG_LOGO, "Throwable", tr)
+ }
+ }
+
+ private fun getAllChildViews(view: View, index: Int): List? {
+ Log.d(TAG_LOGO, "getAllChildViews: $index, $view")
+ if (view is LinearLayout) {
+ Log.d(TAG_LOGO, "legal: $index")
+ view.visibility = View.GONE
+ }
+ val allChildren: MutableList = ArrayList()
+ if (view is ViewGroup) {
+ val vp = view
+ for (i in 0 until vp.childCount) {
+ val viewChild = vp.getChildAt(i)
+ Log.d(TAG_LOGO, "child:$index, $i, $viewChild")
+ allChildren.add(viewChild)
+ allChildren.addAll(getAllChildViews(viewChild, index + 1)!!)
+ }
+ }
+ return allChildren
+ }
+
+ private fun initMap(map: HuaweiMap) {
+ if (this.map != null) return
+
+ loaded = true
+ this.map = map
+
+ map.setOnCameraIdleListener {
+ try {
+ cameraChangeListener?.onCameraChange(map.cameraPosition.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnCameraIdleListener {
+ try {
+ cameraIdleListener?.onCameraIdle()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnCameraMoveListener {
+ try {
+ cameraMoveListener?.onCameraMove()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnCameraMoveStartedListener {
+ try {
+ cameraMoveStartedListener?.onCameraMoveStarted(it)
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnCameraMoveCanceledListener {
+ try {
+ cameraMoveCanceledListener?.onCameraMoveCanceled()
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnMapClickListener { latlng ->
+ try {
+ if (options.liteMode) {
+ val parentView = view.parent?.parent
+ // TODO hms not support disable click listener when liteMode, this just fix for teams
+ if (parentView != null && parentView::class.qualifiedName.equals("com.microsoft.teams.location.ui.map.MapViewLite")) {
+ val clickView = parentView as ViewGroup
+ clickView.performClick()
+ return@setOnMapClickListener
+ }
+ }
+ mapClickListener?.onMapClick(latlng.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ map.setOnMapLongClickListener { latlng ->
+ try {
+ if (options.liteMode) {
+ val parentView = view.parent?.parent
+ // TODO hms not support disable click listener when liteMode, this just fix for teams
+ if (parentView != null && parentView::class.qualifiedName.equals("com.microsoft.teams.location.ui.map.MapViewLite")) {
+ val clickView = parentView as ViewGroup
+ clickView.performLongClick()
+ return@setOnMapLongClickListener
+ }
+ }
+ mapLongClickListener?.onMapLongClick(latlng.toGms())
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+
+ synchronized(mapLock) {
+ initialized = true
+ waitingCameraUpdates.forEach { map.moveCamera(it) }
+ val initializedCallbackList = ArrayList(initializedCallbackList)
+ Log.d(TAG, "Invoking ${initializedCallbackList.size} callbacks delayed, as map is initialized")
+ for (callback in initializedCallbackList) {
+ try {
+ callback.onMapReady(this)
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ }
+ }
+ }
+
+ override fun onResume() = mapView?.onResume() ?: Unit
+ override fun onPause() = mapView?.onPause() ?: Unit
+ override fun onDestroy() {
+ Log.d(TAG, "onDestroy")
+ circles.map { it.value.remove() }
+ circles.clear()
+ polylines.map { it.value.remove() }
+ polylines.clear()
+ polygons.map { it.value.remove() }
+ polygons.clear()
+ markers.map { it.value.remove() }
+ markers.clear()
+// BitmapDescriptorFactoryImpl.unregisterMap(map)
+ view.removeView(mapView)
+ // TODO can crash?
+ mapView?.onDestroy()
+ mapView = null
+
+ // Don't make it null; this object is not deleted immediately, and it may want to access map.* stuff
+ //map = null
+
+ created = false
+ initialized = false
+ loaded = false
+ }
+
+ override fun onStart() {
+ mapView?.onStart()
+ }
+
+ override fun onStop() {
+ 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) {
+ val newBundle = Bundle()
+ mapView?.onSaveInstanceState(newBundle)
+ outState.putAll(newBundle.toGms())
+ }
+
+ fun getMapAsync(callback: IOnMapReadyCallback) {
+ synchronized(mapLock) {
+ if (initialized) {
+ Log.d(TAG, "Invoking callback instantly, as map is initialized")
+ try {
+ callback.onMapReady(this)
+ } catch (e: Exception) {
+ Log.w(TAG, e)
+ }
+ } else {
+ Log.d(TAG, "Delay callback invocation, as map is not yet initialized")
+ initializedCallbackList.add(callback)
+ }
+ }
+ }
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+ if (super.onTransact(code, data, reply, flags)) {
+ Log.d(TAG, "onTransact: $code, $data, $flags")
+ true
+ } else {
+ Log.w(TAG, "onTransact [unknown]: $code, $data, $flags"); false
+ }
+
+ companion object {
+ private const val TAG = "GmsGoogleMap"
+
+ private const val TAG_LOGO = "fakeWatermark"
+ private const val MAX_TIMES = 300
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapFragment.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapFragment.kt
new file mode 100644
index 0000000000..5bb62383b2
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapFragment.kt
@@ -0,0 +1,98 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.app.Activity
+import android.os.Bundle
+import android.os.Parcel
+import android.util.Log
+import android.view.ViewGroup
+import com.google.android.gms.dynamic.IObjectWrapper
+import com.google.android.gms.dynamic.ObjectWrapper
+import com.google.android.gms.maps.GoogleMapOptions
+import com.google.android.gms.maps.internal.IGoogleMapDelegate
+import com.google.android.gms.maps.internal.IMapFragmentDelegate
+import com.google.android.gms.maps.internal.IOnMapReadyCallback
+
+class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stub() {
+
+ private var map: GoogleMapImpl? = null
+ private var options: GoogleMapOptions? = null
+
+ override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle?) {
+ Log.d(TAG, "onInflate: $options")
+ this.options = options
+ map?.options = options
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ if (options == null) {
+ options = savedInstanceState?.getParcelable("MapOptions")
+ }
+ if (options == null) {
+ options = GoogleMapOptions()
+ }
+ Log.d(TAG, "onCreate: $options")
+ map = GoogleMapImpl(activity, options ?: GoogleMapOptions())
+ }
+
+ override fun onCreateView(layoutInflater: IObjectWrapper, container: IObjectWrapper, savedInstanceState: Bundle?): IObjectWrapper {
+ if (options == null) {
+ options = savedInstanceState?.getParcelable("MapOptions")
+ }
+ Log.d(TAG, "onCreateView: ${options?.camera?.target}")
+ if (map == null) {
+ map = GoogleMapImpl(activity, options ?: GoogleMapOptions())
+ }
+ Log.d(TAG, "onCreateView: $options")
+ 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
+ override fun onEnterAmbient(bundle: Bundle?) = map?.onEnterAmbient(bundle) ?: Unit
+ override fun onExitAmbient() = map?.onExitAmbient() ?: Unit
+ override fun onStart() = map?.onStart() ?: Unit
+ override fun onStop() = map?.onStop() ?: Unit
+ override fun onResume() = map?.onResume() ?: Unit
+ 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 onDestroyView() {
+ map?.onDestroy()
+ }
+
+ override fun onDestroy() {
+ map?.onDestroy()
+ map = null
+ options = null
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ if (options != null) {
+ outState.putParcelable("MapOptions", options)
+ }
+ map?.onSaveInstanceState(outState)
+ }
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
+ if (super.onTransact(code, data, reply, flags)) {
+ return true
+ } else {
+ Log.d(TAG, "onTransact [unknown]: $code, $data, $flags")
+ return false
+ }
+ }
+
+ companion object {
+ private val TAG = "GmsMapFragment"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapView.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapView.kt
new file mode 100644
index 0000000000..79475cad06
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapView.kt
@@ -0,0 +1,57 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.content.Context
+import android.os.Bundle
+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.GoogleMapOptions
+import com.google.android.gms.maps.internal.IGoogleMapDelegate
+import com.google.android.gms.maps.internal.IMapViewDelegate
+import com.google.android.gms.maps.internal.IOnMapReadyCallback
+
+class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() {
+
+ private var options: GoogleMapOptions = options ?: GoogleMapOptions()
+ private var map: GoogleMapImpl? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ Log.d(TAG, "onCreate: $options")
+ map = GoogleMapImpl(context, options)
+ map!!.onCreate(savedInstanceState)
+ }
+
+ override fun getMap(): IGoogleMapDelegate? = map
+ override fun onEnterAmbient(bundle: Bundle?) = map?.onEnterAmbient(bundle) ?: Unit
+ override fun onExitAmbient() = map?.onExitAmbient() ?: Unit
+ override fun onStart() = map?.onStart() ?: Unit
+ override fun onStop() = map?.onStop() ?: Unit
+ override fun onResume() = map?.onResume() ?: Unit
+ override fun onPause() = map?.onPause() ?: Unit
+ override fun onDestroy() {
+ map?.onDestroy()
+ map = null
+ }
+
+ 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 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 = "GmsMapView"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/Projection.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/Projection.kt
new file mode 100644
index 0000000000..8e2643b480
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/Projection.kt
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.graphics.Point
+import android.util.Log
+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.internal.IProjectionDelegate
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.VisibleRegion
+import com.huawei.hms.maps.Projection
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toHms
+
+class ProjectionImpl(private val projection: Projection) : IProjectionDelegate.Stub() {
+
+ override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? {
+ Log.d(TAG, "fromScreenLocation")
+ return try {
+ obj.unwrap()?.let {
+ projection.fromScreenLocation(it).toGms()
+ }
+ } catch (e: Exception) {
+ Log.d(TAG, "fromScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
+ LatLng(0.0, 0.0)
+ }
+ }
+
+ override fun toScreenLocation(latLng: LatLng?): IObjectWrapper {
+ Log.d(TAG, "toScreenLocation: $latLng")
+ return try {
+ ObjectWrapper.wrap(latLng?.toHms()?.let {
+ projection.toScreenLocation(it).let { Point(it.x, it.y) }
+ })
+ } catch (e: Exception) {
+ Log.d(TAG, "toScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
+ ObjectWrapper.wrap(Point(0, 0))
+ }
+ }
+
+ override fun getVisibleRegion(): VisibleRegion {
+ val visibleRegion = projection.visibleRegion
+ Log.d(TAG, "getVisibleRegion: $visibleRegion")
+ return visibleRegion.toGms()
+ }
+
+ companion object {
+ private val TAG = "GmsMapProjection"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/UiSettings.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/UiSettings.kt
new file mode 100644
index 0000000000..335064c145
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/UiSettings.kt
@@ -0,0 +1,109 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms
+
+import android.os.Parcel
+import android.util.Log
+
+import com.google.android.gms.maps.internal.IUiSettingsDelegate
+import com.huawei.hms.maps.UiSettings
+
+class UiSettingsImpl(private val uiSettings: UiSettings) : IUiSettingsDelegate.Stub() {
+
+ override fun setZoomControlsEnabled(zoom: Boolean) {
+ Log.d(TAG, "setZoomControlsEnabled: $zoom")
+ uiSettings.isZoomControlsEnabled = zoom
+ }
+
+ override fun setCompassEnabled(compass: Boolean) {
+ uiSettings.isCompassEnabled = compass
+ }
+
+ override fun setMyLocationButtonEnabled(locationButton: Boolean) {
+ uiSettings.isMyLocationButtonEnabled = locationButton
+ }
+
+ override fun setScrollGesturesEnabled(scrollGestures: Boolean) {
+ uiSettings.isScrollGesturesEnabled = scrollGestures
+ }
+
+ override fun setZoomGesturesEnabled(zoomGestures: Boolean) {
+ uiSettings.isZoomGesturesEnabled = zoomGestures
+ }
+
+ override fun setTiltGesturesEnabled(tiltGestures: Boolean) {
+ uiSettings.isTiltGesturesEnabled = tiltGestures
+ }
+
+ override fun setRotateGesturesEnabled(rotateGestures: Boolean) {
+ uiSettings.isRotateGesturesEnabled = rotateGestures
+ }
+
+ override fun setAllGesturesEnabled(gestures: Boolean) {
+ uiSettings.setAllGesturesEnabled(gestures)
+ }
+
+ override fun isZoomControlsEnabled(): Boolean {
+ Log.d(TAG, "isZoomControlsEnabled")
+ return uiSettings.isZoomControlsEnabled
+ }
+
+ override fun isCompassEnabled(): Boolean = uiSettings.isCompassEnabled
+
+ override fun isMyLocationButtonEnabled(): Boolean {
+ Log.d(TAG, "isMyLocationButtonEnabled")
+ return uiSettings.isMyLocationButtonEnabled
+ }
+
+ override fun isScrollGesturesEnabled(): Boolean = uiSettings.isScrollGesturesEnabled
+
+ override fun isZoomGesturesEnabled(): Boolean = uiSettings.isZoomGesturesEnabled
+
+ override fun isTiltGesturesEnabled(): Boolean = uiSettings.isTiltGesturesEnabled
+
+ override fun isRotateGesturesEnabled(): Boolean = uiSettings.isRotateGesturesEnabled
+
+ override fun setIndoorLevelPickerEnabled(indoorLevelPicker: Boolean) {
+ Log.d(TAG, "setIndoorLevelPickerEnabled: $indoorLevelPicker")
+ uiSettings.isIndoorLevelPickerEnabled = indoorLevelPicker
+ }
+
+ override fun isIndoorLevelPickerEnabled(): Boolean {
+ Log.d(TAG, "isIndoorLevelPickerEnabled")
+ return uiSettings.isIndoorLevelPickerEnabled
+ }
+
+ override fun setMapToolbarEnabled(mapToolbar: Boolean) {
+ Log.d(TAG, "setMapToolbarEnabled: $mapToolbar")
+ uiSettings.isMapToolbarEnabled = mapToolbar
+ }
+
+ override fun isMapToolbarEnabled(): Boolean {
+ Log.d(TAG, "isMapToolbarEnabled")
+ return uiSettings.isMapToolbarEnabled
+ }
+
+ override fun setScrollGesturesEnabledDuringRotateOrZoom(scrollDuringZoom: Boolean) {
+ Log.d(TAG, "setScrollGesturesEnabledDuringRotateOrZoom: $scrollDuringZoom")
+ uiSettings.isScrollGesturesEnabledDuringRotateOrZoom = scrollDuringZoom
+ }
+
+ override fun isScrollGesturesEnabledDuringRotateOrZoom(): Boolean {
+ Log.d(TAG, "isScrollGesturesEnabledDuringRotateOrZoom")
+ return uiSettings.isScrollGesturesEnabledDuringRotateOrZoom
+ }
+
+ 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 = "GmsMapsUi"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/BitmapDescriptorFactory.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/BitmapDescriptorFactory.kt
new file mode 100644
index 0000000000..d6838d065c
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/BitmapDescriptorFactory.kt
@@ -0,0 +1,68 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+import android.content.res.Resources
+import android.graphics.*
+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.huawei.hms.maps.HuaweiMap
+import com.huawei.hms.maps.model.BitmapDescriptorFactory
+
+
+object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() {
+ private val TAG = "GmsMapBitmap"
+ private var resources: Resources? = null
+
+ fun initialize(resources: Resources?) {
+ BitmapDescriptorFactoryImpl.resources = resources ?: BitmapDescriptorFactoryImpl.resources
+ }
+
+ override fun fromResource(resourceId: Int): IObjectWrapper? {
+ return BitmapFactory.decodeResource(resources, resourceId)?.let {
+ ObjectWrapper.wrap(BitmapDescriptorFactory.fromBitmap(it))
+ }
+ }
+
+ override fun fromAsset(assetName: String): IObjectWrapper? {
+ return resources?.assets?.open(assetName)?.let {
+ BitmapFactory.decodeStream(it)
+ ?.let { ObjectWrapper.wrap(BitmapDescriptorFactory.fromBitmap(it)) }
+ }
+ }
+
+ override fun fromFile(fileName: String): IObjectWrapper? {
+ return BitmapFactory.decodeFile(fileName)
+ ?.let { ObjectWrapper.wrap(BitmapDescriptorFactory.fromBitmap(it)) }
+ }
+
+ override fun defaultMarker(): IObjectWrapper? {
+ return ObjectWrapper.wrap(BitmapDescriptorFactory.defaultMarker())
+ }
+
+ override fun defaultMarkerWithHue(hue: Float): IObjectWrapper? {
+ return ObjectWrapper.wrap(BitmapDescriptorFactory.defaultMarker(hue))
+ }
+
+ override fun fromBitmap(bitmap: Bitmap): IObjectWrapper? {
+ return ObjectWrapper.wrap(BitmapDescriptorFactory.fromBitmap(bitmap))
+ }
+
+ override fun fromPath(absolutePath: String): IObjectWrapper? {
+ return BitmapFactory.decodeFile(absolutePath)
+ ?.let { ObjectWrapper.wrap(BitmapDescriptorFactory.fromBitmap(it)) }
+ }
+
+ 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
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Circle.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Circle.kt
new file mode 100644
index 0000000000..363f7f5269
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Circle.kt
@@ -0,0 +1,123 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+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.dynamic.unwrap
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.PatternItem
+import com.google.android.gms.maps.model.internal.ICircleDelegate
+import com.huawei.hms.maps.model.Circle
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toHms
+
+class CircleImpl(private val circle: Circle) : ICircleDelegate.Stub() {
+
+ override fun remove() {
+ circle.remove()
+ }
+
+ override fun getId(): String = circle.id
+
+ override fun setCenter(center: LatLng) {
+ circle.center = center.toHms()
+ }
+
+ override fun getCenter(): LatLng = circle.center.toGms()
+
+ override fun setRadius(radius: Double) {
+ circle.radius = radius
+ }
+
+ override fun getRadius(): Double = circle.radius
+
+ override fun setStrokeWidth(width: Float) {
+ circle.strokeWidth = width
+ }
+
+ override fun getStrokeWidth(): Float = circle.strokeWidth
+
+ override fun setStrokeColor(color: Int) {
+ circle.strokeColor = color
+ }
+
+ override fun getStrokeColor(): Int = circle.strokeColor
+
+ override fun setTag(tag: IObjectWrapper) {
+ circle.setTag(tag.unwrap())
+ }
+
+ override fun getTag(): IObjectWrapper? {
+ return ObjectWrapper.wrap(circle.tag)
+ }
+
+ override fun setStrokePattern(pattern: List?) {
+ circle.strokePattern = pattern?.map { it.toHms() }
+ }
+
+ override fun getStrokePattern(): List? {
+ return circle.strokePattern?.map { it.toGms() }
+ }
+
+ override fun setFillColor(color: Int) {
+ circle.fillColor = color
+ }
+
+ override fun getFillColor(): Int = circle.fillColor
+
+ override fun setZIndex(zIndex: Float) {
+ circle.zIndex = zIndex
+ }
+
+ override fun getZIndex(): Float = circle.zIndex
+
+ override fun setVisible(visible: Boolean) {
+ circle.isVisible = visible
+ }
+
+ override fun isVisible(): Boolean = circle.isVisible
+
+ override fun setClickable(clickable: Boolean) {
+ circle.isClickable = clickable
+ }
+
+ override fun isClickable(): Boolean {
+ return circle.isClickable
+ }
+
+ override fun equalsRemote(other: ICircleDelegate?): Boolean = equals(other)
+
+ override fun hashCodeRemote(): Int = hashCode()
+
+ override fun hashCode(): Int {
+ return id.hashCode()
+ }
+
+ override fun toString(): String {
+ return id
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other is CircleImpl) {
+ return other.id == id
+ }
+ 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 {
+ val TAG = "GmsMapCircle"
+ }
+}
\ No newline at end of file
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/GroundOverlay.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/GroundOverlay.kt
new file mode 100644
index 0000000000..4b0d97d4cd
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/GroundOverlay.kt
@@ -0,0 +1,143 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+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.dynamic.unwrap
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.android.gms.maps.model.internal.IGroundOverlayDelegate
+import com.huawei.hms.maps.model.GroundOverlay
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toHms
+
+class GroundOverlayImpl(private val groundOverlay: GroundOverlay) : IGroundOverlayDelegate.Stub() {
+
+ override fun getId(): String {
+ return groundOverlay.id
+ }
+
+ override fun getPosition(): LatLng? {
+ return groundOverlay.position?.toGms()
+ }
+
+ override fun setPosition(pos: LatLng?) {
+ pos?.let { groundOverlay.position = it.toHms() }
+ }
+
+ override fun getWidth(): Float {
+ return groundOverlay.width
+ }
+
+ override fun getHeight(): Float {
+ return groundOverlay.height
+ }
+
+ override fun setDimensions(width: Float, height: Float) {
+ groundOverlay.setDimensions(width, height)
+ }
+
+ override fun getBounds(): LatLngBounds? {
+ return groundOverlay.bounds?.toGms()
+ }
+
+ override fun getBearing(): Float {
+ return groundOverlay.bearing
+ }
+
+ override fun setBearing(bearing: Float) {
+ groundOverlay.bearing = bearing
+ }
+
+ override fun setZIndex(zIndex: Float) {
+ groundOverlay.zIndex = zIndex
+ }
+
+ override fun getZIndex(): Float {
+ return groundOverlay.zIndex
+ }
+
+ override fun isVisible(): Boolean {
+ return groundOverlay.isVisible
+ }
+
+ override fun setVisible(visible: Boolean) {
+ groundOverlay.isVisible = visible
+ }
+
+ override fun getTransparency(): Float {
+ return groundOverlay.transparency
+ }
+
+ override fun setTransparency(transparency: Float) {
+ groundOverlay.transparency = transparency
+ }
+
+ override fun setDimension(dimension: Float) {
+ groundOverlay.setDimensions(dimension)
+ }
+
+ override fun setPositionFromBounds(bounds: LatLngBounds?) {
+ bounds?.let { groundOverlay.setPositionFromBounds(it.toHms()) }
+ }
+
+ override fun getTag(): IObjectWrapper? {
+ return ObjectWrapper.wrap(groundOverlay.tag)
+ }
+
+ override fun isClickable(): Boolean = groundOverlay.isClickable
+
+ override fun setClickable(clickable: Boolean) {
+ groundOverlay.isClickable = clickable
+ }
+
+ override fun setImage(obj: IObjectWrapper?) {
+ groundOverlay.setImage(obj.unwrap())
+ }
+
+ override fun setTag(tag: IObjectWrapper) {
+ groundOverlay.tag = tag.unwrap()
+ }
+
+ override fun equalsRemote(other: IGroundOverlayDelegate?): Boolean {
+ return this == other
+ }
+
+ override fun hashCode(): Int {
+ return groundOverlay.hashCode()
+ }
+
+ override fun hashCodeRemote(): Int {
+ return hashCode()
+ }
+
+// override fun todo(obj: IObjectWrapper?) {
+// Log.d(TAG, "Not yet implemented")
+// }
+
+ override fun equals(other: Any?): Boolean {
+ return groundOverlay == other
+ }
+
+ override fun remove() {
+ Log.d(TAG, "Method: remove")
+ groundOverlay.remove()
+ }
+
+ 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 = "GmsMapGroundOverlay"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Marker.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Marker.kt
new file mode 100644
index 0000000000..bc5cf37831
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Marker.kt
@@ -0,0 +1,143 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+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.dynamic.unwrap
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.internal.IMarkerDelegate
+import com.huawei.hms.maps.model.Marker
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toHms
+
+class MarkerImpl(private val marker: Marker) : IMarkerDelegate.Stub() {
+
+ override fun remove() {
+ marker.remove()
+ }
+
+ override fun getId(): String = marker.id
+
+ override fun setPosition(position: LatLng?) {
+ marker.position = position?.toHms()
+ }
+
+ override fun getPosition(): LatLng {
+ return marker.position.toGms()
+ }
+
+ override fun setTitle(title: String?) {
+ marker.title = title
+ }
+
+ override fun getTitle(): String? = marker.title
+
+ override fun setSnippet(snippet: String?) {
+ marker.snippet = snippet
+ }
+
+ override fun getSnippet(): String? = marker.snippet
+
+ override fun setDraggable(draggable: Boolean) {
+ marker.isDraggable = draggable
+ }
+
+ override fun isDraggable(): Boolean = marker.isDraggable
+
+ override fun showInfoWindow() {
+ marker.showInfoWindow()
+ }
+
+ override fun hideInfoWindow() {
+ marker.hideInfoWindow()
+ }
+
+ override fun isInfoWindowShown(): Boolean {
+ return marker.isInfoWindowShown
+ }
+
+ override fun setVisible(visible: Boolean) {
+ marker.isVisible = visible
+ }
+
+ override fun isVisible(): Boolean = marker.isVisible
+
+ 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()
+ }
+
+ override fun toString(): String {
+ return "$id ($title)"
+ }
+
+ override fun hashCodeRemote(): Int = hashCode()
+
+ override fun setIcon(obj: IObjectWrapper?) {
+ marker.setIcon(obj.unwrap())
+ }
+
+ override fun setAnchor(x: Float, y: Float) {
+ marker.setMarkerAnchor(x, y)
+ }
+
+ override fun setFlat(flat: Boolean) {
+ marker.isFlat = flat
+ }
+
+ override fun isFlat(): Boolean {
+ return marker.isFlat
+ }
+
+ override fun setRotation(rotation: Float) {
+ marker.rotation = rotation
+ }
+
+ override fun getRotation(): Float = marker.rotation
+
+ override fun setInfoWindowAnchor(x: Float, y: Float) {
+ marker.setInfoWindowAnchor(x, y)
+ }
+
+ override fun setAlpha(alpha: Float) {
+ marker.alpha = alpha
+ }
+
+ override fun getAlpha(): Float = marker.alpha
+
+ override fun setZIndex(zIndex: Float) {
+ marker.zIndex = zIndex
+ }
+
+ override fun getZIndex(): Float = marker.zIndex
+
+ override fun setTag(obj: IObjectWrapper?) {
+ marker.tag = obj.unwrap()
+ }
+
+ override fun getTag(): IObjectWrapper = ObjectWrapper.wrap(marker.tag)
+
+ 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 = "GmsMapMarker"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polygon.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polygon.kt
new file mode 100644
index 0000000000..ed3e287b0f
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polygon.kt
@@ -0,0 +1,171 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+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.dynamic.unwrap
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.PatternItem
+import com.google.android.gms.maps.model.internal.IPolygonDelegate
+import com.huawei.hms.maps.model.Polygon
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toHms
+import org.microg.gms.utils.warnOnTransactionIssues
+
+class PolygonImpl(private val polygon: Polygon) : IPolygonDelegate.Stub() {
+
+ override fun remove() {
+ polygon.remove()
+ }
+
+ override fun getId(): String = polygon.id
+
+ override fun setPoints(points: List) {
+ polygon.points = points.map { it.toHms() }
+ }
+
+ override fun getPoints(): List = polygon.points.map { it.toGms() }
+
+ override fun setHoles(holes: List?) {
+ if (holes == null) {
+ polygon.holes = emptyList()
+ } else {
+ val outHmsHoles: MutableList> =
+ ArrayList()
+ for (out in holes) {
+ if (out is List<*>) {
+ val inHmsHoles: MutableList = ArrayList()
+ for (inn in out) {
+ if (inn is LatLng) {
+ inHmsHoles.add(inn.toHms())
+ }
+ }
+ outHmsHoles.add(inHmsHoles)
+ }
+ }
+ polygon.holes = outHmsHoles
+ }
+ }
+
+ override fun getHoles(): List {
+ val outHoles = polygon.holes ?: return emptyList()
+ val outGmsHoles: MutableList> = ArrayList()
+ for (inHoles in outHoles) {
+ if (inHoles != null) {
+ val inGmsHoles: MutableList = ArrayList()
+ for (inHole in inHoles) {
+ inGmsHoles.add(inHole.toGms())
+ }
+ outGmsHoles.add(inGmsHoles)
+ }
+ }
+ return outGmsHoles
+ }
+
+ override fun setStrokeWidth(width: Float) {
+ polygon.strokeWidth = width
+ }
+
+ override fun getStrokeWidth(): Float = polygon.strokeWidth
+
+ override fun setStrokeColor(color: Int) {
+ polygon.strokeColor = color
+ }
+
+ override fun getStrokeColor(): Int = polygon.strokeColor
+
+ override fun setFillColor(color: Int) {
+ polygon.fillColor = color
+ }
+
+ override fun getFillColor(): Int {
+ return polygon.fillColor
+ }
+
+ override fun setZIndex(zIndex: Float) {
+ polygon.zIndex = zIndex
+ }
+
+ override fun getZIndex(): Float {
+ return polygon.zIndex
+ }
+
+ override fun setVisible(visible: Boolean) {
+ polygon.isVisible = visible
+ }
+
+ override fun isVisible(): Boolean {
+ return polygon.isVisible
+ }
+
+ override fun setGeodesic(geod: Boolean) {
+ polygon.isGeodesic = geod
+ }
+
+ override fun isGeodesic(): Boolean {
+ return polygon.isGeodesic
+ }
+
+ override fun getStrokeJointType(): Int {
+ return polygon.strokeJointType
+ }
+
+ override fun getStrokePattern(): List? {
+ return polygon.strokePattern?.map { it.toGms() }
+ }
+
+ override fun getTag(): IObjectWrapper {
+ return ObjectWrapper.wrap(polygon.tag)
+ }
+
+ override fun isClickable(): Boolean {
+ return polygon.isClickable
+ }
+
+ override fun setClickable(clickable: Boolean) {
+ polygon.isClickable = clickable
+ }
+
+ override fun setStrokeJointType(jointType: Int) {
+ polygon.strokeJointType = jointType
+ }
+
+ override fun setStrokePattern(pattern: List?) {
+ polygon.strokePattern = pattern?.map { it.toHms() }
+ }
+
+ override fun setTag(tag: IObjectWrapper?) {
+ polygon.tag = tag.unwrap()
+ }
+
+ override fun equalsRemote(other: IPolygonDelegate?): Boolean = equals(other)
+
+ override fun hashCodeRemote(): Int = hashCode()
+
+ override fun hashCode(): Int {
+ return id.hashCode()
+ }
+
+ override fun toString(): String {
+ return id
+ }
+
+ 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 {
+ private val TAG = "GmsMapPolygon"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polyline.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polyline.kt
new file mode 100644
index 0000000000..14a2c85940
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/Polyline.kt
@@ -0,0 +1,190 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+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.dynamic.unwrap
+import com.google.android.gms.maps.model.*
+import com.google.android.gms.maps.model.BitmapDescriptor
+import com.google.android.gms.maps.model.CustomCap
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.PatternItem
+import com.google.android.gms.maps.model.PolylineOptions
+import com.google.android.gms.maps.model.internal.IPolylineDelegate
+import com.huawei.hms.maps.model.*
+import com.huawei.hms.maps.model.ButtCap as HmsButtCap
+import com.huawei.hms.maps.model.Cap as HmsCap
+import com.huawei.hms.maps.model.CustomCap as HmsCustomCap
+import com.huawei.hms.maps.model.RoundCap as HmsRoundCap
+import com.huawei.hms.maps.model.SquareCap as HmsSquareCap
+import org.microg.gms.maps.hms.utils.toGms
+import org.microg.gms.maps.hms.utils.toGmsPolylineWidth
+import org.microg.gms.maps.hms.utils.toHms
+import org.microg.gms.maps.hms.utils.toHmsPolylineWidth
+
+class PolylineImpl(private val polyline: Polyline, polylineOptions: PolylineOptions) : IPolylineDelegate.Stub() {
+
+ override fun remove() {
+ polyline.remove()
+ }
+
+ override fun getId(): String = polyline.id
+
+ override fun setPoints(points: List) {
+ polyline.points = points.map { it.toHms() }
+ }
+
+ override fun getPoints(): List = polyline.points.map { it.toGms() }
+
+ override fun setWidth(width: Float) {
+ polyline.width = toHmsPolylineWidth(width)
+ }
+
+ override fun getWidth(): Float {
+ return toGmsPolylineWidth(polyline.width)
+ }
+
+ override fun setColor(color: Int) {
+ polyline.color = color
+ }
+
+ override fun getColor(): Int {
+ return polyline.color
+ }
+
+ override fun setZIndex(zIndex: Float) {
+ Log.d(TAG, "setZIndex: $zIndex")
+ polyline.zIndex = zIndex
+ }
+
+ override fun getZIndex(): Float {
+ Log.d(TAG, "getZIndex")
+ return polyline.zIndex
+ }
+
+ override fun setVisible(visible: Boolean) {
+ polyline.isVisible = visible
+ }
+
+ override fun isVisible(): Boolean {
+ return polyline.isVisible
+ }
+
+ override fun setGeodesic(geod: Boolean) {
+ Log.d(TAG, "setGeodesic: $geod")
+ polyline.isGeodesic = geod
+ }
+
+ override fun isGeodesic(): Boolean {
+ Log.d(TAG, "isGeodesic")
+ return polyline.isGeodesic
+ }
+
+ override fun setClickable(clickable: Boolean) {
+ Log.d(TAG, "setClickable: $clickable")
+ polyline.isClickable = clickable
+ }
+
+ override fun isClickable(): Boolean {
+ Log.d(TAG, "isClickable")
+ return polyline.isClickable
+ }
+
+ override fun equalsRemote(other: IPolylineDelegate?): Boolean = equals(other)
+
+ override fun hashCodeRemote(): Int = hashCode()
+
+ override fun hashCode(): Int {
+ return id.hashCode()
+ }
+
+ override fun toString(): String {
+ return id
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other is PolylineImpl) {
+ return other.id == id
+ }
+ return false
+ }
+
+ override fun getPattern(): List? {
+ Log.d(TAG, "Method: getStrokePattern")
+ return polyline.pattern?.map { it.toGms() }
+ }
+
+ override fun getTag(): IObjectWrapper {
+ return ObjectWrapper.wrap(polyline.tag)
+ }
+
+ override fun setJointType(jointType: Int) {
+ polyline.jointType = jointType
+ }
+
+ override fun getJointType(): Int {
+ return polyline.jointType
+ }
+
+ override fun setPattern(pattern: List?) {
+ Log.d(TAG, "Method: setStrokePattern")
+ polyline.pattern = pattern?.map { it.toHms() }
+ }
+
+ override fun setTag(tag: IObjectWrapper?) {
+ polyline.tag = tag.unwrap()
+ }
+
+ override fun setEndCap(endCap: Cap) {
+ polyline.endCap = endCap.toHms()
+ }
+
+ override fun getEndCap(): Cap {
+ return polyline.endCap.toGms()
+ }
+
+ override fun setStartCap(startCap: Cap) {
+ polyline.startCap = startCap.toHms()
+ }
+
+ override fun getStartCap(): Cap {
+ return polyline.startCap.toGms()
+ }
+
+ private fun Cap.toHms(): HmsCap {
+ return when (this) {
+ is ButtCap -> HmsButtCap()
+ is SquareCap -> HmsSquareCap()
+ is RoundCap -> HmsRoundCap()
+ is CustomCap -> HmsCustomCap(bitmapDescriptor.remoteObject.unwrap(), refWidth)
+ else -> HmsButtCap()
+ }
+ }
+
+ private fun com.huawei.hms.maps.model.Cap.toGms(): Cap {
+ return when (this) {
+ is HmsButtCap -> ButtCap()
+ is HmsSquareCap -> SquareCap()
+ is HmsRoundCap -> RoundCap()
+ is HmsCustomCap -> CustomCap(BitmapDescriptor(ObjectWrapper.wrap(bitmapDescriptor)), refWidth)
+ else -> ButtCap()
+ }
+ }
+
+ 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
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/TileOverlay.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/TileOverlay.kt
new file mode 100644
index 0000000000..e7c5184b8a
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/model/TileOverlay.kt
@@ -0,0 +1,81 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.model
+
+import android.os.Parcel
+import android.util.Log
+import com.google.android.gms.maps.model.internal.ITileOverlayDelegate
+import com.huawei.hms.maps.model.TileOverlay
+
+class TileOverlayImpl(private val tileOverlay: TileOverlay) : ITileOverlayDelegate.Stub() {
+
+ override fun clearTileCache() {
+ tileOverlay.clearTileCache()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return tileOverlay == other
+ }
+
+ override fun getFadeIn(): Boolean {
+ return tileOverlay.fadeIn
+ }
+
+ override fun getId(): String {
+ return tileOverlay.id
+ }
+
+ override fun getTransparency(): Float {
+ return tileOverlay.transparency
+ }
+
+ override fun getZIndex(): Float {
+ return tileOverlay.zIndex
+ }
+
+ override fun hashCode(): Int {
+ return tileOverlay.hashCode()
+ }
+
+ override fun isVisible(): Boolean {
+ return tileOverlay.isVisible
+ }
+
+ override fun remove() {
+ return tileOverlay.remove()
+ }
+
+ override fun setFadeIn(fadeIn: Boolean) {
+ tileOverlay.fadeIn = fadeIn
+ }
+
+ override fun setTransparency(transparency: Float) {
+ tileOverlay.transparency = transparency
+ }
+
+ override fun setVisible(visible: Boolean) {
+ tileOverlay.isVisible = visible
+ }
+
+ override fun setZIndex(zIndex: Float) {
+ tileOverlay.zIndex = zIndex
+ }
+
+ override fun equalsRemote(other: ITileOverlayDelegate): Boolean = tileOverlay == (other as? TileOverlayImpl)?.tileOverlay
+ override fun hashCodeRemote(): Int = tileOverlay.hashCode()
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =
+ if (super.onTransact(code, data, reply, flags)) {
+ Log.e(TAG, "onTransact [known]: $code, $data, $flags")
+ true
+ } else {
+ Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
+ }
+
+ companion object {
+ private val TAG = "GmsMapTileOverlay"
+ }
+}
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/MapContext.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/MapContext.kt
new file mode 100644
index 0000000000..e26384505c
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/MapContext.kt
@@ -0,0 +1,72 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.utils
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.SharedPreferences
+import android.util.Log
+import android.view.LayoutInflater
+import androidx.annotation.RequiresApi
+import org.microg.gms.common.Constants
+import java.io.File
+
+class MapContext(private val context: Context) : ContextWrapper(context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY)) {
+ private var layoutInflater: LayoutInflater? = null
+ private val appContext: Context
+ get() = context.applicationContext ?: context
+
+ override fun getApplicationContext(): Context {
+ return this
+ }
+
+ override fun getCacheDir(): File {
+ val cacheDir = File(appContext.cacheDir, "com.google.android.gms")
+ cacheDir.mkdirs()
+ return cacheDir
+ }
+
+ override fun getFilesDir(): File {
+ val filesDir = File(appContext.filesDir, "com.google.android.gms")
+ filesDir.mkdirs()
+ return filesDir
+ }
+
+ override fun getClassLoader(): ClassLoader {
+ return MapContext::class.java.classLoader!!
+ }
+
+ override fun getSharedPreferences(name: String?, mode: Int): SharedPreferences {
+ return appContext.getSharedPreferences("com.google.android.gms_$name", mode)
+ }
+
+ override fun getSystemService(name: String): Any? {
+ if (name == Context.LAYOUT_INFLATER_SERVICE) {
+ if (layoutInflater == null) {
+ layoutInflater = super.getSystemService(name) as LayoutInflater
+ layoutInflater?.cloneInContext(this)?.let { layoutInflater = it }
+ }
+ if (layoutInflater != null) {
+ return layoutInflater
+ }
+ }
+ return context.getSystemService(name)
+ }
+
+ override fun startActivity(intent: Intent?) {
+ context.startActivity(intent)
+ }
+
+ @RequiresApi(24)
+ override fun createDeviceProtectedStorageContext(): Context {
+ return appContext.createDeviceProtectedStorageContext()
+ }
+
+ companion object {
+ val TAG = "GmsMapContext"
+ }
+}
\ No newline at end of file
diff --git a/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt
new file mode 100644
index 0000000000..64cb463f6b
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/utils/typeConverter.kt
@@ -0,0 +1,227 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.microg.gms.maps.hms.utils
+
+import android.os.Bundle
+import android.util.Log
+import com.google.android.gms.dynamic.unwrap
+import com.google.android.gms.maps.GoogleMapOptions
+import com.google.android.gms.maps.internal.ICancelableCallback
+import com.huawei.hms.maps.HuaweiMap
+import com.huawei.hms.maps.HuaweiMapOptions
+import com.huawei.hms.maps.model.*
+import com.google.android.gms.maps.model.CameraPosition as GmsCameraPosition
+import com.google.android.gms.maps.model.CircleOptions as GmsCircleOptions
+import com.google.android.gms.maps.model.Dash as GmsDash
+import com.google.android.gms.maps.model.Dot as GmsDot
+import com.google.android.gms.maps.model.Gap as GmsGap
+import com.google.android.gms.maps.model.GroundOverlayOptions as GmsGroundOverlayOptions
+import com.google.android.gms.maps.model.LatLng as GmsLatLng
+import com.google.android.gms.maps.model.LatLngBounds as GmsLatLngBounds
+import com.google.android.gms.maps.model.MarkerOptions as GmsMarkerOptions
+import com.google.android.gms.maps.model.PatternItem as GmsPatternItem
+import com.google.android.gms.maps.model.PolygonOptions as GmsPolygonOptions
+import com.google.android.gms.maps.model.PolylineOptions as GmsPolylineOptions
+import com.google.android.gms.maps.model.Tile as GmsTile
+import com.google.android.gms.maps.model.TileOverlayOptions as GmsTileOverlayOptions
+import com.google.android.gms.maps.model.VisibleRegion as GmsVisibleRegion
+
+fun GmsCameraPosition.toHms(): CameraPosition {
+ return CameraPosition.Builder().target(target.toHms()).zoom(toHmsZoom(zoom)).tilt(tilt)
+ .bearing(toHmsBearing(bearing)).build()
+}
+
+fun GmsCircleOptions.toHms(): CircleOptions =
+ CircleOptions().center(center.toHms()).clickable(isClickable).fillColor(fillColor)
+ .radius(radius).strokeColor(strokeColor).strokeWidth(strokeWidth).visible(isVisible)
+ .zIndex(zIndex)
+
+fun GmsPatternItem.toHms(): PatternItem {
+ return when (this) {
+ is GmsDash -> Dash(length)
+ is GmsDot -> Dot()
+ is GmsGap -> Gap(length)
+ else -> PatternItem(0,0f)
+ }
+}
+
+fun GoogleMapOptions.toHms(): HuaweiMapOptions {
+ val huaweiMapOptions = HuaweiMapOptions()
+ camera?.let { huaweiMapOptions.camera(camera?.toHms()) }
+ if (maxZoomPreference != 0f) {
+ huaweiMapOptions.maxZoomPreference(toHmsZoom(maxZoomPreference))
+ }
+ if (minZoomPreference != 0f) {
+ huaweiMapOptions.minZoomPreference(toHmsZoom(minZoomPreference))
+ }
+ latLngBoundsForCameraTarget?.let {
+ huaweiMapOptions.latLngBoundsForCameraTarget(
+ latLngBoundsForCameraTarget?.toHms()
+ )
+ }
+
+ return huaweiMapOptions
+ .compassEnabled(isCompassEnabled)
+ .liteMode(liteMode)
+// .mapType(mapType)
+ .rotateGesturesEnabled(rotateGesturesEnabled == true)
+ .scrollGesturesEnabled(scrollGesturesEnabled == true)
+ .tiltGesturesEnabled(tiltGesturesEnabled == true)
+ .useViewLifecycleInFragment(useViewLifecycleInFragment == true)
+ .zOrderOnTop(zOrderOnTop == true)
+ .zoomControlsEnabled(zoomControlsEnabled == true)
+ .zoomGesturesEnabled(zoomGesturesEnabled == true)
+}
+
+fun GmsLatLng.toHms(): LatLng =
+ LatLng(latitude, longitude)
+
+fun GmsLatLngBounds.toHms(): LatLngBounds =
+ LatLngBounds(
+ LatLng(if(southwest.latitude.isNaN()) 0.0 else southwest.latitude, if(southwest.longitude.isNaN()) 0.0 else southwest.longitude),
+ LatLng(if(northeast.latitude.isNaN()) 0.0 else northeast.latitude, if(northeast.longitude.isNaN()) 0.0 else northeast.longitude)
+ )
+
+fun ICancelableCallback.toHms(): HuaweiMap.CancelableCallback =
+ object : HuaweiMap.CancelableCallback {
+ override fun onFinish() = this@toHms.onFinish()
+ override fun onCancel() = this@toHms.onCancel()
+ }
+
+fun GmsMarkerOptions.toHms(): MarkerOptions {
+ val markerOptions = MarkerOptions()
+ icon?.let { markerOptions.icon(it.remoteObject.unwrap()) }
+ return markerOptions.alpha(alpha).anchorMarker(anchorU, anchorV).draggable(isDraggable)
+ .flat(isFlat).infoWindowAnchor(infoWindowAnchorU, infoWindowAnchorV)
+ .position(position.toHms()).rotation(rotation).snippet(snippet).title(title)
+ .visible(isVisible).zIndex(zIndex)
+}
+
+fun GmsGroundOverlayOptions.toHms(): GroundOverlayOptions {
+ val groundOverlayOptions = GroundOverlayOptions()
+ groundOverlayOptions.anchor(anchorU, anchorV).bearing(bearing)
+ .clickable(isClickable)
+ .image(image.remoteObject.unwrap())
+ .visible(isVisible)
+ .zIndex(zIndex)
+ if (height > 0) {
+ groundOverlayOptions.position(location.toHms(), width, height)
+ } else {
+ groundOverlayOptions.position(location.toHms(), width)
+ }
+ bounds?.let { groundOverlayOptions.positionFromBounds(it.toHms()) }
+ return groundOverlayOptions
+}
+
+fun GmsTileOverlayOptions.toHms(): TileOverlayOptions {
+ return TileOverlayOptions().tileProvider(tileProvider?.let { TileProvider { x, y, zoom -> it.getTile(x, y, zoom)?.toHms() } })
+ .fadeIn(fadeIn)
+ .visible(isVisible)
+ .transparency(transparency)
+ .zIndex(zIndex)
+}
+
+fun GmsTile.toHms(): Tile = Tile(width, height, data)
+
+fun GmsPolygonOptions.toHms(): PolygonOptions {
+ val polygonOptions = PolygonOptions()
+ holes?.map {
+ val hole = it?.map { it?.toHms() }
+ polygonOptions.addHole(hole)
+ }
+ return polygonOptions.addAll(points.map { it.toHms() })
+ .clickable(isClickable)
+ .fillColor(fillColor)
+ .geodesic(isGeodesic)
+ .strokeColor(strokeColor).strokeJointType(strokeJointType).strokeWidth(strokeWidth)
+ .visible(isVisible)
+ .zIndex(zIndex)
+}
+
+fun GmsPolylineOptions.toHms(): PolylineOptions {
+ val polylineOptions = PolylineOptions()
+ polylineOptions.addAll(points.map { it.toHms() })
+ return polylineOptions.clickable(isClickable).color(color).geodesic(isGeodesic)
+ .jointType(jointType).visible(isVisible).width(toHmsPolylineWidth(width)).zIndex(zIndex)
+}
+
+fun toHmsPolylineWidth(gmsWidth: Float): Float = gmsWidth / 3
+
+fun toHmsZoom(gmsZoom: Float?): Float {
+ if (gmsZoom == null) {
+ return 3f
+ }
+ if (gmsZoom < 3) {
+ return 3f
+ } else if (gmsZoom > 18) {
+ return 18f
+ }
+ return gmsZoom
+}
+
+fun toHmsBearing(gmsBearing: Float): Float {
+ return 360 - gmsBearing
+}
+
+fun Bundle.toHms(): Bundle {
+ val newBundle = Bundle(this)
+ val oldLoader = newBundle.classLoader
+ newBundle.classLoader = GmsLatLng::class.java.classLoader
+ for (key in newBundle.keySet()) {
+ when (val value = newBundle.get(key)) {
+ is GmsCameraPosition -> newBundle.putParcelable(key, value.toHms())
+ is GmsLatLng -> newBundle.putParcelable(key, value.toHms())
+ is GmsLatLngBounds -> newBundle.putParcelable(key, value.toHms())
+ is Bundle -> newBundle.putBundle(key, value.toHms())
+ }
+ }
+ newBundle.classLoader = oldLoader
+ return newBundle
+}
+
+fun CameraPosition.toGms(): GmsCameraPosition =
+ GmsCameraPosition(target.toGms(), zoom, tilt, bearing)
+
+fun PatternItem.toGms(): GmsPatternItem = when (this) {
+ is Dot -> GmsDot()
+ is Dash -> GmsDash(length)
+ is Gap -> GmsGap(length)
+ else -> GmsGap(0f)
+}
+
+fun LatLng.toGms(): GmsLatLng = GmsLatLng(latitude, longitude)
+
+fun LatLngBounds.toGms(): GmsLatLngBounds = GmsLatLngBounds(
+ GmsLatLng(southwest.latitude, southwest.longitude),
+ GmsLatLng(northeast.latitude, northeast.longitude)
+)
+
+fun VisibleRegion.toGms(): GmsVisibleRegion =
+ GmsVisibleRegion(
+ nearLeft.toGms(),
+ nearRight.toGms(),
+ farLeft.toGms(),
+ farRight.toGms(),
+ latLngBounds.toGms()
+ )
+
+fun toGmsPolylineWidth(hmsWidth: Float): Float = hmsWidth * 3
+
+fun Bundle.toGms(): Bundle {
+ val newBundle = Bundle(this)
+ val oldLoader = newBundle.classLoader
+ newBundle.classLoader = LatLng::class.java.classLoader
+ for (key in newBundle.keySet()) {
+ when (val value = newBundle.get(key)) {
+ is CameraPosition -> newBundle.putParcelable(key, value.toGms())
+ is LatLng -> newBundle.putParcelable(key, value.toGms())
+ is LatLngBounds -> newBundle.putParcelable(key, value.toGms())
+ is Bundle -> newBundle.putBundle(key, value.toGms())
+ }
+ }
+ newBundle.classLoader = oldLoader
+ return newBundle
+}
diff --git a/play-services-maps/core/hms/src/main/res/drawable-hdpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-hdpi/maps_default_marker.png
new file mode 100644
index 0000000000..94c365a3e3
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-hdpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/drawable-ldpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-ldpi/maps_default_marker.png
new file mode 100644
index 0000000000..a8ca2bf634
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-ldpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/drawable-mdpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-mdpi/maps_default_marker.png
new file mode 100644
index 0000000000..c79736e707
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-mdpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/drawable-xhdpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-xhdpi/maps_default_marker.png
new file mode 100644
index 0000000000..0b1b1975b4
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-xhdpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/drawable-xxhdpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-xxhdpi/maps_default_marker.png
new file mode 100644
index 0000000000..709bf78d7b
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-xxhdpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/drawable-xxxhdpi/maps_default_marker.png b/play-services-maps/core/hms/src/main/res/drawable-xxxhdpi/maps_default_marker.png
new file mode 100644
index 0000000000..0de5d255ae
Binary files /dev/null and b/play-services-maps/core/hms/src/main/res/drawable-xxxhdpi/maps_default_marker.png differ
diff --git a/play-services-maps/core/hms/src/main/res/values/strings.xml b/play-services-maps/core/hms/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..73862c416f
--- /dev/null
+++ b/play-services-maps/core/hms/src/main/res/values/strings.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/play-services-maps/src/main/java/com/google/android/gms/maps/model/TileOverlayOptions.java b/play-services-maps/src/main/java/com/google/android/gms/maps/model/TileOverlayOptions.java
index dbf1b2e4fe..c2d88d49a8 100644
--- a/play-services-maps/src/main/java/com/google/android/gms/maps/model/TileOverlayOptions.java
+++ b/play-services-maps/src/main/java/com/google/android/gms/maps/model/TileOverlayOptions.java
@@ -71,6 +71,19 @@ public boolean getFadeIn() {
* @return the {@link TileProvider} of the tile overlay.
*/
public TileProvider getTileProvider() {
+ if (tileProvider == null && tileProviderBinder != null) {
+ ITileProviderDelegate delegate = ITileProviderDelegate.Stub.asInterface(tileProviderBinder);
+ this.tileProvider = new TileProvider() {
+ @Override
+ public Tile getTile(int x, int y, int zoom) {
+ try {
+ return delegate.getTile(x, y, zoom);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+ };
+ }
return tileProvider;
}
diff --git a/settings.gradle b/settings.gradle
index a5b22be861..aceae3a68a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,6 +4,18 @@ def sublude(name) {
project(projectName).projectDir = file(name.substring(1).replace(':', '/'))
}
+def localProperties = new Properties()
+try {
+ var stream = new File(rootDir, 'local.properties').newDataInputStream()
+ localProperties.load(stream)
+ stream.close()
+} catch (ignored) {
+ // Ignore
+}
+def hasModule = (String name, boolean enabledByDefault) -> {
+ return localProperties.getProperty("modules." + name, enabledByDefault.toString()).toBoolean()
+}
+
include ':fake-signature'
include ':safe-parcel-processor'
include ':vending-app'
@@ -76,11 +88,12 @@ sublude ':play-services-location:core'
sublude ':play-services-location:core:base'
sublude ':play-services-location:core:provider'
sublude ':play-services-location:core:system-api'
+if (hasModule("hms", false)) sublude ':play-services-maps:core:hms'
sublude ':play-services-maps:core:mapbox'
sublude ':play-services-maps:core:vtm'
sublude ':play-services-maps:core:vtm:microg-theme'
-sublude ':play-services-nearby:core'
-sublude ':play-services-nearby:core:package'
+if (hasModule("nearby", true)) sublude ':play-services-nearby:core'
+if (hasModule("nearby", true)) sublude ':play-services-nearby:core:package'
sublude ':play-services-oss-licenses:core'
sublude ':play-services-pay:core'
sublude ':play-services-safetynet:core'