[英]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.