Test, Lanes
This commit is contained in:
@@ -13,8 +13,8 @@ android {
|
|||||||
applicationId = "com.kouros.navigation"
|
applicationId = "com.kouros.navigation"
|
||||||
minSdk = 33
|
minSdk = 33
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 56
|
versionCode = 57
|
||||||
versionName = "0.2.0.56"
|
versionName = "0.2.0.57"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.annotation.RequiresPermission
|
import androidx.annotation.RequiresPermission
|
||||||
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -49,7 +50,9 @@ import com.google.android.gms.location.FusedLocationProviderClient
|
|||||||
import com.google.android.gms.location.LocationServices
|
import com.google.android.gms.location.LocationServices
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
||||||
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||||
|
import com.kouros.navigation.data.Constants.TILT
|
||||||
import com.kouros.navigation.data.Constants.homeVogelhart
|
import com.kouros.navigation.data.Constants.homeVogelhart
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.model.BaseStyleModel
|
import com.kouros.navigation.model.BaseStyleModel
|
||||||
@@ -84,10 +87,9 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
var tilt = 50.0
|
var tilt = TILT
|
||||||
val useMock = false
|
val useMock = false
|
||||||
val type = SimulationType.SIMULATE
|
val type = SimulationType.SIMULATE
|
||||||
|
|
||||||
val stepData: MutableLiveData<StepData> by lazy {
|
val stepData: MutableLiveData<StepData> by lazy {
|
||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
@@ -148,9 +150,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
getSettingsViewModel(applicationContext).routingEngine.collect {
|
getSettingsViewModel(applicationContext).routingEngine.first()
|
||||||
|
getSettingsViewModel(applicationContext).recentPlaces.first()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
@@ -169,21 +170,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
permissions = permissions,
|
permissions = permissions,
|
||||||
requiredPermissions = listOf(permissions.first()),
|
requiredPermissions = listOf(permissions.first()),
|
||||||
onGranted = {
|
onGranted = {
|
||||||
App()
|
Application()
|
||||||
// auto navigate
|
|
||||||
if (useMock) {
|
|
||||||
// navigationViewModel.loadRoute(
|
|
||||||
// applicationContext,
|
|
||||||
// homeVogelhart,
|
|
||||||
// homeHohenwaldeck,
|
|
||||||
// 0F
|
|
||||||
// )
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun StartScreen(
|
fun StartScreen(
|
||||||
@@ -193,7 +184,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
val appViewModel: AppViewModel = appViewModel()
|
val appViewModel: AppViewModel = appViewModel()
|
||||||
val darkMode by appViewModel.darkMode.collectAsState()
|
val darkMode by appViewModel.darkMode.collectAsState()
|
||||||
|
|
||||||
|
|
||||||
val baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
|
val baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
|
||||||
val scaffoldState = rememberBottomSheetScaffoldState()
|
val scaffoldState = rememberBottomSheetScaffoldState()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
@@ -207,10 +197,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
val userLocationState = rememberUserLocationState(locationProvider)
|
val userLocationState = rememberUserLocationState(locationProvider)
|
||||||
val locationState = locationProvider.location.collectAsState()
|
val locationState = locationProvider.location.collectAsState()
|
||||||
updateLocation(locationState.value)
|
updateLocation(locationState.value)
|
||||||
var latitude by remember { mutableDoubleStateOf(0.0) }
|
|
||||||
if (locationState.value != null) {
|
|
||||||
latitude = locationState.value!!.position.latitude
|
|
||||||
}
|
|
||||||
val step: StepData? by stepData.observeAsState()
|
val step: StepData? by stepData.observeAsState()
|
||||||
val nextStep: StepData? by nextStepData.observeAsState()
|
val nextStep: StepData? by nextStepData.observeAsState()
|
||||||
fun closeSheet() {
|
fun closeSheet() {
|
||||||
@@ -227,7 +213,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
sheetPeekHeight = sheetPeekHeightState.value,
|
sheetPeekHeight = sheetPeekHeightState.value,
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
SheetContent(latitude, step, nextStep) { closeSheet() }
|
SheetContent(step, nextStep) { closeSheet() }
|
||||||
},
|
},
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Box(
|
Box(
|
||||||
@@ -255,7 +241,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App() {
|
fun Application() {
|
||||||
val appViewModel: AppViewModel = appViewModel()
|
val appViewModel: AppViewModel = appViewModel()
|
||||||
val lastRoute by appViewModel.lastRoute.collectAsState()
|
val lastRoute by appViewModel.lastRoute.collectAsState()
|
||||||
if (lastRoute.isNotEmpty()) {
|
if (lastRoute.isNotEmpty()) {
|
||||||
@@ -286,12 +272,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SheetContent(
|
fun SheetContent(
|
||||||
locationState: Double, step: StepData?, nextStep: StepData?, closeSheet: () -> Unit
|
step: StepData?, nextStep: StepData?, closeSheet: () -> Unit
|
||||||
) {
|
) {
|
||||||
if (!routeModel.isNavigating()) {
|
if (!routeModel.isNavigating()) {
|
||||||
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||||
} else {
|
} else {
|
||||||
if (step != null && nextStep != null) {
|
if (step != null) {
|
||||||
NavigationSheet(
|
NavigationSheet(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
routeModel,
|
routeModel,
|
||||||
@@ -301,8 +287,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
{ simulateNavigation() })
|
{ simulateNavigation() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For recomposition!
|
|
||||||
Text("$locationState", fontSize = 12.sp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLocation(location: Location?) {
|
fun updateLocation(location: Location?) {
|
||||||
@@ -314,7 +298,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (routeModel.isNavigating()) {
|
if (routeModel.isNavigating()) {
|
||||||
val snapedLocation =
|
val snapedLocation =
|
||||||
snapLocation(currentLocation, routeModel.route.maneuverLocations())
|
snapLocation(currentLocation, routeModel.route.maneuverLocations())
|
||||||
updateLocationInternal(snapedLocation, location)
|
updateLocationInternal(currentLocation, location)
|
||||||
} else {
|
} else {
|
||||||
updateLocationInternal(currentLocation, location)
|
updateLocationInternal(currentLocation, location)
|
||||||
}
|
}
|
||||||
@@ -335,7 +319,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (isNavigating()) {
|
if (isNavigating()) {
|
||||||
updateLocation( currentLocation, navigationViewModel)
|
updateLocation( currentLocation, navigationViewModel)
|
||||||
stepData.value = currentStep()
|
stepData.value = currentStep()
|
||||||
|
if (navState.nextStep) {
|
||||||
nextStepData.value = nextStep()
|
nextStepData.value = nextStep()
|
||||||
|
}
|
||||||
if (navState.maneuverType in 39..42 && routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
|
if (navState.maneuverType in 39..42 && routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
|
||||||
// stopNavigation()
|
// stopNavigation()
|
||||||
navState = navState.copy(arrived = true)
|
navState = navState.copy(arrived = true)
|
||||||
@@ -367,7 +353,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
mock.setMockLocation(latitude, longitude, 0F)
|
mock.setMockLocation(latitude, longitude, 0F)
|
||||||
}
|
}
|
||||||
routeData.value = ""
|
routeData.value = ""
|
||||||
stepData.value = StepData("", 0.0, 0, 0, 0, 0.0)
|
stepData.value = StepData("", "",0.0, 0, 0, 0, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun simulateNavigation() {
|
fun simulateNavigation() {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ fun MapView(
|
|||||||
|
|
||||||
val appViewModel: AppViewModel = appViewModel()
|
val appViewModel: AppViewModel = appViewModel()
|
||||||
val showBuildings by appViewModel.show3D.collectAsState()
|
val showBuildings by appViewModel.show3D.collectAsState()
|
||||||
|
val darkMode by appViewModel.darkMode.collectAsState()
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
NavigationInfo(step, nextStep)
|
NavigationInfo(step, nextStep)
|
||||||
@@ -87,7 +88,7 @@ fun MapView(
|
|||||||
duration = 1.seconds
|
duration = 1.seconds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
NavigationImage(paddingValues, width, height / 6)
|
NavigationImage(paddingValues, width, height / 6, "", darkMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ fun NavigationInfo(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(CardPadding),
|
modifier = Modifier.padding(CardPadding),
|
||||||
horizontalAlignment = Alignment.Start
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(top = ElementSpacing)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(currentStep.icon),
|
painter = painterResource(currentStep.icon),
|
||||||
@@ -58,6 +62,9 @@ fun NavigationInfo(
|
|||||||
modifier = Modifier.size(IconSize),
|
modifier = Modifier.size(IconSize),
|
||||||
tint = MaterialTheme.colorScheme.primary
|
tint = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.padding(horizontal = SpacerWidth))
|
||||||
|
DistanceText(distance = currentStep.leftStepDistance)
|
||||||
|
}
|
||||||
|
|
||||||
if (currentStep.isExitManeuver) {
|
if (currentStep.isExitManeuver) {
|
||||||
Text(
|
Text(
|
||||||
@@ -72,8 +79,6 @@ fun NavigationInfo(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(top = ElementSpacing)
|
modifier = Modifier.padding(top = ElementSpacing)
|
||||||
) {
|
) {
|
||||||
DistanceText(distance = currentStep.leftStepDistance)
|
|
||||||
Spacer(modifier = Modifier.padding(horizontal = SpacerWidth))
|
|
||||||
Text(
|
Text(
|
||||||
text = currentStep.instruction,
|
text = currentStep.instruction,
|
||||||
fontSize = PrimaryTextSize,
|
fontSize = PrimaryTextSize,
|
||||||
|
|||||||
@@ -27,16 +27,12 @@ fun NavigationSheet(
|
|||||||
applicationContext: Context,
|
applicationContext: Context,
|
||||||
routeModel: RouteModel,
|
routeModel: RouteModel,
|
||||||
step: StepData,
|
step: StepData,
|
||||||
nextStep: StepData,
|
nextStep: StepData?,
|
||||||
stopNavigation: () -> Unit,
|
stopNavigation: () -> Unit,
|
||||||
simulateNavigation: () -> Unit,
|
simulateNavigation: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val distance = (step.leftDistance / 1000).round(1)
|
val distance = (step.leftDistance / 1000).round(1)
|
||||||
|
|
||||||
if (step.lane.isNotEmpty()) {
|
|
||||||
// routeModel.navState.iconMapper.addLanes( step)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
FlowRow(horizontalArrangement = Arrangement.SpaceEvenly) {
|
FlowRow(horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||||
Text(formatDateTime(step.arrivalTime), fontSize = 22.sp)
|
Text(formatDateTime(step.arrivalTime), fontSize = 22.sp)
|
||||||
|
|||||||
@@ -2,14 +2,6 @@ package com.kouros.navigation.ui.theme
|
|||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
//val Purple80 = Color(0xFFD0BCFF)
|
|
||||||
//val PurpleGrey80 = Color(0xFFCCC2DC)
|
|
||||||
//val Pink80 = Color(0xFFEFB8C8)
|
|
||||||
//
|
|
||||||
//val Purple40 = Color(0xFF6650a4)
|
|
||||||
//val PurpleGrey40 = Color(0xFF625b71)
|
|
||||||
//val Pink40 = Color(0xFF7D5260)
|
|
||||||
|
|
||||||
val md_theme_light_primary = Color(0xFF825500)
|
val md_theme_light_primary = Color(0xFF825500)
|
||||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
||||||
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
|
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.junit.runner.RunWith
|
|||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.maplibre.compose.expressions.dsl.step
|
||||||
import kotlin.collections.forEach
|
import kotlin.collections.forEach
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +66,7 @@ class RouteModelTest {
|
|||||||
val stepData = routeModel.currentStep()
|
val stepData = routeModel.currentStep()
|
||||||
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_RIGHT)
|
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_RIGHT)
|
||||||
assertEquals(stepData.instruction, "Silcherstraße")
|
assertEquals(stepData.instruction, "Silcherstraße")
|
||||||
assertEquals(stepData.leftStepDistance, 70.0, 1.0)
|
assertEquals(stepData.leftStepDistance, 30.0, 1.0)
|
||||||
val nextStepData = routeModel.nextStep()
|
val nextStepData = routeModel.nextStep()
|
||||||
assertEquals(nextStepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_RIGHT)
|
assertEquals(nextStepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_RIGHT)
|
||||||
assertEquals(nextStepData.instruction, "Schmalkaldener Straße")
|
assertEquals(nextStepData.instruction, "Schmalkaldener Straße")
|
||||||
@@ -92,12 +93,16 @@ class RouteModelTest {
|
|||||||
location.longitude = 11.585125
|
location.longitude = 11.585125
|
||||||
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
||||||
val stepData = routeModel.currentStep()
|
val stepData = routeModel.currentStep()
|
||||||
|
if (routeModel.navState.nextStep) {
|
||||||
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_STRAIGHT)
|
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_STRAIGHT)
|
||||||
assertEquals(stepData.instruction, "Ingolstädter Straße")
|
assertEquals(stepData.instruction, "Ingolstädter Straße")
|
||||||
assertEquals(stepData.leftStepDistance, 350.0, 1.0)
|
|
||||||
val nextStepData = routeModel.nextStep()
|
val nextStepData = routeModel.nextStep()
|
||||||
assertEquals(nextStepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_LEFT)
|
assertEquals(nextStepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_LEFT)
|
||||||
assertEquals(nextStepData.instruction, "Schenkendorfstraße")
|
assertEquals(nextStepData.instruction, "Schenkendorfstraße")
|
||||||
|
} else {
|
||||||
|
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_LEFT)
|
||||||
|
}
|
||||||
|
assertEquals(stepData.leftStepDistance, 300.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,7 +114,7 @@ class RouteModelTest {
|
|||||||
val stepData = routeModel.currentStep()
|
val stepData = routeModel.currentStep()
|
||||||
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_LEFT)
|
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_TURN_NORMAL_LEFT)
|
||||||
assertEquals(stepData.instruction, "Schenkendorfstraße")
|
assertEquals(stepData.instruction, "Schenkendorfstraße")
|
||||||
assertEquals(stepData.leftStepDistance, 240.0, 1.0)
|
assertEquals(stepData.leftStepDistance, 170.0, 1.0)
|
||||||
assertEquals(stepData.lane.size, 4)
|
assertEquals(stepData.lane.size, 4)
|
||||||
assertEquals(stepData.lane.first().valid, true)
|
assertEquals(stepData.lane.first().valid, true)
|
||||||
assertEquals(stepData.lane.last().valid, false)
|
assertEquals(stepData.lane.last().valid, false)
|
||||||
@@ -127,6 +132,47 @@ class RouteModelTest {
|
|||||||
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_DESTINATION_LEFT)
|
assertEquals(stepData.currentManeuverType, Maneuver.TYPE_DESTINATION_LEFT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkLanes() {
|
||||||
|
for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) {
|
||||||
|
val curLocation = location(waypoint[0], waypoint[1])
|
||||||
|
if (routeModel.isNavigating()) {
|
||||||
|
if (index in 42..45) {
|
||||||
|
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
||||||
|
val stepData = routeModel.currentStep()
|
||||||
|
assertEquals(stepData.lane.size, 4)
|
||||||
|
assertEquals(stepData.lane.first().valid, true)
|
||||||
|
assertEquals(stepData.lane.first().indications.first(), "SLIGHT_LEFT")
|
||||||
|
}
|
||||||
|
if (index in 61..61) {
|
||||||
|
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
||||||
|
val stepData = routeModel.currentStep()
|
||||||
|
assertEquals(stepData.lane.size, 2)
|
||||||
|
assertEquals(stepData.lane.first().valid, true)
|
||||||
|
assertEquals(stepData.lane.first().indications.first(), "STRAIGHT")
|
||||||
|
}
|
||||||
|
if (index in 74..74) {
|
||||||
|
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
||||||
|
val stepData = routeModel.currentStep()
|
||||||
|
assertEquals(stepData.lane.size, 3)
|
||||||
|
assertEquals(stepData.lane.first().valid, true)
|
||||||
|
assertEquals(stepData.lane.first().indications.first(), "SLIGHT_LEFT")
|
||||||
|
assertEquals(stepData.lane[1].valid, true)
|
||||||
|
assertEquals(stepData.lane[1].indications.first(), "SLIGHT_LEFT")
|
||||||
|
}
|
||||||
|
if (index in 265..265) {
|
||||||
|
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
||||||
|
val stepData = routeModel.currentStep()
|
||||||
|
assertEquals(stepData.lane.size, 2)
|
||||||
|
assertEquals(stepData.lane.first().valid, false)
|
||||||
|
assertEquals(stepData.lane.first().indications.first(), "STRAIGHT")
|
||||||
|
assertEquals(stepData.lane[1].valid, true)
|
||||||
|
assertEquals(stepData.lane[1].indications.first(), "SLIGHT_RIGHT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun simulate() {
|
fun simulate() {
|
||||||
for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) {
|
for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) {
|
||||||
@@ -136,9 +182,24 @@ class RouteModelTest {
|
|||||||
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository()))
|
||||||
val stepData = routeModel.currentStep()
|
val stepData = routeModel.currentStep()
|
||||||
val nextData = routeModel.nextStep()
|
val nextData = routeModel.nextStep()
|
||||||
println("${stepData.leftStepDistance} : ${stepData.instruction} ${stepData.currentManeuverType} : ${nextData.instruction} ${nextData.currentManeuverType}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `leftStepDistance Inglolstädter `() {
|
||||||
|
val location: Location = location( 11.584578, 48.183653)
|
||||||
|
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) )
|
||||||
|
val step = routeModel.currentStep()
|
||||||
|
assertEquals(step.leftStepDistance, 650.0, 0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `leftStepDistance Vogelhart `() {
|
||||||
|
val location: Location = location( 11.578911, 48.185565)
|
||||||
|
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) )
|
||||||
|
val step = routeModel.currentStep()
|
||||||
|
assertEquals(step.leftStepDistance , 30.0, 1.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +151,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
viewModelStoreOwner = object : ViewModelStoreOwner {
|
viewModelStoreOwner = object : ViewModelStoreOwner {
|
||||||
override val viewModelStore = ViewModelStore()
|
override val viewModelStore = ViewModelStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
awaitCancellation()
|
awaitCancellation()
|
||||||
@@ -320,8 +321,6 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
* Snaps location to route and checks for deviation requiring reroute.
|
* Snaps location to route and checks for deviation requiring reroute.
|
||||||
*/
|
*/
|
||||||
private fun handleNavigationLocation(location: Location) {
|
private fun handleNavigationLocation(location: Location) {
|
||||||
routeModel.navState =
|
|
||||||
routeModel.navState.copy(travelMessage = "${location.latitude} ${location.longitude}")
|
|
||||||
navigationScreen.updateTrip(location)
|
navigationScreen.updateTrip(location)
|
||||||
if (routeModel.navState.arrived) return
|
if (routeModel.navState.arrived) return
|
||||||
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
||||||
@@ -349,7 +348,6 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
routeModel.stopNavigation()
|
routeModel.stopNavigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// URI host for deep linking
|
// URI host for deep linking
|
||||||
var uriHost: String = "navigation"
|
var uriHost: String = "navigation"
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.kouros.navigation.car.map.MapLibre
|
|||||||
import com.kouros.navigation.car.map.cameraState
|
import com.kouros.navigation.car.map.cameraState
|
||||||
import com.kouros.navigation.car.map.getPaddingValues
|
import com.kouros.navigation.car.map.getPaddingValues
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
|
import com.kouros.navigation.data.Constants.TILT
|
||||||
import com.kouros.navigation.data.Constants.homeVogelhart
|
import com.kouros.navigation.data.Constants.homeVogelhart
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.model.BaseStyleModel
|
import com.kouros.navigation.model.BaseStyleModel
|
||||||
@@ -104,6 +105,9 @@ class SurfaceRenderer(
|
|||||||
// Speed limit for current road
|
// Speed limit for current road
|
||||||
val maxSpeed = MutableLiveData(0)
|
val maxSpeed = MutableLiveData(0)
|
||||||
|
|
||||||
|
// Current street name
|
||||||
|
val street = MutableLiveData("")
|
||||||
|
|
||||||
// Current view mode (navigation, preview, etc.)
|
// Current view mode (navigation, preview, etc.)
|
||||||
var viewStyle = ViewStyle.VIEW
|
var viewStyle = ViewStyle.VIEW
|
||||||
|
|
||||||
@@ -116,8 +120,8 @@ class SurfaceRenderer(
|
|||||||
// Compose view for rendering the map
|
// Compose view for rendering the map
|
||||||
lateinit var mapView: ComposeView
|
lateinit var mapView: ComposeView
|
||||||
|
|
||||||
// Camera tilt angle (default 55 degrees for navigation)
|
// Camera tilt angle (default 60 degrees for navigation)
|
||||||
var tilt = 55.0
|
var tilt = TILT
|
||||||
|
|
||||||
// Map base style (day/night)
|
// Map base style (day/night)
|
||||||
val style: MutableLiveData<BaseStyle> by lazy {
|
val style: MutableLiveData<BaseStyle> by lazy {
|
||||||
@@ -267,7 +271,7 @@ class SurfaceRenderer(
|
|||||||
speedCameras,
|
speedCameras,
|
||||||
showBuildings
|
showBuildings
|
||||||
)
|
)
|
||||||
ShowPosition(cameraState, position, paddingValues)
|
ShowPosition(cameraState, position, paddingValues, darkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,12 +282,14 @@ class SurfaceRenderer(
|
|||||||
fun ShowPosition(
|
fun ShowPosition(
|
||||||
cameraState: CameraState,
|
cameraState: CameraState,
|
||||||
position: CameraPosition?,
|
position: CameraPosition?,
|
||||||
paddingValues: PaddingValues
|
paddingValues: PaddingValues,
|
||||||
|
darkMode: Int
|
||||||
) {
|
) {
|
||||||
val cameraDuration =
|
val cameraDuration =
|
||||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||||
val currentSpeed: Float? by speed.observeAsState()
|
val currentSpeed: Float? by speed.observeAsState()
|
||||||
val speed: Int? by maxSpeed.observeAsState()
|
val speed: Int? by maxSpeed.observeAsState()
|
||||||
|
val streetName: String? by street.observeAsState()
|
||||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||||
DrawNavigationImages(
|
DrawNavigationImages(
|
||||||
paddingValues,
|
paddingValues,
|
||||||
@@ -291,7 +297,8 @@ class SurfaceRenderer(
|
|||||||
speed!!,
|
speed!!,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
0.0
|
streetName,
|
||||||
|
darkMode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
LaunchedEffect(position, viewStyle) {
|
LaunchedEffect(position, viewStyle) {
|
||||||
@@ -345,6 +352,11 @@ class SurfaceRenderer(
|
|||||||
*/
|
*/
|
||||||
fun updateLocation(location: Location) {
|
fun updateLocation(location: Location) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
if (routeModel.isNavigating()) {
|
||||||
|
street.value = routeModel.currentStep().street
|
||||||
|
} else {
|
||||||
|
street.value = ""
|
||||||
|
}
|
||||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||||
val bearing = if (carOrientation == 999F) {
|
val bearing = if (carOrientation == 999F) {
|
||||||
if (location.hasBearing()) {
|
if (location.hasBearing()) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.car.map
|
|||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -29,7 +30,6 @@ import com.kouros.navigation.data.Constants
|
|||||||
import com.kouros.navigation.data.NavigationColor
|
import com.kouros.navigation.data.NavigationColor
|
||||||
import com.kouros.navigation.data.RouteColor
|
import com.kouros.navigation.data.RouteColor
|
||||||
import com.kouros.navigation.data.SpeedColor
|
import com.kouros.navigation.data.SpeedColor
|
||||||
import com.kouros.navigation.model.RouteModel
|
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.camera.CameraState
|
import org.maplibre.compose.camera.CameraState
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
@@ -292,9 +292,10 @@ fun DrawNavigationImages(
|
|||||||
maxSpeed: Int,
|
maxSpeed: Int,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
lat: Double?
|
streetName: String?,
|
||||||
|
darkMode: Int,
|
||||||
) {
|
) {
|
||||||
NavigationImage(padding, width, height)
|
NavigationImage(padding, width, height, streetName, darkMode)
|
||||||
if (speed != null) {
|
if (speed != null) {
|
||||||
CurrentSpeed(width, height, speed, maxSpeed)
|
CurrentSpeed(width, height, speed, maxSpeed)
|
||||||
}
|
}
|
||||||
@@ -305,9 +306,27 @@ fun DrawNavigationImages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
fun NavigationImage(
|
||||||
|
padding: PaddingValues,
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
streetName: String?,
|
||||||
|
darkMode: Int
|
||||||
|
) {
|
||||||
|
|
||||||
val imageSize = (height / 8)
|
val imageSize = (height / 8)
|
||||||
val navigationColor = remember { NavigationColor }
|
val navigationColor = remember { NavigationColor }
|
||||||
|
|
||||||
|
val textMeasurerStreet = rememberTextMeasurer()
|
||||||
|
val street = streetName.toString()
|
||||||
|
val styleStreet = TextStyle(
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = if (darkMode == 1) Color.Yellow else Color.Red,
|
||||||
|
)
|
||||||
|
val textLayoutStreet = remember(street) {
|
||||||
|
textMeasurerStreet.measure(street, styleStreet)
|
||||||
|
}
|
||||||
|
|
||||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(padding)) {
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(padding)) {
|
||||||
Canvas(
|
Canvas(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -325,6 +344,22 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
|||||||
.size(imageSize.dp, imageSize.dp)
|
.size(imageSize.dp, imageSize.dp)
|
||||||
.scale(scaleX = 1f, scaleY = 0.7f),
|
.scale(scaleX = 1f, scaleY = 0.7f),
|
||||||
)
|
)
|
||||||
|
Canvas(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(imageSize.dp + 100.dp, imageSize.dp + 80.dp)
|
||||||
|
) {
|
||||||
|
if (!streetName.isNullOrEmpty()) {
|
||||||
|
drawText(
|
||||||
|
textMeasurer = textMeasurerStreet,
|
||||||
|
text = streetName,
|
||||||
|
style = styleStreet,
|
||||||
|
topLeft = Offset(
|
||||||
|
x = center.x - textLayoutStreet.size.width / 2,
|
||||||
|
y = center.y + textLayoutStreet.size.height,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,15 +50,19 @@ class RouteCarModel() : RouteModel() {
|
|||||||
}
|
}
|
||||||
val step =
|
val step =
|
||||||
Step.Builder(currentStepCueWithImage)
|
Step.Builder(currentStepCueWithImage)
|
||||||
.setManeuver(
|
|
||||||
maneuver.build()
|
|
||||||
)
|
|
||||||
if (navState.destination.street != null) {
|
if (navState.destination.street != null) {
|
||||||
step.setRoad(navState.destination.street!!)
|
step.setRoad(navState.destination.street!!)
|
||||||
}
|
}
|
||||||
if (stepData.lane.isNotEmpty()) {
|
if (stepData.lane.isNotEmpty()) {
|
||||||
addLanes(carContext, step, stepData)
|
val lanesAdded = addLanes(carContext, step, stepData)
|
||||||
|
if (lanesAdded) {
|
||||||
|
maneuver.setIcon(createCarIcon(carContext, R.drawable.empty))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
step.setManeuver(
|
||||||
|
maneuver.build()
|
||||||
|
)
|
||||||
return step.build()
|
return step.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +123,10 @@ class RouteCarModel() : RouteModel() {
|
|||||||
return travelBuilder.build()
|
return travelBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) {
|
fun addLanes(carContext: CarContext, step: Step.Builder, stepData: StepData) : Boolean {
|
||||||
var laneImageAdded = false
|
var laneImageAdded = false
|
||||||
stepData.lane.forEach {
|
stepData.lane.forEach {
|
||||||
if (it.indications.isNotEmpty() && it.valid) {
|
if (it.indications.isNotEmpty()) {
|
||||||
val sorted = it.indications.sorted()
|
val sorted = it.indications.sorted()
|
||||||
var direction = ""
|
var direction = ""
|
||||||
sorted.forEach { it2 ->
|
sorted.forEach { it2 ->
|
||||||
@@ -140,12 +144,13 @@ class RouteCarModel() : RouteModel() {
|
|||||||
}
|
}
|
||||||
val laneType =
|
val laneType =
|
||||||
Lane.Builder()
|
Lane.Builder()
|
||||||
.addDirection(LaneDirection.create(laneDirection, false))
|
.addDirection(LaneDirection.create(laneDirection, true))
|
||||||
.build()
|
.build()
|
||||||
step.addLane(laneType)
|
step.addLane(laneType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return laneImageAdded
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createString(
|
fun createString(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.car.app.model.Template
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class DarkModeSettings(private val carContext: CarContext) : Screen(carContext) {
|
class DarkModeSettings(private val carContext: CarContext) : Screen(carContext) {
|
||||||
@@ -23,9 +24,7 @@ class DarkModeSettings(private val carContext: CarContext) : Screen(carContext)
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.darkMode.collect {
|
settingsViewModel.darkMode.first()
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.car.app.model.Toggle
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
||||||
@@ -22,9 +23,7 @@ class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.show3D.collect {
|
settingsViewModel.show3D.first()
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ class NavigationScreen(
|
|||||||
init {
|
init {
|
||||||
observerManager.attachAllObservers(this)
|
observerManager.attachAllObservers(this)
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
getSettingsViewModel(carContext).routingEngine.collect {
|
getSettingsViewModel(carContext).routingEngine.first()
|
||||||
}
|
getSettingsViewModel(carContext).recentPlaces.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,14 +258,17 @@ class NavigationScreen(
|
|||||||
} else {
|
} else {
|
||||||
Distance.UNIT_METERS
|
Distance.UNIT_METERS
|
||||||
}
|
}
|
||||||
val nextStep = routeModel.nextStep(carContext = carContext)
|
|
||||||
return RoutingInfo.Builder()
|
val routingInfo = RoutingInfo.Builder()
|
||||||
.setCurrentStep(
|
.setCurrentStep(
|
||||||
routeModel.currentStep(carContext = carContext),
|
routeModel.currentStep(carContext = carContext),
|
||||||
Distance.create(currentDistance, displayUnit)
|
Distance.create(currentDistance, displayUnit)
|
||||||
)
|
)
|
||||||
.setNextStep(nextStep)
|
if (routeModel.navState.nextStep) {
|
||||||
.build()
|
val nextStep = routeModel.nextStep(carContext = carContext)
|
||||||
|
routingInfo.setNextStep(nextStep)
|
||||||
|
}
|
||||||
|
return routingInfo.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createActionStripBuilder(): ActionStrip.Builder {
|
private fun createActionStripBuilder(): ActionStrip.Builder {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@@ -32,13 +33,9 @@ class NavigationSettings(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.avoidTollway.collect {
|
settingsViewModel.avoidTollway.first()
|
||||||
settingsViewModel.avoidMotorway.collect {
|
settingsViewModel.avoidMotorway.first()
|
||||||
settingsViewModel.carLocation.collect {
|
settingsViewModel.carLocation.first()
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +50,12 @@ class NavigationSettings(
|
|||||||
settingsViewModel.onAvoidMotorway(checked)
|
settingsViewModel.onAvoidMotorway(checked)
|
||||||
motorWayToggleState = !motorWayToggleState
|
motorWayToggleState = !motorWayToggleState
|
||||||
}.setChecked(motorWayToggleState).build()
|
}.setChecked(motorWayToggleState).build()
|
||||||
listBuilder.addItem(buildRowForTemplate(R.string.avoid_highways_row_title, highwayToggle))
|
listBuilder.addItem(
|
||||||
|
buildRowForTemplate(
|
||||||
|
R.string.avoid_highways_row_title,
|
||||||
|
highwayToggle
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Tollway
|
// Tollway
|
||||||
val tollwayToggle: Toggle =
|
val tollwayToggle: Toggle =
|
||||||
@@ -92,7 +94,7 @@ class NavigationSettings(
|
|||||||
.setSingleList(listBuilder.build())
|
.setSingleList(listBuilder.build())
|
||||||
.setHeader(
|
.setHeader(
|
||||||
Header.Builder()
|
Header.Builder()
|
||||||
.setTitle(carContext.getString(R.string.display_settings))
|
.setTitle(carContext.getString(R.string.navigation_settings))
|
||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class PasswordSettings(
|
class PasswordSettings(
|
||||||
@@ -24,9 +25,7 @@ class PasswordSettings(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.tomTomApiKey.collect {
|
settingsViewModel.tomTomApiKey.first()
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.model.NavigationViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.utils.getSettingsViewModel
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RoutingSettings(private val carContext: CarContext, private var navigationViewModel: NavigationViewModel) : Screen(carContext) {
|
class RoutingSettings(private val carContext: CarContext, private var navigationViewModel: NavigationViewModel) : Screen(carContext) {
|
||||||
@@ -24,9 +25,7 @@ class RoutingSettings(private val carContext: CarContext, private var navigation
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
settingsViewModel.routingEngine.collect {
|
settingsViewModel.routingEngine.first()
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,17 +55,12 @@ data class ContactData(
|
|||||||
|
|
||||||
data class StepData (
|
data class StepData (
|
||||||
var instruction: String,
|
var instruction: String,
|
||||||
|
var street: String,
|
||||||
var leftStepDistance: Double,
|
var leftStepDistance: Double,
|
||||||
|
|
||||||
var currentManeuverType: Int,
|
var currentManeuverType: Int,
|
||||||
|
|
||||||
var icon: Int,
|
var icon: Int,
|
||||||
|
|
||||||
var arrivalTime : Long,
|
var arrivalTime : Long,
|
||||||
|
|
||||||
var leftDistance: Double,
|
var leftDistance: Double,
|
||||||
|
|
||||||
var lane: List<Lane> = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
var lane: List<Lane> = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
||||||
var exitNumber: Int = 0,
|
var exitNumber: Int = 0,
|
||||||
)
|
)
|
||||||
@@ -76,7 +71,7 @@ data class Locations (
|
|||||||
var lat : Double,
|
var lat : Double,
|
||||||
var lon : Double,
|
var lon : Double,
|
||||||
var street : String = "",
|
var street : String = "",
|
||||||
val search_filter: String,
|
val searchFilter: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SearchFilter(
|
data class SearchFilter(
|
||||||
@@ -123,7 +118,7 @@ object Constants {
|
|||||||
val homeVogelhart = location(11.5793748, 48.185749)
|
val homeVogelhart = location(11.5793748, 48.185749)
|
||||||
val homeHohenwaldeck = location( 11.594322, 48.1164817)
|
val homeHohenwaldeck = location( 11.594322, 48.1164817)
|
||||||
|
|
||||||
const val NEXT_STEP_THRESHOLD = 300.0
|
const val NEXT_STEP_THRESHOLD = 1000.0
|
||||||
|
|
||||||
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
||||||
|
|
||||||
@@ -138,6 +133,8 @@ object Constants {
|
|||||||
const val GMS_CAR_SPEED_PERMISSION = "com.google.android.gms.permission.CAR_SPEED"
|
const val GMS_CAR_SPEED_PERMISSION = "com.google.android.gms.permission.CAR_SPEED"
|
||||||
|
|
||||||
const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED"
|
const val AUTOMOTIVE_CAR_SPEED_PERMISSION = "android.car.permission.CAR_SPEED"
|
||||||
|
|
||||||
|
const val TILT = 60.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ data class NavigationState (
|
|||||||
val currentRouteIndex: Int = 0,
|
val currentRouteIndex: Int = 0,
|
||||||
val destination: Place = Place(),
|
val destination: Place = Place(),
|
||||||
val carConnection: Int = 0,
|
val carConnection: Int = 0,
|
||||||
var routingEngine: Int = 0,
|
val routingEngine: Int = 0,
|
||||||
|
val nextStep: Boolean = false,
|
||||||
)
|
)
|
||||||
@@ -36,8 +36,8 @@ class TomTomRoute {
|
|||||||
val allIntersections = mutableListOf<Intersection>()
|
val allIntersections = mutableListOf<Intersection>()
|
||||||
val steps = mutableListOf<Step>()
|
val steps = mutableListOf<Step>()
|
||||||
var lastPointIndex = 0
|
var lastPointIndex = 0
|
||||||
for (index in 1..< route.guidance.instructions.size) {
|
for (index in 1..<route.guidance.instructions.size) {
|
||||||
val lastInstruction = route.guidance.instructions[index-1]
|
val lastInstruction = route.guidance.instructions[index - 1]
|
||||||
val instruction = route.guidance.instructions[index]
|
val instruction = route.guidance.instructions[index]
|
||||||
val street = lastInstruction.street ?: ""
|
val street = lastInstruction.street ?: ""
|
||||||
val maneuverStreet = instruction.street ?: ""
|
val maneuverStreet = instruction.street ?: ""
|
||||||
@@ -47,7 +47,7 @@ class TomTomRoute {
|
|||||||
type = convertType(instruction.maneuver),
|
type = convertType(instruction.maneuver),
|
||||||
waypoints = points.subList(
|
waypoints = points.subList(
|
||||||
lastPointIndex,
|
lastPointIndex,
|
||||||
instruction.pointIndex+1,
|
instruction.pointIndex + 1,
|
||||||
),
|
),
|
||||||
exit = exitNumber(instruction),
|
exit = exitNumber(instruction),
|
||||||
location = location(
|
location = location(
|
||||||
@@ -61,7 +61,6 @@ class TomTomRoute {
|
|||||||
route.sections?.forEach { section ->
|
route.sections?.forEach { section ->
|
||||||
val lanes = mutableListOf<Lane>()
|
val lanes = mutableListOf<Lane>()
|
||||||
var startIndex = 0
|
var startIndex = 0
|
||||||
|
|
||||||
section.lanes?.forEach { itLane ->
|
section.lanes?.forEach { itLane ->
|
||||||
val lane = Lane(
|
val lane = Lane(
|
||||||
location(
|
location(
|
||||||
@@ -140,6 +139,7 @@ class TomTomRoute {
|
|||||||
"BEAR_RIGHT" -> {
|
"BEAR_RIGHT" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
"BEAR_LEFT" -> {
|
"BEAR_LEFT" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT
|
||||||
}
|
}
|
||||||
@@ -171,12 +171,15 @@ class TomTomRoute {
|
|||||||
"ROUNDABOUT_LEFT" -> {
|
"ROUNDABOUT_LEFT" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
|
||||||
}
|
}
|
||||||
|
|
||||||
"MAKE_UTURN" -> {
|
"MAKE_UTURN" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
"ENTER_MOTORWAY" -> {
|
"ENTER_MOTORWAY" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
"TAKE_EXIT" -> {
|
"TAKE_EXIT" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
||||||
|
|
||||||
@@ -189,8 +192,9 @@ class TomTomRoute {
|
|||||||
private fun exitNumber(
|
private fun exitNumber(
|
||||||
instruction: Instruction
|
instruction: Instruction
|
||||||
): Int {
|
): Int {
|
||||||
return if ( instruction.exitNumber == null
|
return if (instruction.exitNumber == null
|
||||||
|| instruction.exitNumber.isEmpty()) {
|
|| instruction.exitNumber.isEmpty()
|
||||||
|
) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
instruction.exitNumber.toInt()
|
instruction.exitNumber.toInt()
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ class ValhallaRepository : NavigationRepository() {
|
|||||||
Locations(
|
Locations(
|
||||||
lat = currentLocation.latitude,
|
lat = currentLocation.latitude,
|
||||||
lon = currentLocation.longitude,
|
lon = currentLocation.longitude,
|
||||||
search_filter = exclude
|
searchFilter = exclude
|
||||||
),
|
),
|
||||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = exclude)
|
Locations(lat = location.latitude, lon = location.longitude, searchFilter = exclude)
|
||||||
)
|
)
|
||||||
val valhallaLocation = ValhallaLocation(
|
val valhallaLocation = ValhallaLocation(
|
||||||
locations = vLocation,
|
locations = vLocation,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.collections.forEach
|
|
||||||
|
|
||||||
class IconMapper() {
|
class IconMapper() {
|
||||||
|
|
||||||
@@ -148,22 +147,14 @@ class IconMapper() {
|
|||||||
return laneDirection
|
return laneDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createCarIcon(iconCompat: IconCompat): CarIcon {
|
|
||||||
return CarIcon.Builder(iconCompat).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCarIconx(carContext: Context, @DrawableRes iconRes: Int): CarIcon {
|
|
||||||
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createLaneIcon(context: Context, stepData: StepData): IconCompat {
|
fun createLaneIcon(context: Context, stepData: StepData): IconCompat {
|
||||||
val bitmaps = mutableListOf<Bitmap>()
|
val bitmaps = mutableListOf<Bitmap>()
|
||||||
stepData.lane.forEach {
|
stepData.lane.forEach { lane ->
|
||||||
if (it.indications.isNotEmpty()) {
|
if (lane.indications.isNotEmpty()) {
|
||||||
Collections.sort<String>(it.indications)
|
Collections.sort<String>(lane.indications)
|
||||||
val resource = laneToResource(it.indications, stepData)
|
val resource = laneToResource(lane.indications, stepData)
|
||||||
if (resource.isNotEmpty()) {
|
if (resource.isNotEmpty()) {
|
||||||
val id = resourceId(resource);
|
val id = resourceId(resource)
|
||||||
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
val bitMap = BitmapFactory.decodeResource(context.resources, id)
|
||||||
bitmaps.add(bitMap)
|
bitmaps.add(bitMap)
|
||||||
}
|
}
|
||||||
@@ -182,30 +173,30 @@ class IconMapper() {
|
|||||||
return bitmaps.first()
|
return bitmaps.first()
|
||||||
}
|
}
|
||||||
val bmOverlay = createBitmap(
|
val bmOverlay = createBitmap(
|
||||||
bitmaps.first().getWidth() * (bitmaps.size * 1.5).toInt(),
|
bitmaps.first().width * (bitmaps.size * 1.5).toInt(),
|
||||||
bitmaps.first().getHeight(),
|
bitmaps.first().height,
|
||||||
bitmaps.first().getConfig()!!
|
bitmaps.first().config!!
|
||||||
)
|
)
|
||||||
val canvas = Canvas(bmOverlay)
|
val canvas = Canvas(bmOverlay)
|
||||||
canvas.drawBitmap(bitmaps.first(), matrix, null)
|
canvas.drawBitmap(bitmaps.first(), matrix, null)
|
||||||
var i = 0
|
var i = 0
|
||||||
bitmaps.forEach {
|
bitmaps.forEach { bitmap ->
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
matrix.setTranslate(i * 45F, 0F)
|
matrix.setTranslate(i * 45F, 0F)
|
||||||
canvas.drawBitmap(it, matrix, null)
|
canvas.drawBitmap(bitmap, matrix, null)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return bmOverlay
|
return bmOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun laneToResource(directions: List<String>, stepData: StepData): String {
|
fun laneToResource(directions: List<String>, stepData: StepData): String {
|
||||||
var direction = ""
|
var direction = ""
|
||||||
directions.forEach {
|
directions.forEach { indication ->
|
||||||
direction = if (direction.isEmpty()) {
|
direction = if (direction.isEmpty()) {
|
||||||
it.trim()
|
indication.trim()
|
||||||
} else {
|
} else {
|
||||||
"${direction}_${it.trim()}"
|
"${direction}_${indication.trim()}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
direction = direction.lowercase()
|
direction = direction.lowercase()
|
||||||
@@ -232,8 +223,10 @@ class IconMapper() {
|
|||||||
"right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_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"
|
"left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x"
|
||||||
"straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x"
|
"straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x"
|
||||||
"right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT) "${direction}_o" else "${direction}_x"
|
"right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT
|
||||||
"left_slight", "slight_left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_LEFT) "${direction}_o" else "${direction}_x"
|
|| stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "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) "slight_left_o" else "slight_left_x"
|
||||||
else -> {
|
else -> {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
@@ -246,21 +239,21 @@ class IconMapper() {
|
|||||||
return when (variableName) {
|
return when (variableName) {
|
||||||
"left_x" -> R.drawable.left_x
|
"left_x" -> R.drawable.left_x
|
||||||
"left_o" -> R.drawable.left_o
|
"left_o" -> R.drawable.left_o
|
||||||
"left_o_right_x" -> R.drawable.left_o_right_x
|
"left_o_straight_x" -> R.drawable.left_o_straight_x
|
||||||
|
"left_x_straight_o" -> R.drawable.left_x_straight_o
|
||||||
|
"slight_left_x" -> R.drawable.slight_left_x
|
||||||
|
"slight_left_o" -> R.drawable.slight_left_o
|
||||||
"right_x" -> R.drawable.right_x
|
"right_x" -> R.drawable.right_x
|
||||||
"right_o" -> R.drawable.right_o
|
"right_o" -> R.drawable.right_o
|
||||||
"slight_right_x" -> R.drawable.slight_right_x
|
"slight_right_x" -> R.drawable.slight_right_x
|
||||||
"slight_right_o" -> R.drawable.slight_right_o
|
"slight_right_o" -> R.drawable.slight_right_o
|
||||||
"slight_left_x" -> R.drawable.left_x
|
|
||||||
"straight_x" -> R.drawable.straight_x
|
"straight_x" -> R.drawable.straight_x
|
||||||
"right_o_straight_x" -> R.drawable.right_o_straight_x
|
"right_o_straight_x" -> R.drawable.right_o_straight_x
|
||||||
"right_x_straight_x" -> R.drawable.right_x_straight_x
|
"right_x_straight_x" -> R.drawable.right_x_straight_x
|
||||||
"right_x_straight_o" -> R.drawable.right_x_straight_x
|
"right_x_straight_o" -> R.drawable.right_x_straight_o
|
||||||
"straight_o" -> R.drawable.straight_o
|
"straight_o" -> R.drawable.straight_o
|
||||||
"left_o_straight_x" -> R.drawable.left_o_straight_x
|
|
||||||
"left_x_straight_o" -> R.drawable.left_x_straight_o
|
|
||||||
else -> {
|
else -> {
|
||||||
R.drawable.left_x
|
R.drawable.ic_zoom_out_24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ class RouteCalculator(var routeModel: RouteModel) {
|
|||||||
routeModel.navState.route.currentStepIndex = step.index
|
routeModel.navState.route.currentStepIndex = step.index
|
||||||
step.waypointIndex = wayIndex
|
step.waypointIndex = wayIndex
|
||||||
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
step.wayPointLocation = location(waypoint[0], waypoint[1])
|
||||||
//routeModel.navState = routeModel.navState.copy(
|
|
||||||
// routeBearing = routeModel.navState.lastLocation.bearingTo(location)
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,8 +61,13 @@ class RouteCalculator(var routeModel: RouteModel) {
|
|||||||
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
|
val loc1 = location(step.maneuver.waypoints[i][0], step.maneuver.waypoints[i][1])
|
||||||
val loc2 =
|
val loc2 =
|
||||||
location(step.maneuver.waypoints[i + 1][0], step.maneuver.waypoints[i + 1][1])
|
location(step.maneuver.waypoints[i + 1][0], step.maneuver.waypoints[i + 1][1])
|
||||||
|
val locationDistance = loc1.distanceTo(routeModel.navState.lastLocation)
|
||||||
val distance = loc1.distanceTo(loc2)
|
val distance = loc1.distanceTo(loc2)
|
||||||
leftDistance += distance
|
leftDistance += if (locationDistance < distance) {
|
||||||
|
locationDistance
|
||||||
|
} else {
|
||||||
|
distance
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (leftDistance / 10.0).roundToInt() * 10.0
|
return (leftDistance / 10.0).roundToInt() * 10.0
|
||||||
}
|
}
|
||||||
@@ -73,10 +75,12 @@ class RouteCalculator(var routeModel: RouteModel) {
|
|||||||
/** Returns the left distance in m. */
|
/** Returns the left distance in m. */
|
||||||
fun travelLeftDistance(): Double {
|
fun travelLeftDistance(): Double {
|
||||||
var leftDistance = 0.0
|
var leftDistance = 0.0
|
||||||
|
// distance for next steps
|
||||||
for (i in routeModel.route.currentStepIndex + 1..<routeModel.curLeg.steps.size) {
|
for (i in routeModel.route.currentStepIndex + 1..<routeModel.curLeg.steps.size) {
|
||||||
val step = routeModel.route.legs()[0].steps[i]
|
val step = routeModel.route.legs()[0].steps[i]
|
||||||
leftDistance += step.distance
|
leftDistance += step.distance
|
||||||
}
|
}
|
||||||
|
// distance for current step
|
||||||
leftDistance += leftStepDistance()
|
leftDistance += leftStepDistance()
|
||||||
return leftDistance
|
return leftDistance
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,16 +74,19 @@ open class RouteModel {
|
|||||||
val currentStep = navState.route.nextStep(0)
|
val currentStep = navState.route.nextStep(0)
|
||||||
var streetName = currentStep.maneuver.street
|
var streetName = currentStep.maneuver.street
|
||||||
var curManeuverType = currentStep.maneuver.type
|
var curManeuverType = currentStep.maneuver.type
|
||||||
|
if (navState.nextStep) {
|
||||||
if (distanceToNextStep > NEXT_STEP_THRESHOLD) {
|
if (distanceToNextStep > NEXT_STEP_THRESHOLD) {
|
||||||
streetName = currentStep.street
|
streetName = currentStep.street
|
||||||
curManeuverType = Maneuver.TYPE_STRAIGHT
|
curManeuverType = Maneuver.TYPE_STRAIGHT
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val exitNumber = currentStep.maneuver.exit
|
val exitNumber = currentStep.maneuver.exit
|
||||||
val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType)
|
val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType)
|
||||||
navState = navState.copy(maneuverType = curManeuverType)
|
navState = navState.copy(maneuverType = curManeuverType)
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
instruction = streetName,
|
instruction = streetName,
|
||||||
|
street = currentStep.street,
|
||||||
leftStepDistance = distanceToNextStep,
|
leftStepDistance = distanceToNextStep,
|
||||||
currentManeuverType = navState.maneuverType,
|
currentManeuverType = navState.maneuverType,
|
||||||
icon = maneuverIcon,
|
icon = maneuverIcon,
|
||||||
@@ -109,6 +112,7 @@ open class RouteModel {
|
|||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
instruction = streetName,
|
instruction = streetName,
|
||||||
|
street = "",
|
||||||
leftStepDistance = distanceToNextStep,
|
leftStepDistance = distanceToNextStep,
|
||||||
currentManeuverType = maneuverType,
|
currentManeuverType = maneuverType,
|
||||||
icon = maneuverIcon,
|
icon = maneuverIcon,
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
|
|||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val recentPlaces = repository.recentPlacesFlow.stateIn(
|
||||||
|
viewModelScope,
|
||||||
|
SharingStarted.WhileSubscribed(5_000),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
fun onShow3DChanged(enabled: Boolean) {
|
fun onShow3DChanged(enabled: Boolean) {
|
||||||
viewModelScope.launch { repository.setShow3D(enabled) }
|
viewModelScope.launch { repository.setShow3D(enabled) }
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
common/data/src/main/res/drawable/empty.png
Normal file
BIN
common/data/src/main/res/drawable/empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 883 B |
BIN
common/data/src/main/res/drawable/slight_left_o.png
Normal file
BIN
common/data/src/main/res/drawable/slight_left_o.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
BIN
common/data/src/main/res/drawable/slight_left_x.png
Normal file
BIN
common/data/src/main/res/drawable/slight_left_x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,98 @@
|
|||||||
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
|
import androidx.car.app.navigation.model.LaneDirection
|
||||||
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.StepData
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class IconMapperTest {
|
||||||
|
|
||||||
|
private val iconMapper = IconMapper()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `maneuverIcon returns correct icon for each maneuver type`() {
|
||||||
|
assertEquals(R.drawable.ic_turn_name_change, iconMapper.maneuverIcon(Maneuver.TYPE_STRAIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_destination, iconMapper.maneuverIcon(Maneuver.TYPE_DESTINATION))
|
||||||
|
assertEquals(R.drawable.ic_turn_destination, iconMapper.maneuverIcon(Maneuver.TYPE_DESTINATION_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_destination, iconMapper.maneuverIcon(Maneuver.TYPE_DESTINATION_LEFT))
|
||||||
|
assertEquals(R.drawable.ic_turn_destination, iconMapper.maneuverIcon(Maneuver.TYPE_DESTINATION_STRAIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_normal_right, iconMapper.maneuverIcon(Maneuver.TYPE_TURN_NORMAL_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_normal_left, iconMapper.maneuverIcon(Maneuver.TYPE_TURN_NORMAL_LEFT))
|
||||||
|
assertEquals(R.drawable.ic_turn_slight_right, iconMapper.maneuverIcon(Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_slight_right, iconMapper.maneuverIcon(Maneuver.TYPE_TURN_SLIGHT_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_name_change, iconMapper.maneuverIcon(Maneuver.TYPE_KEEP_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_name_change, iconMapper.maneuverIcon(Maneuver.TYPE_KEEP_LEFT))
|
||||||
|
assertEquals(R.drawable.ic_roundabout_ccw, iconMapper.maneuverIcon(Maneuver.TYPE_ROUNDABOUT_ENTER_CCW))
|
||||||
|
assertEquals(R.drawable.ic_roundabout_ccw, iconMapper.maneuverIcon(Maneuver.TYPE_ROUNDABOUT_EXIT_CCW))
|
||||||
|
assertEquals(R.drawable.ic_turn_u_turn_left, iconMapper.maneuverIcon(Maneuver.TYPE_U_TURN_LEFT))
|
||||||
|
assertEquals(R.drawable.ic_turn_u_turn_right, iconMapper.maneuverIcon(Maneuver.TYPE_U_TURN_RIGHT))
|
||||||
|
assertEquals(R.drawable.ic_turn_merge_symmetrical, iconMapper.maneuverIcon(Maneuver.TYPE_MERGE_LEFT))
|
||||||
|
assertEquals(R.drawable.ic_turn_name_change, iconMapper.maneuverIcon(Maneuver.TYPE_UNKNOWN))
|
||||||
|
assertEquals(R.drawable.ic_turn_name_change, iconMapper.maneuverIcon(Maneuver.TYPE_DEPART))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `addLanes returns correct lane direction`() {
|
||||||
|
val stepDataNormalLeft = StepData("", 0.0, Maneuver.TYPE_TURN_NORMAL_LEFT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_LEFT, iconMapper.addLanes("left_straight", stepDataNormalLeft))
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_LEFT, iconMapper.addLanes("left", stepDataNormalLeft))
|
||||||
|
assertEquals(LaneDirection.SHAPE_SLIGHT_LEFT, iconMapper.addLanes("left_slight", stepDataNormalLeft))
|
||||||
|
assertEquals(LaneDirection.SHAPE_SLIGHT_LEFT, iconMapper.addLanes("slight_left", stepDataNormalLeft))
|
||||||
|
|
||||||
|
val stepDataStraight = StepData("", 0.0, Maneuver.TYPE_STRAIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_STRAIGHT, iconMapper.addLanes("left_straight", stepDataStraight))
|
||||||
|
assertEquals(LaneDirection.SHAPE_STRAIGHT, iconMapper.addLanes("straight", stepDataStraight))
|
||||||
|
assertEquals(LaneDirection.SHAPE_STRAIGHT, iconMapper.addLanes("right_straight", stepDataStraight))
|
||||||
|
|
||||||
|
val stepDataKeepLeft = StepData("", 0.0, Maneuver.TYPE_KEEP_LEFT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_STRAIGHT, iconMapper.addLanes("straight", stepDataKeepLeft))
|
||||||
|
assertEquals(LaneDirection.SHAPE_SLIGHT_LEFT, iconMapper.addLanes("left_slight", stepDataKeepLeft))
|
||||||
|
|
||||||
|
val stepDataKeepRight = StepData("", 0.0, Maneuver.TYPE_KEEP_RIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_STRAIGHT, iconMapper.addLanes("straight", stepDataKeepRight))
|
||||||
|
|
||||||
|
val stepDataNormalRight = StepData("", 0.0, Maneuver.TYPE_TURN_NORMAL_RIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_RIGHT, iconMapper.addLanes("right", stepDataNormalRight))
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_RIGHT, iconMapper.addLanes("right_straight", stepDataNormalRight))
|
||||||
|
|
||||||
|
val stepDataSlightRight = StepData("", 0.0, Maneuver.TYPE_TURN_SLIGHT_RIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_RIGHT, iconMapper.addLanes("right_slight", stepDataSlightRight))
|
||||||
|
assertEquals(LaneDirection.SHAPE_NORMAL_RIGHT, iconMapper.addLanes("slight_right", stepDataSlightRight))
|
||||||
|
|
||||||
|
val stepDataUnknown = StepData("", 0.0, Maneuver.TYPE_UNKNOWN, 0, 0L, 0.0)
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("left_straight", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("left", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("straight", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("right", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("right_straight", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("left_slight", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("right_slight", stepDataUnknown))
|
||||||
|
assertEquals(LaneDirection.SHAPE_UNKNOWN, iconMapper.addLanes("invalid_direction", stepDataNormalLeft))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `laneToResource returns correct resource string`() {
|
||||||
|
val stepDataNormalLeft = StepData("", 0.0, Maneuver.TYPE_TURN_NORMAL_LEFT, 0, 0L, 0.0)
|
||||||
|
assertEquals("left_o_straight_x", iconMapper.laneToResource(listOf("left", "straight"), stepDataNormalLeft))
|
||||||
|
assertEquals("left_o", iconMapper.laneToResource(listOf("left"), stepDataNormalLeft))
|
||||||
|
assertEquals("slight_left_o", iconMapper.laneToResource(listOf("slight_left"), stepDataNormalLeft))
|
||||||
|
|
||||||
|
val stepDataStraight = StepData("", 0.0, Maneuver.TYPE_STRAIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals("left_x_straight_o", iconMapper.laneToResource(listOf("left", "straight"), stepDataStraight))
|
||||||
|
assertEquals("straight_o", iconMapper.laneToResource(listOf("straight"), stepDataStraight))
|
||||||
|
assertEquals("right_x_straight_o", iconMapper.laneToResource(listOf("right_straight"), stepDataStraight))
|
||||||
|
|
||||||
|
val stepDataNormalRight = StepData("", 0.0, Maneuver.TYPE_TURN_NORMAL_RIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals("right_x_straight_x", iconMapper.laneToResource(listOf("right_straight"), stepDataNormalRight))
|
||||||
|
assertEquals("right_o", iconMapper.laneToResource(listOf("right"), stepDataNormalRight))
|
||||||
|
|
||||||
|
val stepDataSlightRight = StepData("", 0.0, Maneuver.TYPE_TURN_SLIGHT_RIGHT, 0, 0L, 0.0)
|
||||||
|
assertEquals("right_o_straight_o", iconMapper.laneToResource(listOf("right_straight"), stepDataSlightRight))
|
||||||
|
|
||||||
|
val stepDataUnknown = StepData("", 0.0, Maneuver.TYPE_UNKNOWN, 0, 0L, 0.0)
|
||||||
|
assertEquals("left_x_straight_x", iconMapper.laneToResource(listOf("left", "straight"), stepDataUnknown))
|
||||||
|
assertEquals("", iconMapper.laneToResource(listOf("invalid"), stepDataUnknown))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user