Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/android/view builder and speed json #360

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ import com.stadiamaps.ferrostar.composeui.views.components.TripProgressView
import com.stadiamaps.ferrostar.core.NavigationUiState

data class NavigationViewComponentBuilder(
val instructionsView: @Composable (modifier: Modifier, uiState: NavigationUiState) -> Unit,
val progressView:
internal val instructionsView:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This avoids users accidentally trying to do:

NavigationViewComponentBuilder.Default()
    .instructionsView { }

Which is very easy to do and highly problematic.

@Composable
(modifier: Modifier, uiState: NavigationUiState) -> Unit,
internal val progressView:
@Composable
(modifier: Modifier, uiState: NavigationUiState, onTapExit: (() -> Unit)?) -> Unit,
val streetNameView:
internal val streetNameView:
@Composable
(modifier: Modifier, roadName: String?, cameraControlState: CameraControlState) -> Unit,
val customOverlayView: @Composable (BoxScope.(Modifier) -> Unit)? = null,
internal val customOverlayView: @Composable (BoxScope.(Modifier) -> Unit)? = null,
// TODO: We may reasonably be able to add the NavigationMapView here. But not sure how much
// value that would add
// since most of the hard config already exists within the overlay views which are not
Expand Down Expand Up @@ -72,6 +74,8 @@ data class NavigationViewComponentBuilder(
}
})
}

fun getCustomOverlayView(): @Composable (BoxScope.(Modifier) -> Unit)? = customOverlayView
}

fun NavigationViewComponentBuilder.withInstructionsView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SpeedSerializationAdapter : JsonAdapter<Speed>() {
is Speed.NoLimit -> writer.name("none").value(true)
is Speed.Unknown -> writer.name("unknown").value(true)
is Speed.Value ->
writer.name("value").value(speed.value).name("unit").value(speed.unit.text)
writer.name("speed").value(speed.value).name("unit").value(speed.unit.text)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahmedre I believe the actual key should be "speed" when there's a value. E.g.

{
  "speed": 56,
  "unit": "km/h"
},

Let me know if there are additional cases. Definitely a bit of an obnoxious json object 😄.

Also, do we want to try catch the annotations parsing? On iOS we just convert a json parsing error to nil/null for annotations and an onError callback so that developers can log the failure. We figured better to let navigation succeed even in the case of a degraded/malformed annotations. See

do {
return try decoder.decode(Annotation.self, from: data)
} catch {
onError(error)
return nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right - my mistake! agree re: try/catching the parsing with an error callback 👍

}
writer.endObject()
}
Expand All @@ -33,7 +33,7 @@ class SpeedSerializationAdapter : JsonAdapter<Speed>() {
var unit: String? = null

while (reader.hasNext()) {
when (reader.selectName(JsonReader.Options.of("none", "unknown", "value", "unit"))) {
when (reader.selectName(JsonReader.Options.of("none", "unknown", "speed", "unit"))) {
0 -> none = reader.nextBoolean()
1 -> unknown = reader.nextBoolean()
2 -> value = reader.nextDouble()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.stadiamaps.ferrostar

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.stadiamaps.ferrostar.core.DefaultNavigationViewModel
import com.stadiamaps.ferrostar.core.FerrostarCore
import com.stadiamaps.ferrostar.core.LocationProvider
import com.stadiamaps.ferrostar.core.LocationUpdateListener
import com.stadiamaps.ferrostar.core.NavigationUiState
import com.stadiamaps.ferrostar.core.NavigationViewModel
import com.stadiamaps.ferrostar.core.annotation.AnnotationPublisher
import com.stadiamaps.ferrostar.core.annotation.valhalla.valhallaExtendedOSRMAnnotationPublisher
import com.stadiamaps.ferrostar.core.isNavigating
import java.util.concurrent.Executors
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -24,7 +25,8 @@ import uniffi.ferrostar.UserLocation
class DemoNavigationViewModel(
// This is a simple example, but these would typically be dependency injected
private val ferrostarCore: FerrostarCore = AppModule.ferrostarCore,
) : ViewModel(), LocationUpdateListener, NavigationViewModel {
annotationPublisher: AnnotationPublisher<*> = valhallaExtendedOSRMAnnotationPublisher()
) : DefaultNavigationViewModel(ferrostarCore, annotationPublisher), LocationUpdateListener {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ianthetechie now that this view model uses the DefaultNavigationViewModel. We should be able to refactor it to use the existing uiState from ferrostar and implement its own additional state for things like route loading, etc. Just to highlight how easy it is to customize without interrupting the default navigation behavior.

private val locationStateFlow = MutableStateFlow<UserLocation?>(null)
private val executor = Executors.newSingleThreadScheduledExecutor()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ fun DynamicallyOrientingNavigationView(
}
}

views.customOverlayView?.let { customOverlayView ->
views.getCustomOverlayView()?.let { customOverlayView ->
customOverlayView(Modifier.windowInsetsPadding(WindowInsets.systemBars).padding(gridPadding))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ fun LandscapeNavigationView(
mapViewInsets = mapViewInsets,
onTapExit = onTapExit)

views.customOverlayView?.let { customOverlayView ->
views.getCustomOverlayView()?.let { customOverlayView ->
customOverlayView(Modifier.windowInsetsPadding(WindowInsets.systemBars).padding(gridPadding))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ fun PortraitNavigationView(
mapViewInsets = mapViewInsets,
onTapExit = onTapExit)

views.customOverlayView?.let { customOverlayView ->
views.getCustomOverlayView()?.let { customOverlayView ->
customOverlayView(
Modifier.windowInsetsPadding(WindowInsets.systemBars).padding(gridPadding))
}
Expand Down
Loading