DataStore Manager

This commit is contained in:
Dimitris
2026-02-20 07:20:04 +01:00
parent 65ff41995d
commit ebd97cf1c9
65 changed files with 7975 additions and 13716 deletions

View File

@@ -14,8 +14,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 36
versionCode = 38
versionName = "0.2.0.38"
versionCode = 42
versionName = "0.2.0.42"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -4,7 +4,7 @@ import android.app.Application
import android.content.Context
import com.kouros.navigation.data.ObjectBox
import com.kouros.navigation.di.appModule
import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.utils.NavigationUtils.getViewModel
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
@@ -15,7 +15,7 @@ class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ObjectBox.init(this);
ObjectBox.init(this)
appContext = applicationContext
navigationViewModel = getViewModel(appContext!!)
startKoin {
@@ -30,6 +30,6 @@ class MainApplication : Application() {
var appContext: Context? = null
private set
lateinit var navigationViewModel : ViewModel
lateinit var navigationViewModel : NavigationViewModel
}
}

View File

@@ -1,18 +1,19 @@
package com.kouros.navigation.di
import com.kouros.navigation.data.NavigationRepository
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.BaseStyleModel
import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.model.SettingsViewModel
import com.kouros.navigation.repository.SettingsRepository
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModel
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
import kotlin.math.sin
val appModule = module {
viewModelOf(::ViewModel)
viewModelOf(::NavigationViewModel)
viewModelOf(::SettingsViewModel)
singleOf(::ValhallaRepository)
singleOf(::OsrmRepository)
singleOf(::TomTomRepository)

View File

@@ -43,26 +43,27 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.kouros.data.R
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
import com.kouros.navigation.data.Constants.homeVogelhart
import com.kouros.navigation.data.StepData
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.model.BaseStyleModel
import com.kouros.navigation.model.MockLocation
import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.repository.SettingsRepository
import com.kouros.navigation.ui.app.AppViewModel
import com.kouros.navigation.ui.app.appViewModel
import com.kouros.navigation.ui.navigation.AppNavGraph
import com.kouros.navigation.ui.theme.NavigationTheme
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
import com.kouros.navigation.utils.bearing
import com.kouros.navigation.utils.calculateZoom
import com.kouros.navigation.utils.getSettingsViewModel
import com.kouros.navigation.utils.location
import io.ticofab.androidgpxparser.parser.GPXParser
import io.ticofab.androidgpxparser.parser.domain.Gpx
@@ -70,7 +71,9 @@ import io.ticofab.androidgpxparser.parser.domain.TrackSegment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.joda.time.DateTime
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.location.DesiredAccuracy
@@ -83,12 +86,13 @@ import kotlin.time.Duration.Companion.seconds
class MainActivity : ComponentActivity() {
val routeData = MutableLiveData("")
val routeModel = RouteModel()
var tilt = 50.0
val useMock = false
val type = 3 // simulate 2 test 3 gpx 4 testSingle
val useMock = true
val type = 3 // 1 simulate 2 test 3 gpx 4 testSingle
var currentIndex = 0
val stepData: MutableLiveData<StepData> by lazy {
@@ -134,8 +138,7 @@ class MainActivity : ComponentActivity() {
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val darkModeSettings = getIntKeyValue(applicationContext, Constants.DARK_MODE_SETTINGS)
baseStyle = BaseStyleModel().readStyle(applicationContext, darkModeSettings, false)
if (useMock) {
checkMockLocationEnabled()
}
@@ -150,6 +153,11 @@ class MainActivity : ComponentActivity() {
navigationViewModel.route.observe(this, observer)
}
}
lifecycleScope.launch {
getSettingsViewModel(applicationContext).routingEngine.collect {
}
}
enableEdgeToEdge()
setContent {
CheckPermissionScreen()
@@ -186,9 +194,13 @@ class MainActivity : ComponentActivity() {
@Composable
fun StartScreen(
navController: NavHostController
) {
val appViewModel: AppViewModel = appViewModel()
val darkMode by appViewModel.darkMode.collectAsState()
baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
val scaffoldState = rememberBottomSheetScaffoldState()
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
@@ -212,7 +224,7 @@ class MainActivity : ComponentActivity() {
sheetPeekHeightState.value = 128.dp
}
}
NavigationTheme {
NavigationTheme (useDarkTheme = darkMode == 1) {
BottomSheetScaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
@@ -249,13 +261,12 @@ class MainActivity : ComponentActivity() {
@Composable
fun App() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "startScreen") {
composable("startScreen") { StartScreen(navController) }
composable("settings") { SettingsScreen(navController) { navController.popBackStack() } }
composable("display_settings") { DisplayScreenSettings(applicationContext) { navController.popBackStack() } }
composable("nav_settings") { NavigationScreenSettings(applicationContext) { navController.popBackStack() } }
}
//val lastRoute = NavigationUtils.getStringKeyValue(applicationContext, Constants.LAST_ROUTE)
//if (lastRoute!!.isNotEmpty()) {
// routeModel.startNavigation(lastRoute, applicationContext)
// routeData.value = routeModel.curRoute.routeGeoJson
//}
AppNavGraph(applicationContext = applicationContext, this)
}
@Composable
@@ -285,13 +296,15 @@ class MainActivity : ComponentActivity() {
if (!routeModel.isNavigating()) {
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
} else {
NavigationSheet(
applicationContext,
routeModel,
step!!,
nextStep!!,
{ stopNavigation { closeSheet() } },
{ simulateNavigation() })
if (step != null && nextStep != null) {
NavigationSheet(
applicationContext,
routeModel,
step,
nextStep,
{ stopNavigation { closeSheet() } },
{ simulateNavigation() })
}
}
// For recomposition!
Text("$locationState", fontSize = 12.sp)
@@ -303,17 +316,18 @@ class MainActivity : ComponentActivity() {
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
with(routeModel) {
if (isNavigating()) {
updateLocation(currentLocation, navigationViewModel)
updateLocation(applicationContext,currentLocation, navigationViewModel)
stepData.value = currentStep()
nextStepData.value = nextStep()
if (navState.maneuverType in 39..42 && routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
// stopNavigation()
navState.copy(arrived = true)
navState = navState.copy(arrived = true)
routeData.value = ""
}
}
}
val zoom = calculateZoom(location.speed)
//val zoom = calculateZoom(location.speed)
val zoom = 16.0
cameraPosition.postValue(
cameraPosition.value!!.copy(
zoom = zoom, target = location.position, bearing = bearing
@@ -331,7 +345,7 @@ class MainActivity : ComponentActivity() {
val latitude = routeModel.curRoute.waypoints[0][1]
val longitude = routeModel.curRoute.waypoints[0][0]
closeSheet()
routeModel.stopNavigation()
routeModel.stopNavigation(applicationContext)
if (useMock) {
mock.setMockLocation(latitude, longitude)
}
@@ -370,7 +384,7 @@ class MainActivity : ComponentActivity() {
val deviation = 0.0
if (index in 0..routeModel.curRoute.waypoints.size) {
mock.setMockLocation(waypoint[1], waypoint[0])
Thread.sleep(500)
Thread.sleep(1000)
}
}
}
@@ -381,7 +395,7 @@ class MainActivity : ComponentActivity() {
for ((index, step) in routeModel.curLeg.steps.withIndex()) {
//if (index in 3..3) {
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
routeModel.updateLocation(
routeModel.updateLocation(applicationContext,
location(waypoint[0], waypoint[1]), navigationViewModel
)
val step = routeModel.currentStep()
@@ -402,7 +416,7 @@ class MainActivity : ComponentActivity() {
if (1 == 1) {
mock.setMockLocation(latitude, longitude)
} else {
routeModel.updateLocation(
routeModel.updateLocation(applicationContext,
location(longitude, latitude), navigationViewModel
)
}

View File

@@ -5,18 +5,24 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.dp
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.window.layout.WindowMetricsCalculator
import com.kouros.navigation.car.ViewStyle
import com.kouros.navigation.car.map.MapLibre
import com.kouros.navigation.car.map.NavigationImage
import com.kouros.navigation.car.map.rememberBaseStyle
import com.kouros.navigation.data.StepData
import com.kouros.navigation.data.tomtom.TrafficData
import com.kouros.navigation.ui.app.AppViewModel
import com.kouros.navigation.ui.app.appViewModel
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.rememberCameraState
import org.maplibre.compose.location.LocationTrackingEffect
@@ -57,7 +63,11 @@ fun MapView(
)
)
val rememberBaseStyle = rememberBaseStyle( baseStyle)
val rememberBaseStyle = rememberBaseStyle(baseStyle)
val appViewModel: AppViewModel = appViewModel()
val showBuildings by appViewModel.threedBuilding.collectAsState()
Column {
NavigationInfo(step, nextStep)
Box(contentAlignment = Alignment.Center) {
@@ -67,7 +77,9 @@ fun MapView(
rememberBaseStyle,
route,
emptyMap(),
ViewStyle.VIEW
ViewStyle.VIEW,
speedCameras = "",
showBuildings
)
LocationTrackingEffect(
locationState = userLocationState,
@@ -87,6 +99,3 @@ fun MapView(
}
}
}

View File

@@ -2,10 +2,15 @@ package com.kouros.navigation.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -17,39 +22,43 @@ import com.kouros.data.R
import com.kouros.navigation.data.StepData
import com.kouros.navigation.utils.round
@Composable
fun NavigationInfo(step: StepData?, nextStep: StepData?) {
if (step != null && step.instruction.isNotEmpty()) {
Card(modifier = Modifier.padding(top = 60.dp)) {
Column() {
ElevatedCard(
elevation = CardDefaults.cardElevation(
defaultElevation = 6.dp
), modifier = Modifier
.padding(top = 60.dp)
.fillMaxWidth()
) {
Column {
Icon(
painter = painterResource(step.icon),
contentDescription = stringResource(id = R.string.accept_action_title),
modifier = Modifier.size(48.dp, 48.dp),
)
if (step.currentManeuverType == 46
|| step.currentManeuverType == 45
) {
Text(text = "Exit ${step.exitNumber}", fontSize = 18.sp)
}
Row {
Icon(
painter = painterResource(step.icon),
contentDescription = stringResource(id = R.string.accept_action_title),
modifier = Modifier.size(48.dp, 48.dp),
)
if (step.currentManeuverType == 46
|| step.currentManeuverType == 45) {
Text(text ="Exit ${step.exitNumber}", fontSize = 20.sp)
}
Column {
if (step.leftStepDistance < 1000) {
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 25.sp)
} else {
Text(
text = "${(step.leftStepDistance / 1000).round(1)} km",
fontSize = 25.sp
)
}
Text(text = step.instruction, fontSize = 20.sp)
}
if (nextStep != null && step.icon != nextStep.icon) {
Icon(
painter = painterResource(nextStep.icon),
contentDescription = stringResource(id = R.string.accept_action_title),
modifier = Modifier.size(48.dp, 48.dp),
if (step.leftStepDistance < 1000) {
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
} else {
Text(
text = "${(step.leftStepDistance / 1000).round(1)} km",
fontSize = 24.sp,
color = MaterialTheme.colorScheme.primary
)
}
Spacer(
modifier = Modifier.padding(5.dp)
)
Text(text = step.instruction, fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
}
}
}

View File

@@ -34,7 +34,7 @@ fun NavigationSheet(
val distance = (step.leftDistance / 1000).round(1)
if (step.lane.isNotEmpty()) {
routeModel.navState.iconMapper.addLanes( step)
// routeModel.navState.iconMapper.addLanes( step)
}
Column {

View File

@@ -4,10 +4,8 @@ import android.content.Context
import android.location.Location
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
@@ -29,25 +27,21 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.unit.dp
import com.kouros.data.R
import com.kouros.navigation.data.Place
import com.kouros.navigation.data.PlaceColor
import com.kouros.navigation.data.nominatim.SearchResult
import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.model.NavigationViewModel
import com.kouros.navigation.utils.location
@Composable
fun SearchSheet(
applicationContext: Context,
viewModel: ViewModel,
viewModel: NavigationViewModel,
location: Location,
closeSheet: () -> Unit
) {
@@ -88,7 +82,7 @@ fun SearchSheet(
@Composable
fun Home(
applicationContext: Context,
viewModel: ViewModel,
viewModel: NavigationViewModel,
location: Location,
closeSheet: () -> Unit
) {
@@ -125,7 +119,7 @@ fun SearchBar(
searchPlaces: List<Place>,
searchResults: List<SearchResult>,
modifier: Modifier = Modifier,
viewModel: ViewModel,
viewModel: NavigationViewModel,
context: Context,
location: Location,
closeSheet: () -> Unit
@@ -166,14 +160,14 @@ fun SearchBar(
}
}
private fun searchPlaces(viewModel: ViewModel, location: Location, it: String) {
private fun searchPlaces(viewModel: NavigationViewModel, location: Location, it: String) {
viewModel.searchPlaces(it, location)
}
@Composable
private fun SearchPlaces(
searchResults: List<SearchResult>,
viewModel: ViewModel,
viewModel: NavigationViewModel,
context: Context,
location: Location,
closeSheet: () -> Unit
@@ -222,7 +216,7 @@ private fun SearchPlaces(
@Composable
private fun RecentPlaces(
recentPlaces: List<Place>,
viewModel: ViewModel,
viewModel: NavigationViewModel,
context: Context,
location: Location,
closeSheet: () -> Unit

View File

@@ -6,6 +6,7 @@ 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
@@ -19,7 +20,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.alorma.compose.settings.ui.SettingsMenuLink
import com.kouros.data.R
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@@ -53,34 +53,16 @@ fun SettingsScreen(navController: NavHostController, navigateBack: () -> Unit) {
.verticalScroll(scrollState)
.padding(top = padding.calculateTopPadding()),
) {
SettingsMenuLink(
title = { Text(text = stringResource(R.string.display_settings)) },
modifier = Modifier,
enabled = true,
onClick = { navController.navigate("display_settings")},
icon = {
Icon(
painter = painterResource(R.drawable.ic_place_white_24dp),
contentDescription = stringResource(id = R.string.display_settings),
modifier = Modifier.size(48.dp, 48.dp),
)
Column(modifier = Modifier.padding(16.dp)) {
Button(onClick = { navController.navigate("display_settings") }) {
Text(stringResource(R.string.display_settings))
}
)
SettingsMenuLink(
title = { Text(text = stringResource(R.string.navigation_settings)) },
modifier = Modifier,
enabled = true,
onClick = { navController.navigate("nav_settings")},
icon = {
Icon(
painter = painterResource(R.drawable.navigation_24px),
contentDescription = stringResource(id = R.string.navigation_settings),
modifier = Modifier.size(48.dp, 48.dp),
)
Button(onClick = { navController.navigate("nav_settings") }) {
Text(stringResource(R.string.navigation_settings))
}
)
}
}
}
}

View File

@@ -0,0 +1,27 @@
package com.kouros.navigation.ui.app
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kouros.navigation.repository.SettingsRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
class AppViewModel(
settingsRepository: SettingsRepository
) : ViewModel() {
val darkMode = settingsRepository.darkModeFlow
.stateIn(
viewModelScope,
SharingStarted.Eagerly,
0
)
val threedBuilding = settingsRepository.threedBuildingFlow
.stateIn(
viewModelScope,
SharingStarted.Eagerly,
false
)
}

View File

@@ -0,0 +1,29 @@
package com.kouros.navigation.ui.app
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.repository.SettingsRepository
@Composable
fun appViewModel(): AppViewModel {
val context = LocalContext.current
val dataStoreManager = remember { DataStoreManager(context) }
val repository = remember { SettingsRepository(dataStoreManager) }
return viewModel(
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return AppViewModel(repository) as T
}
}
)
}

View File

@@ -0,0 +1,59 @@
package com.kouros.navigation.ui.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
@Composable
fun RadioButtonSingleSelection(
modifier: Modifier = Modifier,
selectedOption: Int,
radioOptions: List<String>,
onClick: (Int) -> Unit,
) {
Column(modifier.selectableGroup()) {
for ((index, text) in radioOptions.withIndex()) {
Row(
Modifier
.fillMaxWidth()
.height(56.dp)
.selectable(
selected = (index == selectedOption),
onClick = {
onClick(index)
},
role = Role.RadioButton
)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = (index == selectedOption),
onClick = null
)
Text(
text = text,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(start = 16.dp)
)
}
}
}
}

View File

@@ -0,0 +1,17 @@
package com.kouros.navigation.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SectionTitle(title: String) {
Text(
text = title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(top = 24.dp, bottom = 8.dp)
)
}

View File

@@ -0,0 +1,29 @@
package com.kouros.navigation.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SettingItem(
title: String,
value: String? = null
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = title)
value?.let {
Text(text = it, color = MaterialTheme.colorScheme.primary)
}
}
}

View File

@@ -0,0 +1,37 @@
package com.kouros.navigation.ui.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun SettingSwitch(
title: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = title)
Switch(
checked = checked,
onCheckedChange = onCheckedChange,
colors = SwitchDefaults.colors(
checkedThumbColor = Color.White,
uncheckedThumbColor = Color.White
)
)
}
}

View File

@@ -0,0 +1,52 @@
package com.kouros.navigation.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.kouros.navigation.data.NavigationThemeColor
@Composable
fun ThemeColorPicker(
selectedColor: Long,
onColorSelected: (Long) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
NavigationThemeColor.entries.forEach { themeColor ->
val isSelected = selectedColor == themeColor.color
Box(
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(Color(themeColor.color))
.border(
width = if (isSelected) 3.dp else 0.dp,
color = if (isSelected) MaterialTheme.colorScheme.onSurface else Color.Transparent,
shape = CircleShape
)
.clickable {
onColorSelected(themeColor.color)
}
)
}
}
}

View File

@@ -0,0 +1,34 @@
package com.kouros.navigation.ui.navigation
import android.content.Context
import androidx.compose.runtime.Composable
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
@Composable
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() } }
}
}

View File

@@ -1,4 +1,4 @@
package com.kouros.navigation.ui
package com.kouros.navigation.ui.settings
import android.content.Context
import androidx.compose.foundation.layout.Arrangement
@@ -32,13 +32,11 @@ 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.data.Constants.DARK_MODE_SETTINGS
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
import com.kouros.navigation.utils.NavigationUtils
import com.kouros.navigation.utils.getSettingsViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DisplayScreenSettings(context: Context, navigateBack: () -> Unit) {
fun DisplaySettings(context: Context, navigateBack: () -> Unit) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
@@ -75,13 +73,11 @@ fun DisplayScreenSettings(context: Context, navigateBack: () -> Unit) {
@Composable
private fun DisplaySettings(context: Context) {
val settingsViewModel = getSettingsViewModel(context)
Section(title = "Anzeige") {
val state = remember {
mutableStateOf(
NavigationUtils.getBooleanKeyValue(
context,
SHOW_THREED_BUILDING
)
settingsViewModel.threedBuilding.value
)
}
SettingsCheckbox(
@@ -89,17 +85,14 @@ private fun DisplaySettings(context: Context) {
title = { Text(text = stringResource(R.string.threed_building)) },
onCheckedChange = {
state.value = it
NavigationUtils.setBooleanKeyValue(context, it, SHOW_THREED_BUILDING)
settingsViewModel.onThreedBuildingChanged(it)
},
)
}
Section(title = "Dunkles Design") {
val state = remember {
mutableIntStateOf(
NavigationUtils.getIntKeyValue(
context,
DARK_MODE_SETTINGS
)
settingsViewModel.darkMode.value
)
}
DarkModeData(context).darkDesign.forEach { sampleItem ->
@@ -108,7 +101,7 @@ private fun DisplaySettings(context: Context) {
title = { Text(text = sampleItem.title) },
onClick = {
state.intValue = sampleItem.key
NavigationUtils.setIntKeyValue(context, state.intValue, DARK_MODE_SETTINGS)
settingsViewModel.onDarkModeChanged(state.intValue)
},
)
}

View File

@@ -1,4 +1,4 @@
package com.kouros.navigation.ui
package com.kouros.navigation.ui.settings
import android.content.Context
import androidx.compose.foundation.layout.Column
@@ -25,10 +25,10 @@ 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.DARK_MODE_SETTINGS
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
@@ -68,13 +68,11 @@ fun NavigationScreenSettings(context: Context, navigateBack: () -> Unit) {
@Composable
private fun NavigationSettings(context: Context) {
val settingsViewModel = getSettingsViewModel(context)
Section(title = stringResource(id = R.string.options)) {
val avoidMotorwayState = remember {
mutableStateOf(
NavigationUtils.getBooleanKeyValue(
context,
Constants.AVOID_MOTORWAY
)
settingsViewModel.avoidMotorway.value
)
}
SettingsCheckbox(
@@ -82,16 +80,13 @@ private fun NavigationSettings(context: Context) {
title = { Text(text = stringResource(id = R.string.avoid_highways_row_title)) },
onCheckedChange = {
avoidMotorwayState.value = it
NavigationUtils.setBooleanKeyValue(context, it, Constants.AVOID_MOTORWAY)
settingsViewModel.onAvoidMotorway(it)
},
)
val avoidTollwayState = remember {
mutableStateOf(
NavigationUtils.getBooleanKeyValue(
context,
Constants.AVOID_TOLLWAY
)
settingsViewModel.avoidTollway.value
)
}
SettingsCheckbox(
@@ -99,16 +94,13 @@ private fun NavigationSettings(context: Context) {
title = { Text(text = stringResource(id = R.string.avoid_tolls_row_title)) },
onCheckedChange = {
avoidTollwayState.value = it
NavigationUtils.setBooleanKeyValue(context, it, Constants.AVOID_TOLLWAY)
settingsViewModel.onAvoidTollway(it)
},
)
val carLocationState = remember {
mutableStateOf(
NavigationUtils.getBooleanKeyValue(
context,
Constants.CAR_LOCATION
)
settingsViewModel.carLocation.value
)
}
SettingsCheckbox(
@@ -116,7 +108,7 @@ private fun NavigationSettings(context: Context) {
title = { Text(text = stringResource(id = R.string.use_car_location)) },
onCheckedChange = {
carLocationState.value = it
NavigationUtils.setBooleanKeyValue(context, it, Constants.CAR_LOCATION)
settingsViewModel.onCarLocation(it)
},
)
}
@@ -124,10 +116,7 @@ private fun NavigationSettings(context: Context) {
Section(title = stringResource(id = R.string.routing_engine)) {
val state = remember {
mutableIntStateOf(
NavigationUtils.getIntKeyValue(
context,
ROUTING_ENGINE
)
settingsViewModel.routingEngine.value
)
}
RoutingEngineData.engines.forEach { sampleItem ->
@@ -136,7 +125,7 @@ private fun NavigationSettings(context: Context) {
title = { Text(text = sampleItem.title) },
onClick = {
state.intValue = sampleItem.key
NavigationUtils.setIntKeyValue(context, state.intValue, ROUTING_ENGINE)
settingsViewModel.onRoutingEngineChanged(state.intValue)
},
)
}

View File

@@ -0,0 +1,37 @@
package com.kouros.navigation.ui.settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.kouros.navigation.data.datastore.DataStoreManager
import com.kouros.navigation.model.SettingsViewModel
import com.kouros.navigation.repository.SettingsRepository
@Composable
fun SettingsRoute(route: String, navController: NavHostController, function: () -> Boolean) {
val context = LocalContext.current
val dataStoreManager = remember { DataStoreManager(context) }
val repository = remember { SettingsRepository(dataStoreManager) }
val viewModel: SettingsViewModel = viewModel(
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return SettingsViewModel(repository) as T
}
}
)
if (route == "display_settings") {
SettingsScreen(viewModel = viewModel)
}
if (route == "nav_settings") {
SettingsScreen(viewModel = viewModel)
}
///DisplaySettings(context, viewModel, navController.popBackStack())
}

View File

@@ -0,0 +1,151 @@
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.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 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
@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()
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 = 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

@@ -102,7 +102,7 @@ fun NavigationTheme(
}
MaterialTheme(
colorScheme = colorScheme,
colorScheme = if (useDarkTheme) darkColorScheme() else colorScheme,
typography = typography,
content = content,
shapes = shapes,