From 4c00d46a1444c934e626461db6b73a8ebbdfd046 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 17 Jul 2026 14:05:11 +0200 Subject: [PATCH] Napraw otwieranie nawigacji do geofence --- .../java/pl/firmatpp/kierowca/ui/DriverApp.kt | 40 ++++++++++++------- .../pl/firmatpp/kierowca/ui/DriverUiRules.kt | 37 +++++++++++++++++ .../firmatpp/kierowca/ui/DriverUiRulesTest.kt | 33 +++++++++++++++ 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt index 3cb1e3f..3b46603 100644 --- a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt +++ b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverApp.kt @@ -2,6 +2,7 @@ package pl.firmatpp.kierowca.ui import android.Manifest import android.app.Activity +import android.content.ActivityNotFoundException import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -12,6 +13,7 @@ import android.location.LocationManager import android.net.Uri import android.os.Build import android.provider.Settings +import android.widget.Toast import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -3565,7 +3567,10 @@ private fun RoutePointBlock( Text(subtitle, color = TppTheme.colors.muted, style = MaterialTheme.typography.bodyLarge) } } - if (navigationPoint?.hasCoordinates() == true) { + if ( + navigationPoint != null && + hasValidNavigationCoordinates(navigationPoint.latitude, navigationPoint.longitude) + ) { IconButton( onClick = { onNavigate(navigationPoint) }, modifier = Modifier.size(42.dp).background(TppTheme.colors.forest, RoundedCornerShape(21.dp)), @@ -4356,24 +4361,29 @@ private fun createCameraUri(context: Context): Uri { return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) } -private fun NavigationPointDto.hasCoordinates(): Boolean = latitude != null && longitude != null - private fun openNavigation(context: Context, point: NavigationPointDto) { - val latitude = point.latitude ?: return - val longitude = point.longitude ?: return - val googleMapsIntent = Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=$latitude,$longitude")) - .setPackage("com.google.android.apps.maps") + val candidates = navigationIntentCandidates(point.latitude, point.longitude, point.label) - if (googleMapsIntent.resolveActivity(context.packageManager) != null) { - context.startActivity(googleMapsIntent) - return + for (candidate in candidates) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(candidate.uri)).apply { + candidate.packageName?.let(::setPackage) + } + + try { + context.startActivity(intent) + return + } catch (_: ActivityNotFoundException) { + // Try the next navigation provider. + } catch (_: SecurityException) { + // The provider is unavailable to this app; use the next fallback. + } } - val label = Uri.encode(point.label?.takeIf { it.isNotBlank() } ?: "Cel") - val geoIntent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=$latitude,$longitude($label)")) - if (geoIntent.resolveActivity(context.packageManager) != null) { - context.startActivity(geoIntent) - } + Toast.makeText( + context, + "Nie znaleziono aplikacji, która może otworzyć nawigację.", + Toast.LENGTH_LONG, + ).show() } private fun cameraCapturePermissions(context: Context, requirePreciseLocation: Boolean): Array = buildList { diff --git a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt index dbfc848..619bdba 100644 --- a/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt +++ b/app/src/main/java/pl/firmatpp/kierowca/ui/DriverUiRules.kt @@ -3,6 +3,7 @@ package pl.firmatpp.kierowca.ui import java.io.File import java.math.BigDecimal import java.math.RoundingMode +import java.net.URLEncoder import java.time.DayOfWeek import java.time.LocalDate import java.time.OffsetDateTime @@ -24,6 +25,42 @@ import pl.firmatpp.kierowca.data.upload.RouteActionType private val shortDateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM") private val routeWeightKilogramInputThreshold = BigDecimal("1000") private const val routeWeightMaxTons = 999.999 +private const val googleMapsPackage = "com.google.android.apps.maps" + +data class NavigationIntentCandidate( + val uri: String, + val packageName: String? = null, +) + +fun hasValidNavigationCoordinates(latitude: Double?, longitude: Double?): Boolean = + latitude != null && + longitude != null && + latitude.isFinite() && + longitude.isFinite() && + latitude in -90.0..90.0 && + longitude in -180.0..180.0 + +fun navigationIntentCandidates( + latitude: Double?, + longitude: Double?, + label: String?, +): List { + if (!hasValidNavigationCoordinates(latitude, longitude)) return emptyList() + + val coordinates = "$latitude,$longitude" + val encodedLabel = URLEncoder + .encode(label?.takeIf { it.isNotBlank() } ?: "Cel", Charsets.UTF_8.name()) + .replace("+", "%20") + + return listOf( + NavigationIntentCandidate( + uri = "google.navigation:q=$coordinates", + packageName = googleMapsPackage, + ), + NavigationIntentCandidate(uri = "geo:0,0?q=$coordinates($encodedLabel)"), + NavigationIntentCandidate(uri = "https://www.google.com/maps/dir/?api=1&destination=$coordinates"), + ) +} enum class RouteFlowStepState { Todo, diff --git a/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt b/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt index b8c55ec..74cdeec 100644 --- a/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt +++ b/app/src/test/java/pl/firmatpp/kierowca/ui/DriverUiRulesTest.kt @@ -82,6 +82,39 @@ class DriverUiRulesTest { assertEquals("—", routeDateChipLabel("", today)) } + @Test + fun buildsNavigationCandidatesFromGeofenceCoordinatesWithFallbacks() { + val candidates = navigationIntentCandidates( + latitude = 50.0619474, + longitude = 19.9368564, + label = "Brama główna", + ) + + assertEquals(3, candidates.size) + assertEquals("com.google.android.apps.maps", candidates[0].packageName) + assertEquals("google.navigation:q=50.0619474,19.9368564", candidates[0].uri) + assertEquals(null, candidates[1].packageName) + assertEquals( + "geo:0,0?q=50.0619474,19.9368564(Brama%20g%C5%82%C3%B3wna)", + candidates[1].uri, + ) + assertEquals( + "https://www.google.com/maps/dir/?api=1&destination=50.0619474,19.9368564", + candidates[2].uri, + ) + } + + @Test + fun doesNotBuildNavigationCandidatesForMissingOrInvalidCoordinates() { + assertFalse(hasValidNavigationCoordinates(null, 19.9368564)) + assertFalse(hasValidNavigationCoordinates(50.0619474, null)) + assertFalse(hasValidNavigationCoordinates(Double.NaN, 19.9368564)) + assertFalse(hasValidNavigationCoordinates(91.0, 19.9368564)) + assertFalse(hasValidNavigationCoordinates(50.0619474, 181.0)) + assertTrue(hasValidNavigationCoordinates(50.0619474, 19.9368564)) + assertTrue(navigationIntentCandidates(null, null, "Cel").isEmpty()) + } + @Test fun calculatesPhotoGridRowsForTwoColumnInlineGallery() { assertEquals(0, inlinePhotoGridRows(0))