DataStore Manager
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() } }
|
||||
|
||||
}
|
||||
}
|
||||
+8
-15
@@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
+11
-22
@@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ fun NavigationTheme(
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
colorScheme = if (useDarkTheme) darkColorScheme() else colorScheme,
|
||||
typography = typography,
|
||||
content = content,
|
||||
shapes = shapes,
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies {
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.compose.ui.text)
|
||||
implementation(libs.play.services.location)
|
||||
implementation(libs.androidx.datastore.core)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
testImplementation(libs.junit)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.car
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@@ -26,6 +27,9 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.car.screen.NavigationScreen
|
||||
import com.kouros.navigation.car.screen.RequestPermissionScreen
|
||||
@@ -35,13 +39,20 @@ import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
||||
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
||||
import com.kouros.navigation.data.Constants.TAG
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.getViewModel
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
|
||||
@@ -54,7 +65,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
lateinit var surfaceRenderer: SurfaceRenderer
|
||||
|
||||
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
||||
val useCarLocation = getBooleanKeyValue(carContext, CAR_LOCATION)
|
||||
val repository = getSettingsRepository(carContext)
|
||||
val useCarLocation = runBlocking { repository.carLocationFlow.first() }
|
||||
if (!useCarLocation) {
|
||||
updateLocation(location!!)
|
||||
}
|
||||
@@ -75,7 +87,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo
|
||||
val useCarLocation = getBooleanKeyValue(carContext, CAR_LOCATION)
|
||||
val repository = getSettingsRepository(carContext)
|
||||
val useCarLocation = runBlocking { repository.carLocationFlow.first() }
|
||||
if (useCarLocation) {
|
||||
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
||||
carSensors.removeCarHardwareLocationListener(carLocationListener)
|
||||
@@ -88,8 +101,9 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var navigationViewModel: ViewModel
|
||||
lateinit var navigationViewModel: NavigationViewModel
|
||||
|
||||
lateinit var viewModelStoreOwner : ViewModelStoreOwner
|
||||
val carLocationListener: OnCarDataAvailableListener<CarHardwareLocation?> =
|
||||
OnCarDataAvailableListener { data ->
|
||||
if (data.location.status == CarValue.STATUS_SUCCESS) {
|
||||
@@ -123,22 +137,39 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
|
||||
fun onRoutingEngineStateUpdated(routeEngine : Int) {
|
||||
navigationViewModel = when (routeEngine) {
|
||||
RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> ViewModel(OsrmRepository())
|
||||
else -> ViewModel(TomTomRepository())
|
||||
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||
else -> NavigationViewModel(TomTomRepository())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateScreen(intent: Intent): Screen {
|
||||
|
||||
viewModelStoreOwner = object : ViewModelStoreOwner {
|
||||
override val viewModelStore = ViewModelStore()
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
viewModelStoreOwner.viewModelStore.clear()
|
||||
}
|
||||
}
|
||||
|
||||
// lifecycleScope.launch {
|
||||
|
||||
|
||||
//}
|
||||
|
||||
|
||||
navigationViewModel = getViewModel(carContext)
|
||||
|
||||
navigationViewModel.routingEngine.observe(this, ::onRoutingEngineStateUpdated)
|
||||
|
||||
routeModel = RouteCarModel()
|
||||
|
||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel, viewModelStoreOwner)
|
||||
|
||||
navigationScreen =
|
||||
NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel)
|
||||
@@ -175,7 +206,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
|
||||
fun addSensors() {
|
||||
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo
|
||||
val useCarLocation = getBooleanKeyValue(carContext, CAR_LOCATION)
|
||||
val repository = getSettingsRepository(carContext)
|
||||
val useCarLocation = runBlocking { repository.carLocationFlow.first() }
|
||||
if (useCarLocation) {
|
||||
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
||||
carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL,
|
||||
@@ -269,8 +301,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopNavigation() {
|
||||
routeModel.stopNavigation()
|
||||
override fun stopNavigation(context: CarContext) {
|
||||
routeModel.stopNavigation(context)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.car.app.connection.CarConnection
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
@@ -21,6 +22,7 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
import com.kouros.navigation.car.map.DrawNavigationImages
|
||||
@@ -29,21 +31,23 @@ import com.kouros.navigation.car.map.cameraState
|
||||
import com.kouros.navigation.car.map.getPaddingValues
|
||||
import com.kouros.navigation.car.map.rememberBaseStyle
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||
import com.kouros.navigation.data.Constants.homeVogelhart
|
||||
import com.kouros.navigation.data.ObjectBox
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.tomtom.TrafficData
|
||||
import com.kouros.navigation.model.BaseStyleModel
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.utils.bearing
|
||||
import com.kouros.navigation.utils.calculateTilt
|
||||
import com.kouros.navigation.utils.calculateZoom
|
||||
import com.kouros.navigation.utils.duration
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import com.kouros.navigation.utils.previewZoom
|
||||
import com.kouros.navigation.utils.settingsViewModel
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.style.BaseStyle
|
||||
@@ -52,7 +56,8 @@ import org.maplibre.spatialk.geojson.Position
|
||||
|
||||
class SurfaceRenderer(
|
||||
private var carContext: CarContext, lifecycle: Lifecycle,
|
||||
private var routeModel: RouteCarModel
|
||||
private var routeModel: RouteCarModel,
|
||||
private var viewModelStoreOwner: ViewModelStoreOwner
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
var lastLocation = location(0.0, 0.0)
|
||||
@@ -187,9 +192,12 @@ class SurfaceRenderer(
|
||||
@Composable
|
||||
fun MapView() {
|
||||
|
||||
val darkMode = getIntKeyValue(carContext, Constants.DARK_MODE_SETTINGS)
|
||||
val baseStyle = BaseStyleModel().readStyle(carContext, darkMode, carContext.isDarkMode)
|
||||
|
||||
//val appViewModel: AppViewModel = appViewModel(viewModelStoreOwner)
|
||||
//val darkMode by appViewModel.darkMode.collectAsState()
|
||||
val darkMode = settingsViewModel(carContext, viewModelStoreOwner).darkMode.collectAsState().value
|
||||
|
||||
val baseStyle = BaseStyleModel().readStyle(carContext, darkMode, carContext.isDarkMode)
|
||||
val position: CameraPosition? by cameraPosition.observeAsState()
|
||||
val route: String? by routeData.observeAsState()
|
||||
val traffic: Map<String, String> ? by trafficData.observeAsState()
|
||||
@@ -197,7 +205,17 @@ class SurfaceRenderer(
|
||||
val paddingValues = getPaddingValues(height, viewStyle)
|
||||
val cameraState = cameraState(paddingValues, position, tilt)
|
||||
val rememberBaseStyle = rememberBaseStyle(baseStyle)
|
||||
MapLibre(carContext, cameraState, rememberBaseStyle, route, traffic, viewStyle, speedCameras)
|
||||
|
||||
MapLibre(
|
||||
carContext,
|
||||
cameraState,
|
||||
rememberBaseStyle,
|
||||
route,
|
||||
traffic,
|
||||
viewStyle,
|
||||
speedCameras,
|
||||
false
|
||||
)
|
||||
ShowPosition(cameraState, position, paddingValues)
|
||||
}
|
||||
|
||||
@@ -339,7 +357,8 @@ class SurfaceRenderer(
|
||||
}
|
||||
|
||||
fun updateCarLocation(location: Location) {
|
||||
val routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE)
|
||||
val repository = getSettingsRepository(carContext)
|
||||
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
|
||||
if (routingEngine == RouteEngine.OSRM.ordinal) {
|
||||
updateLocation(location)
|
||||
}
|
||||
|
||||
@@ -26,15 +26,18 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.ViewStyle
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
||||
import com.kouros.navigation.data.NavigationColor
|
||||
import com.kouros.navigation.data.RouteColor
|
||||
import com.kouros.navigation.data.SpeedColor
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.model.SettingsViewModel
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
import org.maplibre.compose.camera.CameraState
|
||||
import org.maplibre.compose.camera.rememberCameraState
|
||||
@@ -92,7 +95,8 @@ fun MapLibre(
|
||||
route: String?,
|
||||
traffic: Map<String, String>?,
|
||||
viewStyle: ViewStyle,
|
||||
speedCameras: String? = ""
|
||||
speedCameras: String? = "",
|
||||
showBuildings: Boolean
|
||||
) {
|
||||
MaplibreMap(
|
||||
options = MapOptions(
|
||||
@@ -103,7 +107,7 @@ fun MapLibre(
|
||||
baseStyle = baseStyle
|
||||
) {
|
||||
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
||||
if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) {
|
||||
if (!showBuildings) {
|
||||
BuildingLayer(tiles)
|
||||
}
|
||||
if (viewStyle == ViewStyle.AMENITY_VIEW) {
|
||||
@@ -553,3 +557,4 @@ fun PuckState(cameraState: CameraState, userLocationState: UserLocationState) {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import android.location.Location
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.model.Action
|
||||
@@ -14,17 +13,16 @@ import androidx.core.graphics.drawable.IconCompat
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.car.ViewStyle
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Category
|
||||
import com.kouros.navigation.data.Constants.CHARGING_STATION
|
||||
import com.kouros.navigation.data.Constants.FUEL_STATION
|
||||
import com.kouros.navigation.data.Constants.PHARMACY
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
|
||||
class CategoriesScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val viewModel: ViewModel,
|
||||
private val navigationViewModel: NavigationViewModel,
|
||||
) : Screen(carContext) {
|
||||
|
||||
var categories: List<Category> = listOf(
|
||||
@@ -48,7 +46,7 @@ class CategoriesScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
it.id,
|
||||
viewModel
|
||||
navigationViewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import android.location.Location
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
@@ -19,11 +18,10 @@ import androidx.lifecycle.Observer
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.car.navigation.NavigationMessage
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.overpass.Elements
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
||||
import com.kouros.navigation.utils.location
|
||||
import com.kouros.navigation.utils.round
|
||||
@@ -33,7 +31,7 @@ class CategoryScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val category: String,
|
||||
private val viewModel: ViewModel,
|
||||
private val navigationViewModel: NavigationViewModel,
|
||||
) : Screen(carContext) {
|
||||
|
||||
var elements = listOf<Elements>()
|
||||
@@ -57,8 +55,8 @@ class CategoryScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
viewModel.elements.observe(this, observer)
|
||||
viewModel.getAmenities(category, surfaceRenderer.lastLocation)
|
||||
navigationViewModel.elements.observe(this, observer)
|
||||
navigationViewModel.getAmenities(category, surfaceRenderer.lastLocation)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +128,7 @@ class CategoryScreen(
|
||||
row.addAction(
|
||||
Action.Builder()
|
||||
.setOnClickListener {
|
||||
viewModel.loadRoute(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
currentLocation = surfaceRenderer.lastLocation,
|
||||
location(it.lon!!, it.lat!!),
|
||||
|
||||
@@ -10,23 +10,27 @@ import androidx.car.app.model.ListTemplate
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.SectionedItemList
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
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.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DarkModeSettings(private val carContext: CarContext) : Screen(carContext) {
|
||||
|
||||
private var darkModeSettings = 0
|
||||
|
||||
val settingsViewModel = getSettingsViewModel(carContext)
|
||||
|
||||
init {
|
||||
darkModeSettings = getIntKeyValue(carContext, DARK_MODE_SETTINGS)
|
||||
lifecycleScope.launch {
|
||||
settingsViewModel.darkMode.collect {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
darkModeSettings = settingsViewModel.darkMode.value
|
||||
val templateBuilder = ListTemplate.Builder()
|
||||
val radioList =
|
||||
ItemList.Builder()
|
||||
@@ -52,10 +56,12 @@ class DarkModeSettings(private val carContext: CarContext) : Screen(carContext)
|
||||
.build()
|
||||
|
||||
return templateBuilder
|
||||
.addSectionedList(SectionedItemList.create(
|
||||
radioList,
|
||||
carContext.getString(R.string.dark_mode)
|
||||
))
|
||||
.addSectionedList(
|
||||
SectionedItemList.create(
|
||||
radioList,
|
||||
carContext.getString(R.string.dark_mode)
|
||||
)
|
||||
)
|
||||
.setHeader(
|
||||
Header.Builder()
|
||||
.setTitle(carContext.getString(R.string.dark_mode))
|
||||
@@ -67,7 +73,7 @@ class DarkModeSettings(private val carContext: CarContext) : Screen(carContext)
|
||||
|
||||
|
||||
private fun onSelected(index: Int) {
|
||||
setIntKeyValue(carContext, index, DARK_MODE_SETTINGS)
|
||||
settingsViewModel.onDarkModeChanged(index)
|
||||
CarToast.makeText(
|
||||
carContext,
|
||||
(carContext
|
||||
|
||||
@@ -6,33 +6,34 @@ import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.Header
|
||||
import androidx.car.app.model.ItemList
|
||||
import androidx.car.app.model.ListTemplate
|
||||
import androidx.car.app.model.OnClickListener
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.car.app.model.Toggle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
||||
|
||||
private var buildingToggleState = false
|
||||
|
||||
val settingsViewModel = getSettingsViewModel(carContext)
|
||||
|
||||
init {
|
||||
buildingToggleState = getBooleanKeyValue(carContext, SHOW_THREED_BUILDING)
|
||||
lifecycleScope.launch {
|
||||
settingsViewModel.threedBuilding.collect {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
buildingToggleState = settingsViewModel.threedBuilding.value
|
||||
val listBuilder = ItemList.Builder()
|
||||
val buildingToggle: Toggle =
|
||||
Toggle.Builder { checked: Boolean ->
|
||||
if (checked) {
|
||||
setBooleanKeyValue(carContext, true, SHOW_THREED_BUILDING)
|
||||
} else {
|
||||
setBooleanKeyValue(carContext, false, SHOW_THREED_BUILDING)
|
||||
}
|
||||
settingsViewModel.onThreedBuildingChanged(checked)
|
||||
buildingToggleState = !buildingToggleState
|
||||
}.setChecked(buildingToggleState).build()
|
||||
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle))
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.car.app.navigation.model.NavigationTemplate
|
||||
import androidx.car.app.navigation.model.RoutingInfo
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.car.ViewStyle
|
||||
@@ -29,11 +30,18 @@ import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.data.nominatim.SearchResult
|
||||
import com.kouros.navigation.data.overpass.Elements
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import com.kouros.navigation.utils.GeoUtils
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -43,21 +51,21 @@ class NavigationScreen(
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private var routeModel: RouteCarModel,
|
||||
private var listener: Listener,
|
||||
private val viewModel: ViewModel
|
||||
private val navigationViewModel: NavigationViewModel
|
||||
) :
|
||||
Screen(carContext) {
|
||||
|
||||
/** A listener for navigation start and stop signals. */
|
||||
interface Listener {
|
||||
/** Stops navigation. */
|
||||
fun stopNavigation()
|
||||
fun stopNavigation(context: CarContext)
|
||||
}
|
||||
|
||||
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
||||
var recentPlace = Place()
|
||||
var navigationType = NavigationType.VIEW
|
||||
|
||||
var lastTrafficDate = LocalDateTime.of(1960, 6, 21, 0, 0)
|
||||
var lastTrafficDate: LocalDateTime? = LocalDateTime.of(1960, 6, 21, 0, 0)
|
||||
val observer = Observer<String> { route ->
|
||||
if (route.isNotEmpty()) {
|
||||
navigationType = NavigationType.NAVIGATION
|
||||
@@ -99,7 +107,7 @@ class NavigationScreen(
|
||||
speedCameras = cameras
|
||||
val coordinates = mutableListOf<List<Double>>()
|
||||
cameras.forEach {
|
||||
coordinates.add(listOf(it.lon!!, it.lat!!))
|
||||
coordinates.add(listOf(it.lon, it.lat))
|
||||
}
|
||||
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||
surfaceRenderer.speedCamerasData.value = speedData
|
||||
@@ -107,11 +115,15 @@ class NavigationScreen(
|
||||
|
||||
|
||||
init {
|
||||
viewModel.route.observe(this, observer)
|
||||
viewModel.traffic.observe(this, trafficObserver);
|
||||
viewModel.recentPlace.observe(this, recentObserver)
|
||||
viewModel.placeLocation.observe(this, placeObserver)
|
||||
viewModel.speedCameras.observe(this, speedObserver)
|
||||
navigationViewModel.route.observe(this, observer)
|
||||
navigationViewModel.traffic.observe(this, trafficObserver);
|
||||
navigationViewModel.recentPlace.observe(this, recentObserver)
|
||||
navigationViewModel.placeLocation.observe(this, placeObserver)
|
||||
navigationViewModel.speedCameras.observe(this, speedObserver)
|
||||
lifecycleScope.launch {
|
||||
getSettingsViewModel(carContext).routingEngine.collect {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
@@ -306,7 +318,7 @@ class NavigationScreen(
|
||||
)
|
||||
.setOnClickListener {
|
||||
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
||||
viewModel.loadRoute(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
navigateTo,
|
||||
@@ -349,7 +361,7 @@ class NavigationScreen(
|
||||
return Action.Builder()
|
||||
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px))
|
||||
.setOnClickListener {
|
||||
screenManager.push(SettingsScreen(carContext, viewModel))
|
||||
screenManager.push(SettingsScreen(carContext, navigationViewModel))
|
||||
}
|
||||
.build()
|
||||
}
|
||||
@@ -411,13 +423,13 @@ class NavigationScreen(
|
||||
SearchScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
viewModel
|
||||
navigationViewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
val place = obj as Place
|
||||
if (place.longitude == 0.0) {
|
||||
viewModel.findAddress(
|
||||
navigationViewModel.findAddress(
|
||||
"${obj.city} ${obj.street}},",
|
||||
currentNavigationLocation
|
||||
)
|
||||
@@ -432,9 +444,9 @@ class NavigationScreen(
|
||||
fun navigateToPlace(place: Place) {
|
||||
navigationType = NavigationType.VIEW
|
||||
val location = location(place.longitude, place.latitude)
|
||||
viewModel.saveRecent(place)
|
||||
navigationViewModel.saveRecent(place)
|
||||
currentNavigationLocation = location
|
||||
viewModel.loadRoute(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
location,
|
||||
@@ -446,7 +458,7 @@ class NavigationScreen(
|
||||
|
||||
fun stopNavigation() {
|
||||
navigationType = NavigationType.VIEW
|
||||
listener.stopNavigation()
|
||||
listener.stopNavigation(carContext)
|
||||
surfaceRenderer.routeData.value = ""
|
||||
lastCameraSearch = 0
|
||||
invalidate()
|
||||
@@ -470,7 +482,7 @@ class NavigationScreen(
|
||||
|
||||
fun reRoute(destination: Place) {
|
||||
val dest = location(destination.longitude, destination.latitude)
|
||||
viewModel.loadRoute(
|
||||
navigationViewModel.loadRoute(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
dest,
|
||||
@@ -480,14 +492,14 @@ class NavigationScreen(
|
||||
|
||||
fun updateTrip(location: Location) {
|
||||
val current = LocalDateTime.now(ZoneOffset.UTC)
|
||||
val duration = java.time.Duration.between(current, lastTrafficDate)
|
||||
val duration = Duration.between(current, lastTrafficDate)
|
||||
if (duration.abs().seconds > 360) {
|
||||
lastTrafficDate = current
|
||||
viewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
||||
navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
||||
}
|
||||
updateSpeedCamera(location)
|
||||
with(routeModel) {
|
||||
updateLocation(location, viewModel)
|
||||
updateLocation(carContext,location, navigationViewModel)
|
||||
if ((navState.maneuverType == Maneuver.TYPE_DESTINATION
|
||||
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
||||
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
||||
@@ -506,7 +518,7 @@ class NavigationScreen(
|
||||
|
||||
private fun updateSpeedCamera(location: Location) {
|
||||
if (lastCameraSearch++ % 100 == 0) {
|
||||
viewModel.getSpeedCameras(location, 5.0)
|
||||
navigationViewModel.getSpeedCameras(location, 5.0)
|
||||
}
|
||||
if (speedCameras.isNotEmpty()) {
|
||||
updateDistance(location)
|
||||
|
||||
@@ -9,16 +9,14 @@ import androidx.car.app.model.ListTemplate
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.car.app.model.Toggle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants.AVOID_MOTORWAY
|
||||
import com.kouros.navigation.data.Constants.AVOID_TOLLWAY
|
||||
import com.kouros.navigation.data.Constants.CAR_LOCATION
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class NavigationSettings(private val carContext: CarContext, private var viewModel: ViewModel) :
|
||||
class NavigationSettings(private val carContext: CarContext, private var navigationViewModel: NavigationViewModel) :
|
||||
Screen(carContext) {
|
||||
|
||||
private var motorWayToggleState = false
|
||||
@@ -27,25 +25,25 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
||||
|
||||
private var carLocationToggleState = false
|
||||
|
||||
val settingsViewModel = getSettingsViewModel(carContext)
|
||||
|
||||
init {
|
||||
motorWayToggleState = getBooleanKeyValue(carContext, AVOID_MOTORWAY)
|
||||
|
||||
tollWayToggleState = getBooleanKeyValue(carContext, AVOID_MOTORWAY)
|
||||
|
||||
carLocationToggleState = getBooleanKeyValue(carContext, CAR_LOCATION)
|
||||
|
||||
lifecycleScope.launch {
|
||||
settingsViewModel.avoidTollway.collect {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
motorWayToggleState = settingsViewModel.avoidMotorway.value
|
||||
tollWayToggleState = settingsViewModel.avoidTollway.value
|
||||
carLocationToggleState = settingsViewModel.carLocation.value
|
||||
|
||||
val listBuilder = ItemList.Builder()
|
||||
val highwayToggle: Toggle =
|
||||
Toggle.Builder { checked: Boolean ->
|
||||
if (checked) {
|
||||
setBooleanKeyValue(carContext, true, AVOID_MOTORWAY)
|
||||
} else {
|
||||
setBooleanKeyValue(carContext, false, AVOID_MOTORWAY)
|
||||
}
|
||||
settingsViewModel.onAvoidMotorway(checked)
|
||||
motorWayToggleState = !motorWayToggleState
|
||||
}.setChecked(motorWayToggleState).build()
|
||||
listBuilder.addItem(buildRowForTemplate(R.string.avoid_highways_row_title, highwayToggle))
|
||||
@@ -53,22 +51,14 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
||||
// Tollway
|
||||
val tollwayToggle: Toggle =
|
||||
Toggle.Builder { checked: Boolean ->
|
||||
if (checked) {
|
||||
setBooleanKeyValue(carContext, true, AVOID_TOLLWAY)
|
||||
} else {
|
||||
setBooleanKeyValue(carContext, false, AVOID_TOLLWAY)
|
||||
}
|
||||
settingsViewModel.onAvoidTollway(checked)
|
||||
tollWayToggleState = !tollWayToggleState
|
||||
}.setChecked(tollWayToggleState).build()
|
||||
listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle))
|
||||
|
||||
val carLocationToggle: Toggle =
|
||||
Toggle.Builder { checked: Boolean ->
|
||||
if (checked) {
|
||||
setBooleanKeyValue(carContext, true, CAR_LOCATION)
|
||||
} else {
|
||||
setBooleanKeyValue(carContext, false, CAR_LOCATION)
|
||||
}
|
||||
settingsViewModel.onCarLocation(checked)
|
||||
carLocationToggleState = !carLocationToggleState
|
||||
}.setChecked(carLocationToggleState).build()
|
||||
|
||||
@@ -81,7 +71,7 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForScreenTemplate(
|
||||
RoutingSettings(carContext, viewModel),
|
||||
RoutingSettings(carContext, navigationViewModel),
|
||||
R.string.routing_engine
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import android.location.Location
|
||||
import android.net.Uri
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
@@ -25,14 +24,14 @@ import com.kouros.navigation.data.Constants.CONTACTS
|
||||
import com.kouros.navigation.data.Constants.FAVORITES
|
||||
import com.kouros.navigation.data.Constants.RECENT
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
|
||||
|
||||
class PlaceListScreen(
|
||||
private val carContext: CarContext,
|
||||
private val surfaceRenderer: SurfaceRenderer,
|
||||
private val category: String,
|
||||
private val viewModel: ViewModel
|
||||
private val navigationViewModel: NavigationViewModel
|
||||
) : Screen(carContext) {
|
||||
|
||||
var places = listOf<Place>()
|
||||
@@ -49,30 +48,30 @@ class PlaceListScreen(
|
||||
|
||||
init {
|
||||
if (category == RECENT) {
|
||||
viewModel.places.observe(this, observer)
|
||||
navigationViewModel.places.observe(this, observer)
|
||||
}
|
||||
if (category == CONTACTS) {
|
||||
viewModel.contactAddress.observe(this, observerAddress)
|
||||
navigationViewModel.contactAddress.observe(this, observerAddress)
|
||||
}
|
||||
if (category == FAVORITES) {
|
||||
viewModel.favorites.observe(this, observer)
|
||||
navigationViewModel.favorites.observe(this, observer)
|
||||
}
|
||||
loadPlaces()
|
||||
}
|
||||
|
||||
fun loadPlaces() {
|
||||
if (category == RECENT) {
|
||||
viewModel.loadRecentPlaces(
|
||||
navigationViewModel.loadRecentPlaces(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
surfaceRenderer.carOrientation
|
||||
)
|
||||
}
|
||||
if (category == CONTACTS) {
|
||||
viewModel.loadContacts(carContext)
|
||||
navigationViewModel.loadContacts(carContext)
|
||||
}
|
||||
if (category == FAVORITES) {
|
||||
viewModel.loadFavorites(
|
||||
navigationViewModel.loadFavorites(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
surfaceRenderer.carOrientation
|
||||
@@ -110,7 +109,7 @@ class PlaceListScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
place,
|
||||
viewModel
|
||||
navigationViewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
@@ -163,7 +162,7 @@ class PlaceListScreen(
|
||||
)
|
||||
)
|
||||
.setOnClickListener {
|
||||
viewModel.deletePlace(place)
|
||||
navigationViewModel.deletePlace(place)
|
||||
CarToast.makeText(
|
||||
carContext,
|
||||
R.string.recent_Item_deleted, CarToast.LENGTH_LONG
|
||||
|
||||
@@ -6,7 +6,6 @@ import androidx.annotation.DrawableRes
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.CarToast
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.constraints.ConstraintManager
|
||||
import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||
import androidx.car.app.model.ActionStrip
|
||||
@@ -29,7 +28,7 @@ import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.car.navigation.NavigationMessage
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
@@ -39,7 +38,7 @@ class RoutePreviewScreen(
|
||||
carContext: CarContext,
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private var destination: Place,
|
||||
private val viewModel: ViewModel
|
||||
private val navigationViewModel: NavigationViewModel
|
||||
) :
|
||||
Screen(carContext) {
|
||||
private var isFavorite = false
|
||||
@@ -56,9 +55,9 @@ class RoutePreviewScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
viewModel.previewRoute.observe(this, observer)
|
||||
navigationViewModel.previewRoute.observe(this, observer)
|
||||
val location = location(destination.longitude, destination.latitude)
|
||||
viewModel.loadPreviewRoute(
|
||||
navigationViewModel.loadPreviewRoute(
|
||||
carContext,
|
||||
surfaceRenderer.lastLocation,
|
||||
location,
|
||||
@@ -164,7 +163,7 @@ class RoutePreviewScreen(
|
||||
CarToast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
viewModel.saveFavorite(destination)
|
||||
navigationViewModel.saveFavorite(destination)
|
||||
invalidate()
|
||||
}
|
||||
.build()
|
||||
@@ -172,7 +171,7 @@ class RoutePreviewScreen(
|
||||
private fun deleteFavoriteAction(): Action = Action.Builder()
|
||||
.setOnClickListener {
|
||||
if (isFavorite) {
|
||||
viewModel.deleteFavorite(destination)
|
||||
navigationViewModel.deleteFavorite(destination)
|
||||
}
|
||||
isFavorite = !isFavorite
|
||||
finish()
|
||||
|
||||
@@ -10,28 +10,28 @@ import androidx.car.app.model.ListTemplate
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.SectionedItemList
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.car.app.model.Toggle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.Constants.AVOID_MOTORWAY
|
||||
import com.kouros.navigation.data.Constants.CAR_LOCATION
|
||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
||||
import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RoutingSettings(private val carContext: CarContext, private var viewModel: ViewModel) : Screen(carContext) {
|
||||
class RoutingSettings(private val carContext: CarContext, private var navigationViewModel: NavigationViewModel) : Screen(carContext) {
|
||||
private var routingEngine = RouteEngine.OSRM.ordinal
|
||||
|
||||
val settingsViewModel = getSettingsViewModel(carContext)
|
||||
|
||||
init {
|
||||
routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE)
|
||||
|
||||
lifecycleScope.launch {
|
||||
settingsViewModel.routingEngine.collect {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
routingEngine = settingsViewModel.routingEngine.value
|
||||
val templateBuilder = ListTemplate.Builder()
|
||||
val radioList =
|
||||
ItemList.Builder()
|
||||
@@ -71,8 +71,8 @@ class RoutingSettings(private val carContext: CarContext, private var viewModel:
|
||||
}
|
||||
|
||||
private fun onSelected(index: Int) {
|
||||
setIntKeyValue(carContext, index, ROUTING_ENGINE)
|
||||
viewModel.routingEngine.value = index
|
||||
settingsViewModel.onRoutingEngineChanged(index)
|
||||
navigationViewModel.routingEngine.value = index
|
||||
CarToast.makeText(
|
||||
carContext,
|
||||
(carContext
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.location.Location
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.model.Action
|
||||
@@ -16,18 +15,17 @@ import androidx.lifecycle.Observer
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.SurfaceRenderer
|
||||
import com.kouros.navigation.car.ViewStyle
|
||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||
import com.kouros.navigation.data.Category
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.nominatim.SearchResult
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
|
||||
|
||||
class SearchScreen(
|
||||
carContext: CarContext,
|
||||
private var surfaceRenderer: SurfaceRenderer,
|
||||
private val viewModel: ViewModel,
|
||||
private val navigationViewModel: NavigationViewModel,
|
||||
) : Screen(carContext) {
|
||||
|
||||
var isSearchComplete: Boolean = false
|
||||
@@ -47,7 +45,7 @@ class SearchScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
viewModel.searchPlaces.observe(this, observer)
|
||||
navigationViewModel.searchPlaces.observe(this, observer)
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
@@ -71,7 +69,7 @@ class SearchScreen(
|
||||
CategoriesScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
viewModel
|
||||
navigationViewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||
@@ -87,7 +85,7 @@ class SearchScreen(
|
||||
carContext,
|
||||
surfaceRenderer,
|
||||
it.id,
|
||||
viewModel
|
||||
navigationViewModel
|
||||
)
|
||||
) { obj: Any? ->
|
||||
if (obj != null) {
|
||||
@@ -115,7 +113,7 @@ class SearchScreen(
|
||||
object : SearchCallback {
|
||||
override fun onSearchSubmitted(searchTerm: String) {
|
||||
isSearchComplete = true
|
||||
viewModel.searchPlaces(searchTerm, surfaceRenderer.lastLocation)
|
||||
navigationViewModel.searchPlaces(searchTerm, surfaceRenderer.lastLocation)
|
||||
}
|
||||
})
|
||||
.setHeaderAction(Action.BACK)
|
||||
|
||||
@@ -24,12 +24,12 @@ import androidx.car.app.model.ListTemplate
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.Template
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
|
||||
/** A screen demonstrating selectable lists. */
|
||||
class SettingsScreen(
|
||||
carContext: CarContext,
|
||||
private var viewModel: ViewModel,
|
||||
private var navigationViewModel: NavigationViewModel,
|
||||
) : Screen(carContext) {
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
@@ -42,7 +42,7 @@ class SettingsScreen(
|
||||
)
|
||||
listBuilder.addItem(
|
||||
buildRowForTemplate(
|
||||
NavigationSettings(carContext, viewModel),
|
||||
NavigationSettings(carContext, navigationViewModel),
|
||||
R.string.navigation_settings
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.kouros.navigation.car
|
||||
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,7 @@ import org.junit.Test
|
||||
class ViewModelTest {
|
||||
|
||||
val repo = ValhallaRepository()
|
||||
val viewModel = ViewModel(repo)
|
||||
val navigationViewModel = NavigationViewModel(repo)
|
||||
val model = RouteModel()
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
kotlin("plugin.serialization") version "2.2.21"
|
||||
kotlin("kapt")
|
||||
}
|
||||
@@ -19,6 +20,10 @@ android {
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
@@ -40,6 +45,10 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val composeBom = platform(libs.androidx.compose.bom)
|
||||
implementation(composeBom)
|
||||
androidTestImplementation(composeBom)
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
@@ -49,6 +58,7 @@ dependencies {
|
||||
implementation(libs.koin.compose.viewmodel)
|
||||
implementation(libs.androidx.car.app)
|
||||
implementation(libs.android.sdk.turf)
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
|
||||
|
||||
// objectbox
|
||||
@@ -56,6 +66,8 @@ dependencies {
|
||||
implementation(libs.androidx.material3)
|
||||
annotationProcessor(libs.objectbox.processor)
|
||||
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.maplibre.compose)
|
||||
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
||||
|
||||
@@ -139,7 +139,9 @@ object Constants {
|
||||
const val CAR_LOCATION = "CarLocation"
|
||||
const val ROUTING_ENGINE = "RoutingEngine"
|
||||
|
||||
const val NEXT_STEP_THRESHOLD = 120.0
|
||||
const val LAST_ROUTE = "LastRoute"
|
||||
|
||||
const val NEXT_STEP_THRESHOLD = 500.0
|
||||
|
||||
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
||||
|
||||
@@ -157,3 +159,12 @@ object Constants {
|
||||
enum class RouteEngine {
|
||||
VALHALLA, OSRM, TOMTOM, GRAPHHOPPER
|
||||
}
|
||||
|
||||
enum class NavigationThemeColor(val color: Long) {
|
||||
RED(0xFFD32F2F),
|
||||
ORANGE(0xFFF57C00),
|
||||
YELLOW(0xFFFBC02D),
|
||||
GREEN(0xFF388E3C),
|
||||
BLUE(0xFF1976D2),
|
||||
PURPLE(0xFF7B1FA2)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ package com.kouros.navigation.data
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||
import com.kouros.navigation.data.osrm.OsrmResponse
|
||||
import com.kouros.navigation.data.osrm.OsrmRoute
|
||||
import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
|
||||
import java.net.Authenticator
|
||||
@@ -50,10 +54,18 @@ abstract class NavigationRepository {
|
||||
searchFilter: SearchFilter,
|
||||
context: Context
|
||||
): Double {
|
||||
val route = getRoute(context, currentLocation, location, carOrientation, searchFilter)
|
||||
val routeModel = RouteModel()
|
||||
routeModel.startNavigation(route, context)
|
||||
return routeModel.curRoute.summary.distance
|
||||
val osrm = OsrmRepository()
|
||||
val route = osrm.getRoute(context, currentLocation, location, carOrientation, searchFilter)
|
||||
val gson = GsonBuilder().serializeNulls().create()
|
||||
val osrmJson = gson.fromJson(route, OsrmResponse::class.java)
|
||||
if (osrmJson.routes.isEmpty()) {
|
||||
return 0.0
|
||||
}
|
||||
return osrmJson.routes.first().distance
|
||||
// return osrmJson.destinations.first().distance?.toDouble() ?: 0.0
|
||||
///val routeModel = RouteModel()
|
||||
//routeModel.startNavigation(route, context)
|
||||
//return routeModel.curRoute.summary.distance
|
||||
}
|
||||
|
||||
fun searchPlaces(search: String, location: Location): String {
|
||||
|
||||
@@ -99,8 +99,8 @@ data class Route(
|
||||
}
|
||||
}
|
||||
|
||||
fun nextStep(steps : Int): Step {
|
||||
val nextIndex = currentStepIndex + steps
|
||||
fun nextStep(add: Int): Step {
|
||||
val nextIndex = currentStepIndex + add
|
||||
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
|
||||
legs().first().steps[nextIndex]
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.kouros.navigation.data.datastore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private const val DATASTORE_NAME = "navigation_settings"
|
||||
|
||||
|
||||
/**
|
||||
* Central manager for app settings using DataStore
|
||||
*/
|
||||
class DataStoreManager(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = DATASTORE_NAME)
|
||||
}
|
||||
|
||||
// Keys
|
||||
private object PreferencesKeys {
|
||||
|
||||
val THREED_BUILDING = booleanPreferencesKey("Show3D")
|
||||
|
||||
val DARK_MODE = intPreferencesKey("DarkMode")
|
||||
|
||||
val AVOID_MOTORWAY = booleanPreferencesKey("AvoidMotorway")
|
||||
|
||||
val AVOID_TOLLWAY = booleanPreferencesKey("AvoidTollway")
|
||||
|
||||
val CAR_LOCATION = booleanPreferencesKey("CarLocation")
|
||||
|
||||
val ROUTING_ENGINE = intPreferencesKey("RoutingEngine")
|
||||
}
|
||||
|
||||
// Read values
|
||||
|
||||
val threeDBuildingFlow: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.THREED_BUILDING] == true
|
||||
}
|
||||
val darkModeFlow: Flow<Int> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.DARK_MODE]
|
||||
?: 0
|
||||
}
|
||||
|
||||
val avoidMotorwayFlow: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.AVOID_MOTORWAY] == true
|
||||
}
|
||||
|
||||
val avoidTollwayFlow: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.AVOID_TOLLWAY] == true
|
||||
}
|
||||
|
||||
val useCarLocationFlow: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.CAR_LOCATION] == true
|
||||
}
|
||||
|
||||
val routingEngineFlow: Flow<Int> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[PreferencesKeys.ROUTING_ENGINE]
|
||||
?: 0
|
||||
}
|
||||
|
||||
// Save values
|
||||
suspend fun setThreedBuilding(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PreferencesKeys.THREED_BUILDING] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setDarkMode(mode: Int) {
|
||||
context.dataStore.edit { prefs ->
|
||||
prefs[PreferencesKeys.DARK_MODE] = mode
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setAvoidMotorway(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PreferencesKeys.AVOID_MOTORWAY] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setAvoidTollway(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PreferencesKeys.AVOID_TOLLWAY] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setCarLocation(enabled: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[PreferencesKeys.CAR_LOCATION] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setRoutingEngine(mode: Int) {
|
||||
context.dataStore.edit { prefs ->
|
||||
prefs[PreferencesKeys.ROUTING_ENGINE] = mode
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import android.location.Location
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.SearchFilter
|
||||
|
||||
private const val routeUrl = "https://kouros-online.de/osrm/route/v1/driving/"
|
||||
//private const val routeUrl = "https://kouros-online.de/osrm/route/v1/driving/"
|
||||
|
||||
private const val routeUrl = "https://router.project-osrm.org/route/v1/driving/"
|
||||
|
||||
class OsrmRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
@@ -23,7 +25,7 @@ class OsrmRepository : NavigationRepository() {
|
||||
if (searchFilter.avoidTollway) {
|
||||
exclude = "$exclude&exclude=toll"
|
||||
}
|
||||
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=0"
|
||||
val routeLocation = "${currentLocation.longitude},${currentLocation.latitude};${location.longitude},${location.latitude}?steps=true&alternatives=false"
|
||||
return fetchUrl(routeUrl + routeLocation + exclude, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.net.URL
|
||||
class Overpass {
|
||||
|
||||
//val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||
//val overpassUrl = "https://overpass-api.de/api"
|
||||
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
||||
|
||||
|
||||
@@ -43,13 +44,13 @@ class Overpass {
|
||||
val boundingBox = getBoundingBox(location.latitude, location.longitude, radius)
|
||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||
httpURLConnection.requestMethod = "POST"
|
||||
// node["highway"="speed_camera"]
|
||||
// node[amenity=$category]
|
||||
httpURLConnection.setDoOutput(true);
|
||||
httpURLConnection.setRequestProperty(
|
||||
"Accept",
|
||||
"application/json"
|
||||
)
|
||||
// node["highway"="speed_camera"]
|
||||
// node[amenity=$category]
|
||||
httpURLConnection.setDoOutput(true);
|
||||
// define search query
|
||||
val searchQuery = """
|
||||
|[out:json];
|
||||
@@ -79,7 +80,7 @@ class Overpass {
|
||||
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
println("Speed $e")
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@@ -10,15 +10,14 @@ import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
|
||||
|
||||
private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/"
|
||||
|
||||
val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
|
||||
const val tomtomApiKey = "678k5v6940cSXXIS5oD92qIrDgW3RBZ3"
|
||||
|
||||
val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
|
||||
const val tomtomTrafficUrl = "https://api.tomtom.com/traffic/services/5/incidentDetails"
|
||||
|
||||
|
||||
private val tomtomFields =
|
||||
private const val tomtomFields =
|
||||
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
|
||||
|
||||
const val useAsset = false
|
||||
const val useAsset = true
|
||||
|
||||
class TomTomRepository : NavigationRepository() {
|
||||
override fun getRoute(
|
||||
|
||||
@@ -61,9 +61,7 @@ class TomTomRoute {
|
||||
route.sections?.forEach { section ->
|
||||
val lanes = mutableListOf<Lane>()
|
||||
var startIndex = 0
|
||||
if (section.startPointIndex <= instruction.pointIndex - 3
|
||||
&& instruction.pointIndex <= section.endPointIndex
|
||||
) {
|
||||
|
||||
section.lanes?.forEach { itLane ->
|
||||
val lane = Lane(
|
||||
location(
|
||||
@@ -77,7 +75,7 @@ class TomTomRoute {
|
||||
lanes.add(lane)
|
||||
}
|
||||
intersections.add(Intersection(waypoints[startIndex], lanes))
|
||||
}
|
||||
|
||||
}
|
||||
allIntersections.addAll(intersections)
|
||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||
@@ -166,13 +164,23 @@ class TomTomRoute {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
||||
}
|
||||
|
||||
"ROUNDABOUT_RIGHT" -> {
|
||||
"ROUNDABOUT_RIGHT", "ROUNDABOUT_CROSS" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW
|
||||
}
|
||||
|
||||
"ROUNDABOUT_LEFT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
|
||||
}
|
||||
"MAKE_UTURN" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT
|
||||
}
|
||||
"ENTER_MOTORWAY" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT
|
||||
}
|
||||
"TAKE_EXIT" -> {
|
||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
||||
|
||||
}
|
||||
}
|
||||
return newType
|
||||
}
|
||||
|
||||
@@ -66,28 +66,17 @@ class IconMapper() {
|
||||
|
||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||
}
|
||||
}
|
||||
return currentTurnIcon
|
||||
}
|
||||
|
||||
|
||||
fun addLanes(stepData: StepData) : Int {
|
||||
stepData.lane.forEach {
|
||||
if (it.indications.isNotEmpty() && it.valid) {
|
||||
Collections.sort<String>(it.indications)
|
||||
var direction = ""
|
||||
it.indications.forEach { it2 ->
|
||||
direction = if (direction.isEmpty()) {
|
||||
it2.trim()
|
||||
} else {
|
||||
"${direction}_${it2.trim()}"
|
||||
}
|
||||
}
|
||||
val laneDirection = addLanes(direction, stepData)
|
||||
return laneDirection
|
||||
Maneuver.TYPE_U_TURN_LEFT -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_u_turn_left
|
||||
}
|
||||
Maneuver.TYPE_U_TURN_RIGHT -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_u_turn_right
|
||||
}
|
||||
Maneuver.TYPE_MERGE_LEFT -> {
|
||||
currentTurnIcon = R.drawable.ic_turn_merge_symmetrical
|
||||
}
|
||||
}
|
||||
return 0
|
||||
return currentTurnIcon
|
||||
}
|
||||
|
||||
fun addLanes(direction: String, stepData: StepData): Int {
|
||||
@@ -112,6 +101,8 @@ class IconMapper() {
|
||||
"straight" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_STRAIGHT
|
||||
Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
@@ -136,7 +127,8 @@ class IconMapper() {
|
||||
|
||||
"left_slight", "slight_left" -> {
|
||||
when (stepData.currentManeuverType) {
|
||||
Maneuver.TYPE_TURN_SLIGHT_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
Maneuver.TYPE_TURN_NORMAL_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_SLIGHT_LEFT
|
||||
else
|
||||
-> LaneDirection.SHAPE_UNKNOWN
|
||||
}
|
||||
|
||||
+9
-13
@@ -2,6 +2,7 @@ package com.kouros.navigation.model
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -18,21 +19,21 @@ import com.kouros.navigation.data.nominatim.Search
|
||||
import com.kouros.navigation.data.nominatim.SearchResult
|
||||
import com.kouros.navigation.data.overpass.Elements
|
||||
import com.kouros.navigation.data.overpass.Overpass
|
||||
import com.kouros.navigation.data.tomtom.Features
|
||||
import com.kouros.navigation.data.tomtom.Traffic
|
||||
import com.kouros.navigation.data.tomtom.TrafficData
|
||||
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
||||
import com.kouros.navigation.utils.Levenshtein
|
||||
import com.kouros.navigation.utils.NavigationUtils
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.maplibre.geojson.FeatureCollection
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
class NavigationViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
val route: MutableLiveData<String> by lazy {
|
||||
MutableLiveData()
|
||||
@@ -457,14 +458,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
}
|
||||
|
||||
fun getSearchFilter(context: Context): SearchFilter {
|
||||
val avoidMotorway = NavigationUtils.getBooleanKeyValue(
|
||||
context = context,
|
||||
Constants.AVOID_MOTORWAY
|
||||
)
|
||||
val avoidTollway = NavigationUtils.getBooleanKeyValue(
|
||||
context = context,
|
||||
Constants.AVOID_TOLLWAY
|
||||
)
|
||||
val repository = getSettingsRepository(context)
|
||||
val avoidMotorway = runBlocking { repository.avoidMotorwayFlow.first() }
|
||||
val avoidTollway = runBlocking { repository.avoidTollwayFlow.first() }
|
||||
return SearchFilter(avoidMotorway, avoidTollway)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class RouteCalculator(var routeModel: RouteModel) {
|
||||
return nowUtcMillis + timeToDestinationMillis
|
||||
}
|
||||
|
||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
||||
fun updateSpeedLimit(location: Location, viewModel: NavigationViewModel) {
|
||||
if (routeModel.isNavigating()) {
|
||||
// speed limit
|
||||
val distance = lastSpeedLocation.distanceTo(location)
|
||||
|
||||
@@ -4,15 +4,19 @@ import android.content.Context
|
||||
import android.location.Location
|
||||
import androidx.car.app.navigation.model.Maneuver
|
||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.Route
|
||||
import com.kouros.navigation.data.StepData
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.data.route.Lane
|
||||
import com.kouros.navigation.data.route.Leg
|
||||
import com.kouros.navigation.data.route.Routes
|
||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
open class RouteModel {
|
||||
@@ -20,7 +24,7 @@ open class RouteModel {
|
||||
// Immutable Data Class
|
||||
data class NavigationState(
|
||||
val route: Route = Route.Builder().buildEmpty(),
|
||||
val iconMapper : IconMapper = IconMapper(),
|
||||
val iconMapper: IconMapper = IconMapper(),
|
||||
val navigating: Boolean = false,
|
||||
val arrived: Boolean = false,
|
||||
val travelMessage: String = "",
|
||||
@@ -37,7 +41,7 @@ open class RouteModel {
|
||||
val route: Route
|
||||
get() = navState.route
|
||||
|
||||
val routeCalculator : RouteCalculator = RouteCalculator(this)
|
||||
val routeCalculator: RouteCalculator = RouteCalculator(this)
|
||||
|
||||
val curRoute: Routes
|
||||
get() = navState.route.routes[navState.currentRouteIndex]
|
||||
@@ -46,38 +50,98 @@ open class RouteModel {
|
||||
get() = navState.route.routes[navState.currentRouteIndex].legs.first()
|
||||
|
||||
fun startNavigation(routeString: String, context: Context) {
|
||||
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
||||
val repository = getSettingsRepository(context)
|
||||
val routingEngine = runBlocking { repository.routingEngineFlow.first() }
|
||||
navState = navState.copy(
|
||||
route = Route.Builder()
|
||||
.routeEngine(routeEngine)
|
||||
.routeEngine(routingEngine)
|
||||
.route(routeString)
|
||||
.build()
|
||||
)
|
||||
if (hasLegs()) {
|
||||
navState = navState.copy(navigating = true)
|
||||
//NavigationUtils.setStringKeyValue(context, routeString, LAST_ROUTE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasLegs(): Boolean {
|
||||
return navState.route.routes.isNotEmpty() && navState.route.routes[0].legs.isNotEmpty()
|
||||
return navState.route.routes.isNotEmpty() && navState.route.routes[0].legs.isNotEmpty()
|
||||
}
|
||||
|
||||
fun stopNavigation() {
|
||||
fun stopNavigation(context: Context) {
|
||||
navState = navState.copy(
|
||||
route = Route.Builder().buildEmpty(),
|
||||
navigating = false,
|
||||
arrived = false,
|
||||
maneuverType = Maneuver.TYPE_UNKNOWN
|
||||
)
|
||||
//NavigationUtils.setStringKeyValue(context, "", LAST_ROUTE)
|
||||
}
|
||||
|
||||
fun updateLocation(curLocation: Location, viewModel: ViewModel) {
|
||||
fun updateLocation(context: Context, curLocation: Location, viewModel: NavigationViewModel) {
|
||||
navState = navState.copy(currentLocation = curLocation)
|
||||
routeCalculator.findStep(curLocation)
|
||||
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||
val repository = getSettingsRepository(context)
|
||||
val carLocation = runBlocking { repository.carLocationFlow.first() }
|
||||
if (carLocation) {
|
||||
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||
}
|
||||
navState = navState.copy(lastLocation = navState.currentLocation)
|
||||
}
|
||||
|
||||
fun currentStep(): StepData {
|
||||
val distanceToNextStep = routeCalculator.leftStepDistance()
|
||||
// Determine the maneuver type and corresponding icon
|
||||
val currentStep = navState.route.nextStep(0)
|
||||
val streetName = if (distanceToNextStep > NEXT_STEP_THRESHOLD) {
|
||||
currentStep.street
|
||||
} else {
|
||||
currentStep.maneuver.street
|
||||
}
|
||||
val curManeuverType = if (distanceToNextStep > NEXT_STEP_THRESHOLD) {
|
||||
Maneuver.TYPE_STRAIGHT
|
||||
} else {
|
||||
currentStep.maneuver.type
|
||||
}
|
||||
val exitNumber = currentStep.maneuver.exit
|
||||
val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType)
|
||||
navState = navState.copy(maneuverType = curManeuverType)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
instruction = streetName,
|
||||
leftStepDistance = distanceToNextStep,
|
||||
currentManeuverType = navState.maneuverType,
|
||||
icon = maneuverIcon,
|
||||
arrivalTime = routeCalculator.arrivalTime(),
|
||||
leftDistance = routeCalculator.travelLeftDistance(),
|
||||
lane = currentLanes(),
|
||||
exitNumber = exitNumber
|
||||
)
|
||||
}
|
||||
|
||||
fun nextStep(): StepData {
|
||||
val distanceToNextStep = routeCalculator.leftStepDistance()
|
||||
val step = navState.route.nextStep(1)
|
||||
val streetName = if (distanceToNextStep < NEXT_STEP_THRESHOLD) {
|
||||
step.maneuver.street
|
||||
} else {
|
||||
step.street
|
||||
}
|
||||
val maneuverType = step.maneuver.type
|
||||
|
||||
val maneuverIcon = navState.iconMapper.maneuverIcon(maneuverType)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
instruction = streetName,
|
||||
leftStepDistance = distanceToNextStep,
|
||||
currentManeuverType = maneuverType,
|
||||
icon = maneuverIcon,
|
||||
arrivalTime = routeCalculator.arrivalTime(),
|
||||
leftDistance = routeCalculator.travelLeftDistance(),
|
||||
exitNumber = step.maneuver.exit
|
||||
)
|
||||
}
|
||||
|
||||
private fun currentLanes(): List<Lane> {
|
||||
var lanes = emptyList<Lane>()
|
||||
if (navState.route.legs().isNotEmpty()) {
|
||||
@@ -87,7 +151,7 @@ open class RouteModel {
|
||||
navState.lastLocation.distanceTo(location(it.location[0], it.location[1]))
|
||||
val sectionBearing =
|
||||
navState.lastLocation.bearingTo(location(it.location[0], it.location[1]))
|
||||
if (distance < 500 && (navState.routeBearing.absoluteValue - sectionBearing.absoluteValue).absoluteValue < 10) {
|
||||
if (distance < NEXT_STEP_THRESHOLD && (navState.routeBearing.absoluteValue - sectionBearing.absoluteValue).absoluteValue < 10) {
|
||||
lanes = it.lane
|
||||
}
|
||||
}
|
||||
@@ -96,61 +160,6 @@ open class RouteModel {
|
||||
return lanes
|
||||
}
|
||||
|
||||
fun currentStep(): StepData {
|
||||
val distanceToNextStep = routeCalculator.leftStepDistance()
|
||||
// Determine the maneuver type and corresponding icon
|
||||
val currentStep = navState.route.nextStep(0)
|
||||
// Safely get the street name from the maneuver
|
||||
val streetName = currentStep.maneuver.street
|
||||
val curManeuverType = currentStep.maneuver.type
|
||||
val exitNumber = currentStep.maneuver.exit
|
||||
val maneuverIcon = navState.iconMapper.maneuverIcon(curManeuverType)
|
||||
navState = navState.copy(maneuverType = curManeuverType)
|
||||
|
||||
val lanes = currentLanes()
|
||||
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
streetName,
|
||||
distanceToNextStep,
|
||||
navState.maneuverType,
|
||||
maneuverIcon,
|
||||
routeCalculator.arrivalTime(),
|
||||
routeCalculator.travelLeftDistance(),
|
||||
lanes,
|
||||
exitNumber
|
||||
)
|
||||
}
|
||||
|
||||
fun nextStep(): StepData {
|
||||
val step = navState.route.nextStep(1)
|
||||
val maneuverType = step.maneuver.type
|
||||
val distanceLeft = routeCalculator.leftStepDistance()
|
||||
var text = ""
|
||||
when (distanceLeft) {
|
||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||
}
|
||||
else -> {
|
||||
if (step.street.isNotEmpty()) {
|
||||
text = step.street
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val maneuverIcon = navState.iconMapper.maneuverIcon(maneuverType)
|
||||
// Construct and return the final StepData object
|
||||
return StepData(
|
||||
text,
|
||||
distanceLeft,
|
||||
maneuverType,
|
||||
maneuverIcon,
|
||||
routeCalculator.arrivalTime(),
|
||||
routeCalculator.travelLeftDistance(),
|
||||
listOf(Lane(location(0.0, 0.0), valid = false, indications = emptyList())),
|
||||
step.maneuver.exit
|
||||
)
|
||||
}
|
||||
|
||||
fun isNavigating(): Boolean {
|
||||
return navState.navigating
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsViewModel(private val repository: SettingsRepository) : ViewModel() {
|
||||
|
||||
val threedBuilding = repository.threedBuildingFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
false
|
||||
)
|
||||
|
||||
val darkMode = repository.darkModeFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
0
|
||||
)
|
||||
|
||||
val avoidMotorway = repository.avoidMotorwayFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
false
|
||||
)
|
||||
|
||||
val avoidTollway = repository.avoidTollwayFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
false
|
||||
)
|
||||
|
||||
val carLocation = repository.carLocationFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
false
|
||||
)
|
||||
|
||||
val routingEngine = repository.routingEngineFlow.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000),
|
||||
0
|
||||
)
|
||||
|
||||
fun onThreedBuildingChanged(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setThreedBuilding(enabled) }
|
||||
}
|
||||
|
||||
|
||||
fun onDarkModeChanged(mode: Int) {
|
||||
viewModelScope.launch { repository.setDarkMode(mode) }
|
||||
}
|
||||
|
||||
fun onAvoidMotorway(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setAvoidMotorway(enabled) }
|
||||
}
|
||||
|
||||
fun onAvoidTollway(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setAvoidTollway(enabled) }
|
||||
}
|
||||
|
||||
fun onCarLocation(enabled: Boolean) {
|
||||
viewModelScope.launch { repository.setCarLocation(enabled) }
|
||||
}
|
||||
|
||||
fun onRoutingEngineChanged(mode: Int) {
|
||||
viewModelScope.launch { repository.setRoutingEngine(mode) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.kouros.navigation.repository
|
||||
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class SettingsRepository(
|
||||
private val dataStoreManager: DataStoreManager
|
||||
) {
|
||||
|
||||
val threedBuildingFlow: Flow<Boolean> =
|
||||
dataStoreManager.threeDBuildingFlow
|
||||
val darkModeFlow: Flow<Int> =
|
||||
dataStoreManager.darkModeFlow
|
||||
|
||||
val avoidMotorwayFlow: Flow<Boolean> =
|
||||
dataStoreManager.avoidMotorwayFlow
|
||||
|
||||
val avoidTollwayFlow: Flow<Boolean> =
|
||||
dataStoreManager.avoidTollwayFlow
|
||||
|
||||
val carLocationFlow: Flow<Boolean> =
|
||||
dataStoreManager.useCarLocationFlow
|
||||
|
||||
val routingEngineFlow: Flow<Int> =
|
||||
dataStoreManager.routingEngineFlow
|
||||
|
||||
|
||||
suspend fun setThreedBuilding(enabled: Boolean) {
|
||||
dataStoreManager.setThreedBuilding(enabled)
|
||||
}
|
||||
|
||||
suspend fun setDarkMode(mode: Int) {
|
||||
dataStoreManager.setDarkMode(mode)
|
||||
}
|
||||
|
||||
suspend fun setAvoidMotorway(enabled: Boolean) {
|
||||
dataStoreManager.setAvoidMotorway(enabled)
|
||||
}
|
||||
|
||||
suspend fun setAvoidTollway(enabled: Boolean) {
|
||||
dataStoreManager.setAvoidTollway(enabled)
|
||||
}
|
||||
|
||||
suspend fun setCarLocation(enabled: Boolean) {
|
||||
dataStoreManager.setCarLocation(enabled)
|
||||
}
|
||||
|
||||
suspend fun setRoutingEngine(mode: Int) {
|
||||
dataStoreManager.setRoutingEngine(mode)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,14 +3,20 @@ package com.kouros.navigation.utils
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.core.content.edit
|
||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||
import com.kouros.navigation.model.ViewModel
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.model.SettingsViewModel
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
@@ -26,60 +32,15 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object NavigationUtils {
|
||||
|
||||
fun getViewModel(context: Context): ViewModel {
|
||||
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
||||
fun getViewModel(context: Context): NavigationViewModel {
|
||||
val repository = getSettingsRepository(context)
|
||||
val routeEngine = runBlocking { repository.routingEngineFlow.first() }
|
||||
return when (routeEngine) {
|
||||
RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> ViewModel(OsrmRepository())
|
||||
else -> ViewModel(TomTomRepository())
|
||||
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||
else -> NavigationViewModel(TomTomRepository())
|
||||
}
|
||||
}
|
||||
|
||||
fun getBooleanKeyValue(context: Context, key: String): Boolean {
|
||||
return context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.getBoolean(key, false)
|
||||
}
|
||||
|
||||
fun setBooleanKeyValue(context: Context, `val`: Boolean, key: String) {
|
||||
context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.edit {
|
||||
putBoolean(
|
||||
key, `val`
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun getIntKeyValue(context: Context, key: String, default: Int = 0): Int {
|
||||
return context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.getInt(key, default)
|
||||
}
|
||||
|
||||
fun setIntKeyValue(context: Context, `val`: Int, key: String) {
|
||||
context
|
||||
.getSharedPreferences(
|
||||
SHARED_PREF_KEY,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
.edit {
|
||||
putInt(
|
||||
key, `val`
|
||||
)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateZoom(speed: Double?): Double {
|
||||
@@ -90,8 +51,8 @@ fun calculateZoom(speed: Double?): Double {
|
||||
val zoom = when (speedKmh) {
|
||||
in 0..10 -> 18.0
|
||||
in 11..30 -> 17.5
|
||||
in 31..50 -> 17.0
|
||||
in 61..70 -> 16.5
|
||||
in 31..65 -> 17.0
|
||||
in 66..70 -> 16.5
|
||||
else -> 16.0
|
||||
}
|
||||
return zoom
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.kouros.navigation.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||
import com.kouros.navigation.model.SettingsViewModel
|
||||
import com.kouros.navigation.repository.SettingsRepository
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
@Composable
|
||||
fun settingsViewModel(context: CarContext, viewModelStoreOwner: ViewModelStoreOwner) : SettingsViewModel {
|
||||
val dataStoreManager = remember { DataStoreManager(context) }
|
||||
val repository = remember { SettingsRepository(dataStoreManager) }
|
||||
val settingsViewModel: SettingsViewModel = viewModel(
|
||||
viewModelStoreOwner,
|
||||
factory = object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return SettingsViewModel(repository) as T
|
||||
}
|
||||
}
|
||||
)
|
||||
return settingsViewModel
|
||||
}
|
||||
|
||||
fun getSettingsViewModel(context: Context) : SettingsViewModel {
|
||||
val dataStoreManager = DataStoreManager(context)
|
||||
val repository = SettingsRepository(dataStoreManager)
|
||||
return SettingsViewModel( repository)
|
||||
}
|
||||
|
||||
fun getSettingsRepository(context: Context) : SettingsRepository {
|
||||
val dataStore = DataStoreManager(context)
|
||||
return SettingsRepository(dataStore)
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 832 B |
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
Binary file not shown.
|
After Width: | Height: | Size: 829 B |
Binary file not shown.
|
After Width: | Height: | Size: 835 B |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+17
-14
@@ -13,33 +13,34 @@ junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
kotlinxSerializationJson = "1.10.0"
|
||||
lifecycleRuntimeKtx = "2.10.0"
|
||||
composeBom = "2026.01.01"
|
||||
composeBom = "2026.02.00"
|
||||
appcompat = "1.7.1"
|
||||
material = "1.13.0"
|
||||
carApp = "1.7.0"
|
||||
androidx-car = "1.7.0"
|
||||
objectboxKotlin = "5.1.0"
|
||||
objectboxProcessor = "5.0.1"
|
||||
objectboxKotlin = "5.2.0"
|
||||
objectboxProcessor = "5.2.0"
|
||||
ui = "1.10.0"
|
||||
material3 = "1.4.0"
|
||||
runtimeLivedata = "1.10.2"
|
||||
foundation = "1.10.0"
|
||||
runtimeLivedata = "1.10.3"
|
||||
foundation = "1.10.3"
|
||||
maplibre-composeMaterial3 = "0.12.2"
|
||||
maplibre-compose = "0.12.1"
|
||||
playServicesLocation = "21.3.0"
|
||||
runtime = "1.10.2"
|
||||
runtime = "1.10.3"
|
||||
accompanist = "0.37.3"
|
||||
uiVersion = "1.10.2"
|
||||
uiText = "1.10.2"
|
||||
uiVersion = "1.10.3"
|
||||
uiText = "1.10.3"
|
||||
navigationCompose = "2.9.7"
|
||||
uiToolingPreview = "1.10.2"
|
||||
uiTooling = "1.10.2"
|
||||
uiToolingPreview = "1.10.3"
|
||||
uiTooling = "1.10.3"
|
||||
material3WindowSizeClass = "1.4.0"
|
||||
uiGraphics = "1.10.2"
|
||||
uiGraphics = "1.10.3"
|
||||
window = "1.5.1"
|
||||
foundationLayout = "1.10.2"
|
||||
foundationLayoutVersion = "1.10.2"
|
||||
|
||||
foundationLayout = "1.10.3"
|
||||
foundationLayoutVersion = "1.10.3"
|
||||
datastorePreferences = "1.2.0"
|
||||
datastoreCore = "1.2.0"
|
||||
|
||||
[libraries]
|
||||
android-sdk-turf = { module = "org.maplibre.gl:android-sdk-turf", version.ref = "androidSdkTurf" }
|
||||
@@ -81,6 +82,8 @@ androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graph
|
||||
androidx-window = { group = "androidx.window", name = "window", version.ref = "window" }
|
||||
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" }
|
||||
androidx-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayoutVersion" }
|
||||
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||
androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user