This commit is contained in:
Dimitris
2026-02-20 15:00:47 +01:00
parent ebd97cf1c9
commit 723707dac6
29 changed files with 487 additions and 648 deletions

View File

@@ -14,8 +14,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 36
versionCode = 42
versionName = "0.2.0.42"
versionCode = 43
versionName = "0.2.0.43"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -97,9 +97,6 @@ dependencies {
implementation("com.github.ticofab:android-gpx-parser:2.3.1")
implementation(libs.androidx.navigation.compose)
implementation(libs.kotlinx.serialization.json)
implementation("com.github.alorma.compose-settings:ui-tiles:2.25.0")
implementation("com.github.alorma.compose-settings:ui-tiles-extended:2.25.0")
implementation("com.github.alorma.compose-settings:ui-tiles-expressive:2.25.0")
implementation(libs.androidx.foundation.layout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)

View File

@@ -22,6 +22,8 @@ import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.SheetState
import androidx.compose.material3.SheetValue
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
@@ -34,6 +36,7 @@ import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -91,7 +94,7 @@ class MainActivity : ComponentActivity() {
val routeModel = RouteModel()
var tilt = 50.0
val useMock = true
val useMock = false
val type = 3 // 1 simulate 2 test 3 gpx 4 testSingle
var currentIndex = 0
@@ -198,13 +201,12 @@ class MainActivity : ComponentActivity() {
val appViewModel: AppViewModel = appViewModel()
val darkMode by appViewModel.darkMode.collectAsState()
val sheetPeekHeight = 250.dp
baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
val scaffoldState = rememberBottomSheetScaffoldState()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
val sheetPeekHeightState = remember { mutableStateOf(256.dp) }
val sheetPeekHeightState = remember { mutableStateOf(sheetPeekHeight) }
val locationProvider = rememberDefaultLocationProvider(
updateInterval = 0.5.seconds, desiredAccuracy = DesiredAccuracy.Highest
@@ -221,7 +223,7 @@ class MainActivity : ComponentActivity() {
fun closeSheet() {
scope.launch {
scaffoldState.bottomSheetState.partialExpand()
sheetPeekHeightState.value = 128.dp
sheetPeekHeightState.value = sheetPeekHeight
}
}
NavigationTheme (useDarkTheme = darkMode == 1) {
@@ -261,11 +263,11 @@ class MainActivity : ComponentActivity() {
@Composable
fun App() {
//val lastRoute = NavigationUtils.getStringKeyValue(applicationContext, Constants.LAST_ROUTE)
//if (lastRoute!!.isNotEmpty()) {
// routeModel.startNavigation(lastRoute, applicationContext)
// routeData.value = routeModel.curRoute.routeGeoJson
//}
val appViewModel: AppViewModel = appViewModel()
val lastRoute by appViewModel.lastRoute.collectAsState()
if (lastRoute.isNotEmpty()) {
navigationViewModel.route.value = lastRoute
}
AppNavGraph(applicationContext = applicationContext, this)
}

View File

@@ -23,6 +23,7 @@ import com.kouros.navigation.car.map.rememberBaseStyle
import com.kouros.navigation.data.StepData
import com.kouros.navigation.ui.app.AppViewModel
import com.kouros.navigation.ui.app.appViewModel
import com.kouros.navigation.utils.settingsViewModel
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.location.LocationTrackingEffect
@@ -66,7 +67,7 @@ fun MapView(
val rememberBaseStyle = rememberBaseStyle(baseStyle)
val appViewModel: AppViewModel = appViewModel()
val showBuildings by appViewModel.threedBuilding.collectAsState()
val showBuildings by appViewModel.show3D.collectAsState()
Column {
NavigationInfo(step, nextStep)

View File

@@ -4,10 +4,13 @@ import android.content.Context
import android.location.Location
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
@@ -17,6 +20,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
@@ -51,21 +55,15 @@ fun SearchSheet(
if (search.value != null) {
searchResults.addAll(search.value!!)
}
Home(applicationContext, viewModel, location, closeSheet = { closeSheet() })
if (recentPlaces.value != null) {
val items = listOf(recentPlaces)
if (items.isNotEmpty()) {
RecentPlaces(recentPlaces.value!!, viewModel, applicationContext, location, closeSheet)
}
}
// if (searchResults.isNotEmpty()) {
val textFieldState = rememberTextFieldState()
val items = listOf(searchResults)
// if (items.isNotEmpty()) {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
SearchBar(
textFieldState = textFieldState,
searchPlaces = emptyList<Place>(),
searchPlaces = emptyList(),
searchResults = searchResults,
viewModel = viewModel,
context = applicationContext,
@@ -73,10 +71,20 @@ fun SearchSheet(
closeSheet = { closeSheet() }
)
// }
//}
//Home(applicationContext, viewModel, location, closeSheet = { closeSheet() })
if (recentPlaces.value != null) {
val items = listOf(recentPlaces)
if (items.isNotEmpty()) {
RecentPlaces(
recentPlaces.value!!,
viewModel,
applicationContext,
location,
closeSheet
)
}
}
}
}
@Composable
@@ -124,8 +132,12 @@ fun SearchBar(
location: Location,
closeSheet: () -> Unit
) {
var expanded by rememberSaveable { mutableStateOf(true) }
var expanded by rememberSaveable { mutableStateOf(false) }
SearchBar(
colors = SearchBarDefaults.colors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
modifier = modifier,
inputField = {
SearchBarDefaults.InputField(
leadingIcon = {

View File

@@ -1,68 +0,0 @@
package com.kouros.navigation.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
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.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SettingsScreen(navController: NavHostController, navigateBack: () -> Unit) {
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)) {
Button(onClick = { navController.navigate("display_settings") }) {
Text(stringResource(R.string.display_settings))
}
Button(onClick = { navController.navigate("nav_settings") }) {
Text(stringResource(R.string.navigation_settings))
}
}
}
}
}

View File

@@ -18,10 +18,17 @@ class AppViewModel(
0
)
val threedBuilding = settingsRepository.threedBuildingFlow
val show3D = settingsRepository.show3DFlow
.stateIn(
viewModelScope,
SharingStarted.Eagerly,
false
)
val lastRoute = settingsRepository.lastRouteFlow
.stateIn(
viewModelScope,
SharingStarted.Eagerly,
""
)
}

View File

@@ -6,9 +6,6 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.kouros.navigation.ui.MainActivity
import com.kouros.navigation.ui.settings.NavigationScreenSettings
import com.kouros.navigation.ui.SettingsScreen
import com.kouros.navigation.ui.settings.DisplaySettings
import com.kouros.navigation.ui.settings.SettingsRoute
@@ -18,17 +15,8 @@ fun AppNavGraph(applicationContext: Context, mainActivity: MainActivity) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "startScreen") {
composable("startScreen") { mainActivity.StartScreen(navController) }
composable("settings") { SettingsScreen(navController) { navController.popBackStack() } }
composable("settingsxx") {
SettingsRoute("display_settings", navController) { navController.popBackStack() }
}
// old
//composable("display_settings") { DisplaySettings(applicationContext) { navController.popBackStack() } }
//composable("nav_settings") { NavigationScreenSettings(applicationContext) { navController.popBackStack() } }
// new
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() } }
}
}

View File

@@ -0,0 +1,116 @@
package com.kouros.navigation.ui.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
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.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.Scaffold
import androidx.compose.material3.SegmentedButtonDefaults.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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
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),
)
},
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)
) {
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))
SettingSwitch(
title = stringResource(R.string.threed_building),
checked = show3D,
onCheckedChange = viewModel::onShow3DChanged
)
SectionTitle(stringResource(R.string.dark_mode))
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
)
}
}
}
}

View File

@@ -1,155 +0,0 @@
package com.kouros.navigation.ui.settings
import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
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.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.alorma.compose.settings.ui.SettingsCheckbox
import com.alorma.compose.settings.ui.SettingsGroup
import com.alorma.compose.settings.ui.SettingsRadioButton
import com.alorma.compose.settings.ui.base.internal.LocalSettingsTileColors
import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults
import com.kouros.data.R
import com.kouros.navigation.utils.getSettingsViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DisplaySettings(context: Context, navigateBack: () -> Unit) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
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),
)
}
},
)
},
) { padding ->
val scrollState = rememberScrollState()
Column(
modifier =
Modifier
.consumeWindowInsets(padding)
.verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) {
DisplaySettings(context)
}
}
}
@Composable
private fun DisplaySettings(context: Context) {
val settingsViewModel = getSettingsViewModel(context)
Section(title = "Anzeige") {
val state = remember {
mutableStateOf(
settingsViewModel.threedBuilding.value
)
}
SettingsCheckbox(
state = state.value,
title = { Text(text = stringResource(R.string.threed_building)) },
onCheckedChange = {
state.value = it
settingsViewModel.onThreedBuildingChanged(it)
},
)
}
Section(title = "Dunkles Design") {
val state = remember {
mutableIntStateOf(
settingsViewModel.darkMode.value
)
}
DarkModeData(context).darkDesign.forEach { sampleItem ->
SettingsRadioButton(
state = state.intValue == sampleItem.key,
title = { Text(text = sampleItem.title) },
onClick = {
state.intValue = sampleItem.key
settingsViewModel.onDarkModeChanged(state.intValue)
},
)
}
}
}
@Composable
internal fun Section(
title: String,
enabled: Boolean = true,
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(4.dp),
content: @Composable ColumnScope.() -> Unit,
) {
SettingsGroup(
contentPadding = PaddingValues(0.dp),
verticalArrangement = verticalArrangement,
enabled = enabled,
title = { Text(text = title) },
) {
Card(
colors = CardDefaults.cardColors(
containerColor = (LocalSettingsTileColors.current
?: SettingsTileDefaults.colors()).containerColor
),
) {
content()
}
}
}
internal class DarkModeData(context: Context) {
val darkDesign =
listOf(
Item(
key = 0,
title = context.getString(R.string.off_action_title),
),
Item(
key = 1,
title = context.getString(R.string.on_action_title),
),
Item(
key = 2,
title = context.getString(R.string.use_telephon_settings),
),
)
}
internal data class Item(
val key: Int,
val title: String,
)

View File

@@ -0,0 +1,130 @@
package com.kouros.navigation.ui.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
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.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.kouros.data.R
import com.kouros.navigation.data.RouteEngine
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
fun NavigationScreen(viewModel: SettingsViewModel, navigateBack: () -> Unit) {
NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
stringResource(id = R.string.navigation_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()),
) {
NavigationSettings(viewModel)
}
}
}
}
@Composable
fun NavigationSettings(viewModel: SettingsViewModel) {
val avoidMotorway by viewModel.avoidMotorway.collectAsState()
val avoidTollway by viewModel.avoidTollway.collectAsState()
val carLocation by viewModel.carLocation.collectAsState()
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
)
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
)
SectionTitle(stringResource(R.string.routing_engine))
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 = {
key = it
viewModel::onTomTomApiKeyChanged
},
label = { Text(stringResource(R.string.tomtom_api_key)) },
textStyle = TextStyle(color = Color.Green, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(20.dp)
)
}
}

View File

@@ -1,151 +0,0 @@
package com.kouros.navigation.ui.settings
import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.consumeWindowInsets
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.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.alorma.compose.settings.ui.SettingsCheckbox
import com.alorma.compose.settings.ui.SettingsRadioButton
import com.kouros.data.R
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.utils.NavigationUtils
import com.kouros.navigation.utils.getSettingsViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavigationScreenSettings(context: Context, navigateBack: () -> Unit) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
stringResource(id = R.string.navigation_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()),
) {
NavigationSettings(context)
}
}
}
@Composable
private fun NavigationSettings(context: Context) {
val settingsViewModel = getSettingsViewModel(context)
Section(title = stringResource(id = R.string.options)) {
val avoidMotorwayState = remember {
mutableStateOf(
settingsViewModel.avoidMotorway.value
)
}
SettingsCheckbox(
state = avoidMotorwayState.value,
title = { Text(text = stringResource(id = R.string.avoid_highways_row_title)) },
onCheckedChange = {
avoidMotorwayState.value = it
settingsViewModel.onAvoidMotorway(it)
},
)
val avoidTollwayState = remember {
mutableStateOf(
settingsViewModel.avoidTollway.value
)
}
SettingsCheckbox(
state = avoidTollwayState.value,
title = { Text(text = stringResource(id = R.string.avoid_tolls_row_title)) },
onCheckedChange = {
avoidTollwayState.value = it
settingsViewModel.onAvoidTollway(it)
},
)
val carLocationState = remember {
mutableStateOf(
settingsViewModel.carLocation.value
)
}
SettingsCheckbox(
state = carLocationState.value,
title = { Text(text = stringResource(id = R.string.use_car_location)) },
onCheckedChange = {
carLocationState.value = it
settingsViewModel.onCarLocation(it)
},
)
}
Section(title = stringResource(id = R.string.routing_engine)) {
val state = remember {
mutableIntStateOf(
settingsViewModel.routingEngine.value
)
}
RoutingEngineData.engines.forEach { sampleItem ->
SettingsRadioButton(
state = state.intValue == sampleItem.key,
title = { Text(text = sampleItem.title) },
onClick = {
state.intValue = sampleItem.key
settingsViewModel.onRoutingEngineChanged(state.intValue)
},
)
}
}
}
internal object RoutingEngineData {
val engines =
listOf(
Item(
key = 0,
title = RouteEngine.VALHALLA.toString(),
),
Item(
key = 1,
title = RouteEngine.OSRM.toString(),
),
Item(
key = 2,
title = RouteEngine.TOMTOM.toString(),
),
)
}

View File

@@ -12,7 +12,7 @@ import com.kouros.navigation.model.SettingsViewModel
import com.kouros.navigation.repository.SettingsRepository
@Composable
fun SettingsRoute(route: String, navController: NavHostController, function: () -> Boolean) {
fun SettingsRoute(route: String, navController: NavHostController, function: () -> Unit) {
val context = LocalContext.current
@@ -28,10 +28,12 @@ fun SettingsRoute(route: String, navController: NavHostController, function: ()
}
)
if (route == "display_settings") {
SettingsScreen(viewModel = viewModel)
DisplayScreen(viewModel = viewModel, function)
}
if (route == "nav_settings") {
SettingsScreen(viewModel = viewModel)
NavigationScreen (viewModel = viewModel, function)
}
if (route == "settings") {
SettingsScreen(viewModel, navController, function)
}
///DisplaySettings(context, viewModel, navController.popBackStack())
}

View File

@@ -1,151 +1,72 @@
package com.kouros.navigation.ui.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.consumeWindowInsets
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.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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
fun SettingsScreen(viewModel: SettingsViewModel) {
val darkMode by viewModel.darkMode.collectAsState()
val threedBuilding by viewModel.threedBuilding.collectAsState()
val avoidMotorway by viewModel.avoidMotorway.collectAsState()
val avoidTollway by viewModel.avoidTollway.collectAsState()
val carLocation by viewModel.carLocation.collectAsState()
val routingEngine by viewModel.routingEngine.collectAsState()
fun SettingsScreen(viewModel: SettingsViewModel, navController: NavHostController, navigateBack: () -> Unit) {
NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
stringResource(id = R.string.display_settings),
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),
// )
// }
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 ->
) { padding ->
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 20.dp)
modifier =
Modifier
.consumeWindowInsets(padding)
.verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) {
Column(modifier = Modifier.padding(16.dp)) {
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))
SettingSwitch(
title = stringResource(R.string.threed_building),
checked = threedBuilding,
onCheckedChange = viewModel::onThreedBuildingChanged
)
SectionTitle(stringResource(R.string.dark_mode))
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
)
// Appearance
SectionTitle(stringResource(R.string.navigation_settings))
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.use_car_location),
checked = carLocation,
onCheckedChange = viewModel::onCarLocation
)
SectionTitle(stringResource(R.string.routing_engine))
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
)
Button(onClick = { navController.navigate("display_settings") }) {
Text(stringResource(R.string.display_settings))
}
Button(onClick = { navController.navigate("nav_settings") }) {
Text(stringResource(R.string.navigation_settings))
}
}
}
}
}
}

View File

@@ -2,7 +2,6 @@ package com.kouros.navigation.car
import android.Manifest
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -34,12 +33,10 @@ import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.car.screen.NavigationScreen
import com.kouros.navigation.car.screen.RequestPermissionScreen
import com.kouros.navigation.car.screen.SearchScreen
import com.kouros.navigation.data.Constants.CAR_LOCATION
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.data.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.data.valhalla.ValhallaRepository
@@ -47,7 +44,6 @@ import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.utils.GeoUtils.snapLocation
import com.kouros.navigation.utils.NavigationUtils.getViewModel
import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch

View File

@@ -31,7 +31,6 @@ import com.kouros.navigation.car.map.cameraState
import com.kouros.navigation.car.map.getPaddingValues
import com.kouros.navigation.car.map.rememberBaseStyle
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.ObjectBox
import com.kouros.navigation.data.RouteEngine
@@ -42,7 +41,6 @@ import com.kouros.navigation.utils.calculateTilt
import com.kouros.navigation.utils.calculateZoom
import com.kouros.navigation.utils.duration
import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location
import com.kouros.navigation.utils.previewZoom
import com.kouros.navigation.utils.settingsViewModel

View File

@@ -22,18 +22,18 @@ class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
init {
lifecycleScope.launch {
settingsViewModel.threedBuilding.collect {
settingsViewModel.show3D.collect {
invalidate()
}
}
}
override fun onGetTemplate(): Template {
buildingToggleState = settingsViewModel.threedBuilding.value
buildingToggleState = settingsViewModel.show3D.value
val listBuilder = ItemList.Builder()
val buildingToggle: Toggle =
Toggle.Builder { checked: Boolean ->
settingsViewModel.onThreedBuildingChanged(checked)
settingsViewModel.onShow3DChanged(checked)
buildingToggleState = !buildingToggleState
}.setChecked(buildingToggleState).build()
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle))

View File

@@ -38,9 +38,7 @@ import com.kouros.navigation.repository.SettingsRepository
import com.kouros.navigation.utils.GeoUtils
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.time.Duration
import java.time.LocalDateTime
import java.time.ZoneOffset

View File

@@ -30,10 +30,14 @@ class NavigationSettings(private val carContext: CarContext, private var navigat
init {
lifecycleScope.launch {
settingsViewModel.avoidTollway.collect {
settingsViewModel.avoidMotorway.collect {
settingsViewModel.carLocation.collect {
invalidate()
}
}
}
}
}
override fun onGetTemplate(): Template {
motorWayToggleState = settingsViewModel.avoidMotorway.value

View File

@@ -102,8 +102,6 @@ object Constants {
//const val STYLE_DARK: String = "https://kouros-online.de/liberty_night.json"
const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
const val STYLE_DARK: String = "https://tiles.openfreemap.org/styles/liberty"
const val TAG: String = "Navigation"
const val CATEGORIES: String = "Categories"
@@ -126,21 +124,6 @@ object Constants {
val homeVogelhart = location(11.5793748, 48.185749)
val homeHohenwaldeck = location( 11.594322, 48.1164817)
const val SHARED_PREF_KEY = "NavigationPrefs"
const val SHOW_THREED_BUILDING = "Show3D"
const val DARK_MODE_SETTINGS = "DarkMode"
const val AVOID_MOTORWAY = "AvoidMotorway"
const val AVOID_TOLLWAY = "AvoidTollway"
const val CAR_LOCATION = "CarLocation"
const val ROUTING_ENGINE = "RoutingEngine"
const val LAST_ROUTE = "LastRoute"
const val NEXT_STEP_THRESHOLD = 500.0
const val MAXIMAL_SNAP_CORRECTION = 50.0

View File

@@ -51,11 +51,10 @@ abstract class NavigationRepository {
currentLocation: Location,
location: Location,
carOrientation: Float,
searchFilter: SearchFilter,
context: Context
): Double {
val osrm = OsrmRepository()
val route = osrm.getRoute(context, currentLocation, location, carOrientation, searchFilter)
val route = osrm.getRoute(context, currentLocation, location, carOrientation, SearchFilter())
val gson = GsonBuilder().serializeNulls().create()
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
if (osrmJson.routes.isEmpty()) {

View File

@@ -6,6 +6,7 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
@@ -26,7 +27,7 @@ class DataStoreManager(private val context: Context) {
// Keys
private object PreferencesKeys {
val THREED_BUILDING = booleanPreferencesKey("Show3D")
val SHOW_3D = booleanPreferencesKey("Show3D")
val DARK_MODE = intPreferencesKey("DarkMode")
@@ -37,13 +38,17 @@ class DataStoreManager(private val context: Context) {
val CAR_LOCATION = booleanPreferencesKey("CarLocation")
val ROUTING_ENGINE = intPreferencesKey("RoutingEngine")
val LAST_ROUTE = stringPreferencesKey("LastRoute")
val TOMTOM_APIKEY = stringPreferencesKey("TomTomApiKey")
}
// Read values
val threeDBuildingFlow: Flow<Boolean> =
val show3DFlow: Flow<Boolean> =
context.dataStore.data.map { preferences ->
preferences[PreferencesKeys.THREED_BUILDING] == true
preferences[PreferencesKeys.SHOW_3D] == true
}
val darkModeFlow: Flow<Int> =
context.dataStore.data.map { preferences ->
@@ -72,10 +77,22 @@ class DataStoreManager(private val context: Context) {
?: 0
}
val lastRouteFlow: Flow<String> =
context.dataStore.data.map { preferences ->
preferences[PreferencesKeys.LAST_ROUTE]
?: ""
}
val tomTomApiKeyFlow: Flow<String> =
context.dataStore.data.map { preferences ->
preferences[PreferencesKeys.TOMTOM_APIKEY]
?: ""
}
// Save values
suspend fun setThreedBuilding(enabled: Boolean) {
suspend fun setShow3D(enabled: Boolean) {
context.dataStore.edit { preferences ->
preferences[PreferencesKeys.THREED_BUILDING] = enabled
preferences[PreferencesKeys.SHOW_3D] = enabled
}
}
@@ -108,4 +125,16 @@ class DataStoreManager(private val context: Context) {
prefs[PreferencesKeys.ROUTING_ENGINE] = mode
}
}
suspend fun setLastRoute(route: String) {
context.dataStore.edit { prefs ->
prefs[PreferencesKeys.LAST_ROUTE] = route
}
}
suspend fun setTomtomApiKey(apiKey: String) {
context.dataStore.edit { prefs ->
prefs[PreferencesKeys.TOMTOM_APIKEY] = apiKey
}
}
}

View File

@@ -6,18 +6,21 @@ import com.kouros.data.R
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.SearchFilter
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
import com.kouros.navigation.utils.getSettingsRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/"
const val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
//const val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
private const val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
const val useAsset = true
const val useAsset = false
class TomTomRepository : NavigationRepository() {
override fun getRoute(
@@ -32,6 +35,8 @@ class TomTomRepository : NavigationRepository() {
val routeJsonString = routeJson.bufferedReader().use { it.readText() }
return routeJsonString
}
val repository = getSettingsRepository(context)
val tomtomApiKey = runBlocking { repository.tomTomApiKeyFlow.first() }
val url =
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" +
"/json?vehicleHeading=90&sectionType=traffic&report=effectiveSettings&routeType=eco" +
@@ -47,6 +52,8 @@ class TomTomRepository : NavigationRepository() {
}
override fun getTraffic(context: Context, location: Location, carOrientation: Float): String {
val repository = getSettingsRepository(context)
val tomtomApiKey = runBlocking { repository.tomTomApiKeyFlow.first() }
val bbox = calculateSquareRadius(location.latitude, location.longitude, 15.0)
return if (useAsset) {
val trafficJson = context.resources.openRawResource(R.raw.tomtom_traffic)

View File

@@ -102,7 +102,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
location,
plLocation,
carOrientation,
SearchFilter(),
context
)
place.distance = distance.toFloat()
@@ -135,7 +134,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
location,
plLocation,
carOrientation,
getSearchFilter(context), context
context
)
place.distance = distance.toFloat()
}
@@ -164,7 +163,7 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
location,
plLocation,
carOrientation,
getSearchFilter(context),
context
)
place.distance = distance.toFloat()
@@ -486,7 +485,6 @@ class NavigationViewModel(private val repository: NavigationRepository) : ViewMo
location,
plLocation,
carOrientation,
getSearchFilter(context),
context
)
place.distance = distance.toFloat()

View File

@@ -60,7 +60,7 @@ open class RouteModel {
)
if (hasLegs()) {
navState = navState.copy(navigating = true)
//NavigationUtils.setStringKeyValue(context, routeString, LAST_ROUTE)
getSettingsViewModel(context).onLastRouteChanged(routeString)
}
}
@@ -75,7 +75,7 @@ open class RouteModel {
arrived = false,
maneuverType = Maneuver.TYPE_UNKNOWN
)
//NavigationUtils.setStringKeyValue(context, "", LAST_ROUTE)
getSettingsViewModel(context).onLastRouteChanged("")
}
fun updateLocation(context: Context, curLocation: Location, viewModel: NavigationViewModel) {

View File

@@ -9,7 +9,7 @@ import kotlinx.coroutines.launch
class SettingsViewModel(private val repository: SettingsRepository) : ViewModel() {
val threedBuilding = repository.threedBuildingFlow.stateIn(
val show3D = repository.show3DFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
false
@@ -45,10 +45,21 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
0
)
fun onThreedBuildingChanged(enabled: Boolean) {
viewModelScope.launch { repository.setThreedBuilding(enabled) }
}
val lastRoute = repository.lastRouteFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
""
)
val tomTomApiKey = repository.tomTomApiKeyFlow.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
""
)
fun onShow3DChanged(enabled: Boolean) {
viewModelScope.launch { repository.setShow3D(enabled) }
}
fun onDarkModeChanged(mode: Int) {
viewModelScope.launch { repository.setDarkMode(mode) }
@@ -69,4 +80,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
fun onRoutingEngineChanged(mode: Int) {
viewModelScope.launch { repository.setRoutingEngine(mode) }
}
fun onLastRouteChanged(route: String) {
viewModelScope.launch { repository.setLastRoute(route) }
}
fun onTomTomApiKeyChanged(route: String) {
viewModelScope.launch { repository.setTomTomApiKey(route) }
}
}

View File

@@ -6,9 +6,8 @@ import kotlinx.coroutines.flow.Flow
class SettingsRepository(
private val dataStoreManager: DataStoreManager
) {
val threedBuildingFlow: Flow<Boolean> =
dataStoreManager.threeDBuildingFlow
val show3DFlow: Flow<Boolean> =
dataStoreManager.show3DFlow
val darkModeFlow: Flow<Int> =
dataStoreManager.darkModeFlow
@@ -24,9 +23,14 @@ class SettingsRepository(
val routingEngineFlow: Flow<Int> =
dataStoreManager.routingEngineFlow
val lastRouteFlow: Flow<String> =
dataStoreManager.lastRouteFlow
suspend fun setThreedBuilding(enabled: Boolean) {
dataStoreManager.setThreedBuilding(enabled)
val tomTomApiKeyFlow: Flow<String> =
dataStoreManager.tomTomApiKeyFlow
suspend fun setShow3D(enabled: Boolean) {
dataStoreManager.setShow3D(enabled)
}
suspend fun setDarkMode(mode: Int) {
@@ -49,4 +53,12 @@ class SettingsRepository(
dataStoreManager.setRoutingEngine(mode)
}
suspend fun setLastRoute(route: String) {
dataStoreManager.setLastRoute(route)
}
suspend fun setTomTomApiKey(apiKey: String) {
dataStoreManager.setTomtomApiKey(apiKey)
}
}

View File

@@ -3,18 +3,11 @@ package com.kouros.navigation.utils
import android.content.Context
import android.location.Location
import android.location.LocationManager
import androidx.car.app.CarContext
import androidx.core.content.edit
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
import com.kouros.navigation.data.RouteEngine
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.data.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.data.valhalla.ValhallaRepository
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.model.SettingsViewModel
import com.kouros.navigation.repository.SettingsRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import java.time.LocalDateTime

View File

@@ -36,4 +36,5 @@
<string name="use_car_location">Use car location</string>
<string name="tomtom">TomTom\t</string>
<string name="options">Options</string>
<string name="tomtom_api_key">TomTom ApiKey</string>
</resources>

View File

@@ -1,7 +1,7 @@
[versions]
agp = "9.0.0"
agp = "9.0.1"
androidSdkTurf = "6.0.1"
gradle = "9.0.0"
gradle = "9.0.1"
koinAndroid = "4.1.1"
koinAndroidxCompose = "4.1.1"
koinComposeViewmodel = "4.1.1"