|
| 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.Lifecycle |
| 15 | +import androidx.lifecycle.LifecycleOwner |
| 16 | +import androidx.lifecycle.lifecycleScope |
| 17 | +import androidx.lifecycle.repeatOnLifecycle |
| 18 | +import com.mapbox.androidauto.MapboxCarContext |
| 19 | +import com.mapbox.androidauto.R |
| 20 | +import com.mapbox.androidauto.feedback.ui.CarFeedbackAction |
| 21 | +import com.mapbox.androidauto.internal.extensions.addBackPressedHandler |
| 22 | +import com.mapbox.androidauto.internal.extensions.handleStyleOnAttached |
| 23 | +import com.mapbox.androidauto.internal.extensions.handleStyleOnDetached |
| 24 | +import com.mapbox.androidauto.internal.logAndroidAuto |
| 25 | +import com.mapbox.androidauto.location.CarLocationRenderer |
| 26 | +import com.mapbox.androidauto.navigation.CarCameraMode |
| 27 | +import com.mapbox.androidauto.navigation.CarDistanceFormatter |
| 28 | +import com.mapbox.androidauto.navigation.CarNavigationCamera |
| 29 | +import com.mapbox.androidauto.navigation.audioguidance.muteAudioGuidance |
| 30 | +import com.mapbox.androidauto.navigation.speedlimit.CarSpeedLimitRenderer |
| 31 | +import com.mapbox.androidauto.placeslistonmap.PlacesListOnMapLayerUtil |
| 32 | +import com.mapbox.androidauto.routes.NavigationCarRoutesProvider2 |
| 33 | +import com.mapbox.androidauto.screenmanager.MapboxScreen |
| 34 | +import com.mapbox.androidauto.screenmanager.MapboxScreenManager |
| 35 | +import com.mapbox.androidauto.search.PlaceRecord |
| 36 | +import com.mapbox.geojson.Feature |
| 37 | +import com.mapbox.geojson.FeatureCollection |
| 38 | +import com.mapbox.maps.MapboxExperimental |
| 39 | +import com.mapbox.maps.extension.androidauto.MapboxCarMapObserver |
| 40 | +import com.mapbox.maps.extension.androidauto.MapboxCarMapSurface |
| 41 | +import com.mapbox.maps.plugin.delegates.listeners.OnStyleLoadedListener |
| 42 | +import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI |
| 43 | +import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp |
| 44 | +import kotlinx.coroutines.flow.collect |
| 45 | +import kotlinx.coroutines.launch |
| 46 | + |
| 47 | +/** |
| 48 | + * This will replace [CarRoutePreviewScreen] |
| 49 | + */ |
| 50 | +@OptIn(MapboxExperimental::class, ExperimentalPreviewMapboxNavigationAPI::class) |
| 51 | +internal class CarRoutePreviewScreen2 @UiThread constructor( |
| 52 | + private val mapboxCarContext: MapboxCarContext, |
| 53 | + private val placeRecord: PlaceRecord, |
| 54 | + private val placesLayerUtil: PlacesListOnMapLayerUtil = PlacesListOnMapLayerUtil(), |
| 55 | +) : Screen(mapboxCarContext.carContext) { |
| 56 | + |
| 57 | + private val routesProvider = NavigationCarRoutesProvider2() |
| 58 | + private val carRouteLine = CarRouteLine(routesProvider) |
| 59 | + private val carLocationRenderer = CarLocationRenderer() |
| 60 | + private val carSpeedLimitRenderer = CarSpeedLimitRenderer(mapboxCarContext) |
| 61 | + private val carNavigationCamera = CarNavigationCamera( |
| 62 | + initialCarCameraMode = CarCameraMode.OVERVIEW, |
| 63 | + alternativeCarCameraMode = CarCameraMode.FOLLOWING, |
| 64 | + carRoutesProvider = routesProvider, |
| 65 | + ) |
| 66 | + |
| 67 | + private var styleLoadedListener: OnStyleLoadedListener? = null |
| 68 | + |
| 69 | + private val surfaceListener = object : MapboxCarMapObserver { |
| 70 | + |
| 71 | + override fun onAttached(mapboxCarMapSurface: MapboxCarMapSurface) { |
| 72 | + super.onAttached(mapboxCarMapSurface) |
| 73 | + logAndroidAuto("CarRoutePreviewScreen loaded") |
| 74 | + styleLoadedListener = mapboxCarMapSurface.handleStyleOnAttached { style -> |
| 75 | + placesLayerUtil.initializePlacesListOnMapLayer( |
| 76 | + style, |
| 77 | + carContext.resources |
| 78 | + ) |
| 79 | + val coordinate = placeRecord.coordinate ?: return@handleStyleOnAttached |
| 80 | + val featureCollection = |
| 81 | + FeatureCollection.fromFeature(Feature.fromGeometry(coordinate)) |
| 82 | + placesLayerUtil.updatePlacesListOnMapLayer( |
| 83 | + style, |
| 84 | + featureCollection |
| 85 | + ) |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + override fun onDetached(mapboxCarMapSurface: MapboxCarMapSurface) { |
| 90 | + super.onDetached(mapboxCarMapSurface) |
| 91 | + logAndroidAuto("CarRoutePreviewScreen detached") |
| 92 | + mapboxCarMapSurface.handleStyleOnDetached(styleLoadedListener)?.let { |
| 93 | + placesLayerUtil.removePlacesListOnMapLayer(it) |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + init { |
| 99 | + logAndroidAuto("CarRoutePreviewScreen constructor") |
| 100 | + addBackPressedHandler { |
| 101 | + logAndroidAuto("CarRoutePreviewScreen onBackPressed") |
| 102 | + mapboxCarContext.mapboxScreenManager.goBack() |
| 103 | + } |
| 104 | + lifecycleScope.launch { |
| 105 | + lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { |
| 106 | + routesProvider.routesPreview.collect { |
| 107 | + invalidate() |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + lifecycle.muteAudioGuidance() |
| 112 | + lifecycle.addObserver(object : DefaultLifecycleObserver { |
| 113 | + |
| 114 | + override fun onResume(owner: LifecycleOwner) { |
| 115 | + logAndroidAuto("CarRoutePreviewScreen onResume") |
| 116 | + mapboxCarContext.mapboxCarMap.registerObserver(carLocationRenderer) |
| 117 | + mapboxCarContext.mapboxCarMap.registerObserver(carSpeedLimitRenderer) |
| 118 | + mapboxCarContext.mapboxCarMap.registerObserver(carNavigationCamera) |
| 119 | + mapboxCarContext.mapboxCarMap.registerObserver(carRouteLine) |
| 120 | + mapboxCarContext.mapboxCarMap.registerObserver(surfaceListener) |
| 121 | + } |
| 122 | + |
| 123 | + override fun onPause(owner: LifecycleOwner) { |
| 124 | + logAndroidAuto("CarRoutePreviewScreen onPause") |
| 125 | + mapboxCarContext.mapboxCarMap.unregisterObserver(carLocationRenderer) |
| 126 | + mapboxCarContext.mapboxCarMap.unregisterObserver(carSpeedLimitRenderer) |
| 127 | + mapboxCarContext.mapboxCarMap.unregisterObserver(carNavigationCamera) |
| 128 | + mapboxCarContext.mapboxCarMap.unregisterObserver(carRouteLine) |
| 129 | + mapboxCarContext.mapboxCarMap.unregisterObserver(surfaceListener) |
| 130 | + } |
| 131 | + }) |
| 132 | + } |
| 133 | + |
| 134 | + override fun onGetTemplate(): Template { |
| 135 | + val listBuilder = ItemList.Builder() |
| 136 | + val routesPreview = routesProvider.routesPreview.value |
| 137 | + val navigationRoutes = routesPreview?.originalRoutesList |
| 138 | + ?: emptyList() |
| 139 | + navigationRoutes.forEach { navigationRoute -> |
| 140 | + val route = navigationRoute.directionsRoute |
| 141 | + val title = route.legs()?.first()?.summary() ?: placeRecord.name |
| 142 | + val duration = CarDistanceFormatter.formatDistance(route.duration()) |
| 143 | + val routeSpannableString = SpannableString("$duration $title") |
| 144 | + routeSpannableString.setSpan( |
| 145 | + DurationSpan.create(route.duration().toLong()), |
| 146 | + 0, |
| 147 | + duration.length, |
| 148 | + 0 |
| 149 | + ) |
| 150 | + |
| 151 | + listBuilder.addItem( |
| 152 | + Row.Builder() |
| 153 | + .setTitle(routeSpannableString) |
| 154 | + .addText(duration) |
| 155 | + .build() |
| 156 | + ) |
| 157 | + } |
| 158 | + if (routesPreview != null && navigationRoutes.isNotEmpty()) { |
| 159 | + listBuilder.setSelectedIndex(routesPreview.primaryRouteIndex) |
| 160 | + listBuilder.setOnSelectedListener { index -> |
| 161 | + MapboxNavigationApp.current()?.setRoutesPreview( |
| 162 | + routesPreview.originalRoutesList, |
| 163 | + index |
| 164 | + ) |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + return RoutePreviewNavigationTemplate.Builder() |
| 169 | + .setItemList(listBuilder.build()) |
| 170 | + .setTitle(carContext.getString(R.string.car_action_preview_title)) |
| 171 | + .setActionStrip( |
| 172 | + ActionStrip.Builder() |
| 173 | + .addAction( |
| 174 | + CarFeedbackAction( |
| 175 | + MapboxScreen.ROUTE_PREVIEW_FEEDBACK |
| 176 | + ).getAction(this@CarRoutePreviewScreen2) |
| 177 | + ) |
| 178 | + .build() |
| 179 | + ) |
| 180 | + .setHeaderAction(Action.BACK) |
| 181 | + .setNavigateAction( |
| 182 | + Action.Builder() |
| 183 | + .setTitle(carContext.getString(R.string.car_action_preview_navigate_button)) |
| 184 | + .setOnClickListener { |
| 185 | + MapboxNavigationApp.current()!!.setNavigationRoutes( |
| 186 | + routesPreview!!.routesList |
| 187 | + ) |
| 188 | + MapboxScreenManager.replaceTop(MapboxScreen.ACTIVE_GUIDANCE) |
| 189 | + } |
| 190 | + .build(), |
| 191 | + ) |
| 192 | + .build() |
| 193 | + } |
| 194 | +} |
0 commit comments