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" applicationId = "com.kouros.navigation"
minSdk = 33 minSdk = 33
targetSdk = 36 targetSdk = 36
versionCode = 42 versionCode = 43
versionName = "0.2.0.42" versionName = "0.2.0.43"
base.archivesName = "navi-$versionName" base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -97,9 +97,6 @@ dependencies {
implementation("com.github.ticofab:android-gpx-parser:2.3.1") implementation("com.github.ticofab:android-gpx-parser:2.3.1")
implementation(libs.androidx.navigation.compose) implementation(libs.androidx.navigation.compose)
implementation(libs.kotlinx.serialization.json) 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) implementation(libs.androidx.foundation.layout)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.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.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.SheetState
import androidx.compose.material3.SheetValue
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -34,6 +36,7 @@ import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -91,7 +94,7 @@ class MainActivity : ComponentActivity() {
val routeModel = RouteModel() val routeModel = RouteModel()
var tilt = 50.0 var tilt = 50.0
val useMock = true val useMock = false
val type = 3 // 1 simulate 2 test 3 gpx 4 testSingle val type = 3 // 1 simulate 2 test 3 gpx 4 testSingle
var currentIndex = 0 var currentIndex = 0
@@ -198,13 +201,12 @@ class MainActivity : ComponentActivity() {
val appViewModel: AppViewModel = appViewModel() val appViewModel: AppViewModel = appViewModel()
val darkMode by appViewModel.darkMode.collectAsState() val darkMode by appViewModel.darkMode.collectAsState()
val sheetPeekHeight = 250.dp
baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1) baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
val scaffoldState = rememberBottomSheetScaffoldState() val scaffoldState = rememberBottomSheetScaffoldState()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val sheetPeekHeightState = remember { mutableStateOf(256.dp) } val sheetPeekHeightState = remember { mutableStateOf(sheetPeekHeight) }
val locationProvider = rememberDefaultLocationProvider( val locationProvider = rememberDefaultLocationProvider(
updateInterval = 0.5.seconds, desiredAccuracy = DesiredAccuracy.Highest updateInterval = 0.5.seconds, desiredAccuracy = DesiredAccuracy.Highest
@@ -221,7 +223,7 @@ class MainActivity : ComponentActivity() {
fun closeSheet() { fun closeSheet() {
scope.launch { scope.launch {
scaffoldState.bottomSheetState.partialExpand() scaffoldState.bottomSheetState.partialExpand()
sheetPeekHeightState.value = 128.dp sheetPeekHeightState.value = sheetPeekHeight
} }
} }
NavigationTheme (useDarkTheme = darkMode == 1) { NavigationTheme (useDarkTheme = darkMode == 1) {
@@ -261,11 +263,11 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun App() { fun App() {
//val lastRoute = NavigationUtils.getStringKeyValue(applicationContext, Constants.LAST_ROUTE) val appViewModel: AppViewModel = appViewModel()
//if (lastRoute!!.isNotEmpty()) { val lastRoute by appViewModel.lastRoute.collectAsState()
// routeModel.startNavigation(lastRoute, applicationContext) if (lastRoute.isNotEmpty()) {
// routeData.value = routeModel.curRoute.routeGeoJson navigationViewModel.route.value = lastRoute
//} }
AppNavGraph(applicationContext = applicationContext, this) 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.data.StepData
import com.kouros.navigation.ui.app.AppViewModel import com.kouros.navigation.ui.app.AppViewModel
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.CameraPosition
import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.location.LocationTrackingEffect import org.maplibre.compose.location.LocationTrackingEffect
@@ -66,7 +67,7 @@ fun MapView(
val rememberBaseStyle = rememberBaseStyle(baseStyle) val rememberBaseStyle = rememberBaseStyle(baseStyle)
val appViewModel: AppViewModel = appViewModel() val appViewModel: AppViewModel = appViewModel()
val showBuildings by appViewModel.threedBuilding.collectAsState() val showBuildings by appViewModel.show3D.collectAsState()
Column { Column {
NavigationInfo(step, nextStep) NavigationInfo(step, nextStep)

View File

@@ -4,10 +4,13 @@ import android.content.Context
import android.location.Location import android.location.Location
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState 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.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -51,21 +55,15 @@ fun SearchSheet(
if (search.value != null) { if (search.value != null) {
searchResults.addAll(search.value!!) 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 textFieldState = rememberTextFieldState()
val items = listOf(searchResults) Column(
// if (items.isNotEmpty()) { modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
SearchBar( SearchBar(
textFieldState = textFieldState, textFieldState = textFieldState,
searchPlaces = emptyList<Place>(), searchPlaces = emptyList(),
searchResults = searchResults, searchResults = searchResults,
viewModel = viewModel, viewModel = viewModel,
context = applicationContext, context = applicationContext,
@@ -73,10 +71,20 @@ fun SearchSheet(
closeSheet = { closeSheet() } 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 @Composable
@@ -124,8 +132,12 @@ fun SearchBar(
location: Location, location: Location,
closeSheet: () -> Unit closeSheet: () -> Unit
) { ) {
var expanded by rememberSaveable { mutableStateOf(true) } var expanded by rememberSaveable { mutableStateOf(false) }
SearchBar( SearchBar(
colors = SearchBarDefaults.colors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
modifier = modifier,
inputField = { inputField = {
SearchBarDefaults.InputField( SearchBarDefaults.InputField(
leadingIcon = { 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 0
) )
val threedBuilding = settingsRepository.threedBuildingFlow val show3D = settingsRepository.show3DFlow
.stateIn( .stateIn(
viewModelScope, viewModelScope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
false 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.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.kouros.navigation.ui.MainActivity 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 import com.kouros.navigation.ui.settings.SettingsRoute
@@ -18,17 +15,8 @@ fun AppNavGraph(applicationContext: Context, mainActivity: MainActivity) {
val navController = rememberNavController() val navController = rememberNavController()
NavHost(navController = navController, startDestination = "startScreen") { NavHost(navController = navController, startDestination = "startScreen") {
composable("startScreen") { mainActivity.StartScreen(navController) } 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("display_settings") { SettingsRoute("display_settings", navController) { navController.popBackStack() } }
composable("nav_settings") { SettingsRoute("nav_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 import com.kouros.navigation.repository.SettingsRepository
@Composable @Composable
fun SettingsRoute(route: String, navController: NavHostController, function: () -> Boolean) { fun SettingsRoute(route: String, navController: NavHostController, function: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
@@ -28,10 +28,12 @@ fun SettingsRoute(route: String, navController: NavHostController, function: ()
} }
) )
if (route == "display_settings") { if (route == "display_settings") {
SettingsScreen(viewModel = viewModel) DisplayScreen(viewModel = viewModel, function)
} }
if (route == "nav_settings") { 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 package com.kouros.navigation.ui.settings
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.kouros.data.R import com.kouros.data.R
import com.kouros.navigation.model.SettingsViewModel import com.kouros.navigation.model.SettingsViewModel
import com.kouros.navigation.ui.components.RadioButtonSingleSelection import com.kouros.navigation.ui.theme.NavigationTheme
import com.kouros.navigation.ui.components.SectionTitle
import com.kouros.navigation.ui.components.SettingSwitch
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SettingsScreen(viewModel: SettingsViewModel) { fun SettingsScreen(viewModel: SettingsViewModel, navController: NavHostController, navigateBack: () -> Unit) {
NavigationTheme(useDarkTheme = viewModel.darkMode.collectAsState().value == 1) {
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()
Scaffold( Scaffold(
topBar = { topBar = {
CenterAlignedTopAppBar( CenterAlignedTopAppBar(
title = { title = {
Text( Text(
stringResource(id = R.string.display_settings), stringResource(id = R.string.settings_action_title),
) )
}, },
navigationIcon = { navigationIcon = {
// IconButton(onClick = navigateBack) { IconButton(onClick = navigateBack) {
// Icon( Icon(
// painter = painterResource(R.drawable.arrow_back_24px), painter = painterResource(R.drawable.arrow_back_24px),
// contentDescription = stringResource(id = R.string.accept_action_title), contentDescription = stringResource(id = R.string.accept_action_title),
// modifier = Modifier.size(48.dp, 48.dp), modifier = Modifier.size(48.dp, 48.dp),
// ) )
// } }
}, },
) )
}, },
) ) { padding ->
{ padding ->
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Column( Column(
modifier = Modifier modifier =
.fillMaxSize() Modifier
.padding(top = 20.dp) .consumeWindowInsets(padding)
.verticalScroll(scrollState) .verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) { ) {
Column(modifier = Modifier.padding(16.dp)) {
Text( Button(onClick = { navController.navigate("display_settings") }) {
text = stringResource(R.string.settings_action_title), Text(stringResource(R.string.display_settings))
style = MaterialTheme.typography.headlineMedium }
) Button(onClick = { navController.navigate("nav_settings") }) {
Text(stringResource(R.string.navigation_settings))
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
)
} }
} }
} }

View File

@@ -2,7 +2,6 @@ package com.kouros.navigation.car
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Application
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager 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.NavigationScreen
import com.kouros.navigation.car.screen.RequestPermissionScreen import com.kouros.navigation.car.screen.RequestPermissionScreen
import com.kouros.navigation.car.screen.SearchScreen 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_ROUTE_DEVIATION
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
import com.kouros.navigation.data.Constants.TAG import com.kouros.navigation.data.Constants.TAG
import com.kouros.navigation.data.RouteEngine 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.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.data.valhalla.ValhallaRepository 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.GeoUtils.snapLocation
import com.kouros.navigation.utils.NavigationUtils.getViewModel import com.kouros.navigation.utils.NavigationUtils.getViewModel
import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch 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.getPaddingValues
import com.kouros.navigation.car.map.rememberBaseStyle import com.kouros.navigation.car.map.rememberBaseStyle
import com.kouros.navigation.car.navigation.RouteCarModel 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.Constants.homeVogelhart
import com.kouros.navigation.data.ObjectBox import com.kouros.navigation.data.ObjectBox
import com.kouros.navigation.data.RouteEngine 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.calculateZoom
import com.kouros.navigation.utils.duration import com.kouros.navigation.utils.duration
import com.kouros.navigation.utils.getSettingsRepository import com.kouros.navigation.utils.getSettingsRepository
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location import com.kouros.navigation.utils.location
import com.kouros.navigation.utils.previewZoom import com.kouros.navigation.utils.previewZoom
import com.kouros.navigation.utils.settingsViewModel import com.kouros.navigation.utils.settingsViewModel

View File

@@ -22,18 +22,18 @@ class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
init { init {
lifecycleScope.launch { lifecycleScope.launch {
settingsViewModel.threedBuilding.collect { settingsViewModel.show3D.collect {
invalidate() invalidate()
} }
} }
} }
override fun onGetTemplate(): Template { override fun onGetTemplate(): Template {
buildingToggleState = settingsViewModel.threedBuilding.value buildingToggleState = settingsViewModel.show3D.value
val listBuilder = ItemList.Builder() val listBuilder = ItemList.Builder()
val buildingToggle: Toggle = val buildingToggle: Toggle =
Toggle.Builder { checked: Boolean -> Toggle.Builder { checked: Boolean ->
settingsViewModel.onThreedBuildingChanged(checked) settingsViewModel.onShow3DChanged(checked)
buildingToggleState = !buildingToggleState buildingToggleState = !buildingToggleState
}.setChecked(buildingToggleState).build() }.setChecked(buildingToggleState).build()
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle)) 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.GeoUtils
import com.kouros.navigation.utils.getSettingsViewModel import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location import com.kouros.navigation.utils.location
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.time.Duration import java.time.Duration
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneOffset import java.time.ZoneOffset

View File

@@ -30,10 +30,14 @@ class NavigationSettings(private val carContext: CarContext, private var navigat
init { init {
lifecycleScope.launch { lifecycleScope.launch {
settingsViewModel.avoidTollway.collect { settingsViewModel.avoidTollway.collect {
settingsViewModel.avoidMotorway.collect {
settingsViewModel.carLocation.collect {
invalidate() invalidate()
} }
} }
} }
}
}
override fun onGetTemplate(): Template { override fun onGetTemplate(): Template {
motorWayToggleState = settingsViewModel.avoidMotorway.value 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_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 TAG: String = "Navigation"
const val CATEGORIES: String = "Categories" const val CATEGORIES: String = "Categories"
@@ -126,21 +124,6 @@ object Constants {
val homeVogelhart = location(11.5793748, 48.185749) val homeVogelhart = location(11.5793748, 48.185749)
val homeHohenwaldeck = location( 11.594322, 48.1164817) val homeHohenwaldeck = location( 11.594322, 48.1164817)
const val 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 NEXT_STEP_THRESHOLD = 500.0
const val MAXIMAL_SNAP_CORRECTION = 50.0 const val MAXIMAL_SNAP_CORRECTION = 50.0

View File

@@ -51,11 +51,10 @@ abstract class NavigationRepository {
currentLocation: Location, currentLocation: Location,
location: Location, location: Location,
carOrientation: Float, carOrientation: Float,
searchFilter: SearchFilter,
context: Context context: Context
): Double { ): Double {
val osrm = OsrmRepository() 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 gson = GsonBuilder().serializeNulls().create()
val osrmJson = gson.fromJson(route, OsrmResponse::class.java) val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
if (osrmJson.routes.isEmpty()) { 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.booleanPreferencesKey
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@@ -26,7 +27,7 @@ class DataStoreManager(private val context: Context) {
// Keys // Keys
private object PreferencesKeys { private object PreferencesKeys {
val THREED_BUILDING = booleanPreferencesKey("Show3D") val SHOW_3D = booleanPreferencesKey("Show3D")
val DARK_MODE = intPreferencesKey("DarkMode") val DARK_MODE = intPreferencesKey("DarkMode")
@@ -37,13 +38,17 @@ class DataStoreManager(private val context: Context) {
val CAR_LOCATION = booleanPreferencesKey("CarLocation") val CAR_LOCATION = booleanPreferencesKey("CarLocation")
val ROUTING_ENGINE = intPreferencesKey("RoutingEngine") val ROUTING_ENGINE = intPreferencesKey("RoutingEngine")
val LAST_ROUTE = stringPreferencesKey("LastRoute")
val TOMTOM_APIKEY = stringPreferencesKey("TomTomApiKey")
} }
// Read values // Read values
val threeDBuildingFlow: Flow<Boolean> = val show3DFlow: Flow<Boolean> =
context.dataStore.data.map { preferences -> context.dataStore.data.map { preferences ->
preferences[PreferencesKeys.THREED_BUILDING] == true preferences[PreferencesKeys.SHOW_3D] == true
} }
val darkModeFlow: Flow<Int> = val darkModeFlow: Flow<Int> =
context.dataStore.data.map { preferences -> context.dataStore.data.map { preferences ->
@@ -72,10 +77,22 @@ class DataStoreManager(private val context: Context) {
?: 0 ?: 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 // Save values
suspend fun setThreedBuilding(enabled: Boolean) { suspend fun setShow3D(enabled: Boolean) {
context.dataStore.edit { preferences -> 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 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.NavigationRepository
import com.kouros.navigation.data.SearchFilter import com.kouros.navigation.data.SearchFilter
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius 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/" 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" const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
private const val tomtomFields = private const val tomtomFields =
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}" "{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
const val useAsset = true const val useAsset = false
class TomTomRepository : NavigationRepository() { class TomTomRepository : NavigationRepository() {
override fun getRoute( override fun getRoute(
@@ -32,6 +35,8 @@ class TomTomRepository : NavigationRepository() {
val routeJsonString = routeJson.bufferedReader().use { it.readText() } val routeJsonString = routeJson.bufferedReader().use { it.readText() }
return routeJsonString return routeJsonString
} }
val repository = getSettingsRepository(context)
val tomtomApiKey = runBlocking { repository.tomTomApiKeyFlow.first() }
val url = val url =
routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" + routeUrl + "${currentLocation.latitude},${currentLocation.longitude}:${location.latitude},${location.longitude}" +
"/json?vehicleHeading=90&sectionType=traffic&report=effectiveSettings&routeType=eco" + "/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 { 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) val bbox = calculateSquareRadius(location.latitude, location.longitude, 15.0)
return if (useAsset) { return if (useAsset) {
val trafficJson = context.resources.openRawResource(R.raw.tomtom_traffic) val trafficJson = context.resources.openRawResource(R.raw.tomtom_traffic)

View File

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

View File

@@ -60,7 +60,7 @@ open class RouteModel {
) )
if (hasLegs()) { if (hasLegs()) {
navState = navState.copy(navigating = true) navState = navState.copy(navigating = true)
//NavigationUtils.setStringKeyValue(context, routeString, LAST_ROUTE) getSettingsViewModel(context).onLastRouteChanged(routeString)
} }
} }
@@ -75,7 +75,7 @@ open class RouteModel {
arrived = false, arrived = false,
maneuverType = Maneuver.TYPE_UNKNOWN maneuverType = Maneuver.TYPE_UNKNOWN
) )
//NavigationUtils.setStringKeyValue(context, "", LAST_ROUTE) getSettingsViewModel(context).onLastRouteChanged("")
} }
fun updateLocation(context: Context, curLocation: Location, viewModel: NavigationViewModel) { 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() { class SettingsViewModel(private val repository: SettingsRepository) : ViewModel() {
val threedBuilding = repository.threedBuildingFlow.stateIn( val show3D = repository.show3DFlow.stateIn(
viewModelScope, viewModelScope,
SharingStarted.WhileSubscribed(5_000), SharingStarted.WhileSubscribed(5_000),
false false
@@ -45,10 +45,21 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
0 0
) )
fun onThreedBuildingChanged(enabled: Boolean) { val lastRoute = repository.lastRouteFlow.stateIn(
viewModelScope.launch { repository.setThreedBuilding(enabled) } 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) { fun onDarkModeChanged(mode: Int) {
viewModelScope.launch { repository.setDarkMode(mode) } viewModelScope.launch { repository.setDarkMode(mode) }
@@ -69,4 +80,12 @@ class SettingsViewModel(private val repository: SettingsRepository) : ViewModel(
fun onRoutingEngineChanged(mode: Int) { fun onRoutingEngineChanged(mode: Int) {
viewModelScope.launch { repository.setRoutingEngine(mode) } 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( class SettingsRepository(
private val dataStoreManager: DataStoreManager private val dataStoreManager: DataStoreManager
) { ) {
val show3DFlow: Flow<Boolean> =
val threedBuildingFlow: Flow<Boolean> = dataStoreManager.show3DFlow
dataStoreManager.threeDBuildingFlow
val darkModeFlow: Flow<Int> = val darkModeFlow: Flow<Int> =
dataStoreManager.darkModeFlow dataStoreManager.darkModeFlow
@@ -24,9 +23,14 @@ class SettingsRepository(
val routingEngineFlow: Flow<Int> = val routingEngineFlow: Flow<Int> =
dataStoreManager.routingEngineFlow dataStoreManager.routingEngineFlow
val lastRouteFlow: Flow<String> =
dataStoreManager.lastRouteFlow
suspend fun setThreedBuilding(enabled: Boolean) { val tomTomApiKeyFlow: Flow<String> =
dataStoreManager.setThreedBuilding(enabled) dataStoreManager.tomTomApiKeyFlow
suspend fun setShow3D(enabled: Boolean) {
dataStoreManager.setShow3D(enabled)
} }
suspend fun setDarkMode(mode: Int) { suspend fun setDarkMode(mode: Int) {
@@ -49,4 +53,12 @@ class SettingsRepository(
dataStoreManager.setRoutingEngine(mode) 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.content.Context
import android.location.Location import android.location.Location
import android.location.LocationManager 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.RouteEngine
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.data.osrm.OsrmRepository import com.kouros.navigation.data.osrm.OsrmRepository
import com.kouros.navigation.data.tomtom.TomTomRepository import com.kouros.navigation.data.tomtom.TomTomRepository
import com.kouros.navigation.data.valhalla.ValhallaRepository import com.kouros.navigation.data.valhalla.ValhallaRepository
import com.kouros.navigation.model.NavigationViewModel 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.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.time.LocalDateTime import java.time.LocalDateTime

View File

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

View File

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