Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit b847d737 authored by DaVinci9196's avatar DaVinci9196 Committed by GitHub
Browse files

HMS Maps: Improve compatibility (#2945)

* HMS Maps: Optimize map lag
* Use TextureMapView by default to improve compatibility and avoid layer issues
* Add invalid projection
parent ebf12341
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.huawei.hms.maps.HuaweiMap
import com.huawei.hms.maps.MapView
import com.huawei.hms.maps.MapsInitializer
import com.huawei.hms.maps.OnMapReadyCallback
import com.huawei.hms.maps.TextureMapView
import com.huawei.hms.maps.internal.IOnIndoorStateChangeListener
import com.huawei.hms.maps.internal.IOnInfoWindowCloseListener
import com.huawei.hms.maps.internal.IOnInfoWindowLongClickListener
@@ -95,6 +96,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
    private var markerId = 0L
    val markers = mutableMapOf<String, MarkerImpl>()

    private var projectionImpl: ProjectionImpl? = null

    init {
        BitmapDescriptorFactoryImpl.initialize(context.resources)
        runOnMainLooper {
@@ -411,7 +414,14 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
        }

    override fun getProjection(): IProjectionDelegate {
        return map?.projection?.let { ProjectionImpl(it) } ?: DummyProjection()
        return projectionImpl ?: map?.projection?.let {
            val experiment = try {
                map?.cameraPosition?.tilt == 0.0f && map?.cameraPosition?.bearing == 0.0f
            } catch (e: Exception) {
                Log.w(TAG, e);false
            }
            ProjectionImpl(it, experiment)
        }?.also { projectionImpl = it } ?: DummyProjection()
    }

    override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) = afterInitialize {
@@ -615,6 +625,13 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    mapView?.let { it.parent?.onDescendantInvalidated(it, it) }
                }
                map?.let {
                    val cameraPosition = it.cameraPosition
                    val tilt = cameraPosition.tilt
                    val bearing = cameraPosition.bearing
                    val useFast = tilt < 1f && (bearing % 360f < 1f || bearing % 360f > 359f)
                    projectionImpl?.updateProjectionState(it.projection, useFast)
                }
                cameraMoveListener?.onCameraMove()
                cameraChangeListener?.onCameraChange(map?.cameraPosition?.toGms())
            } catch (e: Exception) {
@@ -659,7 +676,11 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
        if (!created) {
            Log.d(TAG_LOGO, "create: ${context.packageName},\n$options")
            MapsInitializer.initialize(mapContext)
            val mapView = MapView(mapContext, options.toHms()).apply { visibility = View.INVISIBLE }
            val mapView = runCatching { 
                TextureMapView(mapContext, options.toHms())
            }.onFailure {
                Log.w(TAG, "onCreate: init TextureMapView error ", it)
            }.getOrDefault(MapView(mapContext, options.toHms())).apply { visibility = View.INVISIBLE }
            this.mapView = mapView
            view.addView(mapView)
            mapView.onCreate(savedInstanceState?.toHms())
+11 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu

    private var map: GoogleMapImpl? = null
    private var options: GoogleMapOptions? = null
    private var readyCallbackList: MutableList<IOnMapReadyCallback> = mutableListOf()

    override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle?) {
        Log.d(TAG, "onInflate: $options")
@@ -49,6 +50,8 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu
        }
        Log.d(TAG, "onCreateView $this : $options")
        map!!.onCreate(savedInstanceState)
        readyCallbackList.forEach { map!!.getMapAsync(it) }
        readyCallbackList.clear()
        val view = map!!.view
        val parent = view.parent as ViewGroup?
        parent?.removeView(view)
@@ -64,7 +67,14 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu
    override fun onPause() = map?.onPause() ?: Unit
    override fun onLowMemory() = map?.onLowMemory() ?: Unit
    override fun isReady(): Boolean = this.map != null
    override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit
    override fun getMapAsync(callback: IOnMapReadyCallback) {
        Log.d(TAG, "getMapAsync: map: $map")
        if (map == null) {
            readyCallbackList.add(callback)
            return
        }
        map?.getMapAsync(callback)
    }

    override fun onDestroyView() {
        Log.d(TAG, "onDestroyView: $this : $options")
+87 −22
Original line number Diff line number Diff line
@@ -17,39 +17,104 @@ 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
import kotlin.math.roundToInt

private const val TAG = "GmsMapProjection"

class ProjectionImpl(private val projection: Projection) : IProjectionDelegate.Stub() {
private const val TAG = "GmsProjectionImpl"

class ProjectionImpl(private var projection: Projection, private var withoutTiltOrBearing: Boolean) : IProjectionDelegate.Stub() {
    private var lastVisibleRegion: VisibleRegion? = null
    private var visibleRegion = projection.visibleRegion

    private var farLeft: Point? = visibleRegion.farLeft?.let { projection.toScreenLocation(it) }
    private var farRight: Point? = visibleRegion.farRight?.let { projection.toScreenLocation(it) }
    private var nearLeft: Point? = visibleRegion.nearLeft?.let { projection.toScreenLocation(it) }

    private var farLeftLat = visibleRegion.farLeft?.latitude ?: 0.0
    private var nearLeftLat = visibleRegion.nearLeft?.latitude ?: 0.0
    private var farLeftLng = visibleRegion.farLeft?.longitude ?: 0.0
    private var farRightLng = visibleRegion.farRight?.longitude ?: 0.0
    private var farLeftX = farLeft?.x ?: 0
    private var farLeftY = farLeft?.y ?: 0
    private var farRightX = farRight?.x ?: (farLeftX + 1)
    private var nearLeftY = nearLeft?.y ?: (farLeftY + 1)

    override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? {
        Log.d(TAG, "fromScreenLocation")
        return try {
    fun updateProjectionState(newProjection: Projection, useFastMode: Boolean) {
        Log.d(TAG, "updateProjectionState: useFastMode: $useFastMode")
        projection = newProjection
        visibleRegion = newProjection.visibleRegion
        withoutTiltOrBearing = useFastMode

        farLeft = visibleRegion.farLeft?.let { projection.toScreenLocation(it) }
        farRight = visibleRegion.farRight?.let { projection.toScreenLocation(it) }
        nearLeft = visibleRegion.nearLeft?.let { projection.toScreenLocation(it) }

        farLeftLat = visibleRegion.farLeft?.latitude ?: 0.0
        nearLeftLat = visibleRegion.nearLeft?.latitude ?: 0.0
        farLeftLng = visibleRegion.farLeft?.longitude ?: 0.0
        farRightLng = visibleRegion.farRight?.longitude ?: 0.0
        farLeftX = farLeft?.x ?: 0
        farLeftY = farLeft?.y ?: 0
        farRightX = farRight?.x ?: (farLeftX + 1)
        nearLeftY = nearLeft?.y ?: (farLeftY + 1)
    }

    private fun isInvalid(): Boolean {
        return farLeftX == farRightX || farLeftY == nearLeftY || (farRightX == 1 && farLeftX == 0) || (nearLeftY == 1 && farLeftY == 0)
    }

    override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? = try {
        obj.unwrap<Point>()?.let {
                projection.fromScreenLocation(it).toGms()
            if (withoutTiltOrBearing && farLeft != null && farRight != null && nearLeft != null) {
                if (isInvalid()) {
                    Log.w(TAG, "Invalid projection layout, fallback to SDK")
                    projection.fromScreenLocation(Point(it)).toGms()
                } else {
                    val xPercent = (it.x.toFloat() - farLeftX) / (farRightX - farLeftX)
                    val yPercent = (it.y.toFloat() - farLeftY) / (nearLeftY - farLeftY)

                    val lon = farLeftLng + xPercent * (farRightLng - farLeftLng)
                    val lat = farLeftLat + yPercent * (nearLeftLat - farLeftLat)

                    Log.d(TAG, "fromScreenLocation: $it -> lat: $lat lon: $lon")

                    LatLng(lat, lon)
                }
            } else {
                projection.fromScreenLocation(Point(it)).toGms()
            }
        }
    } catch (e: Exception) {
            Log.d(TAG, "fromScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
        Log.d(TAG, "fromScreenLocation() error", e)
        LatLng(0.0, 0.0)
    }
    }

    override fun toScreenLocation(latLng: LatLng?): IObjectWrapper {
        Log.d(TAG, "toScreenLocation: $latLng")
        return try {
    override fun toScreenLocation(latLng: LatLng?): IObjectWrapper = try {
        ObjectWrapper.wrap(latLng?.toHms()?.let {
                projection.toScreenLocation(it).let { Point(it.x, it.y) }
            if (withoutTiltOrBearing && farLeft != null && farRight != null && nearLeft != null) {
                if (isInvalid()) {
                    Log.w(TAG, "Invalid projection layout, fallback to SDK")
                    projection.toScreenLocation(it).let { p -> Point(p.x, p.y) }
                } else {
                    val xPercent = (it.longitude - farLeftLng) / (farRightLng - farLeftLng)
                    val yPercent = (it.latitude - farLeftLat) / (nearLeftLat - farLeftLat)

                    val x = farLeftX + xPercent * (farRightX - farLeftX)
                    val y = farLeftY + yPercent * (nearLeftY - farLeftY)

                    Log.d(TAG, "toScreenLocation: $latLng -> x: $x y: $y")

                    Point(x.roundToInt(), y.roundToInt())
                }
            } else {
                projection.toScreenLocation(it).let { p -> Point(p.x, p.y) }
            }
        })
    } catch (e: Exception) {
            Log.d(TAG, "toScreenLocation() used from outside UI thread on map with tilt or bearing, expect bugs")
        Log.d(TAG, "toScreenLocation() error", e)
        ObjectWrapper.wrap(Point(0, 0))
    }
    }

    override fun getVisibleRegion(): VisibleRegion? {
        val visibleRegion = projection.visibleRegion
        if (visibleRegion.farLeft.latitude.isNaN() || visibleRegion.farLeft.longitude.isNaN()) {
            return lastVisibleRegion
        }