diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt index 14d8e0a..f363f3f 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/host/CarAppConnection.kt @@ -301,11 +301,14 @@ class CarAppConnection(private val context: Context) { /** * "navigate to X" deep link, the same one Android Auto's own Assistant integration sends - * [ACTION_NAVIGATE] with the destination as a `geo:` search query, rather than anything - * app-specific. + * app-specific. Centred on the phone when there's a fix: `geo:0,0` made Organic Maps pick the + * first Del Taco it found anywhere, not the nearest. The location goes only to the map app. */ - private fun navigateIntent(destination: String) = - Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + Uri.encode(destination))) + private fun navigateIntent(destination: String): Intent { + val near = app.seb3thehacker.gearslip.notify.CarMotion.nearHere(context) ?: "0,0" + return Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:$near?q=" + Uri.encode(destination))) .setComponent(component) + } /** * Sends a fresh destination to an app that's already running, instead of tearing it down diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/CarMotion.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/CarMotion.kt index fc87fbc..3d95990 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/CarMotion.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/notify/CarMotion.kt @@ -22,6 +22,33 @@ object CarMotion { private var listener: LocationListener? = null private var speed = 0f private var fixAt = 0L + @Volatile private var last: Location? = null + + /** A fix older than this is somewhere the car has left. */ + private const val NEAR_STALE_MS = 10 * 60_000L + + /** + * Where the phone is, as "lat,lng", for "navigate to Del Taco" to find the nearest one. From + * this drive's GPS, or Android's last known fix; null with no permission or nothing recent. + */ + fun nearHere(context: Context): String? { + val fix = last?.takeIf { SystemClock.elapsedRealtime() - fixAt < NEAR_STALE_MS } + ?: lastKnown(context) + ?: return null + return String.format(java.util.Locale.US, "%.5f,%.5f", fix.latitude, fix.longitude) + } + + private fun lastKnown(context: Context): Location? { + if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED + ) return null + val manager = context.getSystemService(LocationManager::class.java) ?: return null + val now = System.currentTimeMillis() + return manager.getProviders(true) + .mapNotNull { runCatching { manager.getLastKnownLocation(it) }.getOrNull() } + .filter { now - it.time < NEAR_STALE_MS } + .maxByOrNull { it.time } + } val moving: Boolean get() = SystemClock.elapsedRealtime() - fixAt < STALE_MS && speed > MOVING_MS @@ -34,6 +61,7 @@ object CarMotion { override fun onLocationChanged(location: Location) { speed = if (location.hasSpeed()) location.speed else 0f fixAt = SystemClock.elapsedRealtime() + last = location } } listener = l