Skip to content

Commit

Permalink
Autocomplete search for Android Demo App (stadiamaps#295)
Browse files Browse the repository at this point in the history
* Support arbitrary Valhalla options (formerly we only merged costing options)

* Fix argument

* Improve docs of some convenience initializers

* Align Android with iOS costing options

* swiftformat

* Don't forget the web ;)

* Add Android-specific README

* Set up Android to use API keys properly via local.properties

* Fix CI

* Fix subtle bug where Android location providers never updated lastLocation

* Rename U-Turn images to match how Kotlin stringifies the maneuvers

* Allow the fused location provider to report an initial fix faster

* First pass at an actually usable demo app

* Minor build script improvement

* Checkpoint commit phase 1

* Checkpoint commit phase 2 highlighting some bad stuff to clean up

* Add docs on the StaticLocationEngine

* Fix formatting + doc test

* Fix typo in Android readme

Co-authored-by: Jacob Fielding <[email protected]>

* Kotlin let closure idiom

* ktfmtFormat + auto-commit

* Remove extraneous env?

* Try switching to a mix of Apple Silicon Ubuntu runners for Android

* Fix runner permissions (I think)

* Apply automatic changes

* Address open thread re: iOS uturn image name

* Add TODO docs

* Fix the navigation camera (re-)application logic

* Cleanup

* Remove old comment

* Apply automatic changes

---------

Co-authored-by: Jacob Fielding <[email protected]>
Co-authored-by: ianthetechie <[email protected]>
  • Loading branch information
3 people authored and ahmedre committed Oct 22, 2024
1 parent d12757c commit bfb0cd7
Show file tree
Hide file tree
Showing 28 changed files with 378 additions and 137 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gradle-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Install cargo-ndk
run: cargo install cargo-ndk

- name: Creat local.properties (required for cargo-ndk and the demo app)
- name: Create local.properties (required for cargo-ndk and the demo app)
run: echo 'stadiaApiKey=' > local.properties
working-directory: android

Expand Down
16 changes: 8 additions & 8 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git",
"state" : {
"revision" : "e409318144091c3ee9ad551b202e1c36695f8086",
"version" : "6.7.0"
"revision" : "f23db791d7b6f0329e3c6788d8e4152c24c52b6b",
"version" : "6.7.1"
}
},
{
"identity" : "maplibre-swift-macros",
"kind" : "remoteSourceControl",
"location" : "https://github.com/stadiamaps/maplibre-swift-macros.git",
"state" : {
"revision" : "236215c13bff962009e0f0257d6d8349be33442f",
"version" : "0.0.4"
"revision" : "9e27e62dff7fd727aebd0a7c8aa74e7635a5583e",
"version" : "0.0.5"
}
},
{
Expand All @@ -41,17 +41,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax.git",
"state" : {
"revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
"version" : "509.1.1"
"revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82",
"version" : "510.0.3"
}
},
{
"identity" : "swiftui-dsl",
"kind" : "remoteSourceControl",
"location" : "https://github.com/maplibre/swiftui-dsl",
"state" : {
"revision" : "5ba75ef1e4382fcc7ee71e274eb9c2a50906b14e",
"version" : "0.1.0"
"revision" : "c39688db3aac50b523ea062b286c584123022093",
"version" : "0.3.0"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This directory tree contains the Gradle workspace for Ferrostar on Android.
* `composeui` - Jetpack Compose UI elements which are not tightly coupled to any particular map renderer.
* `core` - The core module is where all the "business logic", location management, and other core functionality lives.
* `demo-app` - A minimal demonstration app.
* `google-play-services` - Optional functionality that depends on Google Play Services (like a fused location client wrapper). This is a separate module so that apps are able to "de-Google" if necessary.
* `google-play-services` - Optional functionality that depends on Google Play Services (like the fused location client wrapper). This is a separate module so that apps are able to "de-Google" if necessary.
* `maplibreui` - Map-related user interface components built with MapLibre.

## Running the demo app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fun ManeuverImage(content: VisualInstructionContent, tint: Color = LocalContentC

@Preview
@Composable
fun ManeuverImagePreview() {
fun ManeuverImageLeftTurnPreview() {
ManeuverImage(
VisualInstructionContent(
text = "",
Expand All @@ -56,3 +56,14 @@ fun ManeuverImagePreview() {
roundaboutExitDegrees = null,
laneInfo = null))
}

@Preview
@Composable
fun ManeuverImageContinueUturnPreview() {
ManeuverImage(
VisualInstructionContent(
text = "",
maneuverType = ManeuverType.CONTINUE,
maneuverModifier = ManeuverModifier.U_TURN,
roundaboutExitDegrees = null))
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,19 @@ class AndroidSystemLocationProvider(context: Context) : LocationProvider {
android.util.Log.d(TAG, "Already registered; skipping")
return
}
val androidListener = LocationListener { listener.onLocationUpdated(it.toUserLocation()) }
val androidListener = LocationListener {
val userLocation = it.toUserLocation()
lastLocation = userLocation
listener.onLocationUpdated(userLocation)
}
listeners[listener] = androidListener

val handler = Handler(Looper.getMainLooper())

executor.execute {
handler.post {
val last = locationManager.getLastKnownLocation(getBestProvider())
last?.let { androidListener.onLocationChanged(last) }
locationManager.requestLocationUpdates(getBestProvider(), 100L, 5.0f, androidListener)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ data class NavigationUiState(
*/
val heading: Float?,
/** The geometry of the full route. */
val routeGeometry: List<GeographicCoordinate>,
val routeGeometry: List<GeographicCoordinate>?,
/** Visual instructions which should be displayed based on the user's current progress. */
val visualInstruction: VisualInstruction?,
/**
Expand Down Expand Up @@ -76,6 +76,11 @@ interface NavigationViewModel {
fun toggleMute()

fun stopNavigation()

fun isNavigating(): Boolean = uiState.value.progress != null

// TODO: We think the camera may eventually need to be owned by the view model, but that's going
// to be a very big refactor (maybe even crossing into the MapLibre Compose project)
}

class DefaultNavigationViewModel(
Expand Down
11 changes: 8 additions & 3 deletions android/demo-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ android {
defaultConfig {
applicationId "com.stadiamaps.ferrostar.demo"
minSdk 26
targetSdk 34
targetSdk 35
versionCode 1
versionName "1.0"

Expand Down Expand Up @@ -64,11 +64,16 @@ dependencies {
implementation project(':core')
implementation project(':composeui')
implementation project(':maplibreui')
implementation project(':google-play-services')

implementation libs.maplibre.compose

implementation(platform(libs.okhttp.bom))
implementation(libs.okhttp.core)
implementation platform(libs.okhttp.bom)
implementation libs.okhttp.core

implementation libs.play.services.location

implementation libs.stadiamaps.autocomplete.search

testImplementation libs.junit
androidTestImplementation libs.androidx.test.junit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import com.stadiamaps.ferrostar.core.AlternativeRouteProcessor
import com.stadiamaps.ferrostar.core.AndroidTtsObserver
import com.stadiamaps.ferrostar.core.CorrectiveAction
import com.stadiamaps.ferrostar.core.FerrostarCore
import com.stadiamaps.ferrostar.core.LocationProvider
import com.stadiamaps.ferrostar.core.RouteDeviationHandler
import com.stadiamaps.ferrostar.core.SimulatedLocationProvider
import com.stadiamaps.ferrostar.core.service.FerrostarForegroundServiceManager
import com.stadiamaps.ferrostar.core.service.ForegroundServiceManager
import com.stadiamaps.ferrostar.googleplayservices.FusedLocationProvider
import java.net.URL
import java.time.Duration
import okhttp3.OkHttpClient
Expand Down Expand Up @@ -56,7 +57,10 @@ object AppModule {
appContext = context
}

val locationProvider: SimulatedLocationProvider by lazy { SimulatedLocationProvider() }
val locationProvider: LocationProvider by lazy {
// TODO: Make this configurable?
FusedLocationProvider(appContext)
}
private val httpClient: OkHttpClient by lazy {
OkHttpClient.Builder().callTimeout(Duration.ofSeconds(15)).build()
}
Expand All @@ -69,7 +73,7 @@ object AppModule {
val core =
FerrostarCore(
valhallaEndpointURL = valhallaEndpointUrl,
profile = "bicycle",
profile = "auto",
httpClient = httpClient,
locationProvider = locationProvider,
foregroundServiceManager = foregroundServiceManager,
Expand All @@ -79,7 +83,20 @@ object AppModule {
minimumHorizontalAccuracy = 25U, automaticAdvanceDistance = 10U),
RouteDeviationTracking.StaticThreshold(15U, 25.0),
CourseFiltering.SNAP_TO_ROUTE),
options = mapOf("costingOptions" to mapOf("bicycle" to mapOf("use_roads" to 0.2))))
options =
mapOf(
"costingOptions" to
// Just an example... You can set multiple costing options for any profile
// in Valhalla.
// If your app uses multiple routing modes, you can have a master settings
// map, or construct a new one each time.
mapOf(
"low_speed_vehicle" to
mapOf(
"vehicle_type" to "golf_cart",
"top_speed" to 32 // 24kph ~= 15mph
)),
"units" to "miles"))

// Not all navigation apps will require this sort of extra configuration.
// In fact, we hope that most don't!
Expand All @@ -93,13 +110,7 @@ object AppModule {
Log.i(TAG, "Received alternate route(s): $routes")
if (routes.isNotEmpty()) {
// NB: Use `replaceRoute` for cases like this!
it.replaceRoute(
routes.first(),
NavigationControllerConfig(
StepAdvanceMode.RelativeLineStringDistance(
minimumHorizontalAccuracy = 25U, automaticAdvanceDistance = 10U),
RouteDeviationTracking.StaticThreshold(25U, 10.0),
CourseFiltering.SNAP_TO_ROUTE))
it.replaceRoute(routes.first())
}
}

Expand Down
Loading

0 comments on commit bfb0cd7

Please sign in to comment.