diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ebeeb8b..eb1878a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.kouros.navigation" minSdk = 33 targetSdk = 36 - versionCode = 66 - versionName = "0.2.0.66" + versionCode = 68 + versionName = "0.2.0.68" base.archivesName = "navi-$versionName" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt index 8265b19..6266854 100644 --- a/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt +++ b/app/src/main/java/com/kouros/navigation/ui/MainActivity.kt @@ -10,6 +10,7 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.annotation.RequiresPermission +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -172,11 +173,13 @@ class MainActivity : ComponentActivity() { } enableEdgeToEdge() setContent { - CheckPermissionScreen(app = { - AppNavGraph( - mainActivity = this - ) - }) + NavigationTheme { + CheckPermissionScreen(app = { + AppNavGraph( + mainActivity = this + ) + }) + } } } @@ -185,9 +188,14 @@ class MainActivity : ComponentActivity() { fun StartScreen( navController: NavHostController ) { - val appViewModel: AppViewModel = appViewModel() val darkMode by appViewModel.darkMode.collectAsState() + + if (darkMode == 1) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } val baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1) val locationProvider = rememberDefaultLocationProvider( updateInterval = 0.5.seconds, desiredAccuracy = DesiredAccuracy.Highest diff --git a/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt index aae796f..1b137c4 100644 --- a/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/kouros/navigation/ui/navigation/AppNavGraph.kt @@ -16,10 +16,37 @@ fun AppNavGraph(mainActivity: MainActivity) { val navController = rememberNavController() NavHost(navController = navController, startDestination = "startScreen") { composable("startScreen") { mainActivity.StartScreen(navController) } - composable("display_settings") { SettingsRoute("display_settings", navController) { navController.popBackStack() } } - composable("nav_settings") { SettingsRoute("nav_settings", navController) { navController.popBackStack() } } - composable("settings") { SettingsRoute("settings", navController) { navController.popBackStack() } } - composable("search") { SearchScreen(navController, navController.context, navigationViewModel, mainActivity.lastLocation) { navController.popBackStack() } + composable("display_settings") { + SettingsRoute( + "display_settings", + navController + ) { navController.popBackStack() } + } + composable("nav_settings") { + SettingsRoute( + "nav_settings", + navController + ) { navController.popBackStack() } + } + composable("settings") { + SettingsRoute( + "settings", + navController + ) { navController.popBackStack() } + } + composable("search") { + SearchScreen( + navController, + navController.context, + navigationViewModel, + mainActivity.lastLocation + ) { navController.popBackStack() } + } + composable("settings_screen") { + SettingsRoute( + "settings_screen", + navController + ) { navController.popBackStack() } } } } diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt index 2c35bc0..2bb1913 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchScreen.kt @@ -3,8 +3,10 @@ package com.kouros.navigation.ui.search import android.annotation.SuppressLint import android.content.Context import android.location.Location +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -28,14 +30,17 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -49,8 +54,11 @@ import com.kouros.navigation.data.Place import com.kouros.navigation.data.PlaceColor import com.kouros.navigation.data.nominatim.SearchResult import com.kouros.navigation.model.NavigationViewModel +import com.kouros.navigation.ui.app.AppViewModel +import com.kouros.navigation.ui.app.appViewModel import com.kouros.navigation.ui.theme.NavigationTheme import com.kouros.navigation.utils.location +import kotlin.math.roundToInt @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @@ -64,10 +72,18 @@ fun SearchScreen( function: () -> Unit ) { - NavigationTheme(true) { + val appViewModel: AppViewModel = appViewModel() + val darkMode by appViewModel.darkMode.collectAsState() + + if (darkMode == 1 || darkMode == 2 && isSystemInDarkTheme()) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } + //NavigationTheme(darkMode == 1 || darkMode == 2 && isSystemInDarkTheme()) { Scaffold( topBar = { - CenterAlignedTopAppBar( + TopAppBar( title = { Text( stringResource(id = R.string.search_action_title), @@ -92,7 +108,7 @@ fun SearchScreen( Categories(context, navigationViewModel, location, closeSheet = { }) } } - } + // } } @OptIn(ExperimentalMaterial3Api::class) @@ -118,9 +134,6 @@ fun SearchBar( } SearchBar( - colors = SearchBarDefaults.colors( - containerColor = MaterialTheme.colorScheme.secondaryContainer - ), inputField = { SearchBarDefaults.InputField( modifier = Modifier.focusRequester(focusRequester), @@ -206,11 +219,11 @@ private fun SearchPlaces( ) { val color = remember { PlaceColor } LazyColumn( - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 10.dp), + contentPadding = PaddingValues(horizontal = 6.dp, vertical = 10.dp), verticalArrangement = Arrangement.spacedBy(4.dp), ) { items(searchResults, key = { it.placeId }) { place -> - Row { + Row(verticalAlignment = Alignment.CenterVertically) { Icon( painter = painterResource(id = R.drawable.ic_place_white_24dp), "Navigation", @@ -218,8 +231,11 @@ private fun SearchPlaces( modifier = Modifier.size(24.dp, 24.dp), ) ListItem( - headlineContent = { Text(place.displayName) }, + headlineContent = {Text(place.address.road)}, + leadingContent = {Text(place.name)}, + trailingContent = { Text("${(place.distance/1000).roundToInt()} km") }, modifier = Modifier + .animateItem() .clickable { val pl = Place( name = place.name, diff --git a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt index 5fe6f5d..10cb952 100644 --- a/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt +++ b/app/src/main/java/com/kouros/navigation/ui/search/SearchSheet.kt @@ -160,7 +160,7 @@ private fun RecentPlaces( Icon( painter = painterResource(id = R.drawable.ic_place_white_24dp), "Navigation", - tint = color.copy(alpha = 1f), + //tint = color.copy(alpha = 1f), modifier = Modifier.size(24.dp, 24.dp), ) ListItem( diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt index d2bdfa6..b9d7bd5 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/DisplayScreen.kt @@ -1,35 +1,41 @@ package com.kouros.navigation.ui.settings +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold -import androidx.compose.material3.SegmentedButtonDefaults.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController import com.kouros.data.R import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.ui.components.RadioButtonSingleSelection import com.kouros.navigation.ui.components.SectionTitle import com.kouros.navigation.ui.components.SettingSwitch -import com.kouros.navigation.ui.theme.NavigationTheme + + @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -37,80 +43,100 @@ fun DisplayScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { val darkMode by viewModel.darkMode.collectAsState() val show3D by viewModel.show3D.collectAsState() - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.display_settings), + val showTraffic by viewModel.traffic.collectAsState() + val distanceMode by viewModel.distanceMode.collectAsState() + + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + stringResource(id = R.string.display_settings), + ) + }, + navigationIcon = { + IconButton(onClick = navigateBack) { + Icon( + painter = painterResource(R.drawable.arrow_back_24px), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.size(48.dp, 48.dp), ) - }, - navigationIcon = { - IconButton(onClick = navigateBack) { - Icon( - painter = painterResource(R.drawable.arrow_back_24px), - contentDescription = stringResource(id = R.string.accept_action_title), - modifier = Modifier.size(48.dp, 48.dp), - ) - } - }, - ) - }, - ) - { padding -> - val scrollState = rememberScrollState() - Column( - modifier = Modifier - .fillMaxSize() - .padding(top = 20.dp) - .verticalScroll(scrollState) - ) { + } + }, + ) + }, + ) + { paddingValues -> + val scrollState = rememberScrollState() + Column( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .padding(top = 10.dp) + .verticalScroll(scrollState) + ) { + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SettingSwitch( + title = stringResource(R.string.threed_building), + checked = show3D, + onCheckedChange = viewModel::onShow3DChanged + ) - Text( - text = stringResource(R.string.settings_action_title), - style = MaterialTheme.typography.headlineMedium - ) + SettingSwitch( + title = stringResource(R.string.traffic), + checked = showTraffic, + onCheckedChange = viewModel::onTraffic + ) + } + } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(20.dp)) - // Appearance - SectionTitle(stringResource(R.string.threed_building)) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.dark_mode)) - SettingSwitch( - title = stringResource(R.string.threed_building), - checked = show3D, - onCheckedChange = viewModel::onShow3DChanged - ) + val radioOptions = listOf( + stringResource(R.string.off_action_title), + stringResource(R.string.on_action_title), + stringResource(R.string.use_telephon_settings) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = darkMode, + radioOptions = radioOptions, + onClick = viewModel::onDarkModeChanged + ) + } + } - SectionTitle(stringResource(R.string.dark_mode)) + Spacer(modifier = Modifier.height(20.dp)) - val radioOptions = listOf( - stringResource(R.string.off_action_title), - stringResource(R.string.on_action_title), - stringResource(R.string.use_telephon_settings) - ) - RadioButtonSingleSelection( - modifier = Modifier.padding(), - selectedOption = darkMode, - radioOptions = radioOptions, - onClick = viewModel::onDarkModeChanged - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.distance_units)) + + val radioOptions = listOf( + stringResource(R.string.automatically), + stringResource(R.string.kilometer), + stringResource(R.string.miles) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = distanceMode, + radioOptions = radioOptions, + onClick = viewModel::onDistanceModeChanged + ) + } } } } } - - - - - - - - - - - - - diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt index 0cd2e64..a54c559 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/NavigationScreen.kt @@ -1,7 +1,10 @@ package com.kouros.navigation.ui.settings import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState @@ -10,9 +13,11 @@ import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -38,16 +43,14 @@ import com.kouros.navigation.ui.theme.NavigationTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { - - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.navigation_settings), - ) - }, + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + stringResource(id = R.string.navigation_settings), + ) + }, navigationIcon = { IconButton(onClick = navigateBack) { Icon( @@ -57,19 +60,17 @@ fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) { ) } }, - ) - }, - ) { padding -> - val scrollState = rememberScrollState() - Column( - modifier = - Modifier - .consumeWindowInsets(padding) - .verticalScroll(scrollState) - .padding(top = padding.calculateTopPadding()), - ) { - NavigationSettings(viewModel) - } + ) + }, + ) { padding -> + val scrollState = rememberScrollState() + Column( + modifier = + Modifier + .padding(padding) + .verticalScroll(scrollState) + ) { + NavigationSettings(viewModel) } } } @@ -82,49 +83,63 @@ fun NavigationSettings(viewModel: SettingsViewModel) { val routingEngine by viewModel.routingEngine.collectAsState() val tomTomApiKey by viewModel.tomTomApiKey.collectAsState() - SettingSwitch( - title = stringResource(R.string.avoid_highways_row_title), - checked = avoidMotorway, - onCheckedChange = viewModel::onAvoidMotorway - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SettingSwitch( + title = stringResource(R.string.avoid_highways_row_title), + checked = avoidMotorway, + onCheckedChange = viewModel::onAvoidMotorway + ) - SettingSwitch( - title = stringResource(R.string.avoid_tolls_row_title), - checked = avoidTollway, - onCheckedChange = viewModel::onAvoidTollway - ) + SettingSwitch( + title = stringResource(R.string.avoid_tolls_row_title), + checked = avoidTollway, + onCheckedChange = viewModel::onAvoidTollway + ) - SettingSwitch( - title = stringResource(R.string.use_car_location), - checked = carLocation, - onCheckedChange = viewModel::onCarLocation - ) + SettingSwitch( + title = stringResource(R.string.use_car_location), + checked = carLocation, + onCheckedChange = viewModel::onCarLocation + ) + } + } - SectionTitle(stringResource(R.string.routing_engine)) + Spacer(modifier = Modifier.height(20.dp)) - val routingEngineOptions = listOf( - stringResource(R.string.valhalla), - stringResource(R.string.osrm), - stringResource(R.string.tomtom) - ) - RadioButtonSingleSelection( - modifier = Modifier.padding(), - selectedOption = routingEngine, - radioOptions = routingEngineOptions, - onClick = viewModel::onRoutingEngineChanged - ) + OutlinedCard(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier.padding(10.dp), + ) { + SectionTitle(stringResource(R.string.routing_engine)) - if (routingEngine == RouteEngine.TOMTOM.ordinal) { - var key by remember { mutableStateOf(tomTomApiKey) } - TextField( - value = key, - onValueChange = { - viewModel.onTomTomApiKeyChanged(it) - key = it - }, - label = { Text(stringResource(R.string.tomtom_api_key)) }, - textStyle = TextStyle(color = Color.Green, fontWeight = FontWeight.Bold), - modifier = Modifier.padding(20.dp) - ) + val routingEngineOptions = listOf( + stringResource(R.string.valhalla), + stringResource(R.string.osrm), + stringResource(R.string.tomtom) + ) + RadioButtonSingleSelection( + modifier = Modifier.padding(), + selectedOption = routingEngine, + radioOptions = routingEngineOptions, + onClick = viewModel::onRoutingEngineChanged + ) + + if (routingEngine == RouteEngine.TOMTOM.ordinal) { + var key by remember { mutableStateOf(tomTomApiKey) } + TextField( + value = key, + onValueChange = { + viewModel.onTomTomApiKeyChanged(it) + key = it + }, + label = { Text(stringResource(R.string.tomtom_api_key)) }, + textStyle = TextStyle(color = Color.Green, fontWeight = FontWeight.Bold), + modifier = Modifier.padding(20.dp) + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt b/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt new file mode 100644 index 0000000..9d125dd --- /dev/null +++ b/app/src/main/java/com/kouros/navigation/ui/settings/Settings.kt @@ -0,0 +1,120 @@ +package com.kouros.navigation.ui.settings + +import androidx.appcompat.app.AppCompatDelegate +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.kouros.data.R +import com.kouros.navigation.model.SettingsViewModel +import com.kouros.navigation.ui.theme.NavigationTheme + +data class Settings(val id: String, val name: String, val icon: Int) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Settings( + viewModel: SettingsViewModel, + navController: NavHostController, + navigateBack: () -> Unit +) { + + + val items = listOf( + Settings( + id = "favorites_screen", + name = "Favoriten", + icon = R.drawable.ic_favorite_white_24dp + ), + Settings( + id = "settings_screen", + name = "Einstellungen", + icon = R.drawable.speed_camera_24px + ), + Settings(id = "info_screen", name = "Info", icon = R.drawable.ic_place_white_24dp), + ) + + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(id = R.string.settings_action_title)) }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Spacer(modifier = Modifier.height(10.dp)) + } + items(items) { subItem -> + ScreenItem(item = subItem, onClick = { navController.navigate(subItem.id) }) + Spacer(modifier = Modifier.height(10.dp)) + } + } + + } +} + +@Composable +fun ScreenItem( + item: Settings, + onClick: (Settings) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(item.icon), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleMedium) + } + IconForward() + } + } +} + +@Composable +fun RowScope.IconForward() { + Icon( + painter = painterResource(R.drawable.arrow_back_24px), + contentDescription = stringResource(id = R.string.on_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) +} + + diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt index cd11265..dce7e9d 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsRoute.kt @@ -34,6 +34,9 @@ fun SettingsRoute(route: String, navController: NavHostController, function: () NavigationScreen (viewModel = viewModel, function) } if (route == "settings") { + Settings(viewModel, navController, function) + } + if (route == "settings_screen") { SettingsScreen(viewModel, navController, function) } } diff --git a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt index 4a22154..2285bb7 100644 --- a/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/kouros/navigation/ui/settings/SettingsScreen.kt @@ -1,20 +1,24 @@ package com.kouros.navigation.ui.settings +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Button -import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -24,49 +28,79 @@ import com.kouros.data.R import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.ui.theme.NavigationTheme + +data class Item(val id: String, val name: String, val description: String, val icon: Int) + @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingsScreen(viewModel: SettingsViewModel, navController: NavHostController, navigateBack: () -> Unit) { - NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text( - stringResource(id = R.string.settings_action_title), - ) - }, - navigationIcon = { - IconButton(onClick = navigateBack) { - Icon( - painter = painterResource(R.drawable.arrow_back_24px), - contentDescription = stringResource(id = R.string.accept_action_title), - modifier = Modifier.size(48.dp, 48.dp), - ) - } - }, - ) - }, - ) { padding -> - val scrollState = rememberScrollState() - Column( - modifier = - Modifier - .consumeWindowInsets(padding) - .verticalScroll(scrollState) - .padding(top = padding.calculateTopPadding()), - ) { - Column(modifier = Modifier.padding(16.dp)) { +fun SettingsScreen( + viewModel: SettingsViewModel, + navController: NavHostController, + navigateBack: () -> Unit +) { - Button(onClick = { navController.navigate("display_settings") }) { - Text(stringResource(R.string.display_settings)) - } - Button(onClick = { navController.navigate("nav_settings") }) { - Text(stringResource(R.string.navigation_settings)) - } - } + val items = listOf( + Item( + id = "display_settings", + name = "Display Settings", + description = "", + icon = R.drawable.dark_mode_24px + ), + Item( + id = "nav_settings", + name = "Navigation Settings", + description = "", + icon = R.drawable.navigation_24px + ) + ) + + Scaffold( + topBar = { + TopAppBar( + title = { Text("Settings") }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Spacer(modifier = Modifier.height(10.dp)) + } + items(items) { subItem -> + ScreenItem(item = subItem, onClick = { navController.navigate(subItem.id) }) + Spacer(modifier = Modifier.height(10.dp)) } } } } +@Composable +fun ScreenItem( + item: Item, + onClick: (Item) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + painter = painterResource(item.icon), + contentDescription = stringResource(id = R.string.accept_action_title), + modifier = Modifier.align(Alignment.CenterVertically), + ) + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleMedium) + } + IconForward() + } + } +} + diff --git a/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt b/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt index 63ed86f..6f7acee 100644 --- a/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt +++ b/app/src/main/java/com/kouros/navigation/ui/theme/Theme.kt @@ -84,28 +84,24 @@ private val DarkColors = darkColorScheme( @Composable fun NavigationTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, content: @Composable () -> Unit ) { - val context = LocalContext.current - val colors = run { - if (useDarkTheme) dynamicDarkColorScheme(context) - else dynamicLightColorScheme(context) - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colors.primary.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = - useDarkTheme + val colorScheme = when { + dynamicColor -> { + val context = LocalContext.current + if (useDarkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - MaterialTheme( - colorScheme = if (useDarkTheme) DarkColors else colorScheme, - typography = typography, - content = content, - shapes = shapes, - ) + useDarkTheme -> DarkColors + else -> LightColors } + MaterialTheme( + + colorScheme = colorScheme, + typography = typography, + content = content, + shapes = shapes, + ) } \ No newline at end of file diff --git a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt index b189008..e41a965 100644 --- a/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt +++ b/common/car/src/androidTest/java/com/kouros/navigation/car/RouteModelTest.kt @@ -176,15 +176,21 @@ class RouteModelTest { @Test fun simulate() { for ((index, waypoint) in routeModel.curRoute.waypoints.withIndex()) { - val curLocation = location(waypoint[0], waypoint[1]) if (routeModel.isNavigating()) { + val curLocation = location(waypoint[0], waypoint[1]) if (index in 0..routeModel.curRoute.waypoints.size) { //runBlocking { delay(1000) } val start = System.currentTimeMillis() routeModel.updateLocation(curLocation, NavigationViewModel(TomTomRepository())) val stepData = routeModel.currentStep() - println("${stepData.instruction} ${System.currentTimeMillis() - start}") - // val nextData = routeModel.nextStep() + //println("${stepData.instruction} ${System.currentTimeMillis() - start}") + if (stepData.lane.isNotEmpty()) { + println(stepData.street) + stepData.lane.forEach { + println("${it.indications} ${it.valid}") + } + } + // val nextData = routeModel.nextStep() } } } @@ -192,17 +198,17 @@ class RouteModelTest { @Test fun `leftStepDistance Inglolstädter `() { - val location: Location = location( 11.584578, 48.183653) - routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) ) + val location: Location = location(11.584578, 48.183653) + routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) val step = routeModel.currentStep() assertEquals(step.leftStepDistance, 645.0, 1.0) } @Test fun `leftStepDistance Vogelhart `() { - val location: Location = location( 11.578911, 48.185565) - routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()) ) + val location: Location = location(11.578911, 48.185565) + routeModel.updateLocation(location, NavigationViewModel(TomTomRepository())) val step = routeModel.currentStep() - assertEquals(step.leftStepDistance , 34.0, 1.0) + assertEquals(step.leftStepDistance, 34.0, 1.0) } } \ No newline at end of file diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt index 0a4dcfe..9410eda 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt @@ -2,13 +2,11 @@ package com.kouros.navigation.car.map import android.location.Location import androidx.compose.foundation.Canvas -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.BasicText import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -17,10 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.scale -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText @@ -36,7 +32,6 @@ import com.kouros.navigation.data.NavigationColor import com.kouros.navigation.data.RouteColor import com.kouros.navigation.data.SpeedColor import com.kouros.navigation.utils.isMetricSystem -import com.kouros.navigation.utils.location import org.maplibre.compose.camera.CameraPosition import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState @@ -63,7 +58,6 @@ import org.maplibre.compose.sources.Source import org.maplibre.compose.sources.getBaseSource import org.maplibre.compose.sources.rememberGeoJsonSource import org.maplibre.compose.style.BaseStyle -import org.maplibre.spatialk.geojson.BoundingBox import org.maplibre.spatialk.geojson.Position diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt index 4ae6c26..dabbbb8 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt @@ -114,7 +114,11 @@ class RouteCarModel : RouteModel() { ) .setRemainingTimeColor(CarColor.GREEN) .setRemainingDistanceColor(CarColor.BLUE) - .setTripText(CarText.create("$traffic min")) + if (traffic > 0) { + travelBuilder.setTripText(CarText.create("$traffic min")) + travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px)) + } + if (navState.travelMessage.isNotEmpty()) { travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px)) travelBuilder.setTripText(CarText.create(navState.travelMessage)) @@ -143,7 +147,7 @@ class RouteCarModel : RouteModel() { } val laneType = Lane.Builder() - .addDirection(LaneDirection.create(laneDirection, true)) + .addDirection(LaneDirection.create(laneDirection, it.valid)) .build() step.addLane(laneType) } diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt index 0021673..bf2f0b1 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/Simulation.kt @@ -4,6 +4,8 @@ import android.location.Location import android.location.LocationManager import android.os.SystemClock import androidx.lifecycle.LifecycleCoroutineScope +import com.kouros.navigation.data.Constants.homeVogelhart +import com.kouros.navigation.utils.location import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -17,6 +19,10 @@ class Simulation { lifecycleScope: LifecycleCoroutineScope, updateLocation: (Location) -> Unit ) { + // A92 + //updateLocation(location(11.709508, 48.338923 )) + //updateLocation(homeVogelhart) + if (routeModel.navState.route.isRouteValid()) { val points = routeModel.curRoute.waypoints if (points.isEmpty()) return @@ -38,7 +44,7 @@ class Simulation { // Update your app's state as if a real GPS update occurred updateLocation(fakeLocation) // Wait before moving to the next point (e.g., every 1 second) - delay(1000) + delay(500) lastLocation = fakeLocation } routeModel.stopNavigation() diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt index 71d2b8a..3432a40 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt @@ -1,5 +1,6 @@ package com.kouros.navigation.car.screen +import androidx.activity.OnBackPressedCallback import androidx.car.app.CarContext import androidx.car.app.Screen import androidx.car.app.model.Action @@ -40,8 +41,17 @@ class CategoriesScreen( Category(id = CHARGING_STATION, name = carContext.getString(R.string.charging_station)) ) + private val backPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + navigationViewModel.elements.value = emptyList() + invalidate() + } + } + init { + navigationViewModel.elements.value = emptyList() navigationViewModel.elements.observe(this, categoryObserver) + carContext.onBackPressedDispatcher.addCallback(this, backPressedCallback) } override fun onGetTemplate(): Template { diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt index 0cd422b..59f97ef 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt @@ -530,8 +530,7 @@ class NavigationScreen( * Pushes the search screen and handles the search result. */ private fun startSearchScreen() { - navigationViewModel.recentPlaces.value = emptyList() - navigationViewModel.previewRoute.value = "" + screenManager .pushForResult( SearchScreen( diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt index 303deb6..295358f 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt @@ -39,11 +39,12 @@ class PlaceListScreen( private val places: List ) : Screen(carContext) { - val routeModel = RouteCarModel() var place = Place() + var mPlaces = mutableListOf() + val previewObserver = Observer { route -> if (route.isNotEmpty()) { val repository = getSettingsRepository(carContext) @@ -70,21 +71,25 @@ class PlaceListScreen( } init { - loadPlaces() + // loadPlaces() + navigationViewModel.recentPlaces.value = emptyList() + navigationViewModel.previewRoute.value = "" + + mPlaces.addAll(places) navigationViewModel.previewRoute.observe(this, previewObserver) } override fun onGetTemplate(): Template { val itemListBuilder = ItemList.Builder() .setNoItemsMessage(carContext.getString(R.string.no_places)) - places.forEach { + mPlaces.forEach { val street = if (it.street != null) { it.street } else { "" } val row = Row.Builder() - // .setImage(contactIcon(it.avatar, it.category)) + .setImage(contactIcon(null, it.category)) .setTitle("$street ${it.city}") .setOnClickListener { place = Place( @@ -155,7 +160,7 @@ class PlaceListScreen( carContext, R.string.recent_Item_deleted, CarToast.LENGTH_LONG ).show() - loadPlaces() + mPlaces.remove(place) invalidate() } .build() diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt index 476b1e4..00918e0 100644 --- a/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt +++ b/common/car/src/main/java/com/kouros/navigation/car/screen/settings/NavigationSettings.kt @@ -3,16 +3,17 @@ package com.kouros.navigation.car.screen.settings import androidx.car.app.CarContext import androidx.car.app.Screen import androidx.car.app.model.Action +import androidx.car.app.model.CarIcon import androidx.car.app.model.Header import androidx.car.app.model.ItemList import androidx.car.app.model.ListTemplate import androidx.car.app.model.Row import androidx.car.app.model.Template import androidx.car.app.model.Toggle +import androidx.core.graphics.drawable.IconCompat import androidx.lifecycle.lifecycleScope import com.kouros.data.R -import com.kouros.navigation.car.screen.settings.PasswordSettings -import com.kouros.navigation.car.screen.settings.RoutingSettings +import com.kouros.navigation.car.navigation.NavigationUtils import com.kouros.navigation.model.NavigationViewModel import com.kouros.navigation.utils.getSettingsViewModel import kotlinx.coroutines.flow.first @@ -61,7 +62,8 @@ class NavigationSettings( listBuilder.addItem( buildRowForTemplate( R.string.avoid_highways_row_title, - highwayToggle + highwayToggle, + NavigationUtils(carContext).createCarIcon(R.drawable.baseline_add_road_24) ) ) @@ -71,7 +73,7 @@ class NavigationSettings( settingsViewModel.onAvoidTollway(checked) tollWayToggleState = !tollWayToggleState }.setChecked(tollWayToggleState).build() - listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle)) + listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle, NavigationUtils(carContext).createCarIcon(R.drawable.baseline_toll_24))) // Ferry val ferryToggle: Toggle = @@ -79,7 +81,7 @@ class NavigationSettings( settingsViewModel.onAvoidFerry(checked) ferryToggleState = !ferryToggleState }.setChecked(ferryToggleState).build() - listBuilder.addItem(buildRowForTemplate(R.string.avoid_ferries, ferryToggle)) + listBuilder.addItem(buildRowForTemplate(R.string.avoid_ferries, ferryToggle, NavigationUtils(carContext).createCarIcon(R.drawable.baseline_directions_boat_filled_24))) // CarLocation val carLocationToggle: Toggle = @@ -91,7 +93,8 @@ class NavigationSettings( listBuilder.addItem( buildRowForTemplate( R.string.use_car_location, - carLocationToggle + carLocationToggle, + NavigationUtils(carContext).createCarIcon(R.drawable.ic_place_white_24dp) ) ) @@ -118,10 +121,11 @@ class NavigationSettings( .build() } - private fun buildRowForTemplate(title: Int, toggle: Toggle): Row { + private fun buildRowForTemplate(title: Int, toggle: Toggle, icon: CarIcon): Row { return Row.Builder() .setTitle(carContext.getString(title)) .setToggle(toggle) + .setImage(icon) .build() } diff --git a/common/data/build.gradle.kts b/common/data/build.gradle.kts index 019543e..6d81d16 100644 --- a/common/data/build.gradle.kts +++ b/common/data/build.gradle.kts @@ -62,7 +62,7 @@ dependencies { implementation(libs.kotlinx.serialization.json) implementation(libs.maplibre.compose) - implementation(libs.androidx.compose.material.icons.extended) + //implementation(libs.androidx.compose.material.icons.extended) testImplementation(libs.junit) testImplementation(libs.mockito.core) testImplementation(libs.mockito.kotlin) diff --git a/common/data/src/main/java/com/kouros/navigation/data/Data.kt b/common/data/src/main/java/com/kouros/navigation/data/Data.kt index 77f2128..2894879 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/Data.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/Data.kt @@ -62,7 +62,7 @@ data class StepData ( var icon: Int, var arrivalTime : Long, var leftDistance: Double, - var lane: List = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())), + var lane: List = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList(), 0, 0)), var exitNumber: Int = 0, var message: String = "", ) @@ -116,7 +116,7 @@ object Constants { val homeVogelhart = location(11.5793748, 48.185749) val homeHohenwaldeck = location( 11.594322, 48.1164817) - const val NEXT_STEP_THRESHOLD = 1000.0 + const val NEXT_STEP_THRESHOLD = 500.0 const val MAXIMAL_SNAP_CORRECTION = 50.0 diff --git a/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt b/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt index 28b3251..d86e805 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/NavigationState.kt @@ -15,6 +15,7 @@ data class NavigationState ( val lastLocation: Location = location(0.0, 0.0), val currentLocation: Location = location(0.0, 0.0), val routeBearing: Float = 0F, + // index of current route in the list of routes val currentRouteIndex: Int = 0, val destination: Place = Place(), val carConnection: Int = 0, diff --git a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt index e301f32..c2fb4cc 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/osrm/OsrmRoute.kt @@ -48,7 +48,9 @@ class OsrmRoute { val lane = Lane( location(it2.location[0], it2.location[1]), it3.valid, - it3.indications + it3.indications, + 0, + 0 ) lanes.add(lane) } diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt index 25b12fb..ae3f272 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Intersection.kt @@ -4,5 +4,5 @@ import java.util.Collections data class Intersection( val location: List = listOf(0.0, 0.0), - val lane : List = Collections.emptyList(), + val lane : List = Collections.emptyList(), ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt index 38039c2..5686c74 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Lane.kt @@ -6,4 +6,6 @@ data class Lane ( val location: Location, val valid: Boolean, var indications: List, + val startIndex: Int, + val endIndex: Int, ) \ No newline at end of file diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt index f01462e..27dc4d2 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Leg.kt @@ -2,5 +2,4 @@ package com.kouros.navigation.data.route data class Leg( var steps : List = arrayListOf(), - var intersection: List = arrayListOf() ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt index 8502f27..ea97162 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/route/Maneuver.kt @@ -10,5 +10,6 @@ data class Maneuver( val location: Location, val exit: Int = 0, val street: String = "", - val message: String = "" + val message: String = "", + val pointIndex : Int = 0, ) diff --git a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt index d980502..890195e 100644 --- a/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt +++ b/common/data/src/main/java/com/kouros/navigation/data/tomtom/TomTomRoute.kt @@ -35,7 +35,6 @@ class TomTomRoute { } var stepDistance = 0.0 var stepDuration = 0.0 - val allIntersections = mutableListOf() val steps = mutableListOf() var lastPointIndex = 0 for (index in 1..() route.sections?.forEach { section -> - val lanes = mutableListOf() - var startIndex = 0 - section.lanes?.forEach { itLane -> - val lane = Lane( - location( - waypoints[section.startPointIndex][0], - waypoints[section.startPointIndex][1] - ), - itLane.directions.first() == itLane.follow, - itLane.directions - ) - startIndex = section.startPointIndex - lanes.add(lane) - } - intersections.add(Intersection(waypoints[startIndex], lanes)) + if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex && section.endPointIndex >= lastPointIndex) { + val lanes = mutableListOf() + var startIndex = 0 + var lastLane: Lane? = null + section.lanes?.forEach { itLane -> + val lane = Lane( + location = location( + waypoints[section.startPointIndex][0], + waypoints[section.startPointIndex][1] + ), + valid = itLane.directions.first() == itLane.follow, + indications = itLane.directions, + startIndex = startIndex, + endIndex = section.endPointIndex + ) + startIndex = section.startPointIndex + if (lastLane == null + || (!(lastLane.valid && lane.valid + && lastLane.indications == lane.indications)) + ) { + lanes.add(lane) + } + lastLane = lane + } + intersections.add(Intersection(waypoints[startIndex], lanes)) + } + stepDistance = + route.guidance.instructions[index].routeOffsetInMeters - stepDistance + stepDuration = + route.guidance.instructions[index].travelTimeInSeconds - stepDuration + val step = Step( + index = stepIndex, + street = street, + distance = stepDistance, + duration = stepDuration, + maneuver = maneuver, + intersection = intersections + ) + stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() + stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() + steps.add(step) + stepIndex += 1 } - allIntersections.addAll(intersections) - stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance - stepDuration = route.guidance.instructions[index].travelTimeInSeconds - stepDuration - val step = Step( - index = stepIndex, - street = street, - distance = stepDistance, - duration = stepDuration, - maneuver = maneuver, - intersection = intersections - ) - stepDistance = route.guidance.instructions[index].routeOffsetInMeters.toDouble() - stepDuration = route.guidance.instructions[index].travelTimeInSeconds.toDouble() - steps.add(step) - stepIndex += 1 } - legs.add(Leg(steps, allIntersections)) + legs.add(Leg(steps)) val routeGeoJson = createLineStringCollection(waypoints) val centerLocation = createCenterLocation(createLineStringCollection(waypoints)) val newRoute = com.kouros.navigation.data.route.Routes( @@ -184,8 +196,7 @@ class TomTomRoute { } "TAKE_EXIT" -> { - newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT - + newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT } } return newType @@ -195,8 +206,7 @@ class TomTomRoute { private fun exitNumber( instruction: Instruction ): Int { - return if (instruction.exitNumber == null - || instruction.exitNumber.isEmpty() + return if (instruction.exitNumber.isNullOrEmpty() ) { 0 } else { diff --git a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt index 12e95c4..34c06f8 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/IconMapper.kt @@ -65,12 +65,15 @@ class IconMapper() { currentTurnIcon = R.drawable.ic_roundabout_ccw } + Maneuver.TYPE_U_TURN_LEFT -> { currentTurnIcon = R.drawable.ic_turn_u_turn_left } + Maneuver.TYPE_U_TURN_RIGHT -> { currentTurnIcon = R.drawable.ic_turn_u_turn_right } + Maneuver.TYPE_MERGE_LEFT -> { currentTurnIcon = R.drawable.ic_turn_merge_symmetrical } @@ -136,10 +139,12 @@ class IconMapper() { "right_slight", "slight_right" -> { when (stepData.currentManeuverType) { Maneuver.TYPE_TURN_SLIGHT_RIGHT -> LaneDirection.SHAPE_NORMAL_RIGHT + Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_SLIGHT_RIGHT else -> LaneDirection.SHAPE_UNKNOWN } } + else -> { LaneDirection.SHAPE_UNKNOWN } @@ -151,8 +156,7 @@ class IconMapper() { val bitmaps = mutableListOf() stepData.lane.forEach { lane -> if (lane.indications.isNotEmpty()) { - Collections.sort(lane.indications) - val resource = laneToResource(lane.indications, stepData) + val resource = laneToResource(lane.indications.sorted(), stepData) if (resource.isNotEmpty()) { val id = resourceId(resource) val bitMap = BitmapFactory.decodeResource(context.resources, id) @@ -222,11 +226,21 @@ class IconMapper() { "right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "${direction}_o" else "${direction}_x" "left" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT) "${direction}_o" else "${direction}_x" - "straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT) "${direction}_o" else "${direction}_x" + "straight" -> if (stepData.currentManeuverType == Maneuver.TYPE_STRAIGHT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_RIGHT + ) "${direction}_o" else "${direction}_x" + "right_slight", "slight_right" -> if (stepData.currentManeuverType == Maneuver.TYPE_TURN_SLIGHT_RIGHT - || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT) "slight_right_o" else "slight_right_x" + || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_RIGHT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_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" + || stepData.currentManeuverType == Maneuver.TYPE_TURN_NORMAL_LEFT + || stepData.currentManeuverType == Maneuver.TYPE_KEEP_LEFT + ) "slight_left_o" else "slight_left_x" + else -> { "" } diff --git a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt index d0f02b5..42d67af 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/NavigationViewModel.kt @@ -550,14 +550,15 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo val rp = settingsRepository.recentPlacesFlow.first() val places = mutableListOf() if (rp.isNotEmpty()) { - val recentPlaces = + val rPlaces = gson.fromJson(rp, Places::class.java).places.sortedBy { it.lastDate } - for (curPlace in recentPlaces) { + for (curPlace in rPlaces) { if (curPlace.name != place.name || curPlace.category != place.category) { places.add(curPlace) } } settingsRepository.setRecentPlaces(gson.toJson(Places(places))) + recentPlaces.value = places } } catch (e: Exception) { e.printStackTrace() diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt index e8b595e..cbddb81 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteCalculator.kt @@ -31,7 +31,7 @@ class RouteCalculator(var routeModel: RouteModel) { } } if (nearestDistance < NEAREST_LOCATION_DISTANCE) { - break; + break } } } diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt index b4c6f3a..64fdcdf 100644 --- a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt +++ b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt @@ -11,6 +11,7 @@ import com.kouros.navigation.data.StepData import com.kouros.navigation.data.route.Lane import com.kouros.navigation.data.route.Leg import com.kouros.navigation.data.route.Routes +import com.kouros.navigation.data.route.Step import com.kouros.navigation.utils.location import kotlin.math.absoluteValue @@ -29,6 +30,9 @@ open class RouteModel { val curLeg: Leg get() = navState.route.routes[navState.currentRouteIndex].legs.first() + val currentStep: Step + get() = navState.route.nextStep(0) + fun startNavigation(routeString: String) { navState = navState.copy( route = Route.Builder() @@ -69,42 +73,11 @@ open class RouteModel { navState = navState.copy(lastLocation = navState.currentLocation) } - fun currentStep(): StepData { - val distanceToNextStep = routeCalculator.leftStepDistance() - // Determine the maneuver type and corresponding icon - val currentStep = navState.route.nextStep(0) - var streetName = currentStep.maneuver.street - var curManeuverType = currentStep.maneuver.type - if (navState.nextStep) { - if (distanceToNextStep > NEXT_STEP_THRESHOLD) { - streetName = currentStep.street - curManeuverType = Maneuver.TYPE_STRAIGHT - } - } - val exitNumber = currentStep.maneuver.exit - val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType) - navState = navState.copy(maneuverType = curManeuverType) - // Construct and return the final StepData object - return StepData( - instruction = streetName, - street = currentStep.street, - leftStepDistance = distanceToNextStep, - currentManeuverType = navState.maneuverType, - icon = maneuverIcon, - arrivalTime = routeCalculator.arrivalTime(), - leftDistance = routeCalculator.travelLeftDistance(), - lane = currentLanes(), - exitNumber = exitNumber, - message = currentStep.maneuver.message - ) - } - fun nextStep(): StepData { val distanceToNextStep = routeCalculator.leftStepDistance() - val currentStep = navState.route.nextStep(0) val nextStep = navState.route.nextStep(1) var streetName = nextStep.street - var maneuverType = currentStep.maneuver.type + var maneuverType = currentStep.maneuver.type if (distanceToNextStep < NEXT_STEP_THRESHOLD) { streetName = nextStep.maneuver.street maneuverType = nextStep.maneuver.type @@ -128,16 +101,13 @@ open class RouteModel { private fun currentLanes(): List { var lanes = emptyList() if (navState.route.legs().isNotEmpty()) { - navState.route.legs().first().intersection.forEach { + currentStep.intersection.forEach { if (it.lane.isNotEmpty()) { val distance = navState.lastLocation.distanceTo(location(it.location[0], it.location[1])) - val sectionBearing = - navState.lastLocation.bearingTo(location(it.location[0], it.location[1])) - val bearingDeviation = - (navState.routeBearing.absoluteValue - sectionBearing.absoluteValue).absoluteValue - if (distance < NEXT_STEP_THRESHOLD && bearingDeviation < 10) { + if (distance < NEXT_STEP_THRESHOLD) { lanes = it.lane + return@forEach } } } @@ -145,6 +115,36 @@ open class RouteModel { return lanes } + fun currentStep(): StepData { + val distanceToNextStep = routeCalculator.leftStepDistance() + // Determine the maneuver type and corresponding icon + var streetName = currentStep.maneuver.street + var curManeuverType = currentStep.maneuver.type + if (navState.nextStep) { + if (distanceToNextStep > NEXT_STEP_THRESHOLD) { + streetName = currentStep.street + curManeuverType = Maneuver.TYPE_STRAIGHT + } + } + val exitNumber = currentStep.maneuver.exit + val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType) + navState = navState.copy(maneuverType = curManeuverType) + val currentLanes = currentLanes() + // Construct and return the final StepData object + return StepData( + instruction = streetName, + street = currentStep.street, + leftStepDistance = distanceToNextStep, + currentManeuverType = navState.maneuverType, + icon = maneuverIcon, + arrivalTime = routeCalculator.arrivalTime(), + leftDistance = routeCalculator.travelLeftDistance(), + lane = currentLanes, + exitNumber = exitNumber, + message = currentStep.maneuver.message + ) + } + fun isNavigating(): Boolean { return navState.navigating } diff --git a/common/data/src/main/res/drawable/baseline_add_road_24.xml b/common/data/src/main/res/drawable/baseline_add_road_24.xml new file mode 100644 index 0000000..48bb585 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_add_road_24.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml b/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml new file mode 100644 index 0000000..0c67f81 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_directions_boat_filled_24.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/common/data/src/main/res/drawable/baseline_toll_24.xml b/common/data/src/main/res/drawable/baseline_toll_24.xml new file mode 100644 index 0000000..a97a063 --- /dev/null +++ b/common/data/src/main/res/drawable/baseline_toll_24.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/common/data/src/main/res/drawable/left_x.png b/common/data/src/main/res/drawable/left_x.png index 21d0752..d117cee 100644 Binary files a/common/data/src/main/res/drawable/left_x.png and b/common/data/src/main/res/drawable/left_x.png differ diff --git a/common/data/src/main/res/drawable/left_x_straight_o.png b/common/data/src/main/res/drawable/left_x_straight_o.png index ac944d6..7aed0b0 100644 Binary files a/common/data/src/main/res/drawable/left_x_straight_o.png and b/common/data/src/main/res/drawable/left_x_straight_o.png differ diff --git a/common/data/src/main/res/drawable/none.png b/common/data/src/main/res/drawable/none.png deleted file mode 100644 index c1248e5..0000000 Binary files a/common/data/src/main/res/drawable/none.png and /dev/null differ diff --git a/common/data/src/main/res/drawable/right_x.png b/common/data/src/main/res/drawable/right_x.png index 158a6da..2ee5967 100644 Binary files a/common/data/src/main/res/drawable/right_x.png and b/common/data/src/main/res/drawable/right_x.png differ diff --git a/common/data/src/main/res/drawable/right_x_straight_o.png b/common/data/src/main/res/drawable/right_x_straight_o.png index b80d0b2..139f2c0 100644 Binary files a/common/data/src/main/res/drawable/right_x_straight_o.png and b/common/data/src/main/res/drawable/right_x_straight_o.png differ diff --git a/common/data/src/main/res/drawable/right_x_straight_x.png b/common/data/src/main/res/drawable/right_x_straight_x.png index 3c0d00d..7db37e1 100644 Binary files a/common/data/src/main/res/drawable/right_x_straight_x.png and b/common/data/src/main/res/drawable/right_x_straight_x.png differ diff --git a/common/data/src/main/res/drawable/slight_left_x.png b/common/data/src/main/res/drawable/slight_left_x.png index f968d59..c821ad6 100644 Binary files a/common/data/src/main/res/drawable/slight_left_x.png and b/common/data/src/main/res/drawable/slight_left_x.png differ diff --git a/common/data/src/main/res/drawable/slight_right_x.png b/common/data/src/main/res/drawable/slight_right_x.png index 158a6da..d1f40eb 100644 Binary files a/common/data/src/main/res/drawable/slight_right_x.png and b/common/data/src/main/res/drawable/slight_right_x.png differ diff --git a/common/data/src/main/res/drawable/straight_x.png b/common/data/src/main/res/drawable/straight_x.png index f913be1..10ef7d7 100644 Binary files a/common/data/src/main/res/drawable/straight_x.png and b/common/data/src/main/res/drawable/straight_x.png differ diff --git a/common/data/src/main/res/raw/tomom_routing.json b/common/data/src/main/res/raw/tomom_routing.json index 7b70deb..3e93a2b 100644 --- a/common/data/src/main/res/raw/tomom_routing.json +++ b/common/data/src/main/res/raw/tomom_routing.json @@ -24,7 +24,7 @@ }, { "key": "departAt", - "value": "2026-03-12T09:44:05.900Z" + "value": "2026-03-04T14:38:14.253Z" }, { "key": "guidanceVersion", @@ -44,15 +44,15 @@ }, { "key": "locations", - "value": "48.18610,11.57923:48.11654,11.59449" + "value": "48.18575,11.57937:48.11648,11.59432" }, { "key": "maxAlternatives", - "value": "2" + "value": "0" }, { "key": "maxPathAlternatives", - "value": "2" + "value": "0" }, { "key": "routeRepresentation", @@ -64,11 +64,11 @@ }, { "key": "sectionType", - "value": "traffic" + "value": "lanes" }, { "key": "sectionType", - "value": "lanes" + "value": "traffic" }, { "key": "traffic", @@ -119,28 +119,46 @@ "routes": [ { "summary": { - "lengthInMeters": 10982, - "travelTimeInSeconds": 1142, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:03:08+01:00" + "lengthInMeters": 11122, + "travelTimeInSeconds": 1483, + "trafficDelayInSeconds": 175, + "trafficLengthInMeters": 2191, + "departureTime": "2026-03-04T15:38:14+01:00", + "arrivalTime": "2026-03-04T16:02:57+01:00" }, "legs": [ { "summary": { - "lengthInMeters": 10982, - "travelTimeInSeconds": 1142, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:03:08+01:00" + "lengthInMeters": 11122, + "travelTimeInSeconds": 1483, + "trafficDelayInSeconds": 175, + "trafficLengthInMeters": 2191, + "departureTime": "2026-03-04T15:38:14+01:00", + "arrivalTime": "2026-03-04T16:02:57+01:00" }, - "encodedPolyline": "ijbeHqlteAeDOFkEH{G?M?sA@kB?_FAeC?o@?[@_@\\Ab@CVAz@CJA@?dBGhAGjAExAGlBEvBKdCKTAfCKLAv@ELA|AGnAGt@ClCKjDQpBIf@BXDPDPBF@ZB?S@SAYAUKi@Go@Cc@BgBBs@Bg@JyAJiAXqBDWNs@TaA\\mAFa@Po@`BwF?YL]FSFSl@iB^kALc@L]Ro@f@yAf@{AFQNe@dAoCdBgEx@qBTi@BITe@L[L_@^}@HSXu@pB}El@eAb@e@f@[h@QZCRAL?j@HRFh@Vf@XLJhAn@lAv@TLtAz@r@`@bAn@pAv@f@X~@j@z@h@vBpA`@VHDFFJFf@X`CzApAh@f@L`@Fz@@f@AXEVEVEhA[h@Yn@e@PQFEJKRWV[PW`@w@t@}AHQN]~BiFP]`AoBh@aADGTa@t@aAt@{@PQJKJGFG@Cd@]XSxDmCf@a@n@o@TY\\g@LQHMJSLUP[f@iAPg@b@yAFODMNi@FS|@qCVaAHUHUn@wBHYh@eBpAkEjBiGfAeDj@yADMFQd@sAf@kAJUh@qAf@eAt@sAn@iALSN[p@kAVc@JOLSj@w@z@}@x@q@pAu@p@_@j@Sl@MLCRCb@E`@?^?L@`ABz@?N@~AFdADJ@rAH`DVpCVrAJd@BfHp@zGl@pAJ|ALnGp@jEh@fBJpAFF?P@N@ZCtC]r@GJCFCLCD?TEVEXGhAYzAg@NGv@]`@QJEvB_AXMVK\\Qb@Qn@QJCNAZC^ENA`@FnBb@xEtA^H^JnCl@z@r@`@Pr@TtBlA~C`Bn@\\xAl@PF`@LrAVlCh@bBLl@BlBJdG\\RDjCHn@?pB?xB?R@`@@GxAC^?ZInBIfCAvC?r@@dD@n@@b@@^D`C?TDxAFbBHdB@VHp@RjAJb@NNH`@VlBFf@PzARhBFd@@LRbBFh@\\nC@FNhAb@lEj@tDPpABTBRZlBTdBXjBn@xEBLDTRpAR~@l@jDj@Qv@IrEP", + "encodedPolyline": "sfbeH_rteAE|DQEy@GQ?wDQFkEH{G?M?sA@kB?_FAeC?o@?[@_@\\Ab@CVAz@CJA@?dBGhAGjAExAGlBEvBKdCKTAfCKLAv@ELA|AGnAGt@ClCKjDQpBIf@BXDPDPBF@ZB?S@SAYAUKi@Go@Cc@BgBBs@Bg@JyAJiAXqBDWNs@TaA\\mAFa@Po@`BwF?YL]FSFSl@iB^kALc@L]Ro@f@yAf@{AFQNe@dAoCdBgEx@qBTi@BITe@L[L_@^}@HSXu@pB}El@eAb@e@f@[h@QZCRAL?j@HRFh@Vf@XLJhAn@lAv@TLtAz@r@`@bAn@pAv@f@X~@j@z@h@vBpA`@VHDFFJFf@X`CzApAh@f@L`@Fz@@f@AXEVEVEhA[h@Yn@e@PQFEJKRWV[PW`@w@t@}AHQN]~BiFP]`AoBh@aADGTa@t@aAt@{@PQJKJGFG@Cd@]XSxDmCf@a@n@o@TY\\g@LQHMJSLUP[f@iAPg@b@yAFODMNi@FS|@qCVaAHUHUn@wBHYh@eBpAkEjBiGfAeDj@yADMFQd@sAf@kAJUh@qAf@eAt@sAn@iALSN[p@kAVc@JOLSj@w@z@}@x@q@pAu@p@_@j@Sl@MLCRCb@E`@?^?L@`ABz@?N@~AFdADJ@rAH`DVpCVrAJd@BfHp@zGl@pAJ|ALnGp@jEh@fBJpAFF?P@N@ZCtC]r@GJCFCLCD?TEVEXGhAYzAg@NGv@]`@QJEvB_AXMVK\\Qb@Qn@QJCNAZC^ENA`@FnBb@xEtA^H^JnCl@z@r@`@Pr@TtBlA~C`Bn@\\xAl@PF`@LrAVlCh@bBLl@BlBJdG\\RDjCHn@?pB?xB?R@`@@GxAC^?ZInBIfCAvC?r@@dD@n@@b@@^D`C?TDxAFbBHdB@VHp@RjAJb@NNH`@VlBFf@PzARhBFd@@LRbBFh@\\nC@FNhAb@lEj@tDPpABTBRZlBTdBXjBn@xEBLDTRpAR~@l@jDj@Qv@I~ER", "encodedPolylinePrecision": 5 } ], "sections": [ + { + "startPointIndex": 66, + "endPointIndex": 143, + "sectionType": "TRAFFIC", + "simpleCategory": "JAM", + "effectiveSpeedInKmh": 26, + "delayInSeconds": 175, + "magnitudeOfDelay": 1, + "tec": { + "causes": [ + { + "mainCauseCode": 1 + } + ], + "effectCode": 4 + }, + "eventId": "TTL41056710392017000" + }, { "lanes": [ { @@ -172,8 +190,8 @@ "LONG_DASHED", "SINGLE_SOLID" ], - "startPointIndex": 38, - "endPointIndex": 41, + "startPointIndex": 42, + "endPointIndex": 45, "sectionType": "LANES" }, { @@ -195,8 +213,8 @@ "SHORT_DASHED", "SINGLE_SOLID" ], - "startPointIndex": 57, - "endPointIndex": 58, + "startPointIndex": 61, + "endPointIndex": 62, "sectionType": "LANES" }, { @@ -225,8 +243,8 @@ "SHORT_DASHED", "SINGLE_SOLID" ], - "startPointIndex": 70, - "endPointIndex": 71, + "startPointIndex": 74, + "endPointIndex": 75, "sectionType": "LANES" }, { @@ -248,8 +266,8 @@ "LONG_DASHED", "SINGLE_SOLID" ], - "startPointIndex": 261, - "endPointIndex": 262, + "startPointIndex": 265, + "endPointIndex": 266, "sectionType": "LANES" }, { @@ -283,8 +301,8 @@ "SINGLE_SOLID", "SINGLE_SOLID" ], - "startPointIndex": 283, - "endPointIndex": 284, + "startPointIndex": 287, + "endPointIndex": 288, "sectionType": "LANES" }, { @@ -319,8 +337,8 @@ "SHORT_DASHED", "SINGLE_SOLID" ], - "startPointIndex": 298, - "endPointIndex": 300, + "startPointIndex": 302, + "endPointIndex": 304, "sectionType": "LANES" } ], @@ -330,26 +348,44 @@ "routeOffsetInMeters": 0, "travelTimeInSeconds": 0, "point": { - "latitude": 48.18613, - "longitude": 11.57849 + "latitude": 48.18554, + "longitude": 11.57936 }, "pointIndex": 0, "instructionType": "LOCATION_DEPARTURE", - "street": "Silcherstraße", + "street": "Vogelhartstraße", "countryCode": "DEU", "possibleCombineWithNext": false, "drivingSide": "RIGHT", "maneuver": "DEPART", - "message": "Abfahrt von Silcherstraße" + "message": "Abfahrt von Vogelhartstraße" }, { - "routeOffsetInMeters": 92, - "travelTimeInSeconds": 20, + "routeOffsetInMeters": 71, + "travelTimeInSeconds": 16, + "point": { + "latitude": 48.18557, + "longitude": 11.57841 + }, + "pointIndex": 1, + "instructionType": "TURN", + "street": "Silcherstraße", + "countryCode": "DEU", + "junctionType": "REGULAR", + "turnAngleInDecimalDegrees": 90, + "possibleCombineWithNext": false, + "drivingSide": "RIGHT", + "maneuver": "TURN_RIGHT", + "message": "Biegen Sie rechts ab auf Silcherstraße" + }, + { + "routeOffsetInMeters": 225, + "travelTimeInSeconds": 61, "point": { "latitude": 48.18696, "longitude": 11.57857 }, - "pointIndex": 1, + "pointIndex": 5, "instructionType": "TURN", "street": "Schmalkaldener Straße", "countryCode": "DEU", @@ -361,13 +397,13 @@ "message": "Biegen Sie rechts ab auf Schmalkaldener Straße" }, { - "routeOffsetInMeters": 524, - "travelTimeInSeconds": 94, + "routeOffsetInMeters": 657, + "travelTimeInSeconds": 135, "point": { "latitude": 48.18686, "longitude": 11.58437 }, - "pointIndex": 11, + "pointIndex": 15, "instructionType": "TURN", "roadNumbers": [ "B13" @@ -382,13 +418,13 @@ "message": "Biegen Sie rechts ab auf Ingolstädter Straße/B13" }, { - "routeOffsetInMeters": 1587, - "travelTimeInSeconds": 256, + "routeOffsetInMeters": 1719, + "travelTimeInSeconds": 291, "point": { "latitude": 48.17733, "longitude": 11.58503 }, - "pointIndex": 41, + "pointIndex": 45, "instructionType": "TURN", "roadNumbers": [ "B2R" @@ -404,13 +440,13 @@ "combinedMessage": "Biegen Sie links ab auf Schenkendorfstraße/B2R dann bleiben Sie bei Schenkendorfstraße/B2R Richtung Messe / ICM links" }, { - "routeOffsetInMeters": 1941, - "travelTimeInSeconds": 296, + "routeOffsetInMeters": 2074, + "travelTimeInSeconds": 333, "point": { "latitude": 48.17678, "longitude": 11.58957 }, - "pointIndex": 58, + "pointIndex": 62, "instructionType": "TURN", "roadNumbers": [ "B2R" @@ -427,13 +463,13 @@ "combinedMessage": "Bleiben Sie bei Schenkendorfstraße/B2R Richtung Messe / ICM links dann bleiben Sie bei Schenkendorfstraße/B2R Richtung Passau links" }, { - "routeOffsetInMeters": 2293, - "travelTimeInSeconds": 317, + "routeOffsetInMeters": 2425, + "travelTimeInSeconds": 371, "point": { "latitude": 48.17518, "longitude": 11.59363 }, - "pointIndex": 71, + "pointIndex": 75, "instructionType": "TURN", "roadNumbers": [ "B2R" @@ -449,13 +485,13 @@ "message": "Bleiben Sie bei Schenkendorfstraße/B2R Richtung Passau links" }, { - "routeOffsetInMeters": 2648, - "travelTimeInSeconds": 338, + "routeOffsetInMeters": 2780, + "travelTimeInSeconds": 436, "point": { "latitude": 48.17329, "longitude": 11.59747 }, - "pointIndex": 82, + "pointIndex": 86, "instructionType": "DIRECTION_INFO", "roadNumbers": [ "B2R" @@ -469,13 +505,13 @@ "message": "Folgen Sie Isarring/B2R Richtung München-Ost" }, { - "routeOffsetInMeters": 8299, - "travelTimeInSeconds": 729, + "routeOffsetInMeters": 8431, + "travelTimeInSeconds": 1014, "point": { "latitude": 48.13017, "longitude": 11.6154 }, - "pointIndex": 262, + "pointIndex": 266, "instructionType": "TURN", "street": "Ampfingstraße", "countryCode": "DEU", @@ -487,13 +523,13 @@ "message": "Halten Sie sich rechts bei Ampfingstraße" }, { - "routeOffsetInMeters": 9361, - "travelTimeInSeconds": 879, + "routeOffsetInMeters": 9494, + "travelTimeInSeconds": 1197, "point": { "latitude": 48.12089, "longitude": 11.61285 }, - "pointIndex": 284, + "pointIndex": 288, "instructionType": "TURN", "street": "Anzinger Straße", "countryCode": "DEU", @@ -506,13 +542,13 @@ "combinedMessage": "Biegen Sie rechts ab auf Anzinger Straße dann fahren Sie geradeaus weiter auf Sankt-Martin-Straße" }, { - "routeOffsetInMeters": 9857, - "travelTimeInSeconds": 968, + "routeOffsetInMeters": 9990, + "travelTimeInSeconds": 1286, "point": { "latitude": 48.12087, "longitude": 11.60621 }, - "pointIndex": 300, + "pointIndex": 304, "instructionType": "TURN", "street": "Sankt-Martin-Straße", "countryCode": "DEU", @@ -524,13 +560,13 @@ "message": "Fahren Sie geradeaus weiter auf Sankt-Martin-Straße" }, { - "routeOffsetInMeters": 10807, - "travelTimeInSeconds": 1100, + "routeOffsetInMeters": 10940, + "travelTimeInSeconds": 1440, "point": { "latitude": 48.11811, "longitude": 11.59417 }, - "pointIndex": 331, + "pointIndex": 335, "instructionType": "TURN", "street": "Hohenwaldeckstraße", "countryCode": "DEU", @@ -543,13 +579,13 @@ "combinedMessage": "Biegen Sie links ab auf Hohenwaldeckstraße dann sie haben Hohenwaldeckstraße erreicht. Ihr Ziel liegt auf der linken Seite" }, { - "routeOffsetInMeters": 10982, - "travelTimeInSeconds": 1142, + "routeOffsetInMeters": 11122, + "travelTimeInSeconds": 1483, "point": { - "latitude": 48.11655, - "longitude": 11.59422 + "latitude": 48.11649, + "longitude": 11.59421 }, - "pointIndex": 334, + "pointIndex": 338, "instructionType": "LOCATION_ARRIVAL", "street": "Hohenwaldeckstraße", "countryCode": "DEU", @@ -562,921 +598,27 @@ "instructionGroups": [ { "firstInstructionIndex": 0, - "lastInstructionIndex": 2, - "groupMessage": "Abfahrt von Silcherstraße. Fahren Sie auf die Ingolstädter Straße/B13", - "groupLengthInMeters": 1587 + "lastInstructionIndex": 3, + "groupMessage": "Abfahrt von Vogelhartstraße. Fahren Sie auf die Ingolstädter Straße/B13", + "groupLengthInMeters": 1719 }, { - "firstInstructionIndex": 3, - "lastInstructionIndex": 6, + "firstInstructionIndex": 4, + "lastInstructionIndex": 7, "groupMessage": "Fahren Sie auf die Schenkendorfstraße, Isarring/B2R in Richtung Messe / ICM, Passau, München-Ost", "groupLengthInMeters": 6712 }, - { - "firstInstructionIndex": 7, - "lastInstructionIndex": 9, - "groupMessage": "Fahren Sie auf die Ampfingstraße, Anzinger Straße, Sankt-Martin-Straße", - "groupLengthInMeters": 2508 - }, - { - "firstInstructionIndex": 10, - "lastInstructionIndex": 11, - "groupMessage": "Fahren Sie zu Ihrem Ziel in der Hohenwaldeckstraße", - "groupLengthInMeters": 175 - } - ] - } - }, - { - "summary": { - "lengthInMeters": 9794, - "travelTimeInSeconds": 1779, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:13:45+01:00", - "deviationDistance": 1587, - "deviationTime": 256, - "deviationPoint": { - "latitude": 48.17733, - "longitude": 11.58503 - } - }, - "legs": [ - { - "summary": { - "lengthInMeters": 9794, - "travelTimeInSeconds": 1779, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:13:45+01:00" - }, - "encodedPolyline": "ijbeHqlteAeDOFkEH{G?M?sA@kB?_FAeC?o@?[@_@\\Ab@CVAz@CJA@?dBGhAGjAExAGlBEvBKdCKTAfCKLAv@ELA|AGnAGt@ClCKjDQpBIf@BXDPDPBF@ZBH@J?P?^Cz@Kv@GbAQ`@KtA[|AMpBKXATAhAGn@Ef@CvBK|@EfBGtBG^Ax@EpBMfDOJArBKnAAVAx@?pC@dDQbAGzDMN?`DQP@P?J?p@@hABVBvCNX@fABh@Bf@AjAFD?n@F|A`@f@VbBn@nBv@fC~@j@N^PzDtAnBr@dA^DBD@tBx@XHhA`@pDtAj@TfA^d@Ph@RrB~@DDFFd@f@HFb@N|@CRBhBp@d@NJDnAb@nBv@h@Rl@TjBt@\\JjC|@jAd@`DnAx@ZvChAfC|@FBFBFBD[NkADYFm@TeBHk@Jy@J{@BMLwAb@uDLiBB_@@S@IBOBWHk@@QHe@BODIBIDEBCHG^EL?TCNFzChA`DnA|Al@~@ZLDF@D@H@HAVGJIHIVYdCmCf@g@tAyAXUXIL@NBZJdAXb@HVB^DrANbAVdCj@`@L^PFDPJXNt@l@HLh@l@LTNXFJPd@Nb@Rh@FTJB??@?BDBFDHFFB@F?f@o@POROrAcANI|CqBHIHKNQLYn@cB|@gCNi@DORk@\\iABGBIXiADa@BUDa@V}BFk@@O?Y?YEwAAWASBMIqBEo@DY@CH_@HINST[X]|@oAv@kArAwBRYbB{CdAmBVa@R]d@s@Zi@FIFIFIDGPWd@}@JBB?H?LAPIjAe@jDuAdAe@TIhAo@tCmB|@g@POpA}@\\Uv@g@RUtBwBREX[NMRQ\\[HQRMVQ|DqBl@e@n@a@p@]n@_@p@]JGnBeAj@tDPpABTBRZlBTdBXjBn@xEBLDTRpAR~@l@jDj@Qv@IrEP", - "encodedPolylinePrecision": 5 - } - ], - "sections": [ - { - "lanes": [ - { - "directions": [ - "LEFT" - ], - "follow": "LEFT" - }, - { - "directions": [ - "LEFT" - ], - "follow": "LEFT" - }, - { - "directions": [ - "STRAIGHT" - ] - }, - { - "directions": [ - "RIGHT" - ] - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "LONG_DASHED", - "SINGLE_SOLID", - "LONG_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 132, - "endPointIndex": 133, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "RIGHT" - ], - "follow": "RIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "LONG_DASHED", - "SHORT_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 156, - "endPointIndex": 162, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "STRAIGHT" - ], - "follow": "STRAIGHT" - }, - { - "directions": [ - "STRAIGHT" - ] - }, - { - "directions": [ - "RIGHT" - ] - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "LONG_DASHED", - "SHORT_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 207, - "endPointIndex": 208, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "STRAIGHT" - ] - }, - { - "directions": [ - "SLIGHT_RIGHT" - ], - "follow": "SLIGHT_RIGHT" - }, - { - "directions": [ - "SLIGHT_RIGHT" - ], - "follow": "SLIGHT_RIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "SINGLE_SOLID", - "LONG_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 247, - "endPointIndex": 248, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "STRAIGHT" - ] - }, - { - "directions": [ - "RIGHT" - ], - "follow": "RIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "SHORT_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 273, - "endPointIndex": 274, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "RIGHT" - ], - "follow": "RIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "SHORT_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 306, - "endPointIndex": 308, - "sectionType": "LANES" - } - ], - "guidance": { - "instructions": [ - { - "routeOffsetInMeters": 0, - "travelTimeInSeconds": 0, - "point": { - "latitude": 48.18613, - "longitude": 11.57849 - }, - "pointIndex": 0, - "instructionType": "LOCATION_DEPARTURE", - "street": "Silcherstraße", - "countryCode": "DEU", - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "DEPART", - "message": "Abfahrt von Silcherstraße" - }, - { - "routeOffsetInMeters": 92, - "travelTimeInSeconds": 20, - "point": { - "latitude": 48.18696, - "longitude": 11.57857 - }, - "pointIndex": 1, - "instructionType": "TURN", - "street": "Schmalkaldener Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Schmalkaldener Straße" - }, - { - "routeOffsetInMeters": 524, - "travelTimeInSeconds": 94, - "point": { - "latitude": 48.18686, - "longitude": 11.58437 - }, - "pointIndex": 11, - "instructionType": "TURN", - "roadNumbers": [ - "B13" - ], - "street": "Ingolstädter Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Ingolstädter Straße/B13" - }, - { - "routeOffsetInMeters": 5234, - "travelTimeInSeconds": 931, - "point": { - "latitude": 48.14538, - "longitude": 11.57877 - }, - "pointIndex": 133, - "instructionType": "TURN", - "street": "Von-der-Tann-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Von-der-Tann-Straße", - "combinedMessage": "Biegen Sie links ab auf Von-der-Tann-Straße dann bleiben Sie bei Von-der-Tann-Straße rechts" - }, - { - "routeOffsetInMeters": 5677, - "travelTimeInSeconds": 1002, - "point": { - "latitude": 48.1441, - "longitude": 11.58414 - }, - "pointIndex": 162, - "instructionType": "TURN", - "street": "Von-der-Tann-Straße", - "countryCode": "DEU", - "junctionType": "BIFURCATION", - "turnAngleInDecimalDegrees": 45, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "KEEP_RIGHT", - "message": "Bleiben Sie bei Von-der-Tann-Straße rechts" - }, - { - "routeOffsetInMeters": 6793, - "travelTimeInSeconds": 1177, - "point": { - "latitude": 48.13504, - "longitude": 11.58227 - }, - "pointIndex": 208, - "instructionType": "TURN", - "street": "Isartorplatz", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Isartorplatz" - }, - { - "routeOffsetInMeters": 7483, - "travelTimeInSeconds": 1377, - "point": { - "latitude": 48.13155, - "longitude": 11.58848 - }, - "pointIndex": 248, - "instructionType": "TURN", - "street": "Rosenheimer Straße", - "countryCode": "DEU", - "junctionType": "BIFURCATION", - "turnAngleInDecimalDegrees": 45, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "KEEP_RIGHT", - "message": "Bleiben Sie bei Rosenheimer Straße rechts" - }, - { - "routeOffsetInMeters": 8082, - "travelTimeInSeconds": 1494, - "point": { - "latitude": 48.12822, - "longitude": 11.59436 - }, - "pointIndex": 274, - "instructionType": "TURN", - "street": "Balanstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Balanstraße" - }, - { - "routeOffsetInMeters": 9145, - "travelTimeInSeconds": 1683, - "point": { - "latitude": 48.11956, - "longitude": 11.60016 - }, - "pointIndex": 308, - "instructionType": "TURN", - "street": "Sankt-Martin-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Sankt-Martin-Straße" - }, - { - "routeOffsetInMeters": 9619, - "travelTimeInSeconds": 1737, - "point": { - "latitude": 48.11811, - "longitude": 11.59417 - }, - "pointIndex": 321, - "instructionType": "TURN", - "street": "Hohenwaldeckstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Hohenwaldeckstraße", - "combinedMessage": "Biegen Sie links ab auf Hohenwaldeckstraße dann sie haben Hohenwaldeckstraße erreicht. Ihr Ziel liegt auf der linken Seite" - }, - { - "routeOffsetInMeters": 9794, - "travelTimeInSeconds": 1779, - "point": { - "latitude": 48.11655, - "longitude": 11.59422 - }, - "pointIndex": 324, - "instructionType": "LOCATION_ARRIVAL", - "street": "Hohenwaldeckstraße", - "countryCode": "DEU", - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "ARRIVE_LEFT", - "message": "Sie haben Hohenwaldeckstraße erreicht. Ihr Ziel liegt auf der linken Seite" - } - ], - "instructionGroups": [ - { - "firstInstructionIndex": 0, - "lastInstructionIndex": 2, - "groupMessage": "Abfahrt von Silcherstraße. Fahren Sie auf die Ingolstädter Straße/B13", - "groupLengthInMeters": 5234 - }, - { - "firstInstructionIndex": 3, - "lastInstructionIndex": 4, - "groupMessage": "Fahren Sie auf die Von-der-Tann-Straße", - "groupLengthInMeters": 1559 - }, - { - "firstInstructionIndex": 5, - "lastInstructionIndex": 8, - "groupMessage": "Fahren Sie auf die Isartorplatz, Rosenheimer Straße, Balanstraße, Sankt-Martin-Straße", - "groupLengthInMeters": 2826 - }, - { - "firstInstructionIndex": 9, - "lastInstructionIndex": 10, - "groupMessage": "Fahren Sie zu Ihrem Ziel in der Hohenwaldeckstraße", - "groupLengthInMeters": 175 - } - ] - } - }, - { - "summary": { - "lengthInMeters": 10231, - "travelTimeInSeconds": 2210, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:20:56+01:00", - "deviationDistance": 92, - "deviationTime": 20, - "deviationPoint": { - "latitude": 48.18696, - "longitude": 11.57857 - } - }, - "legs": [ - { - "summary": { - "lengthInMeters": 10231, - "travelTimeInSeconds": 2210, - "trafficDelayInSeconds": 0, - "trafficLengthInMeters": 0, - "departureTime": "2026-03-12T10:44:06+01:00", - "arrivalTime": "2026-03-12T11:20:56+01:00" - }, - "encodedPolyline": "ijbeHqlteAeDOO`MRA~@ErB@n@@^??P@BBDBBLBdFR?d@A`@Ab@ChBd@?bC@vDAL?xDDhD@H?zCAHAbB?l@ED?RHBB@B@D?BC|BA|DN?JJj@HLDVFpA`@RDFBHDHB\\LLFD@LDr@JF@d@Bz@Ep@EhAEhBIVCVCZ?hDUTAhAIpAKbDW\\EPAVAp@GbCSf@Cl@E\\CjBQPCfE]j@GxAKlBMRAr@E`AARCpDYdAENAdD[\\Cl@IHC^ORKLAb@ExBSdE[|BYtC[|BUX?JXBDHFJDVF\\DH?j@Cb@?xBIfF[RCt@G^CbDU`E_@`@C]jBQjAi@hC[zAfEbCfCvAlB`AJFf@\\`@TPHjBfAlCzA`Ah@bAj@z@d@jAn@pCzALFdCvArBhAh@ZRJv@d@\\RvBlAXPLHVLNHh@ZdAl@^T|Az@h@Zj@Zh@X`CrA|@d@hAv@`Ad@lBjAXiAj@yBDUPGBATKb@[RI`@M~@Mp@KVEJ?XAJALBLHPVv@bALRLN\\b@FDFJPRXRRJLBj@JLBPBzANZDR@VDL@PBh@F~@H\\F~@Jn@FX@h@EZMrDmBnAo@l@]^Sd@e@\\]PM^UXOp@a@XSHEHETMPGDCHE\\S\\WDGHKHOBUBg@CmA?Q?UBYDQBMHUFGXc@BE@OVw@Pq@j@eC@M?[?[?_@AG?ICUAGEQx@aAr@}@TUz@sANS~@uAp@cAx@mAn@}@dDeFj@y@r@eAHGLKb@w@\\g@\\e@X_@DIPU\\g@T[V_@RY^k@^i@DGNSNUl@aAFKxBkDjAgBd@s@fAaBz@uAb@g@PMdCuBb@Y\\S`Ak@`@ONE@?RAJ@p@L^P^R\\TlAr@n@RT@P?b@KVO\\YNQXYFCLGHEJCh@QNCHEPGDEJUBI`@{AXcADOZi@HOBGRk@\\cBPiATeAVaAf@cB^kA|CuHJ[Fc@@]F}ANaD@YDo@WMGMCGCECSM_Aa@qCkAoIw@kFj@Qv@IrEP", - "encodedPolylinePrecision": 5 - } - ], - "sections": [ - { - "lanes": [ - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "STRAIGHT" - ], - "follow": "STRAIGHT" - }, - { - "directions": [ - "STRAIGHT" - ], - "follow": "STRAIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "LONG_DASHED", - "SHORT_DASHED", - "LONG_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 41, - "endPointIndex": 42, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "LEFT" - ] - }, - { - "directions": [ - "STRAIGHT" - ], - "follow": "STRAIGHT" - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "SHORT_DASHED", - "SINGLE_SOLID" - ], - "startPointIndex": 323, - "endPointIndex": 326, - "sectionType": "LANES" - }, - { - "lanes": [ - { - "directions": [ - "LEFT" - ], - "follow": "LEFT" - }, - { - "directions": [ - "STRAIGHT" - ] - } - ], - "laneSeparators": [ - "SINGLE_SOLID", - "SINGLE_SOLID", - "SINGLE_SOLID" - ], - "startPointIndex": 342, - "endPointIndex": 343, - "sectionType": "LANES" - } - ], - "guidance": { - "instructions": [ - { - "routeOffsetInMeters": 0, - "travelTimeInSeconds": 0, - "point": { - "latitude": 48.18613, - "longitude": 11.57849 - }, - "pointIndex": 0, - "instructionType": "LOCATION_DEPARTURE", - "street": "Silcherstraße", - "countryCode": "DEU", - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "DEPART", - "message": "Abfahrt von Silcherstraße" - }, - { - "routeOffsetInMeters": 92, - "travelTimeInSeconds": 20, - "point": { - "latitude": 48.18696, - "longitude": 11.57857 - }, - "pointIndex": 1, - "instructionType": "TURN", - "street": "Schmalkaldener Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Schmalkaldener Straße" - }, - { - "routeOffsetInMeters": 259, - "travelTimeInSeconds": 60, - "point": { - "latitude": 48.18704, - "longitude": 11.57632 - }, - "pointIndex": 2, - "instructionType": "TURN", - "street": "Oberhofer Platz", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Oberhofer Platz" - }, - { - "routeOffsetInMeters": 415, - "travelTimeInSeconds": 99, - "point": { - "latitude": 48.18564, - "longitude": 11.57634 - }, - "pointIndex": 7, - "instructionType": "TURN", - "street": "Bad-Kreuznacher-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Bad-Kreuznacher-Straße", - "combinedMessage": "Biegen Sie rechts ab auf Bad-Kreuznacher-Straße dann biegen Sie rechts ab auf Milbertshofener Straße" - }, - { - "routeOffsetInMeters": 565, - "travelTimeInSeconds": 146, - "point": { - "latitude": 48.18437, - "longitude": 11.57606 - }, - "pointIndex": 13, - "instructionType": "TURN", - "street": "Milbertshofener Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Milbertshofener Straße", - "combinedMessage": "Biegen Sie rechts ab auf Milbertshofener Straße dann biegen Sie links ab auf Christoph-von-Gluck-Platz" - }, - { - "routeOffsetInMeters": 645, - "travelTimeInSeconds": 172, - "point": { - "latitude": 48.18441, - "longitude": 11.57499 - }, - "pointIndex": 17, - "instructionType": "TURN", - "street": "Christoph-von-Gluck-Platz", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Christoph-von-Gluck-Platz" - }, - { - "routeOffsetInMeters": 1369, - "travelTimeInSeconds": 324, - "point": { - "latitude": 48.17904, - "longitude": 11.57328 - }, - "pointIndex": 36, - "instructionType": "TURN", - "street": "Knorrstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Knorrstraße", - "combinedMessage": "Biegen Sie links ab auf Knorrstraße dann fahren Sie geradeaus weiter auf Belgradstraße" - }, - { - "routeOffsetInMeters": 1480, - "travelTimeInSeconds": 377, - "point": { - "latitude": 48.17808, - "longitude": 11.57293 - }, - "pointIndex": 42, - "instructionType": "TURN", - "street": "Belgradstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 0, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "STRAIGHT", - "message": "Fahren Sie geradeaus weiter auf Belgradstraße" - }, - { - "routeOffsetInMeters": 4053, - "travelTimeInSeconds": 832, - "point": { - "latitude": 48.15515, - "longitude": 11.57536 - }, - "pointIndex": 117, - "instructionType": "TURN", - "street": "Georgenstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Georgenstraße", - "combinedMessage": "Biegen Sie rechts ab auf Georgenstraße dann biegen Sie links ab auf Arcisstraße" - }, - { - "routeOffsetInMeters": 4220, - "travelTimeInSeconds": 885, - "point": { - "latitude": 48.15574, - "longitude": 11.5733 - }, - "pointIndex": 121, - "instructionType": "TURN", - "street": "Arcisstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Arcisstraße" - }, - { - "routeOffsetInMeters": 5472, - "travelTimeInSeconds": 1149, - "point": { - "latitude": 48.14541, - "longitude": 11.5666 - }, - "pointIndex": 151, - "instructionType": "TURN", - "street": "Katharina-von-Bora-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 0, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "STRAIGHT", - "message": "Fahren Sie geradeaus weiter auf Katharina-von-Bora-Straße" - }, - { - "routeOffsetInMeters": 5818, - "travelTimeInSeconds": 1212, - "point": { - "latitude": 48.14257, - "longitude": 11.56473 - }, - "pointIndex": 159, - "instructionType": "TURN", - "street": "Sophienstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Sophienstraße" - }, - { - "routeOffsetInMeters": 7332, - "travelTimeInSeconds": 1657, - "point": { - "latitude": 48.13184, - "longitude": 11.57056 - }, - "pointIndex": 246, - "instructionType": "TURN", - "street": "Papa-Schmid-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Papa-Schmid-Straße" - }, - { - "routeOffsetInMeters": 9169, - "travelTimeInSeconds": 2028, - "point": { - "latitude": 48.11905, - "longitude": 11.58351 - }, - "pointIndex": 326, - "instructionType": "TURN", - "street": "Sankt-Bonifatius-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 0, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "STRAIGHT", - "message": "Fahren Sie geradeaus weiter auf Sankt-Bonifatius-Straße" - }, - { - "routeOffsetInMeters": 9712, - "travelTimeInSeconds": 2111, - "point": { - "latitude": 48.11699, - "longitude": 11.58995 - }, - "pointIndex": 343, - "instructionType": "TURN", - "street": "Sankt-Martin-Straße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": -90, - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "TURN_LEFT", - "message": "Biegen Sie links ab auf Sankt-Martin-Straße" - }, - { - "routeOffsetInMeters": 10056, - "travelTimeInSeconds": 2171, - "point": { - "latitude": 48.11811, - "longitude": 11.59417 - }, - "pointIndex": 352, - "instructionType": "TURN", - "street": "Hohenwaldeckstraße", - "countryCode": "DEU", - "junctionType": "REGULAR", - "turnAngleInDecimalDegrees": 90, - "possibleCombineWithNext": true, - "drivingSide": "RIGHT", - "maneuver": "TURN_RIGHT", - "message": "Biegen Sie rechts ab auf Hohenwaldeckstraße", - "combinedMessage": "Biegen Sie rechts ab auf Hohenwaldeckstraße dann sie haben Hohenwaldeckstraße erreicht. Ihr Ziel liegt auf der linken Seite" - }, - { - "routeOffsetInMeters": 10231, - "travelTimeInSeconds": 2210, - "point": { - "latitude": 48.11655, - "longitude": 11.59422 - }, - "pointIndex": 355, - "instructionType": "LOCATION_ARRIVAL", - "street": "Hohenwaldeckstraße", - "countryCode": "DEU", - "possibleCombineWithNext": false, - "drivingSide": "RIGHT", - "maneuver": "ARRIVE_LEFT", - "message": "Sie haben Hohenwaldeckstraße erreicht. Ihr Ziel liegt auf der linken Seite" - } - ], - "instructionGroups": [ - { - "firstInstructionIndex": 0, - "lastInstructionIndex": 5, - "groupMessage": "Abfahrt von Silcherstraße. Fahren Sie auf die Schmalkaldener Straße, Oberhofer Platz, Bad-Kreuznacher-Straße, Milbertshofener Straße, Christoph-von-Gluck-Platz", - "groupLengthInMeters": 1369 - }, - { - "firstInstructionIndex": 6, - "lastInstructionIndex": 7, - "groupMessage": "Fahren Sie auf die Knorrstraße, Belgradstraße", - "groupLengthInMeters": 2684 - }, { "firstInstructionIndex": 8, - "lastInstructionIndex": 9, - "groupMessage": "Fahren Sie auf die Georgenstraße, Arcisstraße", - "groupLengthInMeters": 1419 + "lastInstructionIndex": 10, + "groupMessage": "Fahren Sie auf die Ampfingstraße, Anzinger Straße, Sankt-Martin-Straße", + "groupLengthInMeters": 2509 }, { - "firstInstructionIndex": 10, + "firstInstructionIndex": 11, "lastInstructionIndex": 12, - "groupMessage": "Fahren Sie auf die Katharina-von-Bora-Straße, Sophienstraße, Papa-Schmid-Straße", - "groupLengthInMeters": 3697 - }, - { - "firstInstructionIndex": 13, - "lastInstructionIndex": 14, - "groupMessage": "Fahren Sie auf die Sankt-Bonifatius-Straße, Sankt-Martin-Straße", - "groupLengthInMeters": 887 - }, - { - "firstInstructionIndex": 15, - "lastInstructionIndex": 16, "groupMessage": "Fahren Sie zu Ihrem Ziel in der Hohenwaldeckstraße", - "groupLengthInMeters": 175 + "groupLengthInMeters": 182 } ] } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cf0457a..df14c5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,14 +2,14 @@ agp = "9.1.0" androidGpxParser = "2.3.1" androidSdkTurf = "6.0.1" -datastore = "1.2.0" +datastore = "1.2.1" gradle = "9.1.0" koinAndroid = "4.1.1" koinAndroidxCompose = "4.1.1" koinComposeViewmodel = "4.1.1" koinCore = "4.1.1" kotlin = "2.3.10" -coreKtx = "1.17.0" +coreKtx = "1.18.0" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" @@ -21,35 +21,35 @@ material = "1.13.0" carApp = "1.7.0" androidx-car = "1.7.0" materialIconsExtended = "1.7.8" -mockitoCore = "5.22.0" +mockitoCore = "5.23.0" mockitoKotlin = "6.2.3" rules = "1.7.0" runner = "1.7.0" material3 = "1.4.0" -runtimeLivedata = "1.10.4" -foundation = "1.10.4" +runtimeLivedata = "1.10.5" +foundation = "1.10.5" maplibre-compose = "0.12.1" playServicesLocation = "21.3.0" -runtime = "1.10.4" +runtime = "1.10.5" accompanist = "0.37.3" -uiVersion = "1.10.4" -uiText = "1.10.4" +uiVersion = "1.10.5" +uiText = "1.10.5" navigationCompose = "2.9.7" -uiToolingPreview = "1.10.4" -uiTooling = "1.10.4" +uiToolingPreview = "1.10.5" +uiTooling = "1.10.5" material3WindowSizeClass = "1.4.0" -uiGraphics = "1.10.4" +uiGraphics = "1.10.5" window = "1.5.1" -foundationLayout = "1.10.4" -datastorePreferences = "1.2.0" -datastoreCore = "1.2.0" +foundationLayout = "1.10.5" +datastorePreferences = "1.2.1" +datastoreCore = "1.2.1" monitor = "1.8.0" [libraries] android-gpx-parser = { module = "com.github.ticofab:android-gpx-parser", version.ref = "androidGpxParser" } android-sdk-turf = { module = "org.maplibre.gl:android-sdk-turf", version.ref = "androidSdkTurf" } androidx-app-projected = { module = "androidx.car.app:app-projected" } -androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } +#androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx-rules = { module = "androidx.test:rules", version.ref = "rules" }