Stopover and Legs
This commit is contained in:
@@ -5,7 +5,6 @@ import android.location.LocationManager
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants.homeHohenwaldeck
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||
|
||||
@@ -182,6 +182,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
init {
|
||||
lifecycle.addObserver(lifecycleObserver)
|
||||
repository.routingEngineFlow.asLiveData().observe(this, Observer {
|
||||
onRoutingEngineStateUpdated(it)
|
||||
routingEngine = it
|
||||
})
|
||||
|
||||
@@ -199,13 +200,15 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
* Creates appropriate repository based on user selection.
|
||||
*/
|
||||
fun onRoutingEngineStateUpdated(routeEngine: Int) {
|
||||
navigationViewModel = when (routeEngine) {
|
||||
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||
else -> NavigationViewModel(TomTomRepository())
|
||||
if (!::navigationViewModel.isInitialized || routeEngine != routingEngine) {
|
||||
navigationViewModel = when (routeEngine) {
|
||||
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||
else -> NavigationViewModel(TomTomRepository())
|
||||
}
|
||||
observerManager = NavigationObserverManager(navigationViewModel, this)
|
||||
observerManager.attachAllObservers(this)
|
||||
}
|
||||
observerManager = NavigationObserverManager(navigationViewModel, this)
|
||||
observerManager.attachAllObservers(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -578,6 +581,8 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
* Called when user starts navigation
|
||||
*/
|
||||
override fun startNavigation() {
|
||||
Log.d(TAG, "startNavigation")
|
||||
surfaceRenderer.navigation = true
|
||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||
navigationManager.navigationStarted()
|
||||
navigationManagerStarted = true
|
||||
@@ -597,6 +602,8 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
* Called when user exits navigation or arrives at destination.
|
||||
*/
|
||||
override fun stopNavigation() {
|
||||
Log.d(TAG, "stopNavigation")
|
||||
surfaceRenderer.navigation = false
|
||||
routeModel.stopNavigation()
|
||||
navigationManager.navigationEnded()
|
||||
if (autoDriveEnabled) {
|
||||
@@ -609,6 +616,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
navigationScreen.navigationType = NavigationType.VIEW
|
||||
if (notificationActive)
|
||||
notificationManager.stopNotificationService()
|
||||
Log.d(TAG, "end stopNavigation")
|
||||
}
|
||||
|
||||
override fun updateTrip(trip: Trip) {
|
||||
@@ -723,9 +731,20 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
* Loads a route to the specified place and sets it as the destination.
|
||||
*/
|
||||
override fun navigateToPlace(place: Place) {
|
||||
Log.d(TAG, "navigateToPlace ${place.street}")
|
||||
var prevDestination = Place()
|
||||
if (surfaceRenderer.navigation) {
|
||||
prevDestination = routeModel.navState.destination
|
||||
stopNavigation()
|
||||
}
|
||||
val preview = place.route
|
||||
navigationViewModel.previewRoute.value = ""
|
||||
val location = location(place.longitude, place.latitude)
|
||||
val location = if (place.stopOver && prevDestination.latitude != 0.0) {
|
||||
listOf(location(place.longitude, place.latitude), location(prevDestination.longitude, prevDestination.latitude))
|
||||
} else {
|
||||
listOf(location(place.longitude, place.latitude))
|
||||
}
|
||||
|
||||
navigationViewModel.saveRecent(carContext, place)
|
||||
routeModel.navState = routeModel.navState.copy(destination = place)
|
||||
if (preview.isEmpty()) {
|
||||
@@ -813,7 +832,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
location,
|
||||
destination,
|
||||
listOf(destination),
|
||||
surfaceRenderer.carOrientation
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,17 +10,12 @@ import androidx.car.app.AppManager
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.SurfaceCallback
|
||||
import androidx.car.app.SurfaceContainer
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -54,7 +49,6 @@ import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
|
||||
|
||||
@@ -123,6 +117,9 @@ class SurfaceRenderer(
|
||||
// Current view mode (navigation, preview, etc.)
|
||||
var viewStyle = ViewStyle.VIEW
|
||||
|
||||
// Flag to indicate if in navigation mode
|
||||
var navigation = false
|
||||
|
||||
// Center location for route preview
|
||||
lateinit var centerLocation: Location
|
||||
|
||||
@@ -457,7 +454,7 @@ class SurfaceRenderer(
|
||||
with(routeModel) {
|
||||
routeData.value = curRoute.routeGeoJson
|
||||
centerLocation = curRoute.centerLocation
|
||||
previewDistance = curRoute.summary.distance
|
||||
previewDistance = curLeg.summary.distance
|
||||
}
|
||||
tilt = 0.0
|
||||
updateCameraPosition(
|
||||
@@ -473,8 +470,10 @@ class SurfaceRenderer(
|
||||
* Calculates appropriate zoom
|
||||
*/
|
||||
fun setStandardView() {
|
||||
if (!navigation) {
|
||||
setRouteData("")
|
||||
}
|
||||
viewStyle = ViewStyle.VIEW
|
||||
setRouteData("")
|
||||
val zoom = calculateZoom(0.0)
|
||||
tilt = calculateTilt(zoom, tilt)
|
||||
updateCameraPosition(
|
||||
|
||||
@@ -367,7 +367,7 @@ fun NavigationImage(
|
||||
x = topLeftX ,
|
||||
y = topLeftY,
|
||||
),
|
||||
color = if (darkMode) navigationColor else Color.White,
|
||||
color = if (darkMode) NavigationColorLight else Color.White,
|
||||
cornerRadius = CornerRadius(x = 10f, y = 10f),
|
||||
)
|
||||
drawText(
|
||||
|
||||
@@ -20,14 +20,13 @@ import androidx.car.app.model.ForegroundCarColorSpan
|
||||
import androidx.car.app.navigation.model.Lane
|
||||
import androidx.car.app.navigation.model.LaneDirection
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
|
||||
import androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
|
||||
import androidx.car.app.navigation.model.Step
|
||||
import androidx.car.app.navigation.model.TravelEstimate
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.screen.createCarIcon
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.data.route.ManeuverType
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.formattedDistance
|
||||
import java.time.Duration
|
||||
@@ -47,17 +46,15 @@ class RouteCarModel : RouteModel() {
|
||||
|
||||
val maneuver = Maneuver.Builder(stepData.currentManeuverType)
|
||||
.setIcon(createCarIcon(carContext, stepData.icon))
|
||||
if (stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
|
||||
|| stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
|
||||
if (stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW.ordinal
|
||||
) {
|
||||
maneuver.setRoundaboutExitNumber(stepData.exitNumber)
|
||||
}
|
||||
val step =
|
||||
Step.Builder(currentStepCueWithImage)
|
||||
|
||||
if (navState.destination.street != null) {
|
||||
step.setRoad(navState.destination.street!!)
|
||||
}
|
||||
step.setRoad(navState.destination.street!!)
|
||||
if (stepData.lane.isNotEmpty()) {
|
||||
val lanesAdded = addLanes(carContext, step, stepData)
|
||||
if (lanesAdded) {
|
||||
@@ -77,8 +74,8 @@ class RouteCarModel : RouteModel() {
|
||||
createString(stepData.instruction)
|
||||
val maneuver = Maneuver.Builder(stepData.currentManeuverType)
|
||||
.setIcon(createCarIcon(carContext, stepData.icon))
|
||||
if (stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW
|
||||
|| stepData.currentManeuverType == TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW
|
||||
if (stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW.ordinal
|
||||
) {
|
||||
maneuver.setRoundaboutExitNumber(stepData.exitNumber)
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class Simulation {
|
||||
) {
|
||||
if (routeModel.navState.route.isRouteValid()) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
gpxSimulation(routeModel, lifecycleScope, updateLocation)
|
||||
//currentSimulation(routeModel, lifecycleScope, updateLocation)
|
||||
//gpxSimulation(routeModel, lifecycleScope, updateLocation)
|
||||
currentSimulation(routeModel, lifecycleScope, updateLocation)
|
||||
} else {
|
||||
currentSimulation(routeModel, lifecycleScope, updateLocation)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class CategoryScreen(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
currentLocation = surfaceRenderer.lastLocation,
|
||||
location(it.lon, it.lat),
|
||||
listOf(location(it.lon, it.lat)),
|
||||
surfaceRenderer.carOrientation
|
||||
)
|
||||
setResult(
|
||||
@@ -197,7 +197,7 @@ class CategoryScreen(
|
||||
@DrawableRes iconRes: Int,
|
||||
scale: Int
|
||||
): Action {
|
||||
return createAction(carContext, iconRes, FLAG_IS_PERSISTENT, {
|
||||
return createAction(carContext, iconRes, FLAG_IS_PERSISTENT, {
|
||||
surfaceRenderer.handleScale(scale)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,18 +117,18 @@ open class NavigationScreen(
|
||||
)
|
||||
}, { settingsAction() })
|
||||
return when (navigationType) {
|
||||
NavigationType.NAVIGATION -> navigationTemplate(actionStripBuilder)
|
||||
NavigationType.RECENT -> navigationRecentPlacesTemplate()
|
||||
NavigationType.REROUTE -> navigationRerouteTemplate(actionStripBuilder)
|
||||
NavigationType.ARRIVAL -> navigationEndTemplate(actionStripBuilder)
|
||||
else -> navigationViewTemplate(actionStripBuilder)
|
||||
NavigationType.NAVIGATION -> navigation(actionStripBuilder)
|
||||
NavigationType.RECENT -> navigationRecentPlaces()
|
||||
NavigationType.REROUTE -> navigationReroute(actionStripBuilder)
|
||||
NavigationType.ARRIVAL -> navigationEnd(actionStripBuilder)
|
||||
else -> navigationView(actionStripBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a NavigationTemplate for the active navigation state.
|
||||
*/
|
||||
private fun navigationTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
private fun navigation(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
actionStripBuilder.addAction(
|
||||
createAction(
|
||||
carContext,
|
||||
@@ -150,7 +150,7 @@ open class NavigationScreen(
|
||||
carContext = carContext, R.drawable.ic_pan_24,
|
||||
0,
|
||||
onClickAction = {
|
||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||
surfaceRenderer.setStandardView()
|
||||
invalidate()
|
||||
}
|
||||
)
|
||||
@@ -163,7 +163,7 @@ open class NavigationScreen(
|
||||
/**
|
||||
* Creates and returns a template for the default view state.
|
||||
*/
|
||||
private fun navigationViewTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
private fun navigationView(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
return NavigationTemplate.Builder()
|
||||
.setBackgroundColor(backGroundColor)
|
||||
.setActionStrip(actionStripBuilder.build())
|
||||
@@ -187,24 +187,24 @@ open class NavigationScreen(
|
||||
/**
|
||||
* Creates and returns a template for the arrival.
|
||||
*/
|
||||
private fun navigationEndTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
private fun navigationEnd(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
arrivalTimer?.cancel()
|
||||
arrivalTimer = object : CountDownTimer(8000, 1000) {
|
||||
override fun onTick(millisUntilFinished: Long) {}
|
||||
override fun onFinish() {
|
||||
// routeModel.navState = routeModel.navState.copy(arrived = false)
|
||||
// routeModel.navState = routeModel.navState.copy(arrived = false)
|
||||
navigationType = NavigationType.VIEW
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
arrivalTimer?.start()
|
||||
return navigationArrivedTemplate(actionStripBuilder)
|
||||
return navigationArrived(actionStripBuilder)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a NavigationTemplate specifically for when the destination is reached.
|
||||
*/
|
||||
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
||||
fun navigationArrived(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
||||
var street = ""
|
||||
if (destinations.first().address != null) {
|
||||
street = destinations.first().address.toString()
|
||||
@@ -226,7 +226,7 @@ open class NavigationScreen(
|
||||
)
|
||||
.build()
|
||||
)
|
||||
// .setBackgroundColor(routeModel.backGroundColor())
|
||||
.setBackgroundColor(backGroundColor)
|
||||
.setActionStrip(actionStripBuilder.build())
|
||||
.setMapActionStrip(
|
||||
mapActionStrip(
|
||||
@@ -247,10 +247,10 @@ open class NavigationScreen(
|
||||
/**
|
||||
* Creates and returns a template showing recent places or destinations.
|
||||
*/
|
||||
fun navigationRecentPlacesTemplate(): Template {
|
||||
fun navigationRecentPlaces(): Template {
|
||||
if (!tripSuggestion || recentPlaces.isEmpty()) {
|
||||
navigationType = NavigationType.VIEW
|
||||
return navigationViewTemplate(
|
||||
return navigationView(
|
||||
createActionStripBuilder(
|
||||
{
|
||||
createAction(
|
||||
@@ -263,7 +263,7 @@ open class NavigationScreen(
|
||||
)
|
||||
}
|
||||
val listBuilder = ItemList.Builder()
|
||||
recentPlaces.filter { it.category == Constants.RECENT && it.distance > 300F }.forEach {
|
||||
recentPlaces.filter { it.category == Constants.RECENT && it.distance > 300F }.forEach {
|
||||
val row = Row.Builder()
|
||||
.setTitle(it.name)
|
||||
.addAction(
|
||||
@@ -316,7 +316,7 @@ open class NavigationScreen(
|
||||
/**
|
||||
* Creates and returns a template for when the route is being recalculated.
|
||||
*/
|
||||
fun navigationRerouteTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
fun navigationReroute(actionStripBuilder: ActionStrip.Builder): Template {
|
||||
return NavigationTemplate.Builder()
|
||||
.setNavigationInfo(RoutingInfo.Builder().setLoading(true).build())
|
||||
.setActionStrip(actionStripBuilder.build())
|
||||
@@ -486,7 +486,7 @@ open class NavigationScreen(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
destination,
|
||||
listOf(destination),
|
||||
surfaceRenderer.carOrientation
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.CarToast
|
||||
import androidx.car.app.OnScreenResultListener
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.CarIcon
|
||||
@@ -28,7 +27,6 @@ import com.kouros.navigation.data.Constants.CONTACTS
|
||||
import com.kouros.navigation.data.Constants.FAVORITES
|
||||
import com.kouros.navigation.data.Constants.RECENT
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.ViewStyle
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
|
||||
@@ -66,41 +64,12 @@ class PlaceListScreen(
|
||||
val itemListBuilder = ItemList.Builder()
|
||||
.setNoItemsMessage(carContext.getString(R.string.no_places))
|
||||
recentPlaces.filter { it.category == category }.forEach {
|
||||
val street = if (it.street != null) {
|
||||
it.street
|
||||
} else {
|
||||
""
|
||||
}
|
||||
val street = it.street
|
||||
val row = Row.Builder()
|
||||
.setImage(contactIcon(null, it.category))
|
||||
.setTitle("$street ${it.city}")
|
||||
.setOnClickListener {
|
||||
place = Place(
|
||||
0,
|
||||
it.name,
|
||||
it.category,
|
||||
it.latitude,
|
||||
it.longitude,
|
||||
it.postalCode,
|
||||
it.city,
|
||||
it.street,
|
||||
// avatar = null
|
||||
)
|
||||
screenManager
|
||||
.pushForResult(
|
||||
RoutePreviewScreen(
|
||||
carContext,
|
||||
RoutePreviewType.MULTI_ROUTE,
|
||||
surfaceRenderer,
|
||||
place,
|
||||
navigationViewModel,
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
setResult(obj)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
clickOnPlace(it)
|
||||
}
|
||||
if (category != CONTACTS) {
|
||||
row.addText(SpannableString(" ").apply {
|
||||
@@ -138,6 +107,69 @@ class PlaceListScreen(
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Action to navigate to a specific place.
|
||||
*/
|
||||
private fun clickOnPlace(it: Place) {
|
||||
place = Place(
|
||||
0,
|
||||
it.name,
|
||||
it.category,
|
||||
it.latitude,
|
||||
it.longitude,
|
||||
it.postalCode,
|
||||
it.city,
|
||||
it.street,
|
||||
// avatar = null
|
||||
)
|
||||
if (surfaceRenderer.navigation) {
|
||||
startStopOverScreen(place)
|
||||
} else {
|
||||
starPreviewScreen(place)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts preview screen for a specific place.
|
||||
*/
|
||||
private fun starPreviewScreen(place: Place) {
|
||||
screenManager
|
||||
.pushForResult(
|
||||
RoutePreviewScreen(
|
||||
carContext,
|
||||
RoutePreviewType.MULTI_ROUTE,
|
||||
surfaceRenderer,
|
||||
place,
|
||||
navigationViewModel,
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
setResult(obj)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts preview screen for a specific place.
|
||||
*/
|
||||
private fun startStopOverScreen(place: Place) {
|
||||
screenManager
|
||||
.pushForResult(
|
||||
StopOverScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
navigationViewModel,
|
||||
place,
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
setResult(obj)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Action to delete a place.
|
||||
*/
|
||||
|
||||
@@ -71,6 +71,8 @@ class RoutePreviewScreen(
|
||||
|
||||
var loading = true
|
||||
|
||||
var showAlternativeRoute = true
|
||||
|
||||
private val backPressedCallback = object : OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
}
|
||||
@@ -82,6 +84,10 @@ class RoutePreviewScreen(
|
||||
routeModel.startNavigation(route)
|
||||
surfaceRenderer.setPreviewRouteData(routeModel)
|
||||
loading = false
|
||||
if (routeModel.route.routes.size == 1) {
|
||||
routeType = RoutePreviewType.SINGLE_ROUTE
|
||||
showAlternativeRoute = false
|
||||
}
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
@@ -139,15 +145,12 @@ class RoutePreviewScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val street = if (destination.street.isEmpty()) {
|
||||
val street = destination.street.ifEmpty {
|
||||
carContext.getString((R.string.route_preview))
|
||||
} else {
|
||||
destination.street
|
||||
}
|
||||
val header = Header.Builder()
|
||||
.setStartHeaderAction(Action.BACK)
|
||||
.setTitle(street)
|
||||
|
||||
if (routeType == RoutePreviewType.SINGLE_ROUTE) {
|
||||
header.addEndHeaderAction(
|
||||
favoriteAction()
|
||||
@@ -174,16 +177,6 @@ class RoutePreviewScreen(
|
||||
.build()
|
||||
listContent.build()
|
||||
} else {
|
||||
val navigateActionIcon: CarIcon = CarIcon.Builder(
|
||||
IconCompat.createWithResource(
|
||||
carContext, R.drawable.navigation_48px
|
||||
)
|
||||
).build()
|
||||
val selectRouteIcon: CarIcon = CarIcon.Builder(
|
||||
IconCompat.createWithResource(
|
||||
carContext, R.drawable.alt_route_48px
|
||||
)
|
||||
).build()
|
||||
val navigateAction =
|
||||
createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT,{
|
||||
onNavigate(routeModel.navState.currentRouteIndex)
|
||||
@@ -195,7 +188,10 @@ class RoutePreviewScreen(
|
||||
val listContent = MessageTemplate.Builder(message)
|
||||
.setHeader(header.build())
|
||||
.addAction(navigateAction)
|
||||
.addAction(selectRouteAction)
|
||||
|
||||
if (showAlternativeRoute) {
|
||||
listContent.addAction(selectRouteAction)
|
||||
}
|
||||
if (loading) {
|
||||
listContent.setLoading(true)
|
||||
}
|
||||
@@ -211,7 +207,7 @@ class RoutePreviewScreen(
|
||||
} )).build()
|
||||
|
||||
)
|
||||
if (routeType == RoutePreviewType.MULTI_ROUTE && !routeSelected) {
|
||||
if (routeModel.route.routes.size > 1 && routeType == RoutePreviewType.MULTI_ROUTE && !routeSelected) {
|
||||
template.setActionStrip(createActionStrip {
|
||||
createAction(
|
||||
carContext, R.drawable.navigation_48px,
|
||||
@@ -301,7 +297,7 @@ class RoutePreviewScreen(
|
||||
}
|
||||
|
||||
private fun createRow(route: Routes, index: Int): Row {
|
||||
val navigateAction = createAction(carContext, R.drawable.navigation_48px ) {
|
||||
val navigateAction = createAction(carContext, R.drawable.navigation_48px, FLAG_DEFAULT ) {
|
||||
this.onNavigate(index)
|
||||
}
|
||||
val routeText = createRouteText(route)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||
import androidx.car.app.model.Action.FLAG_IS_PERSISTENT
|
||||
import androidx.car.app.model.CarText
|
||||
import androidx.car.app.model.Header
|
||||
import androidx.car.app.model.MessageTemplate
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.car.app.navigation.model.MapController
|
||||
import androidx.car.app.navigation.model.MapWithContentTemplate
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.ViewStyle
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
|
||||
class StopOverScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val navigationViewModel: NavigationViewModel,
|
||||
private val place: Place,
|
||||
) : Screen(carContext) {
|
||||
override fun onGetTemplate(): MapWithContentTemplate {
|
||||
val cancelAction =
|
||||
createAction(carContext, R.drawable.ic_close_white_24dp, FLAG_IS_PERSISTENT,{
|
||||
finish()
|
||||
})
|
||||
val header = Header.Builder()
|
||||
.setStartHeaderAction(Action.BACK)
|
||||
.addEndHeaderAction(cancelAction)
|
||||
.setTitle(place.street)
|
||||
|
||||
val message = CarText.Builder("Neue Fahrt oder Zwischenstopp einfügen")
|
||||
.build()
|
||||
|
||||
val navigateAction = Action.Builder()
|
||||
.setIcon(createCarIcon(carContext, R.drawable.navigation_48px))
|
||||
.setFlags(FLAG_DEFAULT)
|
||||
.setOnClickListener {
|
||||
setResult(place)
|
||||
finish()
|
||||
}
|
||||
.build()
|
||||
|
||||
val selectRouteAction = Action.Builder()
|
||||
.setIcon(createCarIcon(carContext, R.drawable.alt_route_48px))
|
||||
.setFlags(FLAG_IS_PERSISTENT)
|
||||
.setOnClickListener {
|
||||
place.stopOver = true
|
||||
setResult(place)
|
||||
finish()
|
||||
}
|
||||
.build()
|
||||
|
||||
val listContent = MessageTemplate.Builder(message)
|
||||
.setHeader(header.build())
|
||||
.addAction(navigateAction)
|
||||
.addAction(selectRouteAction)
|
||||
|
||||
val template = MapWithContentTemplate.Builder()
|
||||
.setContentTemplate(listContent.build())
|
||||
|
||||
return template.build()
|
||||
}
|
||||
}
|
||||
+21
@@ -33,6 +33,8 @@ class NavigationSettings(
|
||||
|
||||
private var carLocationToggleState = false
|
||||
|
||||
private var alternativeRoutesToggleState = false
|
||||
|
||||
val settingsViewModel = getSettingsViewModel(carContext)
|
||||
|
||||
init {
|
||||
@@ -41,6 +43,7 @@ class NavigationSettings(
|
||||
settingsViewModel.avoidMotorway.first()
|
||||
settingsViewModel.avoidFerry.first()
|
||||
settingsViewModel.carLocation.first()
|
||||
settingsViewModel.alternativeRoutes.first()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +52,7 @@ class NavigationSettings(
|
||||
tollWayToggleState = settingsViewModel.avoidTollway.value
|
||||
ferryToggleState = settingsViewModel.avoidFerry.value
|
||||
carLocationToggleState = settingsViewModel.carLocation.value
|
||||
alternativeRoutesToggleState = settingsViewModel.alternativeRoutes.value
|
||||
|
||||
val listBuilder = ItemList.Builder()
|
||||
|
||||
@@ -89,6 +93,14 @@ class NavigationSettings(
|
||||
carLocationToggleState = !carLocationToggleState
|
||||
}.setChecked(carLocationToggleState).build()
|
||||
|
||||
// Alternative routes
|
||||
val alternativeRoutesToggle: Toggle =
|
||||
Toggle.Builder { checked: Boolean ->
|
||||
settingsViewModel.onAlternativeRoutes(checked)
|
||||
alternativeRoutesToggleState = !alternativeRoutesToggleState
|
||||
}.setChecked(alternativeRoutesToggleState).build()
|
||||
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForTemplate(
|
||||
R.string.use_car_location,
|
||||
@@ -97,6 +109,14 @@ class NavigationSettings(
|
||||
)
|
||||
)
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForTemplate(
|
||||
R.string.alternative_routes,
|
||||
alternativeRoutesToggle,
|
||||
createCarIcon(carContext,R.drawable.alt_route_48px)
|
||||
)
|
||||
)
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForScreenTemplate(
|
||||
RoutingSettings(carContext, navigationViewModel),
|
||||
@@ -109,6 +129,7 @@ class NavigationSettings(
|
||||
R.string.tomtom_api_key
|
||||
)
|
||||
)
|
||||
|
||||
return ListTemplate.Builder()
|
||||
.setSingleList(listBuilder.build())
|
||||
.setHeader(
|
||||
|
||||
@@ -2,11 +2,11 @@ package com.kouros.navigation.data
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val NavigationColorLight = Color(0xFF066462)
|
||||
val NavigationColorLight = Color(0xFF17A119)
|
||||
|
||||
val NavigationColorDark = Color(0xFF10DED9)
|
||||
val NavigationColorDark = Color(0xFF4EDE10)
|
||||
|
||||
val RouteColor = Color(0xFF7B06E1)
|
||||
val RouteColor = Color(0xFF05692D)
|
||||
|
||||
val SpeedColor = Color(0xFF262525)
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ data class Place(
|
||||
var lastDate: Long = 0,
|
||||
var routeIndex: Int = 0,
|
||||
var route: String = "",
|
||||
var stopOver: Boolean = false,
|
||||
)
|
||||
|
||||
data class ContactData(
|
||||
|
||||
@@ -20,7 +20,7 @@ abstract class NavigationRepository {
|
||||
abstract fun getRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
destination: Location,
|
||||
location: List<Location>,
|
||||
carOrientation: Float,
|
||||
searchFilter: SearchFilter
|
||||
): String
|
||||
|
||||
@@ -58,6 +58,8 @@ class DataStoreManager(private val context: Context) {
|
||||
|
||||
val ENGINE_TYPE = intPreferencesKey("EngineType")
|
||||
|
||||
val ALTERNATIVE_ROUTES = booleanPreferencesKey("AlternativeRoutes")
|
||||
|
||||
}
|
||||
|
||||
// Read values
|
||||
@@ -145,6 +147,11 @@ class DataStoreManager(private val context: Context) {
|
||||
?: EngineType.COMBUSTION.ordinal
|
||||
}
|
||||
|
||||
val alternativeRoutesFlow: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.ALTERNATIVE_ROUTES] == true
|
||||
}
|
||||
|
||||
// Save values
|
||||
suspend fun setShow3D(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
@@ -235,4 +242,11 @@ class DataStoreManager(private val context: Context) {
|
||||
prefs[PreferencesKeys.ENGINE_TYPE] = mode
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setAlternativeRoutes(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PreferencesKeys.ALTERNATIVE_ROUTES] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class OsrmRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
location: Location,
|
||||
location: List<Location>,
|
||||
carOrientation: Float,
|
||||
searchFilter: SearchFilter
|
||||
): String {
|
||||
@@ -28,7 +28,7 @@ class OsrmRepository : NavigationRepository() {
|
||||
if (searchFilter.avoidFerry) {
|
||||
exclude = "$exclude&exclude=ferry"
|
||||
}
|
||||
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=false"
|
||||
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.first().longitude},${location.first().latitude}?steps=true&alternatives=false"
|
||||
return fetchUrl(routeUrl + routeLocation + exclude, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class OsrmRoute {
|
||||
steps.add(step)
|
||||
stepIndex += 1
|
||||
}
|
||||
legs.add(Leg(steps))
|
||||
legs.add(Leg(steps, summary))
|
||||
}
|
||||
val routeGeoJson = createLineStringCollection(waypoints)
|
||||
val centerLocation = createCenterLocation(createLineStringCollection(waypoints))
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.kouros.navigation.data.route
|
||||
|
||||
import android.location.Location
|
||||
import com.kouros.navigation.utils.location
|
||||
|
||||
data class Leg(
|
||||
var steps : List<Step> = arrayListOf(),
|
||||
val summary: Summary,
|
||||
)
|
||||
|
||||
@@ -13,3 +13,57 @@ data class Maneuver(
|
||||
val message: String = "",
|
||||
val pointIndex : Int = 0,
|
||||
)
|
||||
|
||||
enum class ManeuverType {
|
||||
TYPE_UNKNOWN,
|
||||
TYPE_DEPART,
|
||||
TYPE_NAME_CHANGE,
|
||||
TYPE_KEEP_LEFT,
|
||||
TYPE_KEEP_RIGHT,
|
||||
TYPE_TURN_SLIGHT_LEFT,
|
||||
TYPE_TURN_SLIGHT_RIGHT,
|
||||
TYPE_TURN_NORMAL_LEFT,
|
||||
TYPE_TURN_NORMAL_RIGHT,
|
||||
TYPE_TURN_SHARP_LEFT,
|
||||
TYPE_TURN_SHARP_RIGHT,
|
||||
TYPE_U_TURN_LEFT,
|
||||
TYPE_U_TURN_RIGHT,
|
||||
TYPE_ON_RAMP_SLIGHT_LEFT,
|
||||
TYPE_ON_RAMP_SLIGHT_RIGHT,
|
||||
TYPE_ON_RAMP_NORMAL_LEFT,
|
||||
TYPE_ON_RAMP_NORMAL_RIGHT,
|
||||
TYPE_ON_RAMP_SHARP_LEFT,
|
||||
TYPE_ON_RAMP_SHARP_RIGHT,
|
||||
TYPE_ON_RAMP_U_TURN_LEFT,
|
||||
TYPE_ON_RAMP_U_TURN_RIGHT,
|
||||
TYPE_OFF_RAMP_SLIGHT_LEFT,
|
||||
TYPE_OFF_RAMP_SLIGHT_RIGHT,
|
||||
TYPE_OFF_RAMP_NORMAL_LEFT,
|
||||
TYPE_OFF_RAMP_NORMAL_RIGHT,
|
||||
TYPE_FORK_LEFT,
|
||||
TYPE_FORK_RIGHT,
|
||||
TYPE_MERGE_LEFT,
|
||||
TYPE_MERGE_RIGHT,
|
||||
TYPE_MERGE_SIDE_UNSPECIFIED,
|
||||
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW,
|
||||
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE,
|
||||
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW,
|
||||
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE,
|
||||
TYPE_STRAIGHT,
|
||||
TYPE_FERRY_BOAT,
|
||||
TYPE_FERRY_TRAIN,
|
||||
TYPE_DESTINATION,
|
||||
TYPE_DESTINATION_STRAIGHT,
|
||||
TYPE_DESTINATION_LEFT,
|
||||
TYPE_DESTINATION_RIGHT,
|
||||
TYPE_ROUNDABOUT_ENTER_CW,
|
||||
TYPE_ROUNDABOUT_EXIT_CW,
|
||||
TYPE_ROUNDABOUT_ENTER_CCW,
|
||||
TYPE_ROUNDABOUT_EXIT_CCW,
|
||||
TYPE_FERRY_BOAT_LEFT,
|
||||
TYPE_FERRY_BOAT_RIGHT,
|
||||
TYPE_FERRY_TRAIN_LEFT,
|
||||
TYPE_FERRY_TRAIN_RIGHT,
|
||||
TYPE_WAYPOINT_RIGHT,
|
||||
TYPE_WAYPOINT_LEFT
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ private const val tomtomFields =
|
||||
|
||||
val useLocal = BuildConfig.DEBUG
|
||||
|
||||
val useLocalTraffic = BuildConfig.DEBUG
|
||||
val useLocalTraffic = BuildConfig.DEBUG
|
||||
|
||||
|
||||
class TomTomRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
location: Location,
|
||||
location: List<Location>,
|
||||
carOrientation: Float,
|
||||
searchFilter: SearchFilter
|
||||
): String {
|
||||
@@ -55,17 +55,34 @@ class TomTomRepository : NavigationRepository() {
|
||||
engineType = "electric"
|
||||
}
|
||||
val repository = getSettingsRepository(context)
|
||||
val tomtomApiKey = runBlocking { repository.tomTomApiKeyFlow.first() }
|
||||
val tomtomApiKey = runBlocking {
|
||||
repository.tomTomApiKeyFlow.first()
|
||||
}
|
||||
val alternativeRoutes = runBlocking {
|
||||
repository.alternativeRoutesFlow.first()
|
||||
}
|
||||
val altRoutes = if (alternativeRoutes) {
|
||||
"&maxAlternatives=2"
|
||||
} else {
|
||||
"&maxAlternatives=0"
|
||||
}
|
||||
val currentLocale = Locale.getDefault()
|
||||
val language = currentLocale.language + "-" + currentLocale.country
|
||||
var loc = ""
|
||||
location.forEach {
|
||||
loc += if (loc.isEmpty()) {
|
||||
"${it.latitude},${it.longitude}"
|
||||
} else {
|
||||
":${it.latitude},${it.longitude}"
|
||||
}
|
||||
}
|
||||
val url =
|
||||
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" +
|
||||
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:$loc" +
|
||||
"/json?sectionType=traffic&report=effectiveSettings&routeType=eco" +
|
||||
"&traffic=true&avoid=unpavedRoads&travelMode=car" +
|
||||
"&vehicleMaxSpeed=120&vehicleCommercial=false" +
|
||||
"&instructionsType=text&language=$language§ionType=lanes" +
|
||||
"&routeRepresentation=encodedPolyline" +
|
||||
"&maxAlternatives=2" +
|
||||
"&routeRepresentation=encodedPolyline$altRoutes" +
|
||||
"&vehicleEngineType=$engineType$filter&key=$tomtomApiKey"
|
||||
return fetchUrl(
|
||||
url,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.kouros.navigation.data.tomtom
|
||||
|
||||
|
||||
import com.kouros.navigation.data.Route
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.route.Intersection
|
||||
import com.kouros.navigation.data.route.Lane
|
||||
import com.kouros.navigation.data.route.Leg
|
||||
import com.kouros.navigation.data.route.ManeuverType
|
||||
import com.kouros.navigation.data.route.Routes
|
||||
import com.kouros.navigation.data.route.Step
|
||||
import com.kouros.navigation.data.route.Summary
|
||||
import com.kouros.navigation.utils.GeoUtils.createCenterLocation
|
||||
@@ -17,12 +20,12 @@ import com.kouros.navigation.data.route.Maneuver as RouteManeuver
|
||||
class TomTomRoute {
|
||||
|
||||
fun mapToRoute(routeJson: TomTomResponse, builder: Route.Builder) {
|
||||
val routes = mutableListOf<com.kouros.navigation.data.route.Routes>()
|
||||
val routes = mutableListOf<Routes>()
|
||||
routeJson.routes.forEach { route ->
|
||||
val waypoints = mutableListOf<List<Double>>()
|
||||
val points = mutableListOf<List<Double>>()
|
||||
val legs = mutableListOf<Leg>()
|
||||
var stepIndex = 0
|
||||
var points = listOf<List<Double>>()
|
||||
val summary = Summary(
|
||||
route.summary.travelTimeInSeconds.toDouble(),
|
||||
route.summary.lengthInMeters.toDouble(),
|
||||
@@ -30,89 +33,98 @@ class TomTomRoute {
|
||||
route.summary.trafficLengthInMeters.toDouble()
|
||||
)
|
||||
route.legs.forEach { leg ->
|
||||
points = decodePolyline(leg.encodedPolyline, leg.encodedPolylinePrecision)
|
||||
waypoints.addAll(points)
|
||||
val p = decodePolyline(leg.encodedPolyline, leg.encodedPolylinePrecision)
|
||||
points.addAll(p)
|
||||
waypoints.addAll(p)
|
||||
}
|
||||
var stepDistance = 0.0
|
||||
var stepDuration = 0.0
|
||||
val steps = mutableListOf<Step>()
|
||||
var lastPointIndex = 0
|
||||
for (index in 1..<route.guidance.instructions.size) {
|
||||
val lastInstruction = route.guidance.instructions[index - 1]
|
||||
val instruction = route.guidance.instructions[index]
|
||||
val street = lastInstruction.street ?: ""
|
||||
val maneuverStreet = instruction.street ?: ""
|
||||
val maneuver = RouteManeuver(
|
||||
bearingBefore = 0,
|
||||
bearingAfter = 0,
|
||||
type = convertType(instruction.maneuver),
|
||||
waypoints = points.subList(
|
||||
lastPointIndex,
|
||||
instruction.pointIndex + 1,
|
||||
),
|
||||
exit = exitNumber(instruction),
|
||||
location = location(
|
||||
instruction.point.longitude, instruction.point.latitude
|
||||
),
|
||||
street = maneuverStreet,
|
||||
message = instruction.message,
|
||||
pointIndex = instruction.pointIndex
|
||||
route.legs.forEach { leg ->
|
||||
var stepDistance = 0.0
|
||||
var stepDuration = 0.0
|
||||
val steps = mutableListOf<Step>()
|
||||
val summary = Summary(
|
||||
leg.summary.travelTimeInSeconds.toDouble(),
|
||||
leg.summary.lengthInMeters.toDouble(),
|
||||
leg.summary.trafficDelayInSeconds.toDouble(),
|
||||
leg.summary.trafficLengthInMeters.toDouble()
|
||||
)
|
||||
var lastPointIndex = 0
|
||||
for (index in 1..<route.guidance.instructions.size) {
|
||||
val lastInstruction = route.guidance.instructions[index - 1]
|
||||
val instruction = route.guidance.instructions[index]
|
||||
val street = lastInstruction.street ?: ""
|
||||
val maneuverStreet = instruction.street ?: ""
|
||||
val maneuver = RouteManeuver(
|
||||
bearingBefore = 0,
|
||||
bearingAfter = 0,
|
||||
type = convertType(instruction.maneuver),
|
||||
waypoints = points.subList(
|
||||
lastPointIndex,
|
||||
instruction.pointIndex + 1,
|
||||
),
|
||||
exit = exitNumber(instruction),
|
||||
location = location(
|
||||
instruction.point.longitude, instruction.point.latitude
|
||||
),
|
||||
street = maneuverStreet,
|
||||
message = instruction.message,
|
||||
pointIndex = instruction.pointIndex
|
||||
)
|
||||
|
||||
lastPointIndex = instruction.pointIndex
|
||||
val intersections = mutableListOf<Intersection>()
|
||||
route.sections?.forEach { section ->
|
||||
if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex
|
||||
&& section.endPointIndex >= lastPointIndex
|
||||
) {
|
||||
val lanes = mutableListOf<Lane>()
|
||||
var startIndex = 0
|
||||
var lastLane: Lane? = null
|
||||
section.lanes?.forEach { itLane ->
|
||||
val lane = Lane(
|
||||
location = location(
|
||||
waypoints[section.startPointIndex][0],
|
||||
waypoints[section.startPointIndex][1]
|
||||
),
|
||||
valid = itLane.directions.first() == itLane.follow,
|
||||
indications = itLane.directions,
|
||||
startIndex = startIndex,
|
||||
endIndex = section.endPointIndex
|
||||
)
|
||||
startIndex = section.startPointIndex
|
||||
if (lastLane == null
|
||||
|| (!(lastLane.valid && lane.valid
|
||||
&& lastLane.indications == lane.indications))
|
||||
) {
|
||||
lanes.add(lane)
|
||||
lastPointIndex = instruction.pointIndex
|
||||
val intersections = mutableListOf<Intersection>()
|
||||
route.sections?.forEach { section ->
|
||||
if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex
|
||||
&& section.endPointIndex >= lastPointIndex
|
||||
) {
|
||||
val lanes = mutableListOf<Lane>()
|
||||
var startIndex = 0
|
||||
var lastLane: Lane? = null
|
||||
section.lanes?.forEach { itLane ->
|
||||
val lane = Lane(
|
||||
location = location(
|
||||
waypoints[section.startPointIndex][0],
|
||||
waypoints[section.startPointIndex][1]
|
||||
),
|
||||
valid = itLane.directions.first() == itLane.follow,
|
||||
indications = itLane.directions,
|
||||
startIndex = startIndex,
|
||||
endIndex = section.endPointIndex
|
||||
)
|
||||
startIndex = section.startPointIndex
|
||||
if (lastLane == null
|
||||
|| (!(lastLane.valid && lane.valid
|
||||
&& lastLane.indications == lane.indications))
|
||||
) {
|
||||
lanes.add(lane)
|
||||
}
|
||||
lastLane = lane
|
||||
}
|
||||
lastLane = lane
|
||||
intersections.add(Intersection(waypoints[startIndex], lanes))
|
||||
}
|
||||
intersections.add(Intersection(waypoints[startIndex], lanes))
|
||||
}
|
||||
stepDistance =
|
||||
route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
stepDuration =
|
||||
route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||
val step = Step(
|
||||
index = stepIndex,
|
||||
street = street,
|
||||
distance = stepDistance,
|
||||
duration = stepDuration,
|
||||
maneuver = maneuver,
|
||||
intersection = intersections,
|
||||
countryCode = lastInstruction.countryCode
|
||||
)
|
||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble()
|
||||
stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble()
|
||||
steps.add(step)
|
||||
stepIndex += 1
|
||||
}
|
||||
stepDistance =
|
||||
route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
stepDuration =
|
||||
route.guidance.instructions[index].travelTimeInSeconds - stepDuration
|
||||
val step = Step(
|
||||
index = stepIndex,
|
||||
street = street,
|
||||
distance = stepDistance,
|
||||
duration = stepDuration,
|
||||
maneuver = maneuver,
|
||||
intersection = intersections,
|
||||
countryCode = lastInstruction.countryCode
|
||||
)
|
||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble()
|
||||
stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble()
|
||||
steps.add(step)
|
||||
stepIndex += 1
|
||||
legs.add(Leg(steps, summary))
|
||||
}
|
||||
legs.add(Leg(steps))
|
||||
val routeGeoJson = createLineStringCollection(waypoints)
|
||||
val centerLocation = createCenterLocation(createLineStringCollection(waypoints))
|
||||
val newRoute = com.kouros.navigation.data.route.Routes(
|
||||
val newRoute = Routes(
|
||||
legs,
|
||||
summary,
|
||||
routeGeoJson,
|
||||
@@ -130,75 +142,79 @@ class TomTomRoute {
|
||||
var newType = 0
|
||||
when (type) {
|
||||
"DEPART" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DEPART
|
||||
newType = ManeuverType.TYPE_DEPART.ordinal
|
||||
}
|
||||
|
||||
"ARRIVE" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION
|
||||
newType = ManeuverType.TYPE_DESTINATION.ordinal
|
||||
}
|
||||
|
||||
|
||||
"ARRIVE_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_LEFT
|
||||
newType = ManeuverType.TYPE_DESTINATION_LEFT.ordinal
|
||||
}
|
||||
|
||||
"ARRIVE_RIGHT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_RIGHT
|
||||
newType = ManeuverType.TYPE_DESTINATION_RIGHT.ordinal
|
||||
}
|
||||
|
||||
"STRAIGHT", "FOLLOW" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT
|
||||
newType = ManeuverType.TYPE_STRAIGHT.ordinal
|
||||
}
|
||||
|
||||
"KEEP_RIGHT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_RIGHT
|
||||
newType = ManeuverType.TYPE_KEEP_RIGHT.ordinal
|
||||
}
|
||||
|
||||
"BEAR_RIGHT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
||||
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal
|
||||
}
|
||||
|
||||
"BEAR_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT
|
||||
newType = ManeuverType.TYPE_TURN_SLIGHT_LEFT.ordinal
|
||||
}
|
||||
|
||||
"KEEP_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_LEFT
|
||||
newType = ManeuverType.TYPE_KEEP_LEFT.ordinal
|
||||
}
|
||||
|
||||
"TURN_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_LEFT
|
||||
newType = ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal
|
||||
}
|
||||
|
||||
"TURN_RIGHT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT
|
||||
newType = ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal
|
||||
}
|
||||
|
||||
"SHARP_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_LEFT
|
||||
newType = ManeuverType.TYPE_TURN_SHARP_LEFT.ordinal
|
||||
}
|
||||
|
||||
"SHARP_RIGHT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
||||
newType = ManeuverType.TYPE_TURN_SHARP_RIGHT.ordinal
|
||||
}
|
||||
|
||||
"ROUNDABOUT_RIGHT", "ROUNDABOUT_CROSS" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW
|
||||
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal
|
||||
}
|
||||
|
||||
"ROUNDABOUT_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
|
||||
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CW.ordinal
|
||||
}
|
||||
|
||||
"MAKE_UTURN" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT
|
||||
"MAKE_UTURN", "TRY_MAKE_UTURN" -> {
|
||||
newType = ManeuverType.TYPE_U_TURN_LEFT.ordinal
|
||||
}
|
||||
|
||||
"ENTER_MOTORWAY" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT
|
||||
newType = ManeuverType.TYPE_MERGE_LEFT.ordinal
|
||||
}
|
||||
|
||||
"TAKE_EXIT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
||||
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal
|
||||
}
|
||||
"WAYPOINT_RIGHT" -> {
|
||||
newType = ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal
|
||||
}
|
||||
}
|
||||
return newType
|
||||
|
||||
@@ -16,7 +16,7 @@ class ValhallaRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
location: Location,
|
||||
location: List<Location>,
|
||||
carOrientation: Float,
|
||||
searchFilter: SearchFilter
|
||||
): String {
|
||||
@@ -35,7 +35,7 @@ class ValhallaRepository : NavigationRepository() {
|
||||
lon = currentLocation.longitude,
|
||||
searchFilter = exclude
|
||||
),
|
||||
Locations(lat = location.latitude, lon = location.longitude, searchFilter = exclude)
|
||||
Locations(lat = location.first().latitude, lon = location.first().longitude, searchFilter = exclude)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
|
||||
@@ -6,15 +6,12 @@ import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.car.app.model.CarIcon
|
||||
import androidx.car.app.navigation.model.LaneDirection
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.StepData
|
||||
import java.util.Collections
|
||||
import com.kouros.navigation.data.route.ManeuverType
|
||||
import java.util.Locale
|
||||
|
||||
class IconMapper {
|
||||
@@ -22,62 +19,66 @@ class IconMapper {
|
||||
fun maneuverIcon(routeManeuverType: Int): Int {
|
||||
var currentTurnIcon = R.drawable.ic_turn_name_change
|
||||
when (routeManeuverType) {
|
||||
Maneuver.TYPE_STRAIGHT -> {
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||
}
|
||||
|
||||
Maneuver.TYPE_DESTINATION,
|
||||
Maneuver.TYPE_DESTINATION_RIGHT,
|
||||
Maneuver.TYPE_DESTINATION_LEFT,
|
||||
Maneuver.TYPE_DESTINATION_STRAIGHT
|
||||
ManeuverType.TYPE_DESTINATION.ordinal,
|
||||
ManeuverType.TYPE_DESTINATION_RIGHT.ordinal,
|
||||
ManeuverType.TYPE_DESTINATION_LEFT.ordinal,
|
||||
ManeuverType.TYPE_DESTINATION_STRAIGHT.ordinal
|
||||
-> {
|
||||
currentTurnIcon = R.drawable.ic_turn_destination
|
||||
}
|
||||
|
||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> {
|
||||
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_normal_right
|
||||
}
|
||||
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> {
|
||||
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_normal_left
|
||||
}
|
||||
|
||||
Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT -> {
|
||||
ManeuverType.TYPE_OFF_RAMP_SLIGHT_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_slight_right
|
||||
}
|
||||
|
||||
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> {
|
||||
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_slight_right
|
||||
}
|
||||
|
||||
Maneuver.TYPE_KEEP_RIGHT -> {
|
||||
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||
}
|
||||
|
||||
Maneuver.TYPE_KEEP_LEFT -> {
|
||||
ManeuverType.TYPE_KEEP_LEFT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_name_change
|
||||
}
|
||||
|
||||
Maneuver.TYPE_ROUNDABOUT_ENTER_CCW -> {
|
||||
ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||
}
|
||||
|
||||
Maneuver.TYPE_ROUNDABOUT_EXIT_CCW -> {
|
||||
ManeuverType.TYPE_ROUNDABOUT_EXIT_CCW.ordinal -> {
|
||||
|
||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||
}
|
||||
|
||||
Maneuver.TYPE_U_TURN_LEFT -> {
|
||||
ManeuverType.TYPE_U_TURN_LEFT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_u_turn_left
|
||||
}
|
||||
|
||||
Maneuver.TYPE_U_TURN_RIGHT -> {
|
||||
ManeuverType.TYPE_U_TURN_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_u_turn_right
|
||||
}
|
||||
|
||||
Maneuver.TYPE_MERGE_LEFT -> {
|
||||
ManeuverType.TYPE_MERGE_LEFT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_merge_symmetrical
|
||||
}
|
||||
|
||||
ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_destination
|
||||
}
|
||||
}
|
||||
return currentTurnIcon
|
||||
}
|
||||
@@ -86,8 +87,8 @@ class IconMapper {
|
||||
val laneDirection = when (direction.lowercase(Locale.getDefault())) {
|
||||
"left_straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT
|
||||
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -95,7 +96,7 @@ class IconMapper {
|
||||
|
||||
"left" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_NORMAL_LEFT
|
||||
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -103,9 +104,9 @@ class IconMapper {
|
||||
|
||||
"straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_STRAIGHT
|
||||
Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
|
||||
ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_STRAIGHT
|
||||
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -113,7 +114,7 @@ class IconMapper {
|
||||
|
||||
"right" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -121,8 +122,8 @@ class IconMapper {
|
||||
|
||||
"right_straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -130,8 +131,8 @@ class IconMapper {
|
||||
|
||||
"left_slight", "slight_left" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -139,8 +140,8 @@ class IconMapper {
|
||||
|
||||
"right_slight", "slight_right" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_SLIGHT_RIGHT
|
||||
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
|
||||
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_SLIGHT_RIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -212,8 +213,8 @@ class IconMapper {
|
||||
return when (direction) {
|
||||
"left_straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> "left_o_straight_x"
|
||||
Maneuver.TYPE_STRAIGHT -> "left_x_straight_o"
|
||||
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> "left_o_straight_x"
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> "left_x_straight_o"
|
||||
else
|
||||
-> "left_x_straight_x"
|
||||
}
|
||||
@@ -221,29 +222,29 @@ class IconMapper {
|
||||
|
||||
"right_straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_NORMAL_RIGHT -> "right_x_straight_x"
|
||||
Maneuver.TYPE_STRAIGHT -> "right_x_straight_o"
|
||||
Maneuver.TYPE_TURN_SLIGHT_RIGHT -> "right_o_straight_o"
|
||||
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> "right_x_straight_x"
|
||||
ManeuverType.TYPE_STRAIGHT.ordinal -> "right_x_straight_o"
|
||||
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> "right_o_straight_o"
|
||||
else
|
||||
-> "right_x_straight_x"
|
||||
}
|
||||
}
|
||||
|
||||
"right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_x"
|
||||
"left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x"
|
||||
"straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT
|
||||
"right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal) "${direction}_o" else "${direction}_x"
|
||||
"left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal) "${direction}_o" else "${direction}_x"
|
||||
"straight" -> if (stepData.currentManeuverType == ManeuverType.TYPE_STRAIGHT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.ordinal
|
||||
) "${direction}_o" else "${direction}_x"
|
||||
|
||||
"right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT
|
||||
"right_slight", "slight_right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.ordinal
|
||||
) "slight_right_o" else "slight_right_x"
|
||||
|
||||
"left_slight", "slight_left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT
|
||||
|| stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT
|
||||
"left_slight", "slight_left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_LEFT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal
|
||||
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.ordinal
|
||||
) "slight_left_o" else "slight_left_x"
|
||||
|
||||
else -> {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.kouros.navigation.model
|
||||
//import com.kouros.navigation.data.Preferences.boxStore
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -147,7 +148,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
fun loadRoute(
|
||||
context: Context,
|
||||
currentLocation: Location,
|
||||
destination: Location,
|
||||
destination: List<Location>,
|
||||
carOrientation: Float
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
@@ -181,9 +182,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
)
|
||||
if (data.isNotEmpty()) {
|
||||
val trafficData = rebuildTraffic(data)
|
||||
traffic.postValue(
|
||||
trafficData
|
||||
)
|
||||
if (trafficData.isNotEmpty()) {
|
||||
traffic.postValue(
|
||||
trafficData
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@@ -234,7 +237,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
|
||||
repository.getRoute(
|
||||
context,
|
||||
currentLocation,
|
||||
location,
|
||||
listOf(location),
|
||||
carOrientation,
|
||||
getSearchFilter(context)
|
||||
)
|
||||
|
||||
@@ -102,6 +102,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
|
||||
0
|
||||
)
|
||||
|
||||
val alternativeRoutes = repository.alternativeRoutesFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
false
|
||||
)
|
||||
|
||||
fun onShow3DChanged(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setShow3D(enabled) }
|
||||
}
|
||||
@@ -159,4 +165,7 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
|
||||
viewModelScope.launch { repository.setEngineType(mode) }
|
||||
}
|
||||
|
||||
fun onAlternativeRoutes(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setAlternativeRoutes(enabled) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ class SettingsRepository(
|
||||
val engineTypeFlow: Flow<Int> =
|
||||
dataStoreManager.engineTypeFlow
|
||||
|
||||
val alternativeRoutesFlow: Flow<Boolean> =
|
||||
dataStoreManager.alternativeRoutesFlow
|
||||
|
||||
suspend fun setShow3D(enabled: Boolean) {
|
||||
dataStoreManager.setShow3D(enabled)
|
||||
}
|
||||
@@ -109,4 +112,8 @@ class SettingsRepository(
|
||||
suspend fun setEngineType(mode: Int) {
|
||||
dataStoreManager.setEngineType(mode)
|
||||
}
|
||||
|
||||
suspend fun setAlternativeRoutes(enabled: Boolean) {
|
||||
dataStoreManager.setAlternativeRoutes(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,5 @@
|
||||
<string name="combustion">Combustion</string>
|
||||
<string name="electric">Electric</string>
|
||||
<string name="engine_type">Engine type</string>
|
||||
<string name="alternative_routes">Alternative routes</string>
|
||||
</resources>
|
||||
|
||||
@@ -55,4 +55,5 @@
|
||||
<string name="combustion">Combustion</string>
|
||||
<string name="electric">Electric</string>
|
||||
<string name="engine_type">Engine type</string>
|
||||
<string name="alternative_routes">Alternative routes</string>
|
||||
</resources>
|
||||
|
||||
@@ -55,4 +55,5 @@
|
||||
<string name="combustion">Combustion</string>
|
||||
<string name="electric">Electric</string>
|
||||
<string name="engine_type">Engine type</string>
|
||||
<string name="alternative_routes">Alternative routes</string>
|
||||
</resources>
|
||||
|
||||
@@ -58,4 +58,5 @@
|
||||
<string name="combustion">Combustion</string>
|
||||
<string name="electric">Electric</string>
|
||||
<string name="engine_type">Engine type</string>
|
||||
<string name="alternative_routes">Alternative routes</string>
|
||||
</resources>
|
||||
@@ -55,8 +55,8 @@ class RouteCalculatorTest {
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupRoute(steps: List<Step>, currentStepIndex: Int = 0): Route {
|
||||
val leg = Leg(steps = steps)
|
||||
private fun setupRoute(steps: List<Step>, currentStepIndex: Int = 0, summary: Summary): Route {
|
||||
val leg = Leg(steps = steps, summary)
|
||||
val routes = Routes(
|
||||
legs = listOf(leg),
|
||||
summary = Summary(),
|
||||
@@ -74,7 +74,7 @@ class RouteCalculatorTest {
|
||||
fun `findStep updates currentStepIndex to step containing the nearest waypoint`() {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2)
|
||||
val step1 = createStep(index = 1, numWaypoints = 2)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1), summary = Summary()))
|
||||
|
||||
val mockLocation: Location = mock()
|
||||
// step0/wp0: 500F, step0/wp1: 400F, step1/wp0: 300F, step1/wp1: 8F
|
||||
@@ -88,7 +88,7 @@ class RouteCalculatorTest {
|
||||
@Test
|
||||
fun `findStep updates waypointIndex to the nearest waypoint within the step`() {
|
||||
val step0 = createStep(index = 0, numWaypoints = 3)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0), summary = Summary()))
|
||||
|
||||
val mockLocation: Location = mock()
|
||||
// wp0: 100F, wp1: 30F (nearest), wp2: 80F
|
||||
@@ -104,7 +104,7 @@ class RouteCalculatorTest {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2)
|
||||
val step1 = createStep(index = 1, numWaypoints = 2)
|
||||
routeModel.navState = routeModel.navState.copy(
|
||||
route = setupRoute(listOf(step0, step1), currentStepIndex = 1)
|
||||
route = setupRoute(listOf(step0, step1), currentStepIndex = 1, summary = Summary())
|
||||
)
|
||||
|
||||
val mockLocation: Location = mock()
|
||||
@@ -123,7 +123,7 @@ class RouteCalculatorTest {
|
||||
val step1 = createStep(index = 1, numWaypoints = 2)
|
||||
val step2 = createStep(index = 2, numWaypoints = 2)
|
||||
routeModel.navState = routeModel.navState.copy(
|
||||
route = setupRoute(listOf(step0, step1, step2))
|
||||
route = setupRoute(listOf(step0, step1, step2), summary = Summary())
|
||||
)
|
||||
|
||||
val mockLocation: Location = mock()
|
||||
@@ -147,7 +147,7 @@ class RouteCalculatorTest {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2, duration = 60.0, waypointIndex = 0)
|
||||
val step1 = createStep(index = 1, numWaypoints = 2, duration = 120.0)
|
||||
val step2 = createStep(index = 2, numWaypoints = 2, duration = 90.0)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2), summary = Summary()))
|
||||
|
||||
val result = routeCalculator.travelLeftTime()
|
||||
|
||||
@@ -160,7 +160,7 @@ class RouteCalculatorTest {
|
||||
// waypointIndex=2, waypoints=4 → percent = 100*(4-2)/4 = 50 → time = 80*50/100 = 40s
|
||||
val step0 = createStep(index = 0, numWaypoints = 4, duration = 80.0, waypointIndex = 2)
|
||||
val step1 = createStep(index = 1, numWaypoints = 2, duration = 40.0)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1), summary = Summary()))
|
||||
|
||||
val result = routeCalculator.travelLeftTime()
|
||||
|
||||
@@ -172,7 +172,7 @@ class RouteCalculatorTest {
|
||||
fun `travelLeftTime returns only future steps when at last step`() {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2, duration = 60.0, waypointIndex = 1)
|
||||
routeModel.navState = routeModel.navState.copy(
|
||||
route = setupRoute(listOf(step0), currentStepIndex = 0)
|
||||
route = setupRoute(listOf(step0), currentStepIndex = 0, summary = Summary())
|
||||
)
|
||||
|
||||
val result = routeCalculator.travelLeftTime()
|
||||
@@ -189,7 +189,7 @@ class RouteCalculatorTest {
|
||||
fun `leftStepDistance returns 0 when waypointIndex is at the last position`() {
|
||||
// Loop range: waypointIndex..<waypoints.size-1 = 2..<2, which is empty
|
||||
val step0 = createStep(index = 0, numWaypoints = 3, waypointIndex = 2)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0), summary = Summary()))
|
||||
|
||||
val result = routeCalculator.leftStepDistance()
|
||||
|
||||
@@ -206,7 +206,7 @@ class RouteCalculatorTest {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2, distance = 100.0, waypointIndex = 1)
|
||||
val step1 = createStep(index = 1, numWaypoints = 2, distance = 200.0)
|
||||
val step2 = createStep(index = 2, numWaypoints = 2, distance = 150.0)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0, step1, step2), summary = Summary()))
|
||||
|
||||
val result = routeCalculator.travelLeftDistance()
|
||||
|
||||
@@ -217,7 +217,7 @@ class RouteCalculatorTest {
|
||||
@Test
|
||||
fun `travelLeftDistance returns 0 when on last step at last waypoint`() {
|
||||
val step0 = createStep(index = 0, numWaypoints = 2, distance = 200.0, waypointIndex = 1)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0)))
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0), summary = Summary()))
|
||||
|
||||
val result = routeCalculator.travelLeftDistance()
|
||||
|
||||
@@ -232,7 +232,8 @@ class RouteCalculatorTest {
|
||||
fun `arrivalTime returns a timestamp roughly travelLeftTime seconds in the future`() {
|
||||
// step0: 2 waypoints at wp0 → 100% of 3600s duration
|
||||
val step0 = createStep(index = 0, numWaypoints = 2, duration = 3600.0, waypointIndex = 0)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0)))
|
||||
val summary = Summary(duration = 3600.0)
|
||||
routeModel.navState = routeModel.navState.copy(route = setupRoute(listOf(step0), summary = summary))
|
||||
|
||||
val before = System.currentTimeMillis()
|
||||
val result = routeCalculator.arrivalTime()
|
||||
|
||||
@@ -48,7 +48,7 @@ class RouteModelTest {
|
||||
}
|
||||
|
||||
private fun setupRoute(steps: List<Step>, currentStepIndex: Int = 0): Route {
|
||||
val leg = Leg(steps = steps)
|
||||
val leg = Leg(steps = steps, summary = Summary())
|
||||
val routes = Routes(
|
||||
legs = listOf(leg),
|
||||
summary = Summary(),
|
||||
|
||||
Reference in New Issue
Block a user