mirror of
https://github.com/microg/android_packages_apps_GmsCore
synced 2026-08-06 14:26:05 -04:00
HmsMaps: Added compatibility adapter (#3368)
This commit is contained in:
parent
65f04ed083
commit
cd1f5b8997
3 changed files with 187 additions and 17 deletions
|
|
@ -61,6 +61,7 @@ fun runOnMainLooper(forceQueue: Boolean = false, method: () -> Unit) {
|
|||
class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) : IGoogleMapDelegate.Stub() {
|
||||
|
||||
internal val mapContext = MapContext(context)
|
||||
private val compatAdapter = MapCompatAdapter(context)
|
||||
|
||||
val view: FrameLayout
|
||||
var map: HuaweiMap? = null
|
||||
|
|
@ -721,7 +722,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
|
|||
Log.w(TAG, "onCreate: init TextureMapView error ", it)
|
||||
}.getOrDefault(MapView(mapContext, options.toHms())).apply { visibility = View.INVISIBLE }
|
||||
this.mapView = mapView
|
||||
view.addView(mapView)
|
||||
view.addView(compatAdapter.wrapMapView(mapContext, mapView))
|
||||
|
||||
mapView.onCreate(savedInstanceState?.toHms())
|
||||
mapView.getMapAsync(this::initMap)
|
||||
|
||||
|
|
@ -815,14 +817,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
|
|||
}
|
||||
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
|
||||
}
|
||||
if (options.liteMode && compatAdapter.interceptLiteModeClick(view)) {
|
||||
return@setOnMapClickListener
|
||||
}
|
||||
mapClickListener?.onMapClick(latlng.toGms())
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -831,14 +827,8 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions)
|
|||
}
|
||||
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
|
||||
}
|
||||
if (options.liteMode && compatAdapter.interceptLiteModeLongClick(view)) {
|
||||
return@setOnMapLongClickListener
|
||||
}
|
||||
mapLongClickListener?.onMapLongClick(latlng.toGms())
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2026 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.maps.hms.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
|
||||
/**
|
||||
* Adapter that applies app-specific compatibility fixes for HMS Maps
|
||||
* based on the calling application's package name.
|
||||
*/
|
||||
class MapCompatAdapter(context: Context) {
|
||||
|
||||
private val callerPackageName: String = context.applicationContext?.packageName ?: context.packageName
|
||||
|
||||
/**
|
||||
* Wraps the mapView in a traversal-blocking container if needed.
|
||||
* Some apps traverse the view hierarchy and encounter HMS internal views,
|
||||
* causing crashes or unexpected behavior.
|
||||
*
|
||||
* @return the wrapper view to add to the root, or mapView itself if no wrapping is needed.
|
||||
*/
|
||||
fun wrapMapView(mapContext: Context, mapView: View): View {
|
||||
if (!needsViewTraversalBlock().also { Log.d(TAG, "$callerPackageName need wrapMapView ? $it") }) return mapView
|
||||
|
||||
val blocker = object : FrameLayout(mapContext) {
|
||||
override fun getChildCount(): Int = 0
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val lp = mapView.layoutParams
|
||||
?: LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
val childW = getChildMeasureSpec(widthMeasureSpec, 0, lp.width)
|
||||
val childH = getChildMeasureSpec(heightMeasureSpec, 0, lp.height)
|
||||
mapView.measure(childW, childH)
|
||||
setMeasuredDimension(
|
||||
resolveSize(mapView.measuredWidth, widthMeasureSpec),
|
||||
resolveSize(mapView.measuredHeight, heightMeasureSpec)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
mapView.layout(0, 0, right - left, bottom - top)
|
||||
}
|
||||
}
|
||||
blocker.addView(
|
||||
mapView,
|
||||
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
)
|
||||
return blocker
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts map click events in liteMode for apps that need special handling.
|
||||
* @return true if the click was intercepted and handled, false to proceed normally.
|
||||
*/
|
||||
fun interceptLiteModeClick(rootView: View): Boolean {
|
||||
return findLiteModeParent(rootView)?.let {
|
||||
it.performClick()
|
||||
true
|
||||
} ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts map long-click events in liteMode for apps that need special handling.
|
||||
* @return true if the long-click was intercepted and handled, false to proceed normally.
|
||||
*/
|
||||
fun interceptLiteModeLongClick(rootView: View): Boolean {
|
||||
return findLiteModeParent(rootView)?.let {
|
||||
it.performLongClick()
|
||||
true
|
||||
} ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the mapView needs to be wrapped in a traversal-blocking container.
|
||||
*/
|
||||
private fun needsViewTraversalBlock(): Boolean {
|
||||
return callerPackageName in VIEW_TRAVERSAL_BLOCK_PACKAGES
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the parent view that should receive redirected click events in liteMode.
|
||||
* Returns null if no redirection is needed for the current app.
|
||||
*/
|
||||
private fun findLiteModeParent(rootView: View): ViewGroup? {
|
||||
val targetClass = LITE_MODE_CLICK_REDIRECT[callerPackageName] ?: return null
|
||||
val parentView = rootView.parent?.parent ?: return null
|
||||
if (parentView::class.qualifiedName == targetClass) {
|
||||
return parentView as? ViewGroup
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MapCompatAdapter"
|
||||
private val VIEW_TRAVERSAL_BLOCK_PACKAGES = setOf(
|
||||
"com.studioeleven.windfinder",
|
||||
)
|
||||
private val LITE_MODE_CLICK_REDIRECT = mapOf(
|
||||
"com.microsoft.teams" to "com.microsoft.teams.location.ui.map.MapViewLite",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,10 +5,13 @@
|
|||
|
||||
package org.microg.gms.maps.hms.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.huawei.hms.maps.MapClientIdentify
|
||||
|
|
@ -74,6 +77,75 @@ class MapContext(private val context: Context) : ContextWrapper(context.createPa
|
|||
return appContext.createDeviceProtectedStorageContext()
|
||||
}
|
||||
|
||||
private val appRes: Resources get() = appContext.resources
|
||||
|
||||
internal val mergedResources: Resources by lazy(LazyThreadSafetyMode.NONE) {
|
||||
val gmsRes = try { super.getResources() } catch (e: Exception) { return@lazy appRes }
|
||||
@SuppressLint("DiscouragedApi")
|
||||
@Suppress("DEPRECATION")
|
||||
object : Resources(gmsRes.assets, gmsRes.displayMetrics, gmsRes.configuration) {
|
||||
override fun getText(id: Int): CharSequence {
|
||||
return try {
|
||||
gmsRes.getText(id)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
appRes.getText(id)
|
||||
} catch (e2: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getText(id: Int, def: CharSequence?): CharSequence {
|
||||
return try {
|
||||
gmsRes.getText(id, def)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
appRes.getText(id, def)
|
||||
} catch (e2: Exception) {
|
||||
def ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getString(id: Int): String {
|
||||
return try {
|
||||
gmsRes.getString(id)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
appRes.getString(id)
|
||||
} catch (e2: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getString(id: Int, vararg formatArgs: Any?): String {
|
||||
return try {
|
||||
gmsRes.getString(id, *formatArgs)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
appRes.getString(id, *formatArgs)
|
||||
} catch (e2: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getResources(): Resources = mergedResources
|
||||
|
||||
override fun createConfigurationContext(overrideConfiguration: Configuration): Context {
|
||||
val configCtx = super.createConfigurationContext(overrideConfiguration)
|
||||
return object : ContextWrapper(configCtx) {
|
||||
override fun getResources(): Resources = mergedResources
|
||||
override fun createConfigurationContext(configuration: Configuration): Context {
|
||||
return this@MapContext.createConfigurationContext(configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = "GmsMapContext"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue