繁体   English   中英

如何将您的应用程序添加到 Google 地图中的“共享此地点”列表

[英]How to add your application to the "share this place" list in Google maps

谷歌地图现在提供了一种与似乎预定义的资源列表“共享位置”的方法。 当用户在 Google 地图上搜索某个地点时,无论是特定地址、十字路口还是餐馆名称,都会有一个名为“分享此地点”的新按钮将位置信息发布到 Google Buzz、Facebook、Twitter 或通过电子邮件- 邮件或短信。 我想将我的应用程序包含在此列表中,或者确定如何获取所选位置的纬度/经度。 有人有什么想法吗?

我想到了。 这是一个示例manifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp" android:versionCode="1"
    android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".YourApp" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND"></action>
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
</manifest> 

只需将SEND intent过滤器添加到您的活动中即可。 “共享此地点”只是使用mime类型“text / plain”执行“SEND”意图。 如果您为该类型注册了一个意图过滤器,那么您的应用程序将显示在列表中。 我做了。 =)

您是否知道如何获取 GPS 坐标? 如何?

我还想知道你是如何从中得到 GPS 坐标的。

以下是您可以尝试获取坐标的方法:

正如@HenryAdamsJr 所说,您需要先声明一个<intent-filter> ,然后根据意图,应用程序将获得一些如下所示的文本:

 Letícia Bronzoni Fotografi
 +46 73 255 28 32
 https://maps.app.goo.gl/kijJBem2FGkj2S99A

如您所见,有一个 url 被调用时会重定向到类似以下内容:

1. https://www.google.com/maps/place/Grodhavet,+Karlbergs+strand+12,+171+73+Solna,+Sweden/data=!4m2!3m1!1s0x465f9d8ec1ecc2c3:0xbc9f46e4d42faea4?utm_source=mstt_1&entry=gps&g_ep=IIgnKgA%3D&ucbcb=1

2. https://www.google.com/maps/place/Karlav%C3%A4gen+32,+114+31+Stockholm,+Sweden/data=!4m6!3m5!1s0x465f9d4383805daf:0xffd88b394b6ac3b8!7e2!8m2!3d59.33992!4d18.0729719?utm_source=mstt_1&entry=gps&g_ep=CAESCTExLjYwLjcwMxgAIIgnKgBCAlJV&ucbcb=1 

3. https://www.google.com/maps/place/59.360982,18.069179/data=!4m6!3m5!1s0!7e2!8m2!3d59.360981499999994!4d18.0691788?utm_source=mstt_1&entry=gps&g_ep=CAESCTExLjYwLjcwMxgAIIgnKgBCAlJV&ucbcb=1

案例 1。最糟糕的一个。
在 GMaps 上选择 POI 时会发生这种情况。 结果 url 只有一个更详细的地址,需要使用 Geocoder 等工具查找(参见下面的示例)。

案例2。最好的一个!
It happens when a random point was selected but then GMaps converted it to an address. 生成的 url 具有带坐标的“data=.....3dXXXX!4dYYYY”部分。

案例 3. 还可以的那个。
It happens when a random point was selected and GMaps could not figure out any address. 生成的 url 具有带坐标的“place/XXXX,YYYY”部分。

这是我的处理方式:


// Somewhere in Activity::onCreate or ::onNewIntent
override fun onCreate(savedInstanceState: Bundle?) {
    .....
    
    val linkHash = IncomingShareFromGoogleMaps.getGoogleMapsShareLinkHash(intent)
}


// Then somewhere in a ViewModel or so 
val shareHandler: IncomingShareFromGoogleMaps = ...
fun processShare(linkHash:String) {
    viewModelScope.launch {
      val coordinates = shareHandler.shortenedUrlToCoordinates(linkHash)
      // Profit!
    }
}



class IncomingShareFromGoogleMaps @Inject constructor(
    private val errorReporter: ErrorReporter,
    private val geocoder: Geocoder,
) {
    companion object {
        private const val TIMEOUT_CONNECT_MS = 5_000
        private const val TIMEOUT_WRITE_MS = 5_000
        private const val TIMEOUT_READ_MS = 5_000
        private const val GOOGLE_SHORT_URL_PREFIX = "https://maps.app.goo.gl/"


        fun getGoogleMapsShareLinkHash(intent: Intent): String? =
            intent
                .takeIf { it.action == "android.intent.action.SEND" }?.extras
                ?.getString("android.intent.extra.TEXT")
                ?.takeUnless { it.isBlank() }
                ?.split("\n")
                ?.lastOrNull()
                ?.takeIf { it.startsWith(GOOGLE_SHORT_URL_PREFIX) }
                ?.removePrefix(GOOGLE_SHORT_URL_PREFIX)
                
    }

    suspend fun shortenedUrlToCoordinates(shortenedUrl: String) = withContext(Dispatchers.IO) {
        val resultingUrl =
            createHttpClient()
                .performGetRequest(GOOGLE_SHORT_URL_PREFIX + shortenedUrl)
                .request.url.toString()

        return@withContext tryToGetCoordinates(resultingUrl)
    }

    /**
     * Takes a url which was returned by Google.Maps and tries to extract some coordinates.
     * Example of the `resultingUrl`:
     * https://www.google.com/maps/place/Grodhavet,+Karlbergs+strand+12,+171+73+Solna,+Sweden/data=!4m2!3m1!1s0x465f9d8ec1ecc2c3:0xbc9f46e4d42faea4?utm_source=mstt_1&entry=gps&g_ep=IIgnKgA%3D&ucbcb=1
     * https://www.google.com/maps/place/Karlav%C3%A4gen+32,+114+31+Stockholm,+Sweden/data=!4m6!3m5!1s0x465f9d4383805daf:0xffd88b394b6ac3b8!7e2!8m2!3d59.33992!4d18.0729719?utm_source=mstt_1&entry=gps&g_ep=CAESCTExLjYwLjcwMxgAIIgnKgBCAlJV&ucbcb=1
     * https://www.google.com/maps/place/59.360982,18.069179/data=!4m6!3m5!1s0!7e2!8m2!3d59.360981499999994!4d18.0691788?utm_source=mstt_1&entry=gps&g_ep=CAESCTExLjYwLjcwMxgAIIgnKgBCAlJV&ucbcb=1
     * So it tries first to take coordinates from "data=....!3dXXX!4dYYY" part of the url,
     * then from "place/XXX,YYY" part of the url,
     * then it tries to look up the address from "place/" part with Mapbox.Geocoder SDK.
     */
    private fun tryToGetCoordinates(resultingUrl: String) =
        tryToGetCoordinatesFromDataSection(resultingUrl)
            ?: tryToGetCoordinatesFromPlaceSection(resultingUrl)
            ?: tryToGetCoordinatesByAddress(resultingUrl)

    private fun tryToGetCoordinatesFromPlaceSection(resultingUrl: String) =
        try {
            """place/(\d+\.\d+),(\d+\.\d+)""".toRegex()
                .find(resultingUrl)?.groupValues
                ?.takeIf { it.size == 3 }
                ?.let { list ->
                    Coordinates(
                        longitude = list[2].toDouble(),
                        latitude = list[1].toDouble()
                    )
                }
        } catch (e: NumberFormatException) {
            errorReporter.reportError(e, "shortenedUrlToCoordinates, failed")
            null
        }

    private fun tryToGetCoordinatesFromDataSection(resultingUrl: String) =
        try {
            val lat = """data=\S*!3d(\d+\.\d+)""".toRegex()
                .find(resultingUrl)?.groupValues
                ?.takeIf { it.size > 1 }
                ?.get(1)
                ?.toDouble()

            val lng = """data=\S*!4d(\d+\.\d+)""".toRegex()
                .find(resultingUrl)?.groupValues
                ?.takeIf { it.size > 1 }
                ?.get(1)
                ?.toDouble()

            when {
                lat != null && lng != null -> Coordinates(latitude = lat, longitude = lng)
                else -> null
            }
        } catch (e: NumberFormatException) {
            errorReporter.reportError(e, "tryToGetCoordinatesFromDataSection, failed")
            null
        }

    private fun tryToGetCoordinatesByAddress(resultingUrl: String): Coordinates? =
        """place/(\S*)/data""".toRegex()
            .find(resultingUrl)?.groupValues
            ?.takeIf { it.size == 2 }?.get(1)
            ?.let { geocoder.getCoordinatesForAddress(URLDecoder.decode(it, "UTF-8")) }

    private fun createHttpClient() =
        OkHttpClient.Builder()
            .connectTimeout(TIMEOUT_CONNECT_MS.toLong(), TimeUnit.MILLISECONDS)
            .writeTimeout(TIMEOUT_WRITE_MS.toLong(), TimeUnit.MILLISECONDS)
            .readTimeout(TIMEOUT_READ_MS.toLong(), TimeUnit.MILLISECONDS)
            .build()

    private fun OkHttpClient.performGetRequest(url: String): Response =
        newCall(
            Request.Builder()
                .url(url)
                .get()
                .build()
        ).execute()
}

我一直在使用Mapbox.Geocoder因为我的项目是基于它的。 这是我对地理编码器的实现:

class GeocoderImpl @Inject constructor(
    private val runtime: RuntimeMapInfo,
) : Geocoder {
    override fun getCoordinatesForAddress(address: String): Coordinates? =
        MapboxGeocoding.builder()
            .accessToken(runtime.mapboxAccessToken())
            .query(address)
            .build()
            .executeCall()
            .body()
            ?.features()
            ?.preferPoiOrAddress()
            ?.geometry()
            ?.let { (it as? Point) }
            ?.toCoordinates()

    /**
     * Takes first "placeType=[poi]" or "placeType=[address]" otherwise any first feature.
     */
    private fun List<CarmenFeature>.preferPoiOrAddress(): CarmenFeature? =
        firstOrNull { feature ->
            feature.placeType()?.let {
                it.contains("address") || it.contains("poi")
            } == true
        } ?: firstOrNull()
}

免责声明:我理解这不是最好的解决方案,但它确实有效。 我花了一天时间想弄明白。 也许它会帮助某人节省一天的时间:-)

附言:

另一种更 hacky 的方式可能是 ->
Ofc Ggoole.Maps 最了解如何展开它自己的缩短网址。 因此,如果要增加一个透明的 webview 并在那里调用短路的 url,那么重定向之一将类似于“案例 3”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM