fix : prevent crash on geo: intent when no compatible map app is inst… (#1936)

Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
emp3r0r7 2025-05-26 14:09:56 +02:00 committed by GitHub
parent ac22ac3f28
commit f6f3cf5c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 78 additions and 37 deletions

View file

@ -86,6 +86,14 @@
android:name="android.hardware.usb.host"
android:required="false" />
<!-- Declare geo: intent visibility for Android 11+ (needed for resolveActivity with map apps) -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
</queries>
<!-- hardware acceleration is required for zxing barcode lib -->
<application
android:name="com.geeksville.mesh.MeshUtilApplication"

View file

@ -19,6 +19,8 @@ package com.geeksville.mesh.ui
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.Intent
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.material3.MaterialTheme
@ -29,7 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.Clipboard
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
@ -37,6 +39,7 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.core.net.toUri
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat
import com.geeksville.mesh.android.BuildUtils.debug
import com.geeksville.mesh.ui.theme.AppTheme
@ -54,53 +57,29 @@ fun LinkedCoordinates(
format: Int,
nodeName: String,
) {
val uriHandler = LocalUriHandler.current
val context = LocalContext.current
val clipboard: Clipboard = LocalClipboard.current
val coroutineScope = rememberCoroutineScope()
val style = SpanStyle(
color = HyperlinkBlue,
fontSize = MaterialTheme.typography.labelLarge.fontSize,
textDecoration = TextDecoration.Underline
)
val annotatedString = buildAnnotatedString {
pushStringAnnotation(
tag = "gps",
// URI scheme is defined at:
// https://developer.android.com/guide/components/intents-common#Maps
annotation = "geo:0,0?q=$latitude,$longitude&z=17&label=${
URLEncoder.encode(nodeName, "utf-8")
}"
)
withStyle(style = style) {
val gpsString = when (format) {
GpsCoordinateFormat.DEC_VALUE -> GPSFormat.toDEC(latitude, longitude)
GpsCoordinateFormat.DMS_VALUE -> GPSFormat.toDMS(latitude, longitude)
GpsCoordinateFormat.UTM_VALUE -> GPSFormat.toUTM(latitude, longitude)
GpsCoordinateFormat.MGRS_VALUE -> GPSFormat.toMGRS(latitude, longitude)
else -> GPSFormat.toDEC(latitude, longitude)
}
append(gpsString)
}
pop()
}
val clipboard: Clipboard = LocalClipboard.current
val coroutineScope = rememberCoroutineScope()
val annotatedString = rememberAnnotatedString(latitude, longitude, format, nodeName, style)
Text(
modifier = modifier.combinedClickable(
onClick = {
annotatedString.getStringAnnotations(
tag = "gps",
start = 0,
end = annotatedString.length
).firstOrNull()?.let {
try {
uriHandler.openUri(it.item)
} catch (ex: ActivityNotFoundException) {
debug("No application found: $ex")
}
}
handleClick(context, annotatedString)
},
onLongClick = {
coroutineScope.launch {
clipboard.setClipEntry(ClipEntry(ClipData.newPlainText("", annotatedString)))
clipboard.setClipEntry(
ClipEntry(
ClipData.newPlainText("", annotatedString)
)
)
debug("Copied to clipboard")
}
}
@ -109,6 +88,60 @@ fun LinkedCoordinates(
)
}
@Composable
private fun rememberAnnotatedString(
latitude: Double,
longitude: Double,
format: Int,
nodeName: String,
style: SpanStyle
) = buildAnnotatedString {
pushStringAnnotation(
tag = "gps",
annotation = "geo:0,0?q=$latitude,$longitude&z=17&label=${
URLEncoder.encode(nodeName, "utf-8")
}"
)
withStyle(style = style) {
val gpsString = when (format) {
GpsCoordinateFormat.DEC_VALUE -> GPSFormat.toDEC(latitude, longitude)
GpsCoordinateFormat.DMS_VALUE -> GPSFormat.toDMS(latitude, longitude)
GpsCoordinateFormat.UTM_VALUE -> GPSFormat.toUTM(latitude, longitude)
GpsCoordinateFormat.MGRS_VALUE -> GPSFormat.toMGRS(latitude, longitude)
else -> GPSFormat.toDEC(latitude, longitude)
}
append(gpsString)
}
pop()
}
private fun handleClick(context: android.content.Context, annotatedString: androidx.compose.ui.text.AnnotatedString) {
annotatedString.getStringAnnotations(
tag = "gps",
start = 0,
end = annotatedString.length
).firstOrNull()?.let {
val uri = it.item.toUri()
val intent = Intent(Intent.ACTION_VIEW, uri).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
try {
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
} else {
Toast.makeText(
context,
"No application available to open this location!",
Toast.LENGTH_LONG
).show()
}
} catch (ex: ActivityNotFoundException) {
debug("Failed to open geo intent: $ex")
}
}
}
@PreviewLightDark
@Composable
fun LinkedCoordinatesPreview(