Skip to content

Commit b847d73

Browse files
authored
HMS Maps: Improve compatibility (#2945)
* HMS Maps: Optimize map lag * Use TextureMapView by default to improve compatibility and avoid layer issues * Add invalid projection
1 parent ebf1234 commit b847d73

3 files changed

Lines changed: 121 additions & 25 deletions

File tree

play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/GoogleMap.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import com.huawei.hms.maps.HuaweiMap
3232
import com.huawei.hms.maps.MapView
3333
import com.huawei.hms.maps.MapsInitializer
3434
import com.huawei.hms.maps.OnMapReadyCallback
35+
import com.huawei.hms.maps.TextureMapView
3536
import com.huawei.hms.maps.internal.IOnIndoorStateChangeListener
3637
import com.huawei.hms.maps.internal.IOnInfoWindowCloseListener
3738
import com.huawei.hms.maps.internal.IOnInfoWindowLongClickListener
@@ -95,6 +96,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
9596
private var markerId = 0L
9697
val markers = mutableMapOf<String, MarkerImpl>()
9798

99+
private var projectionImpl: ProjectionImpl? = null
100+
98101
init {
99102
BitmapDescriptorFactoryImpl.initialize(context.resources)
100103
runOnMainLooper {
@@ -411,7 +414,14 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
411414
}
412415

413416
override fun getProjection(): IProjectionDelegate {
414-
return map?.projection?.let { ProjectionImpl(it) } ?: DummyProjection()
417+
return projectionImpl ?: map?.projection?.let {
418+
val experiment = try {
419+
map?.cameraPosition?.tilt == 0.0f && map?.cameraPosition?.bearing == 0.0f
420+
} catch (e: Exception) {
421+
Log.w(TAG, e);false
422+
}
423+
ProjectionImpl(it, experiment)
424+
}?.also { projectionImpl = it } ?: DummyProjection()
415425
}
416426

417427
override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) = afterInitialize {
@@ -615,6 +625,13 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
615625
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
616626
mapView?.let { it.parent?.onDescendantInvalidated(it, it) }
617627
}
628+
map?.let {
629+
val cameraPosition = it.cameraPosition
630+
val tilt = cameraPosition.tilt
631+
val bearing = cameraPosition.bearing
632+
val useFast = tilt < 1f && (bearing % 360f < 1f || bearing % 360f > 359f)
633+
projectionImpl?.updateProjectionState(it.projection, useFast)
634+
}
618635
cameraMoveListener?.onCameraMove()
619636
cameraChangeListener?.onCameraChange(map?.cameraPosition?.toGms())
620637
} catch (e: Exception) {
@@ -659,7 +676,11 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
659676
if (!created) {
660677
Log.d(TAG_LOGO, "create: ${context.packageName},\n$options")
661678
MapsInitializer.initialize(mapContext)
662-
val mapView = MapView(mapContext, options.toHms()).apply { visibility = View.INVISIBLE }
679+
val mapView = runCatching {
680+
TextureMapView(mapContext, options.toHms())
681+
}.onFailure {
682+
Log.w(TAG, "onCreate: init TextureMapView error ", it)
683+
}.getOrDefault(MapView(mapContext, options.toHms())).apply { visibility = View.INVISIBLE }
663684
this.mapView = mapView
664685
view.addView(mapView)
665686
mapView.onCreate(savedInstanceState?.toHms())

play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/MapFragment.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu
2121

2222
private var map: GoogleMapImpl? = null
2323
private var options: GoogleMapOptions? = null
24+
private var readyCallbackList: MutableList<IOnMapReadyCallback> = mutableListOf()
2425

2526
override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle?) {
2627
Log.d(TAG, "onInflate: $options")
@@ -49,6 +50,8 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu
4950
}
5051
Log.d(TAG, "onCreateView $this : $options")
5152
map!!.onCreate(savedInstanceState)
53+
readyCallbackList.forEach { map!!.getMapAsync(it) }
54+
readyCallbackList.clear()
5255
val view = map!!.view
5356
val parent = view.parent as ViewGroup?
5457
parent?.removeView(view)
@@ -64,7 +67,14 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu
6467
override fun onPause() = map?.onPause() ?: Unit
6568
override fun onLowMemory() = map?.onLowMemory() ?: Unit
6669
override fun isReady(): Boolean = this.map != null
67-
override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit
70+
override fun getMapAsync(callback: IOnMapReadyCallback) {
71+
Log.d(TAG, "getMapAsync: map: $map")
72+
if (map == null) {
73+
readyCallbackList.add(callback)
74+
return
75+
}
76+
map?.getMapAsync(callback)
77+
}
6878

6979
override fun onDestroyView() {
7080
Log.d(TAG, "onDestroyView: $this : $options")

play-services-maps/core/hms/src/main/kotlin/org/microg/gms/maps/hms/Projection.kt

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,104 @@ import com.google.android.gms.maps.model.VisibleRegion
1717
import com.huawei.hms.maps.Projection
1818
import org.microg.gms.maps.hms.utils.toGms
1919
import org.microg.gms.maps.hms.utils.toHms
20+
import kotlin.math.roundToInt
2021

21-
private const val TAG = "GmsMapProjection"
22-
23-
class ProjectionImpl(private val projection: Projection) : IProjectionDelegate.Stub() {
22+
private const val TAG = "GmsProjectionImpl"
2423

24+
class ProjectionImpl(private var projection: Projection, private var withoutTiltOrBearing: Boolean) : IProjectionDelegate.Stub() {
2525
private var lastVisibleRegion: VisibleRegion? = null
26+
private var visibleRegion = projection.visibleRegion
27+
28+
private var farLeft: Point? = visibleRegion.farLeft?.let { projection.toScreenLocation(it) }
29+
private var farRight: Point? = visibleRegion.farRight?.let { projection.toScreenLocation(it) }
30+
private var nearLeft: Point? = visibleRegion.nearLeft?.let { projection.toScreenLocation(it) }
31+
32+
private var farLeftLat = visibleRegion.farLeft?.latitude ?: 0.0
33+
private var nearLeftLat = visibleRegion.nearLeft?.latitude ?: 0.0
34+
private var farLeftLng = visibleRegion.farLeft?.longitude ?: 0.0
35+
private var farRightLng = visibleRegion.farRight?.longitude ?: 0.0
36+
private var farLeftX = farLeft?.x ?: 0
37+
private var farLeftY = farLeft?.y ?: 0
38+
private var farRightX = farRight?.x ?: (farLeftX + 1)
39+
private var nearLeftY = nearLeft?.y ?: (farLeftY + 1)
40+
41+
fun updateProjectionState(newProjection: Projection, useFastMode: Boolean) {
42+
Log.d(TAG, "updateProjectionState: useFastMode: $useFastMode")
43+
projection = newProjection
44+
visibleRegion = newProjection.visibleRegion
45+
withoutTiltOrBearing = useFastMode
46+
47+
farLeft = visibleRegion.farLeft?.let { projection.toScreenLocation(it) }
48+
farRight = visibleRegion.farRight?.let { projection.toScreenLocation(it) }
49+
nearLeft = visibleRegion.nearLeft?.let { projection.toScreenLocation(it) }
50+
51+
farLeftLat = visibleRegion.farLeft?.latitude ?: 0.0
52+
nearLeftLat = visibleRegion.nearLeft?.latitude ?: 0.0
53+
farLeftLng = visibleRegion.farLeft?.longitude ?: 0.0
54+
farRightLng = visibleRegion.farRight?.longitude ?: 0.0
55+
farLeftX = farLeft?.x ?: 0
56+
farLeftY = farLeft?.y ?: 0
57+
farRightX = farRight?.x ?: (farLeftX + 1)
58+
nearLeftY = nearLeft?.y ?: (farLeftY + 1)
59+
}
60+
61+
private fun isInvalid(): Boolean {
62+
return farLeftX == farRightX || farLeftY == nearLeftY || (farRightX == 1 && farLeftX == 0) || (nearLeftY == 1 && farLeftY == 0)
63+
}
2664

27-
override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? {
28-
Log.d(TAG, "fromScreenLocation")
29-
return try {
30-
obj.unwrap<Point>()?.let {
31-
projection.fromScreenLocation(it).toGms()
65+
override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? = try {
66+
obj.unwrap<Point>()?.let {
67+
if (withoutTiltOrBearing && farLeft != null && farRight != null && nearLeft != null) {
68+
if (isInvalid()) {
69+
Log.w(TAG, "Invalid projection layout, fallback to SDK")
70+
projection.fromScreenLocation(Point(it)).toGms()
71+
} else {
72+
val xPercent = (it.x.toFloat() - farLeftX) / (farRightX - farLeftX)
73+
val yPercent = (it.y.toFloat() - farLeftY) / (nearLeftY - farLeftY)
74+
75+
val lon = farLeftLng + xPercent * (farRightLng - farLeftLng)
76+
val lat = farLeftLat + yPercent * (nearLeftLat - farLeftLat)
77+
78+
Log.d(TAG, "fromScreenLocation: $it -> lat: $lat lon: $lon")
79+
80+
LatLng(lat, lon)
81+
}
82+
} else {
83+
projection.fromScreenLocation(Point(it)).toGms()
3284
}
33-
} catch (e: Exception) {
34-
Log.d(TAG, "fromScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
35-
LatLng(0.0, 0.0)
3685
}
86+
} catch (e: Exception) {
87+
Log.d(TAG, "fromScreenLocation() error", e)
88+
LatLng(0.0, 0.0)
3789
}
3890

39-
override fun toScreenLocation(latLng: LatLng?): IObjectWrapper {
40-
Log.d(TAG, "toScreenLocation: $latLng")
41-
return try {
42-
ObjectWrapper.wrap(latLng?.toHms()?.let {
43-
projection.toScreenLocation(it).let { Point(it.x, it.y) }
44-
})
45-
} catch (e: Exception) {
46-
Log.d(TAG, "toScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
47-
ObjectWrapper.wrap(Point(0, 0))
48-
}
91+
override fun toScreenLocation(latLng: LatLng?): IObjectWrapper = try {
92+
ObjectWrapper.wrap(latLng?.toHms()?.let {
93+
if (withoutTiltOrBearing && farLeft != null && farRight != null && nearLeft != null) {
94+
if (isInvalid()) {
95+
Log.w(TAG, "Invalid projection layout, fallback to SDK")
96+
projection.toScreenLocation(it).let { p -> Point(p.x, p.y) }
97+
} else {
98+
val xPercent = (it.longitude - farLeftLng) / (farRightLng - farLeftLng)
99+
val yPercent = (it.latitude - farLeftLat) / (nearLeftLat - farLeftLat)
100+
101+
val x = farLeftX + xPercent * (farRightX - farLeftX)
102+
val y = farLeftY + yPercent * (nearLeftY - farLeftY)
103+
104+
Log.d(TAG, "toScreenLocation: $latLng -> x: $x y: $y")
105+
106+
Point(x.roundToInt(), y.roundToInt())
107+
}
108+
} else {
109+
projection.toScreenLocation(it).let { p -> Point(p.x, p.y) }
110+
}
111+
})
112+
} catch (e: Exception) {
113+
Log.d(TAG, "toScreenLocation() error", e)
114+
ObjectWrapper.wrap(Point(0, 0))
49115
}
50116

51117
override fun getVisibleRegion(): VisibleRegion? {
52-
val visibleRegion = projection.visibleRegion
53118
if (visibleRegion.farLeft.latitude.isNaN() || visibleRegion.farLeft.longitude.isNaN()) {
54119
return lastVisibleRegion
55120
}

0 commit comments

Comments
 (0)