Stopover and Legs, ManeuverType

This commit is contained in:
Dimitris
2026-04-01 16:24:51 +02:00
parent 6838ad09c4
commit 52f8dec2e6
28 changed files with 208 additions and 281 deletions
+2 -2
View File
@@ -17,8 +17,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 36
versionCode = 87
versionName = "0.2.0.87"
versionCode = 90
versionName = "0.2.3.90"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -179,7 +179,7 @@ fun Categories(
modifier = Modifier.horizontalScroll(scrollState)
) {
Button(onClick = {
val places = viewModel.loadRecentPlace(applicationContext)
val places = viewModel.loadRecentPlaces(applicationContext)
val toLocation = location(places.first()!!.longitude, places.first()!!.latitude)
viewModel.loadRoute(applicationContext, location, listOf(toLocation), 0F)
closeSheet()
@@ -116,7 +116,7 @@ fun Home(
) {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Button(onClick = {
val places = viewModel.loadRecentPlace(applicationContext)
val places = viewModel.loadRecentPlaces(applicationContext)
val toLocation = location(places.first()!!.longitude, places.first()!!.latitude)
viewModel.loadRoute(applicationContext, location, listOf(toLocation), 0F)
closeSheet()
@@ -104,6 +104,10 @@ internal class ClusterSession : Session(), NavigationListener {
}
override fun recalcRoute(destination: Place) {
}
companion object {
val TAG: String = ClusterSession::class.java.getSimpleName()
}
@@ -475,26 +475,27 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
val streetName = routeModel.currentStep().street
val currentDate = LocalDateTime.now(ZoneOffset.UTC)
snapLocation(location, streetName)
checkTraffic(currentDate, location)
updateSpeedCamera(location)
checkRoute(currentDate, location)
updateNavigationScreen()
checkArrival()
if (snapLocation(location, streetName)) {
checkTraffic(currentDate, location)
updateSpeedCamera(location)
checkRoute(currentDate, location)
updateNavigationScreen()
checkArrival()
}
}
/**
* Updates the surface renderer with snapped location and street name.
* Checks if maximal route deviation is exceeded and reroutes if needed.
*/
private fun snapLocation(location: Location, streetName: String) {
private fun snapLocation(location: Location, streetName: String) : Boolean {
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
val distance = location.distanceTo(snappedLocation)
when {
distance > MAXIMAL_ROUTE_DEVIATION -> {
stopNavigation()
navigationScreen.calculateNewRoute(routeModel.navState.destination)
return false
}
distance < MAXIMAL_SNAP_CORRECTION -> {
@@ -505,6 +506,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
surfaceRenderer.updateLocation(location, streetName)
}
}
return true
}
/**
@@ -616,7 +618,6 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
navigationScreen.navigationType = NavigationType.VIEW
if (notificationActive)
notificationManager.stopNotificationService()
Log.d(TAG, "end stopNavigation")
}
override fun updateTrip(trip: Trip) {
@@ -625,6 +626,19 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
}
}
/**
* Recalculates a route for the specified place.
*/
override fun recalcRoute(destination: Place) {
val destination = location(destination.longitude, destination.latitude)
navigationViewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
listOf(destination),
surfaceRenderer.carOrientation
)
}
/**
* Handle guidance audio
* Called when user wants to hear the step-by-step instructions
@@ -646,6 +660,7 @@ class NavigationSession : Session(), NavigationListener, NavigationObserverCallb
* Starts navigation and invalidates the screen.
*/
override fun onRouteReceived(route: String) {
Log.d(TAG, "onRouteReceived")
if (route.isNotEmpty()) {
this.route = route
if (routeModel.isNavigating()) {
@@ -31,7 +31,7 @@ import com.kouros.navigation.car.map.getPaddingValues
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Constants.TILT
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.DarkMode
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.BaseStyleModel
@@ -59,7 +59,7 @@ import java.time.LocalDateTime
*/
class SurfaceRenderer(
private var carContext: CarContext,
private var lifecycle: Lifecycle,
lifecycle: Lifecycle,
//private var routeModel: RouteCarModel,
private var viewModelStoreOwner: ViewModelStoreOwner
) : DefaultLifecycleObserver {
@@ -74,10 +74,6 @@ class SurfaceRenderer(
val cameraPosition = MutableLiveData(
CameraPosition(
zoom = 16.0,
target = Position(
latitude = homeVogelhart.latitude,
longitude = homeVogelhart.longitude
)
)
)
@@ -164,7 +160,7 @@ class SurfaceRenderer(
lifecycleOwner = CustomLifecycleOwner()
lifecycleOwner.performRestore(null)
// technically, we only really need any one of these instead of all 3
// i add them to be consistent with the actual lifecycle.
// add them to be consistent with the actual lifecycle.
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -251,10 +247,6 @@ class SurfaceRenderer(
speed.value = 0F
}
fun onBaseStyleStateUpdated(style: BaseStyle) {
}
/**
* Composable function that renders the map and navigation UI.
* Observes various LiveData sources and updates the map accordingly.
@@ -272,7 +264,8 @@ class SurfaceRenderer(
val paddingValues = getPaddingValues(height, viewStyle)
val cameraState = cameraState(paddingValues, position, tilt)
val baseStyle = BaseStyleModel().readStyle(carContext, darkMode, carContext.isDarkMode)
val dark = darkMode == 1 || darkMode == 2 && carContext.isDarkMode
val dark = darkMode == DarkMode.DARK.ordinal
|| (darkMode == DarkMode.USE_CAR.ordinal && carContext.isDarkMode)
MapLibre(
cameraState,
@@ -334,7 +327,6 @@ class SurfaceRenderer(
}
override fun onCreate(owner: LifecycleOwner) {
style.observe(owner, ::onBaseStyleStateUpdated)
Log.i(TAG, "SurfaceRenderer created")
carContext.getCarService(AppManager::class.java)
.setSurfaceCallback(mSurfaceCallback)
@@ -499,18 +491,6 @@ class SurfaceRenderer(
}
}
/**
* Updates car location from the connected car system.
* Only updates location when using OSRM routing engine.
*/
fun updateCarLocation(location: Location, streetName: String) {
val repository = getSettingsRepository(carContext)
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
if (routingEngine == RouteEngine.OSRM.ordinal) {
updateLocation(location, streetName)
}
}
/**
* Updates current speed for display.
*/
@@ -146,10 +146,10 @@ fun RouteLayer(routeData: String?, trafficData: Map<String, String>) {
interpolate(
type = exponential(1.2f),
input = zoom(),
5 to const(0.4.dp),
6 to const(0.7.dp),
7 to const(1.75.dp),
20 to const(22.dp),
5 to const(0.7.dp),
6 to const(1.0.dp),
7 to const(2.4.dp),
20 to const(26.dp),
),
)
}
@@ -3,6 +3,7 @@ package com.kouros.navigation.car.navigation
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.util.Log
import androidx.annotation.StringRes
import androidx.car.app.AppManager
import androidx.car.app.CarContext
@@ -54,7 +55,7 @@ class RouteCarModel : RouteModel() {
val step =
Step.Builder(currentStepCueWithImage)
step.setRoad(navState.destination.street!!)
step.setRoad(navState.destination.street)
if (stepData.lane.isNotEmpty()) {
val lanesAdded = addLanes(carContext, step, stepData)
if (lanesAdded) {
@@ -16,4 +16,6 @@ interface NavigationListener {
fun updateTrip(trip: Trip)
fun navigateToPlace(place: Place)
fun recalcRoute(destination: Place)
}
@@ -2,6 +2,7 @@ package com.kouros.navigation.car.screen
import android.os.CountDownTimer
import android.os.Handler
import android.util.Log
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
@@ -32,6 +33,7 @@ import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.screen.settings.SettingsScreen
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.ViewStyle
import com.kouros.navigation.model.NavigationViewModel
@@ -471,25 +473,14 @@ open class NavigationScreen(
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
navigationType = NavigationType.NAVIGATION
reRoute(destination)
listener.recalcRoute(destination)
}
}
reRouteTimer?.start()
}
}
/**
* Re-requests a route for the specified place.
*/
fun reRoute(place: Place) {
val destination = location(place.longitude, place.latitude)
navigationViewModel.loadRoute(
carContext,
surfaceRenderer.lastLocation,
listOf(destination),
surfaceRenderer.carOrientation
)
}
/**
* Updates navigation state with the current location, checks for arrival, and traffic updates.
@@ -6,7 +6,7 @@ val NavigationColorLight = Color(0xFF17A119)
val NavigationColorDark = Color(0xFF4EDE10)
val RouteColor = Color(0xFF05692D)
val RouteColor = Color(0xFF195D02)
val SpeedColor = Color(0xFF262525)
@@ -17,9 +17,11 @@
package com.kouros.navigation.data
import android.net.Uri
import com.google.gson.annotations.Expose
import com.kouros.navigation.data.route.Lane
import com.kouros.navigation.utils.location
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
data class Category(
val id: String,
@@ -27,6 +29,7 @@ data class Category(
)
data class Places(
val places: List<Place>,
)
@@ -41,11 +44,15 @@ data class Place(
var postalCode: String = "",
var city: String = "",
var street: String = "",
@Transient
var distance: Float = 0F,
//var avatar: Uri? = null,
var lastDate: Long = 0,
@Transient
var routeIndex: Int = 0,
@Transient
var route: String = "",
@Transient
var stopOver: Boolean = false,
)
@@ -113,8 +120,6 @@ object Constants {
const val CHARGING_STATION: String ="charging_station"
val categories = listOf("Tankstelle", "Apotheke", "Ladestationen")
/** The initial location to use as an anchor for searches. */
val homeVogelhart = location(11.5793748, 48.185749)
val homeHohenwaldeck = location( 11.594322, 48.1164817)
@@ -158,6 +163,10 @@ enum class RouteEngine {
VALHALLA, OSRM, TOMTOM
}
enum class DarkMode {
LIGHT, DARK, USE_CAR
}
enum class EngineType {
COMBUSTION, ELECTRIC
}
@@ -3,67 +3,67 @@ package com.kouros.navigation.data.route
import android.location.Location
data class Maneuver(
val bearingBefore : Int = 0,
val bearingAfter : Int = 0,
val bearingBefore: Int = 0,
val bearingAfter: Int = 0,
val type: Int = 0,
val waypoints: List<List<Double>>,
val location: Location,
val exit: Int = 0,
val street: String = "",
val message: String = "",
val pointIndex : Int = 0,
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
enum class ManeuverType(val value: Int) {
TYPE_UNKNOWN(0),
TYPE_DEPART(1),
TYPE_NAME_CHANGE(2),
TYPE_KEEP_LEFT(3),
TYPE_KEEP_RIGHT(4),
TYPE_TURN_SLIGHT_LEFT(5),
TYPE_TURN_SLIGHT_RIGHT(6),
TYPE_TURN_NORMAL_LEFT(7),
TYPE_TURN_NORMAL_RIGHT(8),
TYPE_TURN_SHARP_LEFT(9),
TYPE_TURN_SHARP_RIGHT(10),
TYPE_U_TURN_LEFT(11),
TYPE_U_TURN_RIGHT(12),
TYPE_ON_RAMP_SLIGHT_LEFT(13),
TYPE_ON_RAMP_SLIGHT_RIGHT(14),
TYPE_ON_RAMP_NORMAL_LEFT(15),
TYPE_ON_RAMP_NORMAL_RIGHT(16),
TYPE_ON_RAMP_SHARP_LEFT(17),
TYPE_ON_RAMP_SHARP_RIGHT(18),
TYPE_ON_RAMP_U_TURN_LEFT(19),
TYPE_ON_RAMP_U_TURN_RIGHT(20),
TYPE_OFF_RAMP_SLIGHT_LEFT(21),
TYPE_OFF_RAMP_SLIGHT_RIGHT(22),
TYPE_OFF_RAMP_NORMAL_LEFT(23),
TYPE_OFF_RAMP_NORMAL_RIGHT(24),
TYPE_FORK_LEFT(25),
TYPE_FORK_RIGHT(26),
TYPE_MERGE_LEFT(27),
TYPE_MERGE_RIGHT(28),
TYPE_MERGE_SIDE_UNSPECIFIED (29),
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW (32),
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE(33),
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW(34),
TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE(35),
TYPE_STRAIGHT(36),
TYPE_FERRY_BOAT (37),
TYPE_FERRY_TRAIN (38),
TYPE_DESTINATION (39),
TYPE_DESTINATION_STRAIGHT (40),
TYPE_DESTINATION_LEFT(41),
TYPE_DESTINATION_RIGHT(42),
TYPE_ROUNDABOUT_ENTER_CW(43),
TYPE_ROUNDABOUT_EXIT_CW(44),
TYPE_ROUNDABOUT_ENTER_CCW(45),
TYPE_ROUNDABOUT_EXIT_CCW(46),
TYPE_FERRY_BOAT_LEFT(47),
TYPE_FERRY_BOAT_RIGHT(48),
TYPE_FERRY_TRAIN_LEFT(49),
TYPE_FERRY_TRAIN_RIGHT(50),
TYPE_WAYPOINT_RIGHT(51),
TYPE_WAYPOINT_LEFT(52),
}
@@ -1,5 +0,0 @@
package com.kouros.navigation.data.tomtom
data class Cause(
val mainCauseCode: Int
)
@@ -1,10 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Events (
@SerializedName("description" ) var description : String? = null
)
@@ -1,12 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Features (
@SerializedName("type" ) var type : String? = null,
@SerializedName("properties" ) var properties : Properties? = Properties(),
@SerializedName("geometry" ) var geometry : Geometry? = Geometry()
)
@@ -1,11 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Geometry (
@SerializedName("type" ) var type : String? = null,
@SerializedName("coordinates" ) var coordinates : List<List<Double>> = arrayListOf()
)
@@ -1,12 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Incidents (
@SerializedName("type" ) var type : String? = null,
@SerializedName("properties" ) var properties : Properties? = Properties(),
@SerializedName("geometry" ) var geometry : Geometry? = Geometry()
)
@@ -1,11 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Properties (
@SerializedName("iconCategory" ) var iconCategory : Int? = null,
@SerializedName("events" ) var events : ArrayList<Events> = arrayListOf()
)
@@ -21,9 +21,9 @@ const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incident
private const val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
val useLocal = BuildConfig.DEBUG
val useLocal = BuildConfig.DEBUG
val useLocalTraffic = BuildConfig.DEBUG
val useLocalTraffic = BuildConfig.DEBUG
class TomTomRepository : NavigationRepository() {
@@ -142,79 +142,79 @@ class TomTomRoute {
var newType = 0
when (type) {
"DEPART" -> {
newType = ManeuverType.TYPE_DEPART.ordinal
newType = ManeuverType.TYPE_DEPART.value
}
"ARRIVE" -> {
newType = ManeuverType.TYPE_DESTINATION.ordinal
newType = ManeuverType.TYPE_DESTINATION.value
}
"ARRIVE_LEFT" -> {
newType = ManeuverType.TYPE_DESTINATION_LEFT.ordinal
newType = ManeuverType.TYPE_DESTINATION_LEFT.value
}
"ARRIVE_RIGHT" -> {
newType = ManeuverType.TYPE_DESTINATION_RIGHT.ordinal
newType = ManeuverType.TYPE_DESTINATION_RIGHT.value
}
"STRAIGHT", "FOLLOW" -> {
newType = ManeuverType.TYPE_STRAIGHT.ordinal
newType = ManeuverType.TYPE_STRAIGHT.value
}
"KEEP_RIGHT" -> {
newType = ManeuverType.TYPE_KEEP_RIGHT.ordinal
newType = ManeuverType.TYPE_KEEP_RIGHT.value
}
"BEAR_RIGHT" -> {
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value
}
"BEAR_LEFT" -> {
newType = ManeuverType.TYPE_TURN_SLIGHT_LEFT.ordinal
newType = ManeuverType.TYPE_TURN_SLIGHT_LEFT.value
}
"KEEP_LEFT" -> {
newType = ManeuverType.TYPE_KEEP_LEFT.ordinal
newType = ManeuverType.TYPE_KEEP_LEFT.value
}
"TURN_LEFT" -> {
newType = ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal
newType = ManeuverType.TYPE_TURN_NORMAL_LEFT.value
}
"TURN_RIGHT" -> {
newType = ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal
newType = ManeuverType.TYPE_TURN_NORMAL_RIGHT.value
}
"SHARP_LEFT" -> {
newType = ManeuverType.TYPE_TURN_SHARP_LEFT.ordinal
newType = ManeuverType.TYPE_TURN_SHARP_LEFT.value
}
"SHARP_RIGHT" -> {
newType = ManeuverType.TYPE_TURN_SHARP_RIGHT.ordinal
newType = ManeuverType.TYPE_TURN_SHARP_RIGHT.value
}
"ROUNDABOUT_RIGHT", "ROUNDABOUT_CROSS" -> {
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.value
}
"ROUNDABOUT_LEFT" -> {
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CW.ordinal
newType = ManeuverType.TYPE_ROUNDABOUT_ENTER_CW.value
}
"MAKE_UTURN", "TRY_MAKE_UTURN" -> {
newType = ManeuverType.TYPE_U_TURN_LEFT.ordinal
newType = ManeuverType.TYPE_U_TURN_LEFT.value
}
"ENTER_MOTORWAY" -> {
newType = ManeuverType.TYPE_MERGE_LEFT.ordinal
newType = ManeuverType.TYPE_MERGE_LEFT.value
}
"TAKE_EXIT" -> {
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal
newType = ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value
}
"WAYPOINT_RIGHT" -> {
newType = ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal
newType = ManeuverType.TYPE_WAYPOINT_RIGHT.value
}
}
return newType
@@ -1,12 +0,0 @@
package com.kouros.navigation.data.tomtom
import com.google.gson.annotations.SerializedName
data class Traffic (
//@SerializedName("incidents" ) var incidents : ArrayList<Incidents> = arrayListOf()
@SerializedName("type" ) var type : String = "",
@SerializedName("features" ) var features : ArrayList<Features> = arrayListOf()
)
@@ -1,6 +0,0 @@
package com.kouros.navigation.data.tomtom
data class TrafficData (
var traffic : Traffic ,
var trafficData: String = ""
)
@@ -19,64 +19,64 @@ class IconMapper {
fun maneuverIcon(routeManeuverType: Int): Int {
var currentTurnIcon = R.drawable.ic_turn_name_change
when (routeManeuverType) {
ManeuverType.TYPE_STRAIGHT.ordinal -> {
ManeuverType.TYPE_STRAIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_name_change
}
ManeuverType.TYPE_DESTINATION.ordinal,
ManeuverType.TYPE_DESTINATION_RIGHT.ordinal,
ManeuverType.TYPE_DESTINATION_LEFT.ordinal,
ManeuverType.TYPE_DESTINATION_STRAIGHT.ordinal
ManeuverType.TYPE_DESTINATION.value,
ManeuverType.TYPE_DESTINATION_RIGHT.value,
ManeuverType.TYPE_DESTINATION_LEFT.value,
ManeuverType.TYPE_DESTINATION_STRAIGHT.value
-> {
currentTurnIcon = R.drawable.ic_turn_destination
}
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> {
ManeuverType.TYPE_TURN_NORMAL_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_normal_right
}
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> {
ManeuverType.TYPE_TURN_NORMAL_LEFT.value -> {
currentTurnIcon = R.drawable.ic_turn_normal_left
}
ManeuverType.TYPE_OFF_RAMP_SLIGHT_RIGHT.ordinal -> {
ManeuverType.TYPE_OFF_RAMP_SLIGHT_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_slight_right
}
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> {
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_slight_right
}
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> {
ManeuverType.TYPE_KEEP_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_name_change
}
ManeuverType.TYPE_KEEP_LEFT.ordinal -> {
ManeuverType.TYPE_KEEP_LEFT.value -> {
currentTurnIcon = R.drawable.ic_turn_name_change
}
ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.ordinal -> {
ManeuverType.TYPE_ROUNDABOUT_ENTER_CCW.value -> {
currentTurnIcon = R.drawable.ic_roundabout_ccw
}
ManeuverType.TYPE_ROUNDABOUT_EXIT_CCW.ordinal -> {
ManeuverType.TYPE_ROUNDABOUT_EXIT_CCW.value -> {
currentTurnIcon = R.drawable.ic_roundabout_ccw
}
ManeuverType.TYPE_U_TURN_LEFT.ordinal -> {
ManeuverType.TYPE_U_TURN_LEFT.value -> {
currentTurnIcon = R.drawable.ic_turn_u_turn_left
}
ManeuverType.TYPE_U_TURN_RIGHT.ordinal -> {
ManeuverType.TYPE_U_TURN_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_u_turn_right
}
ManeuverType.TYPE_MERGE_LEFT.ordinal -> {
ManeuverType.TYPE_MERGE_LEFT.value -> {
currentTurnIcon = R.drawable.ic_turn_merge_symmetrical
}
ManeuverType.TYPE_WAYPOINT_RIGHT.ordinal -> {
ManeuverType.TYPE_WAYPOINT_RIGHT.value -> {
currentTurnIcon = R.drawable.ic_turn_destination
}
}
@@ -87,8 +87,8 @@ class IconMapper {
val laneDirection = when (direction.lowercase(Locale.getDefault())) {
"left_straight" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_TURN_NORMAL_LEFT.value -> LaneDirection.SHAPE_NORMAL_LEFT
ManeuverType.TYPE_STRAIGHT.value -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -96,7 +96,7 @@ class IconMapper {
"left" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_NORMAL_LEFT
ManeuverType.TYPE_TURN_NORMAL_LEFT.value -> LaneDirection.SHAPE_NORMAL_LEFT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -104,9 +104,9 @@ class IconMapper {
"straight" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_STRAIGHT.value -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_KEEP_LEFT.value -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_KEEP_RIGHT.value -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -114,7 +114,7 @@ class IconMapper {
"right" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
ManeuverType.TYPE_TURN_NORMAL_RIGHT.value -> LaneDirection.SHAPE_NORMAL_RIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -122,8 +122,8 @@ class IconMapper {
"right_straight" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
ManeuverType.TYPE_STRAIGHT.ordinal -> LaneDirection.SHAPE_STRAIGHT
ManeuverType.TYPE_TURN_NORMAL_RIGHT.value -> LaneDirection.SHAPE_NORMAL_RIGHT
ManeuverType.TYPE_STRAIGHT.value -> LaneDirection.SHAPE_STRAIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -131,8 +131,8 @@ class IconMapper {
"left_slight", "slight_left" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT
ManeuverType.TYPE_KEEP_LEFT.ordinal -> LaneDirection.SHAPE_SLIGHT_LEFT
ManeuverType.TYPE_TURN_NORMAL_LEFT.value -> LaneDirection.SHAPE_SLIGHT_LEFT
ManeuverType.TYPE_KEEP_LEFT.value -> LaneDirection.SHAPE_SLIGHT_LEFT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -140,8 +140,8 @@ class IconMapper {
"right_slight", "slight_right" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.ordinal -> LaneDirection.SHAPE_NORMAL_RIGHT
ManeuverType.TYPE_KEEP_RIGHT.ordinal -> LaneDirection.SHAPE_SLIGHT_RIGHT
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value -> LaneDirection.SHAPE_NORMAL_RIGHT
ManeuverType.TYPE_KEEP_RIGHT.value -> LaneDirection.SHAPE_SLIGHT_RIGHT
else
-> LaneDirection.SHAPE_UNKNOWN
}
@@ -213,8 +213,8 @@ class IconMapper {
return when (direction) {
"left_straight" -> {
when (stepData.currentManeuverType) {
ManeuverType.TYPE_TURN_NORMAL_LEFT.ordinal -> "left_o_straight_x"
ManeuverType.TYPE_STRAIGHT.ordinal -> "left_x_straight_o"
ManeuverType.TYPE_TURN_NORMAL_LEFT.value -> "left_o_straight_x"
ManeuverType.TYPE_STRAIGHT.value -> "left_x_straight_o"
else
-> "left_x_straight_x"
}
@@ -222,29 +222,29 @@ class IconMapper {
"right_straight" -> {
when (stepData.currentManeuverType) {
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"
ManeuverType.TYPE_TURN_NORMAL_RIGHT.value -> "right_x_straight_x"
ManeuverType.TYPE_STRAIGHT.value -> "right_x_straight_o"
ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value -> "right_o_straight_o"
else
-> "right_x_straight_x"
}
}
"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
"right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.value) "${direction}_o" else "${direction}_x"
"left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.value) "${direction}_o" else "${direction}_x"
"straight" -> if (stepData.currentManeuverType == ManeuverType.TYPE_STRAIGHT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.value
) "${direction}_o" else "${direction}_x"
"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
"right_slight", "slight_right" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_RIGHT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_RIGHT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_RIGHT.value
) "slight_right_o" else "slight_right_x"
"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
"left_slight", "slight_left" -> if (stepData.currentManeuverType == ManeuverType.TYPE_TURN_SLIGHT_LEFT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_TURN_NORMAL_LEFT.value
|| stepData.currentManeuverType == ManeuverType.TYPE_KEEP_LEFT.value
) "slight_left_o" else "slight_left_x"
else -> {
@@ -3,7 +3,6 @@ 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
@@ -27,6 +26,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.maplibre.geojson.FeatureCollection
import java.lang.reflect.Modifier
import java.time.LocalDateTime
import java.time.ZoneOffset
@@ -101,6 +101,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
MutableLiveData()
}
val gson: com.google.gson.Gson = GsonBuilder().create()
/**
* Loads all recent places from Preferences and calculates distances.
@@ -111,14 +112,14 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
try {
val settingsRepository = getSettingsRepository(context)
val rp = settingsRepository.recentPlacesFlow.first()
val gson = GsonBuilder().serializeNulls().create()
val places = gson.fromJson(rp, Places::class.java)
val pl = mutableListOf<Place>()
var id: Long = 0
if (rp.isNotEmpty()) {
for (place in places.places) {
if (place.category.equals(Constants.RECENT)
|| place.category.equals(Constants.FAVORITES)) {
if (place.category == Constants.RECENT
|| place.category == Constants.FAVORITES
) {
val plLocation = location(place.longitude, place.latitude)
if (place.latitude != 0.0) {
val distance =
@@ -289,7 +290,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
var sortedList: List<SearchResult>
viewModelScope.launch(Dispatchers.IO) {
val placesJson = repository.searchPlaces(search, location)
val gson = GsonBuilder().serializeNulls().create()
val places = gson.fromJson(placesJson, Search::class.java)
val distPlaces = mutableListOf<SearchResult>()
places.forEach {
@@ -314,7 +314,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
viewModelScope.launch(Dispatchers.IO) {
val placesJson = repository.searchPlaces(search, location)
if (placesJson.isNotEmpty()) {
val gson = GsonBuilder().serializeNulls().create()
val places = gson.fromJson(placesJson, Search::class.java)
val distPlaces = mutableListOf<SearchResult>()
places.forEach {
@@ -434,7 +433,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
viewModelScope.launch(Dispatchers.IO) {
try {
val places = mutableListOf<Place>()
val gson = GsonBuilder().serializeNulls().create()
val settingsRepository = getSettingsRepository(context)
val rp = settingsRepository.recentPlacesFlow.first()
var id: Long = 0
@@ -444,6 +442,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
for (curPlace in recentPlaces) {
if (curPlace.name != place.name || curPlace.category != place.category) {
curPlace.id = id
curPlace.route = ""
places.add(curPlace)
id += 1
}
@@ -451,6 +450,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
}
val current = LocalDateTime.now(ZoneOffset.UTC)
place.lastDate = current.atZone(ZoneOffset.UTC).toEpochSecond()
place.route = ""
places.add(place)
settingsRepository.setRecentPlaces(gson.toJson(Places(places)))
} catch (e: Exception) {
@@ -473,7 +473,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
fun deletePlace(context: Context, place: Place) {
viewModelScope.launch(Dispatchers.IO) {
try {
val gson = GsonBuilder().serializeNulls().create()
val settingsRepository = getSettingsRepository(context)
val rp = settingsRepository.recentPlacesFlow.first()
val places = mutableListOf<Place>()
@@ -482,6 +481,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
gson.fromJson(rp, Places::class.java).places.sortedBy { it.lastDate }
for (curPlace in rPlaces) {
if (curPlace.name != place.name || curPlace.category != place.category) {
curPlace.route = ""
places.add(curPlace)
}
}
@@ -511,12 +511,11 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
* Loads recent places as Compose SnapshotStateList.
* @return SnapshotStateList of recent places
*/
fun loadRecentPlace(context: Context): SnapshotStateList<Place?> {
fun loadRecentPlaces(context: Context): SnapshotStateList<Place?> {
val pl = mutableListOf<Place>()
val settingsRepository = getSettingsRepository(context)
val rp = runBlocking { settingsRepository.recentPlacesFlow.first() }
if (rp.isNotEmpty()) {
val gson = GsonBuilder().serializeNulls().create()
val recentPlaces = gson.fromJson(rp, Places::class.java).places.sortedBy { it.lastDate }
for (place in recentPlaces) {
if (place.category == Constants.RECENT) {
@@ -1,5 +1,6 @@
package com.kouros.navigation.repository
import android.util.Log
import com.kouros.navigation.data.datastore.DataStoreManager
import kotlinx.coroutines.flow.Flow
@@ -147,7 +147,7 @@ fun duration(
if (updateDuration.toMillis().absoluteValue < 1000) {
2.seconds
} else {
((updateDuration!!.toMillis().absoluteValue * 1.2).toDuration(DurationUnit.MILLISECONDS))
((updateDuration!!.toMillis().absoluteValue * 1.8).toDuration(DurationUnit.MILLISECONDS))
}
}
return cameraDuration
+14 -10
View File
@@ -15,7 +15,7 @@ junitVersion = "1.3.0"
espressoCore = "3.7.0"
kotlinxSerializationJson = "1.10.0"
lifecycleRuntimeKtx = "2.10.0"
composeBom = "2026.03.00"
composeBom = "2026.03.01"
appcompat = "1.7.1"
material = "1.13.0"
carApp = "1.7.0"
@@ -26,27 +26,29 @@ mockitoKotlin = "6.3.0"
rules = "1.7.0"
runner = "1.7.0"
material3 = "1.4.0"
runtimeLivedata = "1.10.5"
foundation = "1.10.5"
runtimeLivedata = "1.10.6"
foundation = "1.10.6"
maplibre-compose = "0.12.1"
playServicesLocation = "21.3.0"
runtime = "1.10.5"
runtime = "1.10.6"
accompanist = "0.37.3"
uiVersion = "1.10.5"
uiText = "1.10.5"
uiVersion = "1.10.6"
uiText = "1.10.6"
navigationCompose = "2.9.7"
uiToolingPreview = "1.10.5"
uiTooling = "1.10.5"
uiToolingPreview = "1.10.6"
uiTooling = "1.10.6"
material3WindowSizeClass = "1.4.0"
uiGraphics = "1.10.5"
uiGraphics = "1.10.6"
window = "1.5.1"
foundationLayout = "1.10.5"
foundationLayout = "1.10.6"
datastorePreferences = "1.2.1"
datastoreCore = "1.2.1"
monitor = "1.8.0"
robolectric = "4.16.1"
truth = "1.4.5"
testCore = "1.7.0"
archCoreTesting = "2.2.0"
kotlinxCoroutinesTest = "1.10.1"
[libraries]
android-gpx-parser = { module = "com.github.ticofab:android-gpx-parser", version.ref = "androidGpxParser" }
@@ -98,6 +100,8 @@ androidx-monitor = { group = "androidx.test", name = "monitor", version.ref = "m
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
google-truth = { module = "com.google.truth:truth", version.ref = "truth" }
androidx-test-core = { module = "androidx.test:core", version.ref = "testCore" }
androidx-arch-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "archCoreTesting" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }