This commit is contained in:
Dimitris
2026-03-15 17:18:02 +01:00
parent 619ceb9f83
commit 5198725879
46 changed files with 840 additions and 1326 deletions

View File

@@ -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"
}

View File

@@ -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,6 +173,7 @@ class MainActivity : ComponentActivity() {
}
enableEdgeToEdge()
setContent {
NavigationTheme {
CheckPermissionScreen(app = {
AppNavGraph(
mainActivity = this
@@ -179,15 +181,21 @@ class MainActivity : ComponentActivity() {
})
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
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

View File

@@ -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() }
}
}
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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,10 +43,12 @@ 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) {
val showTraffic by viewModel.traffic.collectAsState()
val distanceMode by viewModel.distanceMode.collectAsState()
Scaffold(
topBar = {
CenterAlignedTopAppBar(
TopAppBar(
title = {
Text(
stringResource(id = R.string.display_settings),
@@ -58,31 +66,39 @@ fun DisplayScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) {
)
},
)
{ padding ->
{ paddingValues ->
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
.padding(top = 20.dp)
.padding(top = 10.dp)
.verticalScroll(scrollState)
) {
Text(
text = stringResource(R.string.settings_action_title),
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(24.dp))
// Appearance
SectionTitle(stringResource(R.string.threed_building))
OutlinedCard(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(10.dp),
) {
SettingSwitch(
title = stringResource(R.string.threed_building),
checked = show3D,
onCheckedChange = viewModel::onShow3DChanged
)
SettingSwitch(
title = stringResource(R.string.traffic),
checked = showTraffic,
onCheckedChange = viewModel::onTraffic
)
}
}
Spacer(modifier = Modifier.height(20.dp))
OutlinedCard(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(10.dp),
) {
SectionTitle(stringResource(R.string.dark_mode))
val radioOptions = listOf(
@@ -98,19 +114,29 @@ fun DisplayScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) {
)
}
}
Spacer(modifier = Modifier.height(20.dp))
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
)
}
}
}
}
}

View File

@@ -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,11 +43,9 @@ 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(
TopAppBar(
title = {
Text(
stringResource(id = R.string.navigation_settings),
@@ -64,14 +67,12 @@ fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) {
Column(
modifier =
Modifier
.consumeWindowInsets(padding)
.padding(padding)
.verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) {
NavigationSettings(viewModel)
}
}
}
}
@Composable
@@ -82,6 +83,10 @@ fun NavigationSettings(viewModel: SettingsViewModel) {
val routingEngine by viewModel.routingEngine.collectAsState()
val tomTomApiKey by viewModel.tomTomApiKey.collectAsState()
OutlinedCard(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(10.dp),
) {
SettingSwitch(
title = stringResource(R.string.avoid_highways_row_title),
checked = avoidMotorway,
@@ -99,7 +104,15 @@ fun NavigationSettings(viewModel: SettingsViewModel) {
checked = carLocation,
onCheckedChange = viewModel::onCarLocation
)
}
}
Spacer(modifier = Modifier.height(20.dp))
OutlinedCard(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(10.dp),
) {
SectionTitle(stringResource(R.string.routing_engine))
val routingEngineOptions = listOf(
@@ -127,4 +140,6 @@ fun NavigationSettings(viewModel: SettingsViewModel) {
modifier = Modifier.padding(20.dp)
)
}
}
}
}

View File

@@ -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),
)
}

View File

@@ -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)
}
}

View File

@@ -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) {
fun SettingsScreen(
viewModel: SettingsViewModel,
navController: NavHostController,
navigateBack: () -> Unit
) {
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 = {
CenterAlignedTopAppBar(
title = {
Text(
stringResource(id = R.string.settings_action_title),
TopAppBar(
title = { Text("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),
)
}
},
)
},
) { padding ->
val scrollState = rememberScrollState()
Column(
modifier =
Modifier
.consumeWindowInsets(padding)
.verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) { paddingValues ->
LazyColumn(
modifier = Modifier
.padding(paddingValues)
.padding(8.dp),
) {
Column(modifier = Modifier.padding(16.dp)) {
Button(onClick = { navController.navigate("display_settings") }) {
Text(stringResource(R.string.display_settings))
}
Button(onClick = { navController.navigate("nav_settings") }) {
Text(stringResource(R.string.navigation_settings))
}
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()
}
}
}

View File

@@ -84,28 +84,24 @@ private val DarkColors = darkColorScheme(
@Composable
fun NavigationTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor -> {
val context = LocalContext.current
val colors = run {
if (useDarkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
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
useDarkTheme -> DarkColors
else -> LightColors
}
MaterialTheme(
colorScheme = if (useDarkTheme) DarkColors else colorScheme,
colorScheme = colorScheme,
typography = typography,
content = content,
shapes = shapes,
)
}
}

View File

@@ -176,14 +176,20 @@ 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}")
//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)
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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(

View File

@@ -39,11 +39,12 @@ class PlaceListScreen(
private val places: List<Place>
) : Screen(carContext) {
val routeModel = RouteCarModel()
var place = Place()
var mPlaces = mutableListOf<Place>()
val previewObserver = Observer<String> { 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()

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -62,7 +62,7 @@ data class StepData (
var icon: Int,
var arrivalTime : Long,
var leftDistance: Double,
var lane: List<Lane> = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
var lane: List<Lane> = listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList(), 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

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -4,5 +4,5 @@ import java.util.Collections
data class Intersection(
val location: List<Double> = listOf(0.0, 0.0),
val lane : List<Lane> = Collections.emptyList<Lane>(),
val lane : List<Lane> = Collections.emptyList(),
)

View File

@@ -6,4 +6,6 @@ data class Lane (
val location: Location,
val valid: Boolean,
var indications: List<String>,
val startIndex: Int,
val endIndex: Int,
)

View File

@@ -2,5 +2,4 @@ package com.kouros.navigation.data.route
data class Leg(
var steps : List<Step> = arrayListOf(),
var intersection: List<Intersection> = arrayListOf()
)

View File

@@ -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,
)

View File

@@ -35,7 +35,6 @@ class TomTomRoute {
}
var stepDistance = 0.0
var stepDuration = 0.0
val allIntersections = mutableListOf<Intersection>()
val steps = mutableListOf<Step>()
var lastPointIndex = 0
for (index in 1..<route.guidance.instructions.size) {
@@ -56,32 +55,44 @@ class TomTomRoute {
instruction.point.longitude, instruction.point.latitude
),
street = maneuverStreet,
message = instruction.message
message = instruction.message,
pointIndex = instruction.pointIndex
)
lastPointIndex = instruction.pointIndex
val intersections = mutableListOf<Intersection>()
route.sections?.forEach { section ->
if (section.sectionType == "LANES" && section.startPointIndex <= lastPointIndex && section.endPointIndex >= lastPointIndex) {
val lanes = mutableListOf<Lane>()
var startIndex = 0
var lastLane: Lane? = null
section.lanes?.forEach { itLane ->
val lane = Lane(
location(
location = location(
waypoints[section.startPointIndex][0],
waypoints[section.startPointIndex][1]
),
itLane.directions.first() == itLane.follow,
itLane.directions
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))
}
allIntersections.addAll(intersections)
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
stepDuration = route.guidance.instructions[index].travelTimeInSeconds - stepDuration
stepDistance =
route.guidance.instructions[index].routeOffsetInMeters - stepDistance
stepDuration =
route.guidance.instructions[index].travelTimeInSeconds - stepDuration
val step = Step(
index = stepIndex,
street = street,
@@ -95,7 +106,8 @@ class TomTomRoute {
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 {

View File

@@ -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<Bitmap>()
stepData.lane.forEach { lane ->
if (lane.indications.isNotEmpty()) {
Collections.sort<String>(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 -> {
""
}

View File

@@ -550,14 +550,15 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
val rp = settingsRepository.recentPlacesFlow.first()
val places = mutableListOf<Place>()
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()

View File

@@ -31,7 +31,7 @@ class RouteCalculator(var routeModel: RouteModel) {
}
}
if (nearestDistance < NEAREST_LOCATION_DISTANCE) {
break;
break
}
}
}

View File

@@ -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,39 +73,8 @@ 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
@@ -128,16 +101,13 @@ open class RouteModel {
private fun currentLanes(): List<Lane> {
var lanes = emptyList<Lane>()
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
}

View File

@@ -0,0 +1,26 @@
<!--
Copyright 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,18l0,-3l-2,0l0,3l-3,0l0,2l3,0l0,3l2,0l0,-3l3,0l0,-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M18,4h2v9h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M4,4h2v16h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M11,4h2v4h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M11,10h2v4h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M11,16h2v4h-2z"/>
</vector>

View File

@@ -0,0 +1,21 @@
<!--
Copyright 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,21c-1.39,0 -2.78,-0.47 -4,-1.32c-2.44,1.71 -5.56,1.71 -8,0C6.78,20.53 5.39,21 4,21H2v2h2c1.38,0 2.74,-0.35 4,-0.99c2.52,1.29 5.48,1.29 8,0c1.26,0.65 2.62,0.99 4,0.99h2v-2H20zM3.95,19H4c1.6,0 3.02,-0.88 4,-2c0.98,1.12 2.4,2 4,2s3.02,-0.88 4,-2c0.98,1.12 2.4,2 4,2h0.05l1.9,-6.68c0.11,-0.37 0.04,-1.06 -0.66,-1.28L20,10.62V6c0,-1.1 -0.9,-2 -2,-2h-3V1H9v3H6C4.9,4 4,4.9 4,6v4.62l-1.29,0.42c-0.63,0.19 -0.81,0.84 -0.66,1.28L3.95,19zM6,6h12v3.97L12,8L6,9.97V6z"/>
</vector>

View File

@@ -0,0 +1,22 @@
<!--
Copyright 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8 8,-3.58 8,-8 -3.58,-8 -8,-8zM15,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
<path android:fillColor="@android:color/white" android:pathData="M3,12c0,-2.61 1.67,-4.83 4,-5.65V4.26C3.55,5.15 1,8.27 1,12s2.55,6.85 6,7.74v-2.09c-2.33,-0.82 -4,-3.04 -4,-5.65z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -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" }