|
| 1 | +package com.mapbox.androidauto.car.placeslistonmap |
| 2 | + |
| 3 | +import androidx.car.app.model.ItemList |
| 4 | +import com.mapbox.androidauto.car.search.PlaceRecord |
| 5 | +import com.mapbox.androidauto.internal.car.extensions.handleStyleOnAttached |
| 6 | +import com.mapbox.androidauto.internal.car.extensions.handleStyleOnDetached |
| 7 | +import com.mapbox.androidauto.internal.car.extensions.mapboxNavigationForward |
| 8 | +import com.mapbox.androidauto.internal.logAndroidAuto |
| 9 | +import com.mapbox.androidauto.navigation.location.CarAppLocation |
| 10 | +import com.mapbox.geojson.Feature |
| 11 | +import com.mapbox.geojson.FeatureCollection |
| 12 | +import com.mapbox.geojson.Point |
| 13 | +import com.mapbox.maps.MapboxExperimental |
| 14 | +import com.mapbox.maps.extension.androidauto.MapboxCarMapObserver |
| 15 | +import com.mapbox.maps.extension.androidauto.MapboxCarMapSurface |
| 16 | +import com.mapbox.maps.plugin.delegates.listeners.OnStyleLoadedListener |
| 17 | +import com.mapbox.navigation.core.MapboxNavigation |
| 18 | +import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp |
| 19 | +import kotlinx.coroutines.CoroutineScope |
| 20 | +import kotlinx.coroutines.Dispatchers |
| 21 | +import kotlinx.coroutines.MainScope |
| 22 | +import kotlinx.coroutines.cancel |
| 23 | +import kotlinx.coroutines.flow.MutableStateFlow |
| 24 | +import kotlinx.coroutines.flow.StateFlow |
| 25 | +import kotlinx.coroutines.flow.asStateFlow |
| 26 | +import kotlinx.coroutines.launch |
| 27 | +import kotlinx.coroutines.withContext |
| 28 | + |
| 29 | +@OptIn(MapboxExperimental::class) |
| 30 | +class PlacesListOnMapManager( |
| 31 | + private val placesListOnMapProvider: PlacesListOnMapProvider, |
| 32 | +) : MapboxCarMapObserver { |
| 33 | + |
| 34 | + private var carMapSurface: MapboxCarMapSurface? = null |
| 35 | + private var coroutineScope: CoroutineScope? = null |
| 36 | + private var styleLoadedListener: OnStyleLoadedListener? = null |
| 37 | + private var placesListItemMapper: PlacesListItemMapper? = null |
| 38 | + private val placesLayerUtil: PlacesListOnMapLayerUtil = PlacesListOnMapLayerUtil() |
| 39 | + private val navigationObserver = mapboxNavigationForward(this::onAttached) { onDetached() } |
| 40 | + |
| 41 | + private val _placeRecords = MutableStateFlow(listOf<PlaceRecord>()) |
| 42 | + val placeRecords: StateFlow<List<PlaceRecord>> = _placeRecords.asStateFlow() |
| 43 | + |
| 44 | + private val _placeSelected = MutableStateFlow<PlaceRecord?>(null) |
| 45 | + val placeSelected: StateFlow<PlaceRecord?> = _placeSelected.asStateFlow() |
| 46 | + |
| 47 | + private val placeClickListener = object : PlacesListItemClickListener { |
| 48 | + override fun onItemClick(placeRecord: PlaceRecord) { |
| 49 | + logAndroidAuto("PlacesListOnMapScreen request $placeRecord") |
| 50 | + _placeSelected.value = placeRecord |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + fun currentItemList(): ItemList? { |
| 55 | + val carAppLocation = MapboxNavigationApp.getObserver(CarAppLocation::class) |
| 56 | + val currentLocation = carAppLocation.navigationLocationProvider.lastLocation |
| 57 | + ?: return null |
| 58 | + return placesListItemMapper?.mapToItemList( |
| 59 | + currentLocation, |
| 60 | + placeRecords.value, |
| 61 | + placeClickListener |
| 62 | + ) |
| 63 | + } |
| 64 | + |
| 65 | + override fun onAttached(mapboxCarMapSurface: MapboxCarMapSurface) { |
| 66 | + super.onAttached(mapboxCarMapSurface) |
| 67 | + carMapSurface = mapboxCarMapSurface |
| 68 | + coroutineScope = MainScope() |
| 69 | + MapboxNavigationApp.registerObserver(navigationObserver) |
| 70 | + |
| 71 | + styleLoadedListener = mapboxCarMapSurface.handleStyleOnAttached { |
| 72 | + placesLayerUtil.initializePlacesListOnMapLayer( |
| 73 | + it, |
| 74 | + mapboxCarMapSurface.carContext.resources |
| 75 | + ) |
| 76 | + loadPlaceRecords() |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + override fun onDetached(mapboxCarMapSurface: MapboxCarMapSurface) { |
| 81 | + super.onDetached(mapboxCarMapSurface) |
| 82 | + mapboxCarMapSurface.handleStyleOnDetached(styleLoadedListener)?.let { |
| 83 | + placesLayerUtil.removePlacesListOnMapLayer(it) |
| 84 | + } |
| 85 | + styleLoadedListener = null |
| 86 | + MapboxNavigationApp.unregisterObserver(navigationObserver) |
| 87 | + carMapSurface = null |
| 88 | + } |
| 89 | + |
| 90 | + private fun onAttached(mapboxNavigation: MapboxNavigation) { |
| 91 | + placesListItemMapper = PlacesListItemMapper( |
| 92 | + PlaceMarkerRenderer(carMapSurface?.carContext!!), |
| 93 | + mapboxNavigation |
| 94 | + .navigationOptions |
| 95 | + .distanceFormatterOptions |
| 96 | + .unitType |
| 97 | + ) |
| 98 | + } |
| 99 | + |
| 100 | + private fun onDetached() { |
| 101 | + placesListItemMapper = null |
| 102 | + coroutineScope?.cancel() |
| 103 | + coroutineScope = null |
| 104 | + } |
| 105 | + |
| 106 | + private fun loadPlaceRecords() { |
| 107 | + coroutineScope?.launch { |
| 108 | + val expectedPlaceRecords = withContext(Dispatchers.IO) { |
| 109 | + placesListOnMapProvider.getPlaces() |
| 110 | + } |
| 111 | + _placeRecords.value = emptyList() |
| 112 | + expectedPlaceRecords.fold( |
| 113 | + { |
| 114 | + logAndroidAuto( |
| 115 | + "PlacesListOnMapScreen ${it.errorMessage}, ${it.throwable?.stackTrace}" |
| 116 | + ) |
| 117 | + }, |
| 118 | + { |
| 119 | + _placeRecords.value = it |
| 120 | + addPlaceIconsToMap(it) |
| 121 | + } |
| 122 | + ) |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + private fun addPlaceIconsToMap(places: List<PlaceRecord>) { |
| 127 | + logAndroidAuto("PlacesListOnMapScreen addPlaceIconsToMap with ${places.size} places.") |
| 128 | + carMapSurface?.mapSurface?.getMapboxMap()?.let { mapboxMap -> |
| 129 | + val features = places.filter { it.coordinate != null }.map { |
| 130 | + Feature.fromGeometry( |
| 131 | + Point.fromLngLat(it.coordinate!!.longitude(), it.coordinate.latitude()) |
| 132 | + ) |
| 133 | + } |
| 134 | + val featureCollection = FeatureCollection.fromFeatures(features) |
| 135 | + mapboxMap.getStyle()?.let { |
| 136 | + placesLayerUtil.updatePlacesListOnMapLayer(it, featureCollection) |
| 137 | + } |
| 138 | + } |
| 139 | + } |
| 140 | +} |
0 commit comments