Skip to content

Commit 6ad2eb3

Browse files
committed
Test new route preview
1 parent 7d2b3ed commit 6ad2eb3

File tree

5 files changed

+289
-1
lines changed

5 files changed

+289
-1
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package com.mapbox.androidauto.preview
2+
3+
import android.text.SpannableString
4+
import androidx.annotation.UiThread
5+
import androidx.car.app.Screen
6+
import androidx.car.app.model.Action
7+
import androidx.car.app.model.ActionStrip
8+
import androidx.car.app.model.DurationSpan
9+
import androidx.car.app.model.ItemList
10+
import androidx.car.app.model.Row
11+
import androidx.car.app.model.Template
12+
import androidx.car.app.navigation.model.RoutePreviewNavigationTemplate
13+
import androidx.lifecycle.DefaultLifecycleObserver
14+
import androidx.lifecycle.LifecycleOwner
15+
import com.mapbox.androidauto.MapboxCarContext
16+
import com.mapbox.androidauto.R
17+
import com.mapbox.androidauto.feedback.ui.CarFeedbackAction
18+
import com.mapbox.androidauto.internal.extensions.addBackPressedHandler
19+
import com.mapbox.androidauto.internal.extensions.handleStyleOnAttached
20+
import com.mapbox.androidauto.internal.extensions.handleStyleOnDetached
21+
import com.mapbox.androidauto.internal.logAndroidAuto
22+
import com.mapbox.androidauto.location.CarLocationRenderer
23+
import com.mapbox.androidauto.navigation.CarCameraMode
24+
import com.mapbox.androidauto.navigation.CarDistanceFormatter
25+
import com.mapbox.androidauto.navigation.CarNavigationCamera
26+
import com.mapbox.androidauto.navigation.audioguidance.muteAudioGuidance
27+
import com.mapbox.androidauto.navigation.speedlimit.CarSpeedLimitRenderer
28+
import com.mapbox.androidauto.placeslistonmap.PlacesListOnMapLayerUtil
29+
import com.mapbox.androidauto.routes.NavigationCarRoutesProvider2
30+
import com.mapbox.androidauto.screenmanager.MapboxScreen
31+
import com.mapbox.androidauto.screenmanager.MapboxScreenManager
32+
import com.mapbox.androidauto.search.PlaceRecord
33+
import com.mapbox.geojson.Feature
34+
import com.mapbox.geojson.FeatureCollection
35+
import com.mapbox.maps.MapboxExperimental
36+
import com.mapbox.maps.extension.androidauto.MapboxCarMapObserver
37+
import com.mapbox.maps.extension.androidauto.MapboxCarMapSurface
38+
import com.mapbox.maps.plugin.delegates.listeners.OnStyleLoadedListener
39+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
40+
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
41+
42+
/**
43+
* This will replace [CarRoutePreviewScreen]
44+
*/
45+
@OptIn(MapboxExperimental::class, ExperimentalPreviewMapboxNavigationAPI::class)
46+
internal class CarRoutePreviewScreen2 @UiThread constructor(
47+
private val mapboxCarContext: MapboxCarContext,
48+
private val placeRecord: PlaceRecord,
49+
private val placesLayerUtil: PlacesListOnMapLayerUtil = PlacesListOnMapLayerUtil(),
50+
) : Screen(mapboxCarContext.carContext) {
51+
52+
private val routesProvider = NavigationCarRoutesProvider2()
53+
private val carRouteLine = CarRouteLine(routesProvider)
54+
private val carLocationRenderer = CarLocationRenderer()
55+
private val carSpeedLimitRenderer = CarSpeedLimitRenderer(mapboxCarContext)
56+
private val carNavigationCamera = CarNavigationCamera(
57+
initialCarCameraMode = CarCameraMode.OVERVIEW,
58+
alternativeCarCameraMode = CarCameraMode.FOLLOWING,
59+
carRoutesProvider = routesProvider,
60+
)
61+
62+
private var styleLoadedListener: OnStyleLoadedListener? = null
63+
64+
private val surfaceListener = object : MapboxCarMapObserver {
65+
66+
override fun onAttached(mapboxCarMapSurface: MapboxCarMapSurface) {
67+
super.onAttached(mapboxCarMapSurface)
68+
logAndroidAuto("CarRoutePreviewScreen loaded")
69+
styleLoadedListener = mapboxCarMapSurface.handleStyleOnAttached { style ->
70+
placesLayerUtil.initializePlacesListOnMapLayer(
71+
style,
72+
carContext.resources
73+
)
74+
val coordinate = placeRecord.coordinate ?: return@handleStyleOnAttached
75+
val featureCollection =
76+
FeatureCollection.fromFeature(Feature.fromGeometry(coordinate))
77+
placesLayerUtil.updatePlacesListOnMapLayer(
78+
style,
79+
featureCollection
80+
)
81+
}
82+
}
83+
84+
override fun onDetached(mapboxCarMapSurface: MapboxCarMapSurface) {
85+
super.onDetached(mapboxCarMapSurface)
86+
logAndroidAuto("CarRoutePreviewScreen detached")
87+
mapboxCarMapSurface.handleStyleOnDetached(styleLoadedListener)?.let {
88+
placesLayerUtil.removePlacesListOnMapLayer(it)
89+
}
90+
}
91+
}
92+
93+
init {
94+
logAndroidAuto("CarRoutePreviewScreen constructor")
95+
addBackPressedHandler {
96+
logAndroidAuto("CarRoutePreviewScreen onBackPressed")
97+
mapboxCarContext.mapboxScreenManager.goBack()
98+
}
99+
lifecycle.muteAudioGuidance()
100+
lifecycle.addObserver(object : DefaultLifecycleObserver {
101+
102+
override fun onResume(owner: LifecycleOwner) {
103+
logAndroidAuto("CarRoutePreviewScreen onResume")
104+
mapboxCarContext.mapboxCarMap.registerObserver(carLocationRenderer)
105+
mapboxCarContext.mapboxCarMap.registerObserver(carSpeedLimitRenderer)
106+
mapboxCarContext.mapboxCarMap.registerObserver(carNavigationCamera)
107+
mapboxCarContext.mapboxCarMap.registerObserver(carRouteLine)
108+
mapboxCarContext.mapboxCarMap.registerObserver(surfaceListener)
109+
}
110+
111+
override fun onPause(owner: LifecycleOwner) {
112+
logAndroidAuto("CarRoutePreviewScreen onPause")
113+
mapboxCarContext.mapboxCarMap.unregisterObserver(carLocationRenderer)
114+
mapboxCarContext.mapboxCarMap.unregisterObserver(carSpeedLimitRenderer)
115+
mapboxCarContext.mapboxCarMap.unregisterObserver(carNavigationCamera)
116+
mapboxCarContext.mapboxCarMap.unregisterObserver(carRouteLine)
117+
mapboxCarContext.mapboxCarMap.unregisterObserver(surfaceListener)
118+
}
119+
})
120+
}
121+
122+
override fun onGetTemplate(): Template {
123+
val listBuilder = ItemList.Builder()
124+
val routesPreview = routesProvider.routesPreview.value
125+
val navigationRoutes = routesPreview?.originalRoutesList
126+
?: emptyList()
127+
navigationRoutes.forEach { navigationRoute ->
128+
val route = navigationRoute.directionsRoute
129+
val title = route.legs()?.first()?.summary() ?: placeRecord.name
130+
val duration = CarDistanceFormatter.formatDistance(route.duration())
131+
val routeSpannableString = SpannableString("$duration $title")
132+
routeSpannableString.setSpan(
133+
DurationSpan.create(route.duration().toLong()),
134+
0,
135+
duration.length,
136+
0
137+
)
138+
139+
listBuilder.addItem(
140+
Row.Builder()
141+
.setTitle(routeSpannableString)
142+
.addText(duration)
143+
.build()
144+
)
145+
}
146+
if (routesPreview != null && navigationRoutes.isNotEmpty()) {
147+
listBuilder.setSelectedIndex(routesPreview.primaryRouteIndex)
148+
listBuilder.setOnSelectedListener { index ->
149+
MapboxNavigationApp.current()?.setRoutesPreview(
150+
routesPreview.originalRoutesList,
151+
index
152+
)
153+
}
154+
}
155+
156+
return RoutePreviewNavigationTemplate.Builder()
157+
.setItemList(listBuilder.build())
158+
.setTitle(carContext.getString(R.string.car_action_preview_title))
159+
.setActionStrip(
160+
ActionStrip.Builder()
161+
.addAction(
162+
CarFeedbackAction(
163+
MapboxScreen.ROUTE_PREVIEW_FEEDBACK
164+
).getAction(this@CarRoutePreviewScreen2)
165+
)
166+
.build()
167+
)
168+
.setHeaderAction(Action.BACK)
169+
.setNavigateAction(
170+
Action.Builder()
171+
.setTitle(carContext.getString(R.string.car_action_preview_navigate_button))
172+
.setOnClickListener {
173+
MapboxNavigationApp.current()!!.setNavigationRoutes(
174+
routesPreview!!.routesList
175+
)
176+
MapboxScreenManager.replaceTop(MapboxScreen.ACTIVE_GUIDANCE)
177+
}
178+
.build(),
179+
)
180+
.build()
181+
}
182+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.mapbox.androidauto.preview
2+
3+
import androidx.car.app.CarContext
4+
import androidx.car.app.Screen
5+
import com.mapbox.androidauto.MapboxCarContext
6+
import com.mapbox.androidauto.freedrive.FreeDriveCarScreen
7+
import com.mapbox.androidauto.internal.logAndroidAuto
8+
import com.mapbox.androidauto.screenmanager.MapboxScreenFactory
9+
import com.mapbox.androidauto.screenmanager.factories.RoutePreviewScreenFactory
10+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
11+
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
12+
13+
/**
14+
* This will replace [RoutePreviewScreenFactory]
15+
*/
16+
@ExperimentalPreviewMapboxNavigationAPI
17+
class RoutePreviewScreenFactory2(
18+
private val mapboxCarContext: MapboxCarContext
19+
) : MapboxScreenFactory {
20+
override fun create(carContext: CarContext): Screen {
21+
val repository = mapboxCarContext.routePreviewRequest.repository
22+
val placeRecord = repository?.placeRecord?.value
23+
val routes = repository?.routes?.value
24+
?: MapboxNavigationApp.current()?.getRoutesPreview()?.originalRoutesList
25+
?: emptyList()
26+
return if (placeRecord == null || routes.isEmpty()) {
27+
logAndroidAuto(
28+
"Showing free drive screen because route preview can only be shown " +
29+
"when there is a route. placeRecord=$placeRecord routes.size=${routes.size}"
30+
)
31+
FreeDriveCarScreen(mapboxCarContext)
32+
} else {
33+
MapboxNavigationApp.current()!!.setRoutesPreview(routes)
34+
CarRoutePreviewScreen2(mapboxCarContext, placeRecord)
35+
}
36+
}
37+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.mapbox.androidauto.routes
2+
3+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
4+
import com.mapbox.navigation.base.route.NavigationRoute
5+
import com.mapbox.navigation.core.MapboxNavigation
6+
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
7+
import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver
8+
import com.mapbox.navigation.core.preview.RoutesPreview
9+
import com.mapbox.navigation.core.preview.RoutesPreviewObserver
10+
import kotlinx.coroutines.ExperimentalCoroutinesApi
11+
import kotlinx.coroutines.channels.awaitClose
12+
import kotlinx.coroutines.flow.Flow
13+
import kotlinx.coroutines.flow.MutableStateFlow
14+
import kotlinx.coroutines.flow.StateFlow
15+
import kotlinx.coroutines.flow.asStateFlow
16+
import kotlinx.coroutines.flow.callbackFlow
17+
18+
/**
19+
* This will replace [NavigationCarRoutesProvider]
20+
*/
21+
@ExperimentalPreviewMapboxNavigationAPI
22+
@OptIn(ExperimentalCoroutinesApi::class)
23+
class NavigationCarRoutesProvider2 : CarRoutesProvider {
24+
25+
private val _routesPreview = MutableStateFlow<RoutesPreview?>(null)
26+
27+
val routesPreview: StateFlow<RoutesPreview?> = _routesPreview.asStateFlow()
28+
29+
/**
30+
* Observes the routes set to [MapboxNavigation].
31+
*/
32+
override val navigationRoutes: Flow<List<NavigationRoute>> = MapboxNavigationApp
33+
.flowNavigationRoutes()
34+
35+
private fun MapboxNavigationApp.flowNavigationRoutes(): Flow<List<NavigationRoute>> =
36+
callbackFlow {
37+
val routesObserver = RoutesPreviewObserver {
38+
val navigationRoutes: List<NavigationRoute> = it.routesPreview?.routesList
39+
?: emptyList()
40+
_routesPreview.value = it.routesPreview
41+
trySend(navigationRoutes)
42+
}
43+
val observer = object : MapboxNavigationObserver {
44+
override fun onAttached(mapboxNavigation: MapboxNavigation) {
45+
mapboxNavigation.registerRoutesPreviewObserver(routesObserver)
46+
}
47+
48+
override fun onDetached(mapboxNavigation: MapboxNavigation) {
49+
mapboxNavigation.unregisterRoutesPreviewObserver(routesObserver)
50+
trySend(emptyList())
51+
}
52+
}
53+
registerObserver(observer)
54+
awaitClose {
55+
unregisterObserver(observer)
56+
}
57+
}
58+
}

libnavui-androidauto/src/main/java/com/mapbox/androidauto/screenmanager/MapboxScreenGraph.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package com.mapbox.androidauto.screenmanager
44

55
import com.mapbox.androidauto.MapboxCarContext
6+
import com.mapbox.androidauto.preview.RoutePreviewScreenFactory2
67
import com.mapbox.androidauto.screenmanager.MapboxScreen.ACTIVE_GUIDANCE
78
import com.mapbox.androidauto.screenmanager.MapboxScreen.ACTIVE_GUIDANCE_FEEDBACK
89
import com.mapbox.androidauto.screenmanager.MapboxScreen.ARRIVAL
@@ -31,6 +32,7 @@ import com.mapbox.androidauto.screenmanager.factories.RoutePreviewScreenFactory
3132
import com.mapbox.androidauto.screenmanager.factories.SearchPlacesFeedbackScreenFactory
3233
import com.mapbox.androidauto.screenmanager.factories.SearchPlacesScreenFactory
3334
import com.mapbox.androidauto.screenmanager.factories.SettingsScreenFactory
35+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
3436

3537
/**
3638
* This is a predefined application that is designed to collect feedback from drivers.
@@ -70,3 +72,8 @@ fun MapboxCarContext.prepareScreens() = apply {
7072
to ArrivalScreenFactory(mapboxCarContext)
7173
)
7274
}
75+
76+
@ExperimentalPreviewMapboxNavigationAPI
77+
fun MapboxCarContext.prepareExperimentalScreens() = apply {
78+
mapboxScreenManager[ROUTE_PREVIEW] = RoutePreviewScreenFactory2(this)
79+
}

qa-test-app/src/main/java/com/mapbox/navigation/qa_test_app/car/MainCarSession.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,29 @@ import com.mapbox.androidauto.map.logo.CarLogoSurfaceRenderer
1919
import com.mapbox.androidauto.notification.MapboxCarNotificationOptions
2020
import com.mapbox.androidauto.screenmanager.MapboxScreen
2121
import com.mapbox.androidauto.screenmanager.MapboxScreenManager
22+
import com.mapbox.androidauto.screenmanager.prepareExperimentalScreens
2223
import com.mapbox.androidauto.screenmanager.prepareScreens
2324
import com.mapbox.maps.MapInitOptions
2425
import com.mapbox.maps.MapboxExperimental
2526
import com.mapbox.maps.extension.androidauto.MapboxCarMap
27+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
2628
import com.mapbox.navigation.base.options.NavigationOptions
2729
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
2830
import com.mapbox.navigation.core.trip.session.TripSessionState
2931
import com.mapbox.navigation.qa_test_app.utils.Utils
3032
import kotlinx.coroutines.CoroutineScope
3133
import kotlinx.coroutines.launch
3234

33-
@OptIn(MapboxExperimental::class)
35+
@OptIn(MapboxExperimental::class, ExperimentalPreviewMapboxNavigationAPI::class)
3436
class MainCarSession : Session() {
3537

3638
private val carMapLoader = MainCarMapLoader()
3739
private val mapboxCarMap = MapboxCarMap()
3840
.registerObserver(carMapLoader)
41+
3942
private val mapboxCarContext = MapboxCarContext(lifecycle, mapboxCarMap)
4043
.prepareScreens()
44+
.prepareExperimentalScreens()
4145
.customize {
4246
notificationOptions = MapboxCarNotificationOptions.Builder()
4347
.startAppService(MainCarAppService::class.java)

0 commit comments

Comments
 (0)