DataStore Manager
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
|||||||
applicationId = "com.kouros.navigation"
|
applicationId = "com.kouros.navigation"
|
||||||
minSdk = 33
|
minSdk = 33
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 38
|
versionCode = 42
|
||||||
versionName = "0.2.0.38"
|
versionName = "0.2.0.42"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.app.Application
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
import com.kouros.navigation.di.appModule
|
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 com.kouros.navigation.utils.NavigationUtils.getViewModel
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.android.ext.koin.androidLogger
|
import org.koin.android.ext.koin.androidLogger
|
||||||
@@ -15,7 +15,7 @@ class MainApplication : Application() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
ObjectBox.init(this);
|
ObjectBox.init(this)
|
||||||
appContext = applicationContext
|
appContext = applicationContext
|
||||||
navigationViewModel = getViewModel(appContext!!)
|
navigationViewModel = getViewModel(appContext!!)
|
||||||
startKoin {
|
startKoin {
|
||||||
@@ -30,6 +30,6 @@ class MainApplication : Application() {
|
|||||||
var appContext: Context? = null
|
var appContext: Context? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
lateinit var navigationViewModel : ViewModel
|
lateinit var navigationViewModel : NavigationViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
package com.kouros.navigation.di
|
package com.kouros.navigation.di
|
||||||
|
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
|
||||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.BaseStyleModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.model.ViewModel
|
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.singleOf
|
||||||
|
import org.koin.core.module.dsl.viewModel
|
||||||
import org.koin.core.module.dsl.viewModelOf
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import kotlin.math.sin
|
|
||||||
|
|
||||||
val appModule = module {
|
val appModule = module {
|
||||||
viewModelOf(::ViewModel)
|
viewModelOf(::NavigationViewModel)
|
||||||
|
viewModelOf(::SettingsViewModel)
|
||||||
singleOf(::ValhallaRepository)
|
singleOf(::ValhallaRepository)
|
||||||
singleOf(::OsrmRepository)
|
singleOf(::OsrmRepository)
|
||||||
singleOf(::TomTomRepository)
|
singleOf(::TomTomRepository)
|
||||||
|
|||||||
@@ -43,26 +43,27 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
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.FusedLocationProviderClient
|
||||||
import com.google.android.gms.location.LocationServices
|
import com.google.android.gms.location.LocationServices
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.MainApplication.Companion.navigationViewModel
|
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.DESTINATION_ARRIVAL_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.homeVogelhart
|
import com.kouros.navigation.data.Constants.homeVogelhart
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
|
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||||
import com.kouros.navigation.model.BaseStyleModel
|
import com.kouros.navigation.model.BaseStyleModel
|
||||||
import com.kouros.navigation.model.MockLocation
|
import com.kouros.navigation.model.MockLocation
|
||||||
import com.kouros.navigation.model.RouteModel
|
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.ui.theme.NavigationTheme
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
|
||||||
import com.kouros.navigation.utils.bearing
|
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 com.kouros.navigation.utils.location
|
||||||
import io.ticofab.androidgpxparser.parser.GPXParser
|
import io.ticofab.androidgpxparser.parser.GPXParser
|
||||||
import io.ticofab.androidgpxparser.parser.domain.Gpx
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.location.DesiredAccuracy
|
import org.maplibre.compose.location.DesiredAccuracy
|
||||||
@@ -83,12 +86,13 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
|
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
var tilt = 50.0
|
var tilt = 50.0
|
||||||
val useMock = false
|
val useMock = true
|
||||||
val type = 3 // simulate 2 test 3 gpx 4 testSingle
|
val type = 3 // 1 simulate 2 test 3 gpx 4 testSingle
|
||||||
|
|
||||||
var currentIndex = 0
|
var currentIndex = 0
|
||||||
val stepData: MutableLiveData<StepData> by lazy {
|
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])
|
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val darkModeSettings = getIntKeyValue(applicationContext, Constants.DARK_MODE_SETTINGS)
|
|
||||||
baseStyle = BaseStyleModel().readStyle(applicationContext, darkModeSettings, false)
|
|
||||||
if (useMock) {
|
if (useMock) {
|
||||||
checkMockLocationEnabled()
|
checkMockLocationEnabled()
|
||||||
}
|
}
|
||||||
@@ -150,6 +153,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
navigationViewModel.route.observe(this, observer)
|
navigationViewModel.route.observe(this, observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
getSettingsViewModel(applicationContext).routingEngine.collect {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
CheckPermissionScreen()
|
CheckPermissionScreen()
|
||||||
@@ -186,9 +194,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun StartScreen(
|
fun StartScreen(
|
||||||
navController: NavHostController
|
navController: NavHostController
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val appViewModel: AppViewModel = appViewModel()
|
||||||
|
val darkMode by appViewModel.darkMode.collectAsState()
|
||||||
|
|
||||||
|
baseStyle = BaseStyleModel().readStyle(applicationContext, darkMode, darkMode == 1)
|
||||||
|
|
||||||
val scaffoldState = rememberBottomSheetScaffoldState()
|
val scaffoldState = rememberBottomSheetScaffoldState()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -212,7 +224,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
sheetPeekHeightState.value = 128.dp
|
sheetPeekHeightState.value = 128.dp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigationTheme {
|
NavigationTheme (useDarkTheme = darkMode == 1) {
|
||||||
BottomSheetScaffold(
|
BottomSheetScaffold(
|
||||||
snackbarHost = {
|
snackbarHost = {
|
||||||
SnackbarHost(hostState = snackbarHostState)
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
@@ -249,13 +261,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App() {
|
fun App() {
|
||||||
val navController = rememberNavController()
|
//val lastRoute = NavigationUtils.getStringKeyValue(applicationContext, Constants.LAST_ROUTE)
|
||||||
NavHost(navController = navController, startDestination = "startScreen") {
|
//if (lastRoute!!.isNotEmpty()) {
|
||||||
composable("startScreen") { StartScreen(navController) }
|
// routeModel.startNavigation(lastRoute, applicationContext)
|
||||||
composable("settings") { SettingsScreen(navController) { navController.popBackStack() } }
|
// routeData.value = routeModel.curRoute.routeGeoJson
|
||||||
composable("display_settings") { DisplayScreenSettings(applicationContext) { navController.popBackStack() } }
|
//}
|
||||||
composable("nav_settings") { NavigationScreenSettings(applicationContext) { navController.popBackStack() } }
|
AppNavGraph(applicationContext = applicationContext, this)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -285,14 +296,16 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (!routeModel.isNavigating()) {
|
if (!routeModel.isNavigating()) {
|
||||||
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||||
} else {
|
} else {
|
||||||
|
if (step != null && nextStep != null) {
|
||||||
NavigationSheet(
|
NavigationSheet(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
routeModel,
|
routeModel,
|
||||||
step!!,
|
step,
|
||||||
nextStep!!,
|
nextStep,
|
||||||
{ stopNavigation { closeSheet() } },
|
{ stopNavigation { closeSheet() } },
|
||||||
{ simulateNavigation() })
|
{ simulateNavigation() })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// For recomposition!
|
// For recomposition!
|
||||||
Text("$locationState", fontSize = 12.sp)
|
Text("$locationState", fontSize = 12.sp)
|
||||||
}
|
}
|
||||||
@@ -303,17 +316,18 @@ class MainActivity : ComponentActivity() {
|
|||||||
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
if (isNavigating()) {
|
if (isNavigating()) {
|
||||||
updateLocation(currentLocation, navigationViewModel)
|
updateLocation(applicationContext,currentLocation, navigationViewModel)
|
||||||
stepData.value = currentStep()
|
stepData.value = currentStep()
|
||||||
nextStepData.value = nextStep()
|
nextStepData.value = nextStep()
|
||||||
if (navState.maneuverType in 39..42 && routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
|
if (navState.maneuverType in 39..42 && routeCalculator.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
|
||||||
// stopNavigation()
|
// stopNavigation()
|
||||||
navState.copy(arrived = true)
|
navState = navState.copy(arrived = true)
|
||||||
routeData.value = ""
|
routeData.value = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val zoom = calculateZoom(location.speed)
|
//val zoom = calculateZoom(location.speed)
|
||||||
|
val zoom = 16.0
|
||||||
cameraPosition.postValue(
|
cameraPosition.postValue(
|
||||||
cameraPosition.value!!.copy(
|
cameraPosition.value!!.copy(
|
||||||
zoom = zoom, target = location.position, bearing = bearing
|
zoom = zoom, target = location.position, bearing = bearing
|
||||||
@@ -331,7 +345,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val latitude = routeModel.curRoute.waypoints[0][1]
|
val latitude = routeModel.curRoute.waypoints[0][1]
|
||||||
val longitude = routeModel.curRoute.waypoints[0][0]
|
val longitude = routeModel.curRoute.waypoints[0][0]
|
||||||
closeSheet()
|
closeSheet()
|
||||||
routeModel.stopNavigation()
|
routeModel.stopNavigation(applicationContext)
|
||||||
if (useMock) {
|
if (useMock) {
|
||||||
mock.setMockLocation(latitude, longitude)
|
mock.setMockLocation(latitude, longitude)
|
||||||
}
|
}
|
||||||
@@ -370,7 +384,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val deviation = 0.0
|
val deviation = 0.0
|
||||||
if (index in 0..routeModel.curRoute.waypoints.size) {
|
if (index in 0..routeModel.curRoute.waypoints.size) {
|
||||||
mock.setMockLocation(waypoint[1], waypoint[0])
|
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()) {
|
for ((index, step) in routeModel.curLeg.steps.withIndex()) {
|
||||||
//if (index in 3..3) {
|
//if (index in 3..3) {
|
||||||
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
for ((windex, waypoint) in step.maneuver.waypoints.withIndex()) {
|
||||||
routeModel.updateLocation(
|
routeModel.updateLocation(applicationContext,
|
||||||
location(waypoint[0], waypoint[1]), navigationViewModel
|
location(waypoint[0], waypoint[1]), navigationViewModel
|
||||||
)
|
)
|
||||||
val step = routeModel.currentStep()
|
val step = routeModel.currentStep()
|
||||||
@@ -402,7 +416,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (1 == 1) {
|
if (1 == 1) {
|
||||||
mock.setMockLocation(latitude, longitude)
|
mock.setMockLocation(latitude, longitude)
|
||||||
} else {
|
} else {
|
||||||
routeModel.updateLocation(
|
routeModel.updateLocation(applicationContext,
|
||||||
location(longitude, latitude), navigationViewModel
|
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.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.window.layout.WindowMetricsCalculator
|
import androidx.window.layout.WindowMetricsCalculator
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.car.map.MapLibre
|
import com.kouros.navigation.car.map.MapLibre
|
||||||
import com.kouros.navigation.car.map.NavigationImage
|
import com.kouros.navigation.car.map.NavigationImage
|
||||||
import com.kouros.navigation.car.map.rememberBaseStyle
|
import com.kouros.navigation.car.map.rememberBaseStyle
|
||||||
import com.kouros.navigation.data.StepData
|
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.CameraPosition
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
import org.maplibre.compose.location.LocationTrackingEffect
|
import org.maplibre.compose.location.LocationTrackingEffect
|
||||||
@@ -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 {
|
Column {
|
||||||
NavigationInfo(step, nextStep)
|
NavigationInfo(step, nextStep)
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
@@ -67,7 +77,9 @@ fun MapView(
|
|||||||
rememberBaseStyle,
|
rememberBaseStyle,
|
||||||
route,
|
route,
|
||||||
emptyMap(),
|
emptyMap(),
|
||||||
ViewStyle.VIEW
|
ViewStyle.VIEW,
|
||||||
|
speedCameras = "",
|
||||||
|
showBuildings
|
||||||
)
|
)
|
||||||
LocationTrackingEffect(
|
LocationTrackingEffect(
|
||||||
locationState = userLocationState,
|
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.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
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.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -17,39 +22,43 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.utils.round
|
import com.kouros.navigation.utils.round
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavigationInfo(step: StepData?, nextStep: StepData?) {
|
fun NavigationInfo(step: StepData?, nextStep: StepData?) {
|
||||||
if (step != null && step.instruction.isNotEmpty()) {
|
if (step != null && step.instruction.isNotEmpty()) {
|
||||||
Card(modifier = Modifier.padding(top = 60.dp)) {
|
ElevatedCard(
|
||||||
Column() {
|
elevation = CardDefaults.cardElevation(
|
||||||
Row {
|
defaultElevation = 6.dp
|
||||||
|
|
||||||
|
), modifier = Modifier
|
||||||
|
.padding(top = 60.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(step.icon),
|
painter = painterResource(step.icon),
|
||||||
contentDescription = stringResource(id = R.string.accept_action_title),
|
contentDescription = stringResource(id = R.string.accept_action_title),
|
||||||
modifier = Modifier.size(48.dp, 48.dp),
|
modifier = Modifier.size(48.dp, 48.dp),
|
||||||
)
|
)
|
||||||
if (step.currentManeuverType == 46
|
if (step.currentManeuverType == 46
|
||||||
|| step.currentManeuverType == 45) {
|
|| step.currentManeuverType == 45
|
||||||
Text(text ="Exit ${step.exitNumber}", fontSize = 20.sp)
|
) {
|
||||||
|
Text(text = "Exit ${step.exitNumber}", fontSize = 18.sp)
|
||||||
}
|
}
|
||||||
Column {
|
Row {
|
||||||
if (step.leftStepDistance < 1000) {
|
if (step.leftStepDistance < 1000) {
|
||||||
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 25.sp)
|
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
|
||||||
} else {
|
} else {
|
||||||
Text(
|
Text(
|
||||||
text = "${(step.leftStepDistance / 1000).round(1)} km",
|
text = "${(step.leftStepDistance / 1000).round(1)} km",
|
||||||
fontSize = 25.sp
|
fontSize = 24.sp,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(text = step.instruction, fontSize = 20.sp)
|
Spacer(
|
||||||
}
|
modifier = Modifier.padding(5.dp)
|
||||||
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),
|
|
||||||
)
|
)
|
||||||
}
|
Text(text = step.instruction, fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fun NavigationSheet(
|
|||||||
val distance = (step.leftDistance / 1000).round(1)
|
val distance = (step.leftDistance / 1000).round(1)
|
||||||
|
|
||||||
if (step.lane.isNotEmpty()) {
|
if (step.lane.isNotEmpty()) {
|
||||||
routeModel.navState.iconMapper.addLanes( step)
|
// routeModel.navState.iconMapper.addLanes( step)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import android.content.Context
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@@ -29,25 +27,21 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
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 androidx.compose.ui.unit.dp
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.PlaceColor
|
import com.kouros.navigation.data.PlaceColor
|
||||||
import com.kouros.navigation.data.nominatim.SearchResult
|
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
|
import com.kouros.navigation.utils.location
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchSheet(
|
fun SearchSheet(
|
||||||
applicationContext: Context,
|
applicationContext: Context,
|
||||||
viewModel: ViewModel,
|
viewModel: NavigationViewModel,
|
||||||
location: Location,
|
location: Location,
|
||||||
closeSheet: () -> Unit
|
closeSheet: () -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -88,7 +82,7 @@ fun SearchSheet(
|
|||||||
@Composable
|
@Composable
|
||||||
fun Home(
|
fun Home(
|
||||||
applicationContext: Context,
|
applicationContext: Context,
|
||||||
viewModel: ViewModel,
|
viewModel: NavigationViewModel,
|
||||||
location: Location,
|
location: Location,
|
||||||
closeSheet: () -> Unit
|
closeSheet: () -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -125,7 +119,7 @@ fun SearchBar(
|
|||||||
searchPlaces: List<Place>,
|
searchPlaces: List<Place>,
|
||||||
searchResults: List<SearchResult>,
|
searchResults: List<SearchResult>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ViewModel,
|
viewModel: NavigationViewModel,
|
||||||
context: Context,
|
context: Context,
|
||||||
location: Location,
|
location: Location,
|
||||||
closeSheet: () -> Unit
|
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)
|
viewModel.searchPlaces(it, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchPlaces(
|
private fun SearchPlaces(
|
||||||
searchResults: List<SearchResult>,
|
searchResults: List<SearchResult>,
|
||||||
viewModel: ViewModel,
|
viewModel: NavigationViewModel,
|
||||||
context: Context,
|
context: Context,
|
||||||
location: Location,
|
location: Location,
|
||||||
closeSheet: () -> Unit
|
closeSheet: () -> Unit
|
||||||
@@ -222,7 +216,7 @@ private fun SearchPlaces(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun RecentPlaces(
|
private fun RecentPlaces(
|
||||||
recentPlaces: List<Place>,
|
recentPlaces: List<Place>,
|
||||||
viewModel: ViewModel,
|
viewModel: NavigationViewModel,
|
||||||
context: Context,
|
context: Context,
|
||||||
location: Location,
|
location: Location,
|
||||||
closeSheet: () -> Unit
|
closeSheet: () -> Unit
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
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.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.alorma.compose.settings.ui.SettingsMenuLink
|
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@@ -53,34 +53,16 @@ fun SettingsScreen(navController: NavHostController, navigateBack: () -> Unit) {
|
|||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(top = padding.calculateTopPadding()),
|
.padding(top = padding.calculateTopPadding()),
|
||||||
) {
|
) {
|
||||||
SettingsMenuLink(
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
title = { Text(text = stringResource(R.string.display_settings)) },
|
|
||||||
modifier = Modifier,
|
Button(onClick = { navController.navigate("display_settings") }) {
|
||||||
enabled = true,
|
Text(stringResource(R.string.display_settings))
|
||||||
onClick = { navController.navigate("display_settings")},
|
}
|
||||||
icon = {
|
Button(onClick = { navController.navigate("nav_settings") }) {
|
||||||
Icon(
|
Text(stringResource(R.string.navigation_settings))
|
||||||
painter = painterResource(R.drawable.ic_place_white_24dp),
|
|
||||||
contentDescription = stringResource(id = R.string.display_settings),
|
|
||||||
modifier = Modifier.size(48.dp, 48.dp),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
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),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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() } }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.kouros.navigation.ui
|
package com.kouros.navigation.ui.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.LocalSettingsTileColors
|
||||||
import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults
|
import com.alorma.compose.settings.ui.base.internal.SettingsTileDefaults
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants.DARK_MODE_SETTINGS
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DisplayScreenSettings(context: Context, navigateBack: () -> Unit) {
|
fun DisplaySettings(context: Context, navigateBack: () -> Unit) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
CenterAlignedTopAppBar(
|
CenterAlignedTopAppBar(
|
||||||
@@ -75,13 +73,11 @@ fun DisplayScreenSettings(context: Context, navigateBack: () -> Unit) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DisplaySettings(context: Context) {
|
private fun DisplaySettings(context: Context) {
|
||||||
|
val settingsViewModel = getSettingsViewModel(context)
|
||||||
Section(title = "Anzeige") {
|
Section(title = "Anzeige") {
|
||||||
val state = remember {
|
val state = remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
NavigationUtils.getBooleanKeyValue(
|
settingsViewModel.threedBuilding.value
|
||||||
context,
|
|
||||||
SHOW_THREED_BUILDING
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsCheckbox(
|
SettingsCheckbox(
|
||||||
@@ -89,17 +85,14 @@ private fun DisplaySettings(context: Context) {
|
|||||||
title = { Text(text = stringResource(R.string.threed_building)) },
|
title = { Text(text = stringResource(R.string.threed_building)) },
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
state.value = it
|
state.value = it
|
||||||
NavigationUtils.setBooleanKeyValue(context, it, SHOW_THREED_BUILDING)
|
settingsViewModel.onThreedBuildingChanged(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Section(title = "Dunkles Design") {
|
Section(title = "Dunkles Design") {
|
||||||
val state = remember {
|
val state = remember {
|
||||||
mutableIntStateOf(
|
mutableIntStateOf(
|
||||||
NavigationUtils.getIntKeyValue(
|
settingsViewModel.darkMode.value
|
||||||
context,
|
|
||||||
DARK_MODE_SETTINGS
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DarkModeData(context).darkDesign.forEach { sampleItem ->
|
DarkModeData(context).darkDesign.forEach { sampleItem ->
|
||||||
@@ -108,7 +101,7 @@ private fun DisplaySettings(context: Context) {
|
|||||||
title = { Text(text = sampleItem.title) },
|
title = { Text(text = sampleItem.title) },
|
||||||
onClick = {
|
onClick = {
|
||||||
state.intValue = sampleItem.key
|
state.intValue = sampleItem.key
|
||||||
NavigationUtils.setIntKeyValue(context, state.intValue, DARK_MODE_SETTINGS)
|
settingsViewModel.onDarkModeChanged(state.intValue)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.kouros.navigation.ui
|
package com.kouros.navigation.ui.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.layout.Column
|
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.alorma.compose.settings.ui.SettingsRadioButton
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants
|
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.Constants.ROUTING_ENGINE
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
import com.kouros.navigation.utils.NavigationUtils
|
||||||
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -68,13 +68,11 @@ fun NavigationScreenSettings(context: Context, navigateBack: () -> Unit) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NavigationSettings(context: Context) {
|
private fun NavigationSettings(context: Context) {
|
||||||
|
val settingsViewModel = getSettingsViewModel(context)
|
||||||
Section(title = stringResource(id = R.string.options)) {
|
Section(title = stringResource(id = R.string.options)) {
|
||||||
val avoidMotorwayState = remember {
|
val avoidMotorwayState = remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
NavigationUtils.getBooleanKeyValue(
|
settingsViewModel.avoidMotorway.value
|
||||||
context,
|
|
||||||
Constants.AVOID_MOTORWAY
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsCheckbox(
|
SettingsCheckbox(
|
||||||
@@ -82,16 +80,13 @@ private fun NavigationSettings(context: Context) {
|
|||||||
title = { Text(text = stringResource(id = R.string.avoid_highways_row_title)) },
|
title = { Text(text = stringResource(id = R.string.avoid_highways_row_title)) },
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
avoidMotorwayState.value = it
|
avoidMotorwayState.value = it
|
||||||
NavigationUtils.setBooleanKeyValue(context, it, Constants.AVOID_MOTORWAY)
|
settingsViewModel.onAvoidMotorway(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val avoidTollwayState = remember {
|
val avoidTollwayState = remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
NavigationUtils.getBooleanKeyValue(
|
settingsViewModel.avoidTollway.value
|
||||||
context,
|
|
||||||
Constants.AVOID_TOLLWAY
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsCheckbox(
|
SettingsCheckbox(
|
||||||
@@ -99,16 +94,13 @@ private fun NavigationSettings(context: Context) {
|
|||||||
title = { Text(text = stringResource(id = R.string.avoid_tolls_row_title)) },
|
title = { Text(text = stringResource(id = R.string.avoid_tolls_row_title)) },
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
avoidTollwayState.value = it
|
avoidTollwayState.value = it
|
||||||
NavigationUtils.setBooleanKeyValue(context, it, Constants.AVOID_TOLLWAY)
|
settingsViewModel.onAvoidTollway(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val carLocationState = remember {
|
val carLocationState = remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
NavigationUtils.getBooleanKeyValue(
|
settingsViewModel.carLocation.value
|
||||||
context,
|
|
||||||
Constants.CAR_LOCATION
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsCheckbox(
|
SettingsCheckbox(
|
||||||
@@ -116,7 +108,7 @@ private fun NavigationSettings(context: Context) {
|
|||||||
title = { Text(text = stringResource(id = R.string.use_car_location)) },
|
title = { Text(text = stringResource(id = R.string.use_car_location)) },
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
carLocationState.value = it
|
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)) {
|
Section(title = stringResource(id = R.string.routing_engine)) {
|
||||||
val state = remember {
|
val state = remember {
|
||||||
mutableIntStateOf(
|
mutableIntStateOf(
|
||||||
NavigationUtils.getIntKeyValue(
|
settingsViewModel.routingEngine.value
|
||||||
context,
|
|
||||||
ROUTING_ENGINE
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RoutingEngineData.engines.forEach { sampleItem ->
|
RoutingEngineData.engines.forEach { sampleItem ->
|
||||||
@@ -136,7 +125,7 @@ private fun NavigationSettings(context: Context) {
|
|||||||
title = { Text(text = sampleItem.title) },
|
title = { Text(text = sampleItem.title) },
|
||||||
onClick = {
|
onClick = {
|
||||||
state.intValue = sampleItem.key
|
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(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = if (useDarkTheme) darkColorScheme() else colorScheme,
|
||||||
typography = typography,
|
typography = typography,
|
||||||
content = content,
|
content = content,
|
||||||
shapes = shapes,
|
shapes = shapes,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.androidx.compose.ui.text)
|
implementation(libs.androidx.compose.ui.text)
|
||||||
implementation(libs.play.services.location)
|
implementation(libs.play.services.location)
|
||||||
|
implementation(libs.androidx.datastore.core)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.car
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@@ -26,6 +27,9 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleObserver
|
import androidx.lifecycle.LifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
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.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.car.screen.NavigationScreen
|
import com.kouros.navigation.car.screen.NavigationScreen
|
||||||
import com.kouros.navigation.car.screen.RequestPermissionScreen
|
import com.kouros.navigation.car.screen.RequestPermissionScreen
|
||||||
@@ -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.MAXIMAL_SNAP_CORRECTION
|
||||||
import com.kouros.navigation.data.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
|
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getViewModel
|
import com.kouros.navigation.utils.NavigationUtils.getViewModel
|
||||||
|
import com.kouros.navigation.utils.getSettingsRepository
|
||||||
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
|
import kotlinx.coroutines.awaitCancellation
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
|
||||||
class NavigationSession : Session(), NavigationScreen.Listener {
|
class NavigationSession : Session(), NavigationScreen.Listener {
|
||||||
|
|
||||||
@@ -54,7 +65,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
lateinit var surfaceRenderer: SurfaceRenderer
|
lateinit var surfaceRenderer: SurfaceRenderer
|
||||||
|
|
||||||
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
||||||
val useCarLocation = getBooleanKeyValue(carContext, CAR_LOCATION)
|
val repository = getSettingsRepository(carContext)
|
||||||
|
val useCarLocation = runBlocking { repository.carLocationFlow.first() }
|
||||||
if (!useCarLocation) {
|
if (!useCarLocation) {
|
||||||
updateLocation(location!!)
|
updateLocation(location!!)
|
||||||
}
|
}
|
||||||
@@ -75,7 +87,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
|
|
||||||
override fun onDestroy(owner: LifecycleOwner) {
|
override fun onDestroy(owner: LifecycleOwner) {
|
||||||
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo
|
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) {
|
if (useCarLocation) {
|
||||||
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
||||||
carSensors.removeCarHardwareLocationListener(carLocationListener)
|
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?> =
|
val carLocationListener: OnCarDataAvailableListener<CarHardwareLocation?> =
|
||||||
OnCarDataAvailableListener { data ->
|
OnCarDataAvailableListener { data ->
|
||||||
if (data.location.status == CarValue.STATUS_SUCCESS) {
|
if (data.location.status == CarValue.STATUS_SUCCESS) {
|
||||||
@@ -123,22 +137,39 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
|
|
||||||
fun onRoutingEngineStateUpdated(routeEngine : Int) {
|
fun onRoutingEngineStateUpdated(routeEngine : Int) {
|
||||||
navigationViewModel = when (routeEngine) {
|
navigationViewModel = when (routeEngine) {
|
||||||
RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository())
|
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||||
RouteEngine.OSRM.ordinal -> ViewModel(OsrmRepository())
|
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||||
else -> ViewModel(TomTomRepository())
|
else -> NavigationViewModel(TomTomRepository())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateScreen(intent: Intent): Screen {
|
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 = getViewModel(carContext)
|
||||||
|
|
||||||
navigationViewModel.routingEngine.observe(this, ::onRoutingEngineStateUpdated)
|
navigationViewModel.routingEngine.observe(this, ::onRoutingEngineStateUpdated)
|
||||||
|
|
||||||
routeModel = RouteCarModel()
|
routeModel = RouteCarModel()
|
||||||
|
|
||||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel, viewModelStoreOwner)
|
||||||
|
|
||||||
navigationScreen =
|
navigationScreen =
|
||||||
NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel)
|
NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel)
|
||||||
@@ -175,7 +206,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
|
|
||||||
fun addSensors() {
|
fun addSensors() {
|
||||||
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo
|
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) {
|
if (useCarLocation) {
|
||||||
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors
|
||||||
carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL,
|
carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL,
|
||||||
@@ -269,8 +301,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopNavigation() {
|
override fun stopNavigation(context: CarContext) {
|
||||||
routeModel.stopNavigation()
|
routeModel.stopNavigation(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.car.app.connection.CarConnection
|
|||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
@@ -21,6 +22,7 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModelStoreOwner
|
||||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||||
import com.kouros.navigation.car.map.DrawNavigationImages
|
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.getPaddingValues
|
||||||
import com.kouros.navigation.car.map.rememberBaseStyle
|
import com.kouros.navigation.car.map.rememberBaseStyle
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.data.Constants
|
|
||||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||||
import com.kouros.navigation.data.Constants.homeVogelhart
|
import com.kouros.navigation.data.Constants.homeVogelhart
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
import com.kouros.navigation.data.tomtom.TrafficData
|
|
||||||
import com.kouros.navigation.model.BaseStyleModel
|
import com.kouros.navigation.model.BaseStyleModel
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
|
||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
import com.kouros.navigation.utils.calculateTilt
|
import com.kouros.navigation.utils.calculateTilt
|
||||||
import com.kouros.navigation.utils.calculateZoom
|
import com.kouros.navigation.utils.calculateZoom
|
||||||
import com.kouros.navigation.utils.duration
|
import com.kouros.navigation.utils.duration
|
||||||
|
import com.kouros.navigation.utils.getSettingsRepository
|
||||||
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import com.kouros.navigation.utils.previewZoom
|
import com.kouros.navigation.utils.previewZoom
|
||||||
|
import com.kouros.navigation.utils.settingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.camera.CameraState
|
import org.maplibre.compose.camera.CameraState
|
||||||
import org.maplibre.compose.style.BaseStyle
|
import org.maplibre.compose.style.BaseStyle
|
||||||
@@ -52,7 +56,8 @@ import org.maplibre.spatialk.geojson.Position
|
|||||||
|
|
||||||
class SurfaceRenderer(
|
class SurfaceRenderer(
|
||||||
private var carContext: CarContext, lifecycle: Lifecycle,
|
private var carContext: CarContext, lifecycle: Lifecycle,
|
||||||
private var routeModel: RouteCarModel
|
private var routeModel: RouteCarModel,
|
||||||
|
private var viewModelStoreOwner: ViewModelStoreOwner
|
||||||
) : DefaultLifecycleObserver {
|
) : DefaultLifecycleObserver {
|
||||||
|
|
||||||
var lastLocation = location(0.0, 0.0)
|
var lastLocation = location(0.0, 0.0)
|
||||||
@@ -187,9 +192,12 @@ class SurfaceRenderer(
|
|||||||
@Composable
|
@Composable
|
||||||
fun MapView() {
|
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 position: CameraPosition? by cameraPosition.observeAsState()
|
||||||
val route: String? by routeData.observeAsState()
|
val route: String? by routeData.observeAsState()
|
||||||
val traffic: Map<String, String> ? by trafficData.observeAsState()
|
val traffic: Map<String, String> ? by trafficData.observeAsState()
|
||||||
@@ -197,7 +205,17 @@ class SurfaceRenderer(
|
|||||||
val paddingValues = getPaddingValues(height, viewStyle)
|
val paddingValues = getPaddingValues(height, viewStyle)
|
||||||
val cameraState = cameraState(paddingValues, position, tilt)
|
val cameraState = cameraState(paddingValues, position, tilt)
|
||||||
val rememberBaseStyle = rememberBaseStyle(baseStyle)
|
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)
|
ShowPosition(cameraState, position, paddingValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +357,8 @@ class SurfaceRenderer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateCarLocation(location: Location) {
|
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) {
|
if (routingEngine == RouteEngine.OSRM.ordinal) {
|
||||||
updateLocation(location)
|
updateLocation(location)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,18 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.data.Constants
|
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.NavigationColor
|
||||||
import com.kouros.navigation.data.RouteColor
|
import com.kouros.navigation.data.RouteColor
|
||||||
import com.kouros.navigation.data.SpeedColor
|
import com.kouros.navigation.data.SpeedColor
|
||||||
|
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||||
import com.kouros.navigation.model.RouteModel
|
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.CameraPosition
|
||||||
import org.maplibre.compose.camera.CameraState
|
import org.maplibre.compose.camera.CameraState
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
@@ -92,7 +95,8 @@ fun MapLibre(
|
|||||||
route: String?,
|
route: String?,
|
||||||
traffic: Map<String, String>?,
|
traffic: Map<String, String>?,
|
||||||
viewStyle: ViewStyle,
|
viewStyle: ViewStyle,
|
||||||
speedCameras: String? = ""
|
speedCameras: String? = "",
|
||||||
|
showBuildings: Boolean
|
||||||
) {
|
) {
|
||||||
MaplibreMap(
|
MaplibreMap(
|
||||||
options = MapOptions(
|
options = MapOptions(
|
||||||
@@ -103,7 +107,7 @@ fun MapLibre(
|
|||||||
baseStyle = baseStyle
|
baseStyle = baseStyle
|
||||||
) {
|
) {
|
||||||
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
||||||
if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) {
|
if (!showBuildings) {
|
||||||
BuildingLayer(tiles)
|
BuildingLayer(tiles)
|
||||||
}
|
}
|
||||||
if (viewStyle == ViewStyle.AMENITY_VIEW) {
|
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
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.location.Location
|
|
||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
@@ -14,17 +13,16 @@ import androidx.core.graphics.drawable.IconCompat
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
|
||||||
import com.kouros.navigation.data.Category
|
import com.kouros.navigation.data.Category
|
||||||
import com.kouros.navigation.data.Constants.CHARGING_STATION
|
import com.kouros.navigation.data.Constants.CHARGING_STATION
|
||||||
import com.kouros.navigation.data.Constants.FUEL_STATION
|
import com.kouros.navigation.data.Constants.FUEL_STATION
|
||||||
import com.kouros.navigation.data.Constants.PHARMACY
|
import com.kouros.navigation.data.Constants.PHARMACY
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
|
||||||
class CategoriesScreen(
|
class CategoriesScreen(
|
||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
private val viewModel: ViewModel,
|
private val navigationViewModel: NavigationViewModel,
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var categories: List<Category> = listOf(
|
var categories: List<Category> = listOf(
|
||||||
@@ -48,7 +46,7 @@ class CategoriesScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
it.id,
|
it.id,
|
||||||
viewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.location.Location
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
@@ -19,11 +18,10 @@ import androidx.lifecycle.Observer
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.navigation.NavigationMessage
|
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.Constants
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
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.GeoUtils.createPointCollection
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import com.kouros.navigation.utils.round
|
import com.kouros.navigation.utils.round
|
||||||
@@ -33,7 +31,7 @@ class CategoryScreen(
|
|||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
private val category: String,
|
private val category: String,
|
||||||
private val viewModel: ViewModel,
|
private val navigationViewModel: NavigationViewModel,
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var elements = listOf<Elements>()
|
var elements = listOf<Elements>()
|
||||||
@@ -57,8 +55,8 @@ class CategoryScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.elements.observe(this, observer)
|
navigationViewModel.elements.observe(this, observer)
|
||||||
viewModel.getAmenities(category, surfaceRenderer.lastLocation)
|
navigationViewModel.getAmenities(category, surfaceRenderer.lastLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -130,7 +128,7 @@ class CategoryScreen(
|
|||||||
row.addAction(
|
row.addAction(
|
||||||
Action.Builder()
|
Action.Builder()
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
viewModel.loadRoute(
|
navigationViewModel.loadRoute(
|
||||||
carContext,
|
carContext,
|
||||||
currentLocation = surfaceRenderer.lastLocation,
|
currentLocation = surfaceRenderer.lastLocation,
|
||||||
location(it.lon!!, it.lat!!),
|
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.Row
|
||||||
import androidx.car.app.model.SectionedItemList
|
import androidx.car.app.model.SectionedItemList
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants.DARK_MODE_SETTINGS
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
import kotlinx.coroutines.launch
|
||||||
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
|
|
||||||
|
|
||||||
class DarkModeSettings(private val carContext: CarContext) : Screen(carContext) {
|
class DarkModeSettings(private val carContext: CarContext) : Screen(carContext) {
|
||||||
|
|
||||||
private var darkModeSettings = 0
|
private var darkModeSettings = 0
|
||||||
|
|
||||||
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
darkModeSettings = getIntKeyValue(carContext, DARK_MODE_SETTINGS)
|
lifecycleScope.launch {
|
||||||
|
settingsViewModel.darkMode.collect {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
darkModeSettings = settingsViewModel.darkMode.value
|
||||||
val templateBuilder = ListTemplate.Builder()
|
val templateBuilder = ListTemplate.Builder()
|
||||||
val radioList =
|
val radioList =
|
||||||
ItemList.Builder()
|
ItemList.Builder()
|
||||||
@@ -52,10 +56,12 @@ class DarkModeSettings(private val carContext: CarContext) : Screen(carContext)
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
return templateBuilder
|
return templateBuilder
|
||||||
.addSectionedList(SectionedItemList.create(
|
.addSectionedList(
|
||||||
|
SectionedItemList.create(
|
||||||
radioList,
|
radioList,
|
||||||
carContext.getString(R.string.dark_mode)
|
carContext.getString(R.string.dark_mode)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
.setHeader(
|
.setHeader(
|
||||||
Header.Builder()
|
Header.Builder()
|
||||||
.setTitle(carContext.getString(R.string.dark_mode))
|
.setTitle(carContext.getString(R.string.dark_mode))
|
||||||
@@ -67,7 +73,7 @@ class DarkModeSettings(private val carContext: CarContext) : Screen(carContext)
|
|||||||
|
|
||||||
|
|
||||||
private fun onSelected(index: Int) {
|
private fun onSelected(index: Int) {
|
||||||
setIntKeyValue(carContext, index, DARK_MODE_SETTINGS)
|
settingsViewModel.onDarkModeChanged(index)
|
||||||
CarToast.makeText(
|
CarToast.makeText(
|
||||||
carContext,
|
carContext,
|
||||||
(carContext
|
(carContext
|
||||||
|
|||||||
@@ -6,33 +6,34 @@ import androidx.car.app.model.Action
|
|||||||
import androidx.car.app.model.Header
|
import androidx.car.app.model.Header
|
||||||
import androidx.car.app.model.ItemList
|
import androidx.car.app.model.ItemList
|
||||||
import androidx.car.app.model.ListTemplate
|
import androidx.car.app.model.ListTemplate
|
||||||
import androidx.car.app.model.OnClickListener
|
|
||||||
import androidx.car.app.model.Row
|
import androidx.car.app.model.Row
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import androidx.car.app.model.Toggle
|
import androidx.car.app.model.Toggle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
import kotlinx.coroutines.launch
|
||||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
|
||||||
|
|
||||||
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
class DisplaySettings(private val carContext: CarContext) : Screen(carContext) {
|
||||||
|
|
||||||
private var buildingToggleState = false
|
private var buildingToggleState = false
|
||||||
|
|
||||||
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
buildingToggleState = getBooleanKeyValue(carContext, SHOW_THREED_BUILDING)
|
lifecycleScope.launch {
|
||||||
|
settingsViewModel.threedBuilding.collect {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
buildingToggleState = settingsViewModel.threedBuilding.value
|
||||||
val listBuilder = ItemList.Builder()
|
val listBuilder = ItemList.Builder()
|
||||||
val buildingToggle: Toggle =
|
val buildingToggle: Toggle =
|
||||||
Toggle.Builder { checked: Boolean ->
|
Toggle.Builder { checked: Boolean ->
|
||||||
if (checked) {
|
settingsViewModel.onThreedBuildingChanged(checked)
|
||||||
setBooleanKeyValue(carContext, true, SHOW_THREED_BUILDING)
|
|
||||||
} else {
|
|
||||||
setBooleanKeyValue(carContext, false, SHOW_THREED_BUILDING)
|
|
||||||
}
|
|
||||||
buildingToggleState = !buildingToggleState
|
buildingToggleState = !buildingToggleState
|
||||||
}.setChecked(buildingToggleState).build()
|
}.setChecked(buildingToggleState).build()
|
||||||
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle))
|
listBuilder.addItem(buildRowForTemplate(R.string.threed_building, buildingToggle))
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.car.app.navigation.model.NavigationTemplate
|
|||||||
import androidx.car.app.navigation.model.RoutingInfo
|
import androidx.car.app.navigation.model.RoutingInfo
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.ViewStyle
|
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
|
||||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||||
import com.kouros.navigation.data.Place
|
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.nominatim.SearchResult
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
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.GeoUtils
|
||||||
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.time.Duration
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@@ -43,21 +51,21 @@ class NavigationScreen(
|
|||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private var routeModel: RouteCarModel,
|
private var routeModel: RouteCarModel,
|
||||||
private var listener: Listener,
|
private var listener: Listener,
|
||||||
private val viewModel: ViewModel
|
private val navigationViewModel: NavigationViewModel
|
||||||
) :
|
) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
|
|
||||||
/** A listener for navigation start and stop signals. */
|
/** A listener for navigation start and stop signals. */
|
||||||
interface Listener {
|
interface Listener {
|
||||||
/** Stops navigation. */
|
/** Stops navigation. */
|
||||||
fun stopNavigation()
|
fun stopNavigation(context: CarContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
var recentPlace = Place()
|
var recentPlace = Place()
|
||||||
var navigationType = NavigationType.VIEW
|
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 ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
navigationType = NavigationType.NAVIGATION
|
navigationType = NavigationType.NAVIGATION
|
||||||
@@ -99,7 +107,7 @@ class NavigationScreen(
|
|||||||
speedCameras = cameras
|
speedCameras = cameras
|
||||||
val coordinates = mutableListOf<List<Double>>()
|
val coordinates = mutableListOf<List<Double>>()
|
||||||
cameras.forEach {
|
cameras.forEach {
|
||||||
coordinates.add(listOf(it.lon!!, it.lat!!))
|
coordinates.add(listOf(it.lon, it.lat))
|
||||||
}
|
}
|
||||||
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
val speedData = GeoUtils.createPointCollection(coordinates, "radar")
|
||||||
surfaceRenderer.speedCamerasData.value = speedData
|
surfaceRenderer.speedCamerasData.value = speedData
|
||||||
@@ -107,11 +115,15 @@ class NavigationScreen(
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.route.observe(this, observer)
|
navigationViewModel.route.observe(this, observer)
|
||||||
viewModel.traffic.observe(this, trafficObserver);
|
navigationViewModel.traffic.observe(this, trafficObserver);
|
||||||
viewModel.recentPlace.observe(this, recentObserver)
|
navigationViewModel.recentPlace.observe(this, recentObserver)
|
||||||
viewModel.placeLocation.observe(this, placeObserver)
|
navigationViewModel.placeLocation.observe(this, placeObserver)
|
||||||
viewModel.speedCameras.observe(this, speedObserver)
|
navigationViewModel.speedCameras.observe(this, speedObserver)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
getSettingsViewModel(carContext).routingEngine.collect {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
@@ -306,7 +318,7 @@ class NavigationScreen(
|
|||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
||||||
viewModel.loadRoute(
|
navigationViewModel.loadRoute(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
navigateTo,
|
navigateTo,
|
||||||
@@ -349,7 +361,7 @@ class NavigationScreen(
|
|||||||
return Action.Builder()
|
return Action.Builder()
|
||||||
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px))
|
.setIcon(routeModel.createCarIcon(carContext, R.drawable.settings_48px))
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
screenManager.push(SettingsScreen(carContext, viewModel))
|
screenManager.push(SettingsScreen(carContext, navigationViewModel))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -411,13 +423,13 @@ class NavigationScreen(
|
|||||||
SearchScreen(
|
SearchScreen(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
viewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
val place = obj as Place
|
val place = obj as Place
|
||||||
if (place.longitude == 0.0) {
|
if (place.longitude == 0.0) {
|
||||||
viewModel.findAddress(
|
navigationViewModel.findAddress(
|
||||||
"${obj.city} ${obj.street}},",
|
"${obj.city} ${obj.street}},",
|
||||||
currentNavigationLocation
|
currentNavigationLocation
|
||||||
)
|
)
|
||||||
@@ -432,9 +444,9 @@ class NavigationScreen(
|
|||||||
fun navigateToPlace(place: Place) {
|
fun navigateToPlace(place: Place) {
|
||||||
navigationType = NavigationType.VIEW
|
navigationType = NavigationType.VIEW
|
||||||
val location = location(place.longitude, place.latitude)
|
val location = location(place.longitude, place.latitude)
|
||||||
viewModel.saveRecent(place)
|
navigationViewModel.saveRecent(place)
|
||||||
currentNavigationLocation = location
|
currentNavigationLocation = location
|
||||||
viewModel.loadRoute(
|
navigationViewModel.loadRoute(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
location,
|
location,
|
||||||
@@ -446,7 +458,7 @@ class NavigationScreen(
|
|||||||
|
|
||||||
fun stopNavigation() {
|
fun stopNavigation() {
|
||||||
navigationType = NavigationType.VIEW
|
navigationType = NavigationType.VIEW
|
||||||
listener.stopNavigation()
|
listener.stopNavigation(carContext)
|
||||||
surfaceRenderer.routeData.value = ""
|
surfaceRenderer.routeData.value = ""
|
||||||
lastCameraSearch = 0
|
lastCameraSearch = 0
|
||||||
invalidate()
|
invalidate()
|
||||||
@@ -470,7 +482,7 @@ class NavigationScreen(
|
|||||||
|
|
||||||
fun reRoute(destination: Place) {
|
fun reRoute(destination: Place) {
|
||||||
val dest = location(destination.longitude, destination.latitude)
|
val dest = location(destination.longitude, destination.latitude)
|
||||||
viewModel.loadRoute(
|
navigationViewModel.loadRoute(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
dest,
|
dest,
|
||||||
@@ -480,14 +492,14 @@ class NavigationScreen(
|
|||||||
|
|
||||||
fun updateTrip(location: Location) {
|
fun updateTrip(location: Location) {
|
||||||
val current = LocalDateTime.now(ZoneOffset.UTC)
|
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) {
|
if (duration.abs().seconds > 360) {
|
||||||
lastTrafficDate = current
|
lastTrafficDate = current
|
||||||
viewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
navigationViewModel.loadTraffic(carContext, location, surfaceRenderer.carOrientation)
|
||||||
}
|
}
|
||||||
updateSpeedCamera(location)
|
updateSpeedCamera(location)
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
updateLocation(location, viewModel)
|
updateLocation(carContext,location, navigationViewModel)
|
||||||
if ((navState.maneuverType == Maneuver.TYPE_DESTINATION
|
if ((navState.maneuverType == Maneuver.TYPE_DESTINATION
|
||||||
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_LEFT
|
||||||
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
|| navState.maneuverType == Maneuver.TYPE_DESTINATION_RIGHT
|
||||||
@@ -506,7 +518,7 @@ class NavigationScreen(
|
|||||||
|
|
||||||
private fun updateSpeedCamera(location: Location) {
|
private fun updateSpeedCamera(location: Location) {
|
||||||
if (lastCameraSearch++ % 100 == 0) {
|
if (lastCameraSearch++ % 100 == 0) {
|
||||||
viewModel.getSpeedCameras(location, 5.0)
|
navigationViewModel.getSpeedCameras(location, 5.0)
|
||||||
}
|
}
|
||||||
if (speedCameras.isNotEmpty()) {
|
if (speedCameras.isNotEmpty()) {
|
||||||
updateDistance(location)
|
updateDistance(location)
|
||||||
|
|||||||
@@ -9,16 +9,14 @@ import androidx.car.app.model.ListTemplate
|
|||||||
import androidx.car.app.model.Row
|
import androidx.car.app.model.Row
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import androidx.car.app.model.Toggle
|
import androidx.car.app.model.Toggle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants.AVOID_MOTORWAY
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.data.Constants.AVOID_TOLLWAY
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.data.Constants.CAR_LOCATION
|
import kotlinx.coroutines.launch
|
||||||
import com.kouros.navigation.model.ViewModel
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationSettings(private val carContext: CarContext, private var viewModel: ViewModel) :
|
class NavigationSettings(private val carContext: CarContext, private var navigationViewModel: NavigationViewModel) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
|
|
||||||
private var motorWayToggleState = false
|
private var motorWayToggleState = false
|
||||||
@@ -27,25 +25,25 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
|||||||
|
|
||||||
private var carLocationToggleState = false
|
private var carLocationToggleState = false
|
||||||
|
|
||||||
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
motorWayToggleState = getBooleanKeyValue(carContext, AVOID_MOTORWAY)
|
lifecycleScope.launch {
|
||||||
|
settingsViewModel.avoidTollway.collect {
|
||||||
tollWayToggleState = getBooleanKeyValue(carContext, AVOID_MOTORWAY)
|
invalidate()
|
||||||
|
}
|
||||||
carLocationToggleState = getBooleanKeyValue(carContext, CAR_LOCATION)
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
motorWayToggleState = settingsViewModel.avoidMotorway.value
|
||||||
|
tollWayToggleState = settingsViewModel.avoidTollway.value
|
||||||
|
carLocationToggleState = settingsViewModel.carLocation.value
|
||||||
|
|
||||||
val listBuilder = ItemList.Builder()
|
val listBuilder = ItemList.Builder()
|
||||||
val highwayToggle: Toggle =
|
val highwayToggle: Toggle =
|
||||||
Toggle.Builder { checked: Boolean ->
|
Toggle.Builder { checked: Boolean ->
|
||||||
if (checked) {
|
settingsViewModel.onAvoidMotorway(checked)
|
||||||
setBooleanKeyValue(carContext, true, AVOID_MOTORWAY)
|
|
||||||
} else {
|
|
||||||
setBooleanKeyValue(carContext, false, AVOID_MOTORWAY)
|
|
||||||
}
|
|
||||||
motorWayToggleState = !motorWayToggleState
|
motorWayToggleState = !motorWayToggleState
|
||||||
}.setChecked(motorWayToggleState).build()
|
}.setChecked(motorWayToggleState).build()
|
||||||
listBuilder.addItem(buildRowForTemplate(R.string.avoid_highways_row_title, highwayToggle))
|
listBuilder.addItem(buildRowForTemplate(R.string.avoid_highways_row_title, highwayToggle))
|
||||||
@@ -53,22 +51,14 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
|||||||
// Tollway
|
// Tollway
|
||||||
val tollwayToggle: Toggle =
|
val tollwayToggle: Toggle =
|
||||||
Toggle.Builder { checked: Boolean ->
|
Toggle.Builder { checked: Boolean ->
|
||||||
if (checked) {
|
settingsViewModel.onAvoidTollway(checked)
|
||||||
setBooleanKeyValue(carContext, true, AVOID_TOLLWAY)
|
|
||||||
} else {
|
|
||||||
setBooleanKeyValue(carContext, false, AVOID_TOLLWAY)
|
|
||||||
}
|
|
||||||
tollWayToggleState = !tollWayToggleState
|
tollWayToggleState = !tollWayToggleState
|
||||||
}.setChecked(tollWayToggleState).build()
|
}.setChecked(tollWayToggleState).build()
|
||||||
listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle))
|
listBuilder.addItem(buildRowForTemplate(R.string.avoid_tolls_row_title, tollwayToggle))
|
||||||
|
|
||||||
val carLocationToggle: Toggle =
|
val carLocationToggle: Toggle =
|
||||||
Toggle.Builder { checked: Boolean ->
|
Toggle.Builder { checked: Boolean ->
|
||||||
if (checked) {
|
settingsViewModel.onCarLocation(checked)
|
||||||
setBooleanKeyValue(carContext, true, CAR_LOCATION)
|
|
||||||
} else {
|
|
||||||
setBooleanKeyValue(carContext, false, CAR_LOCATION)
|
|
||||||
}
|
|
||||||
carLocationToggleState = !carLocationToggleState
|
carLocationToggleState = !carLocationToggleState
|
||||||
}.setChecked(carLocationToggleState).build()
|
}.setChecked(carLocationToggleState).build()
|
||||||
|
|
||||||
@@ -81,7 +71,7 @@ class NavigationSettings(private val carContext: CarContext, private var viewMod
|
|||||||
|
|
||||||
listBuilder.addItem(
|
listBuilder.addItem(
|
||||||
buildRowForScreenTemplate(
|
buildRowForScreenTemplate(
|
||||||
RoutingSettings(carContext, viewModel),
|
RoutingSettings(carContext, navigationViewModel),
|
||||||
R.string.routing_engine
|
R.string.routing_engine
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.location.Location
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
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.FAVORITES
|
||||||
import com.kouros.navigation.data.Constants.RECENT
|
import com.kouros.navigation.data.Constants.RECENT
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
|
||||||
|
|
||||||
class PlaceListScreen(
|
class PlaceListScreen(
|
||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
private val category: String,
|
private val category: String,
|
||||||
private val viewModel: ViewModel
|
private val navigationViewModel: NavigationViewModel
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var places = listOf<Place>()
|
var places = listOf<Place>()
|
||||||
@@ -49,30 +48,30 @@ class PlaceListScreen(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (category == RECENT) {
|
if (category == RECENT) {
|
||||||
viewModel.places.observe(this, observer)
|
navigationViewModel.places.observe(this, observer)
|
||||||
}
|
}
|
||||||
if (category == CONTACTS) {
|
if (category == CONTACTS) {
|
||||||
viewModel.contactAddress.observe(this, observerAddress)
|
navigationViewModel.contactAddress.observe(this, observerAddress)
|
||||||
}
|
}
|
||||||
if (category == FAVORITES) {
|
if (category == FAVORITES) {
|
||||||
viewModel.favorites.observe(this, observer)
|
navigationViewModel.favorites.observe(this, observer)
|
||||||
}
|
}
|
||||||
loadPlaces()
|
loadPlaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadPlaces() {
|
fun loadPlaces() {
|
||||||
if (category == RECENT) {
|
if (category == RECENT) {
|
||||||
viewModel.loadRecentPlaces(
|
navigationViewModel.loadRecentPlaces(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
surfaceRenderer.carOrientation
|
surfaceRenderer.carOrientation
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (category == CONTACTS) {
|
if (category == CONTACTS) {
|
||||||
viewModel.loadContacts(carContext)
|
navigationViewModel.loadContacts(carContext)
|
||||||
}
|
}
|
||||||
if (category == FAVORITES) {
|
if (category == FAVORITES) {
|
||||||
viewModel.loadFavorites(
|
navigationViewModel.loadFavorites(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
surfaceRenderer.carOrientation
|
surfaceRenderer.carOrientation
|
||||||
@@ -110,7 +109,7 @@ class PlaceListScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
place,
|
place,
|
||||||
viewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
@@ -163,7 +162,7 @@ class PlaceListScreen(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
viewModel.deletePlace(place)
|
navigationViewModel.deletePlace(place)
|
||||||
CarToast.makeText(
|
CarToast.makeText(
|
||||||
carContext,
|
carContext,
|
||||||
R.string.recent_Item_deleted, CarToast.LENGTH_LONG
|
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.CarContext
|
||||||
import androidx.car.app.CarToast
|
import androidx.car.app.CarToast
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.constraints.ConstraintManager
|
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
import androidx.car.app.model.Action.FLAG_DEFAULT
|
import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||||
import androidx.car.app.model.ActionStrip
|
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.NavigationMessage
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.data.Place
|
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 com.kouros.navigation.utils.location
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
@@ -39,7 +38,7 @@ class RoutePreviewScreen(
|
|||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private var destination: Place,
|
private var destination: Place,
|
||||||
private val viewModel: ViewModel
|
private val navigationViewModel: NavigationViewModel
|
||||||
) :
|
) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
private var isFavorite = false
|
private var isFavorite = false
|
||||||
@@ -56,9 +55,9 @@ class RoutePreviewScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.previewRoute.observe(this, observer)
|
navigationViewModel.previewRoute.observe(this, observer)
|
||||||
val location = location(destination.longitude, destination.latitude)
|
val location = location(destination.longitude, destination.latitude)
|
||||||
viewModel.loadPreviewRoute(
|
navigationViewModel.loadPreviewRoute(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer.lastLocation,
|
surfaceRenderer.lastLocation,
|
||||||
location,
|
location,
|
||||||
@@ -164,7 +163,7 @@ class RoutePreviewScreen(
|
|||||||
CarToast.LENGTH_SHORT
|
CarToast.LENGTH_SHORT
|
||||||
)
|
)
|
||||||
.show()
|
.show()
|
||||||
viewModel.saveFavorite(destination)
|
navigationViewModel.saveFavorite(destination)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
@@ -172,7 +171,7 @@ class RoutePreviewScreen(
|
|||||||
private fun deleteFavoriteAction(): Action = Action.Builder()
|
private fun deleteFavoriteAction(): Action = Action.Builder()
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
viewModel.deleteFavorite(destination)
|
navigationViewModel.deleteFavorite(destination)
|
||||||
}
|
}
|
||||||
isFavorite = !isFavorite
|
isFavorite = !isFavorite
|
||||||
finish()
|
finish()
|
||||||
|
|||||||
@@ -10,28 +10,28 @@ import androidx.car.app.model.ListTemplate
|
|||||||
import androidx.car.app.model.Row
|
import androidx.car.app.model.Row
|
||||||
import androidx.car.app.model.SectionedItemList
|
import androidx.car.app.model.SectionedItemList
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import androidx.car.app.model.Toggle
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.kouros.data.R
|
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.data.RouteEngine
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
import com.kouros.navigation.utils.getSettingsViewModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
import kotlinx.coroutines.launch
|
||||||
import com.kouros.navigation.utils.NavigationUtils.setBooleanKeyValue
|
|
||||||
import com.kouros.navigation.utils.NavigationUtils.setIntKeyValue
|
|
||||||
|
|
||||||
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
|
private var routingEngine = RouteEngine.OSRM.ordinal
|
||||||
|
|
||||||
|
val settingsViewModel = getSettingsViewModel(carContext)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
routingEngine = getIntKeyValue(carContext, ROUTING_ENGINE)
|
lifecycleScope.launch {
|
||||||
|
settingsViewModel.routingEngine.collect {
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
routingEngine = settingsViewModel.routingEngine.value
|
||||||
val templateBuilder = ListTemplate.Builder()
|
val templateBuilder = ListTemplate.Builder()
|
||||||
val radioList =
|
val radioList =
|
||||||
ItemList.Builder()
|
ItemList.Builder()
|
||||||
@@ -71,8 +71,8 @@ class RoutingSettings(private val carContext: CarContext, private var viewModel:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onSelected(index: Int) {
|
private fun onSelected(index: Int) {
|
||||||
setIntKeyValue(carContext, index, ROUTING_ENGINE)
|
settingsViewModel.onRoutingEngineChanged(index)
|
||||||
viewModel.routingEngine.value = index
|
navigationViewModel.routingEngine.value = index
|
||||||
CarToast.makeText(
|
CarToast.makeText(
|
||||||
carContext,
|
carContext,
|
||||||
(carContext
|
(carContext
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.location.Location
|
|
||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
@@ -16,18 +15,17 @@ import androidx.lifecycle.Observer
|
|||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
|
||||||
import com.kouros.navigation.data.Category
|
import com.kouros.navigation.data.Category
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.nominatim.SearchResult
|
import com.kouros.navigation.data.nominatim.SearchResult
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
|
||||||
|
|
||||||
class SearchScreen(
|
class SearchScreen(
|
||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private val viewModel: ViewModel,
|
private val navigationViewModel: NavigationViewModel,
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var isSearchComplete: Boolean = false
|
var isSearchComplete: Boolean = false
|
||||||
@@ -47,7 +45,7 @@ class SearchScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.searchPlaces.observe(this, observer)
|
navigationViewModel.searchPlaces.observe(this, observer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
@@ -71,7 +69,7 @@ class SearchScreen(
|
|||||||
CategoriesScreen(
|
CategoriesScreen(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
viewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||||
@@ -87,7 +85,7 @@ class SearchScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
it.id,
|
it.id,
|
||||||
viewModel
|
navigationViewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
@@ -115,7 +113,7 @@ class SearchScreen(
|
|||||||
object : SearchCallback {
|
object : SearchCallback {
|
||||||
override fun onSearchSubmitted(searchTerm: String) {
|
override fun onSearchSubmitted(searchTerm: String) {
|
||||||
isSearchComplete = true
|
isSearchComplete = true
|
||||||
viewModel.searchPlaces(searchTerm, surfaceRenderer.lastLocation)
|
navigationViewModel.searchPlaces(searchTerm, surfaceRenderer.lastLocation)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setHeaderAction(Action.BACK)
|
.setHeaderAction(Action.BACK)
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import androidx.car.app.model.ListTemplate
|
|||||||
import androidx.car.app.model.Row
|
import androidx.car.app.model.Row
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
|
|
||||||
/** A screen demonstrating selectable lists. */
|
/** A screen demonstrating selectable lists. */
|
||||||
class SettingsScreen(
|
class SettingsScreen(
|
||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
private var viewModel: ViewModel,
|
private var navigationViewModel: NavigationViewModel,
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
@@ -42,7 +42,7 @@ class SettingsScreen(
|
|||||||
)
|
)
|
||||||
listBuilder.addItem(
|
listBuilder.addItem(
|
||||||
buildRowForTemplate(
|
buildRowForTemplate(
|
||||||
NavigationSettings(carContext, viewModel),
|
NavigationSettings(carContext, navigationViewModel),
|
||||||
R.string.navigation_settings
|
R.string.navigation_settings
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.kouros.navigation.car
|
|||||||
|
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.NavigationViewModel
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,7 +12,7 @@ import org.junit.Test
|
|||||||
class ViewModelTest {
|
class ViewModelTest {
|
||||||
|
|
||||||
val repo = ValhallaRepository()
|
val repo = ValhallaRepository()
|
||||||
val viewModel = ViewModel(repo)
|
val navigationViewModel = NavigationViewModel(repo)
|
||||||
val model = RouteModel()
|
val model = RouteModel()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.library)
|
alias(libs.plugins.android.library)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
kotlin("plugin.serialization") version "2.2.21"
|
kotlin("plugin.serialization") version "2.2.21"
|
||||||
kotlin("kapt")
|
kotlin("kapt")
|
||||||
}
|
}
|
||||||
@@ -19,6 +20,10 @@ android {
|
|||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
@@ -40,6 +45,10 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
val composeBom = platform(libs.androidx.compose.bom)
|
||||||
|
implementation(composeBom)
|
||||||
|
androidTestImplementation(composeBom)
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
@@ -49,6 +58,7 @@ dependencies {
|
|||||||
implementation(libs.koin.compose.viewmodel)
|
implementation(libs.koin.compose.viewmodel)
|
||||||
implementation(libs.androidx.car.app)
|
implementation(libs.androidx.car.app)
|
||||||
implementation(libs.android.sdk.turf)
|
implementation(libs.android.sdk.turf)
|
||||||
|
implementation(libs.androidx.compose.runtime)
|
||||||
|
|
||||||
|
|
||||||
// objectbox
|
// objectbox
|
||||||
@@ -56,6 +66,8 @@ dependencies {
|
|||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
annotationProcessor(libs.objectbox.processor)
|
annotationProcessor(libs.objectbox.processor)
|
||||||
|
|
||||||
|
implementation(libs.androidx.datastore.preferences)
|
||||||
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.maplibre.compose)
|
implementation(libs.maplibre.compose)
|
||||||
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ object Constants {
|
|||||||
const val CAR_LOCATION = "CarLocation"
|
const val CAR_LOCATION = "CarLocation"
|
||||||
const val ROUTING_ENGINE = "RoutingEngine"
|
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
|
const val MAXIMAL_SNAP_CORRECTION = 50.0
|
||||||
|
|
||||||
@@ -157,3 +159,12 @@ object Constants {
|
|||||||
enum class RouteEngine {
|
enum class RouteEngine {
|
||||||
VALHALLA, OSRM, TOMTOM, GRAPHHOPPER
|
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.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import com.kouros.data.R
|
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.model.RouteModel
|
||||||
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
|
import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
@@ -50,10 +54,18 @@ abstract class NavigationRepository {
|
|||||||
searchFilter: SearchFilter,
|
searchFilter: SearchFilter,
|
||||||
context: Context
|
context: Context
|
||||||
): Double {
|
): Double {
|
||||||
val route = getRoute(context, currentLocation, location, carOrientation, searchFilter)
|
val osrm = OsrmRepository()
|
||||||
val routeModel = RouteModel()
|
val route = osrm.getRoute(context, currentLocation, location, carOrientation, searchFilter)
|
||||||
routeModel.startNavigation(route, context)
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
return routeModel.curRoute.summary.distance
|
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 {
|
fun searchPlaces(search: String, location: Location): String {
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ data class Route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextStep(steps : Int): Step {
|
fun nextStep(add: Int): Step {
|
||||||
val nextIndex = currentStepIndex + steps
|
val nextIndex = currentStepIndex + add
|
||||||
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
|
return if (isRouteValid() && nextIndex < legs().first().steps.size) {
|
||||||
legs().first().steps[nextIndex]
|
legs().first().steps[nextIndex]
|
||||||
} else {
|
} 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.NavigationRepository
|
||||||
import com.kouros.navigation.data.SearchFilter
|
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() {
|
class OsrmRepository : NavigationRepository() {
|
||||||
override fun getRoute(
|
override fun getRoute(
|
||||||
@@ -23,7 +25,7 @@ class OsrmRepository : NavigationRepository() {
|
|||||||
if (searchFilter.avoidTollway) {
|
if (searchFilter.avoidTollway) {
|
||||||
exclude = "$exclude&exclude=toll"
|
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)
|
return fetchUrl(routeUrl + routeLocation + exclude, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.net.URL
|
|||||||
class Overpass {
|
class Overpass {
|
||||||
|
|
||||||
//val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
//val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||||
|
//val overpassUrl = "https://overpass-api.de/api"
|
||||||
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
val overpassUrl = "https://kouros-online.de/overpass/interpreter"
|
||||||
|
|
||||||
|
|
||||||
@@ -43,13 +44,13 @@ class Overpass {
|
|||||||
val boundingBox = getBoundingBox(location.latitude, location.longitude, radius)
|
val boundingBox = getBoundingBox(location.latitude, location.longitude, radius)
|
||||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
httpURLConnection.requestMethod = "POST"
|
httpURLConnection.requestMethod = "POST"
|
||||||
|
// node["highway"="speed_camera"]
|
||||||
|
// node[amenity=$category]
|
||||||
|
httpURLConnection.setDoOutput(true);
|
||||||
httpURLConnection.setRequestProperty(
|
httpURLConnection.setRequestProperty(
|
||||||
"Accept",
|
"Accept",
|
||||||
"application/json"
|
"application/json"
|
||||||
)
|
)
|
||||||
// node["highway"="speed_camera"]
|
|
||||||
// node[amenity=$category]
|
|
||||||
httpURLConnection.setDoOutput(true);
|
|
||||||
// define search query
|
// define search query
|
||||||
val searchQuery = """
|
val searchQuery = """
|
||||||
|[out:json];
|
|[out:json];
|
||||||
@@ -79,7 +80,7 @@ class Overpass {
|
|||||||
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
println("Speed $e")
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,14 @@ import com.kouros.navigation.utils.GeoUtils.calculateSquareRadius
|
|||||||
|
|
||||||
private const val routeUrl = "https://api.tomtom.com/routing/1/calculateRoute/"
|
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 const val tomtomFields =
|
||||||
private val tomtomFields =
|
|
||||||
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
|
"{incidents{type,geometry{type,coordinates},properties{iconCategory,events{description}}}}"
|
||||||
|
|
||||||
const val useAsset = false
|
const val useAsset = true
|
||||||
|
|
||||||
class TomTomRepository : NavigationRepository() {
|
class TomTomRepository : NavigationRepository() {
|
||||||
override fun getRoute(
|
override fun getRoute(
|
||||||
|
|||||||
@@ -61,9 +61,7 @@ class TomTomRoute {
|
|||||||
route.sections?.forEach { section ->
|
route.sections?.forEach { section ->
|
||||||
val lanes = mutableListOf<Lane>()
|
val lanes = mutableListOf<Lane>()
|
||||||
var startIndex = 0
|
var startIndex = 0
|
||||||
if (section.startPointIndex <= instruction.pointIndex - 3
|
|
||||||
&& instruction.pointIndex <= section.endPointIndex
|
|
||||||
) {
|
|
||||||
section.lanes?.forEach { itLane ->
|
section.lanes?.forEach { itLane ->
|
||||||
val lane = Lane(
|
val lane = Lane(
|
||||||
location(
|
location(
|
||||||
@@ -77,7 +75,7 @@ class TomTomRoute {
|
|||||||
lanes.add(lane)
|
lanes.add(lane)
|
||||||
}
|
}
|
||||||
intersections.add(Intersection(waypoints[startIndex], lanes))
|
intersections.add(Intersection(waypoints[startIndex], lanes))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
allIntersections.addAll(intersections)
|
allIntersections.addAll(intersections)
|
||||||
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
stepDistance = route.guidance.instructions[index].routeOffsetInMeters - stepDistance
|
||||||
@@ -166,13 +164,23 @@ class TomTomRoute {
|
|||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_RIGHT
|
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
|
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW
|
||||||
}
|
}
|
||||||
|
|
||||||
"ROUNDABOUT_LEFT" -> {
|
"ROUNDABOUT_LEFT" -> {
|
||||||
newType = androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW
|
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
|
return newType
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,30 +66,19 @@ class IconMapper() {
|
|||||||
|
|
||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
}
|
}
|
||||||
|
Maneuver.TYPE_U_TURN_LEFT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_u_turn_left
|
||||||
|
}
|
||||||
|
Maneuver.TYPE_U_TURN_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_u_turn_right
|
||||||
|
}
|
||||||
|
Maneuver.TYPE_MERGE_LEFT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_merge_symmetrical
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return currentTurnIcon
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addLanes(direction: String, stepData: StepData): Int {
|
fun addLanes(direction: String, stepData: StepData): Int {
|
||||||
val laneDirection = when (direction.lowercase(Locale.getDefault())) {
|
val laneDirection = when (direction.lowercase(Locale.getDefault())) {
|
||||||
"left_straight" -> {
|
"left_straight" -> {
|
||||||
@@ -112,6 +101,8 @@ class IconMapper() {
|
|||||||
"straight" -> {
|
"straight" -> {
|
||||||
when (stepData.currentManeuverType) {
|
when (stepData.currentManeuverType) {
|
||||||
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
Maneuver.TYPE_STRAIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||||
|
Maneuver.TYPE_KEEP_LEFT -> LaneDirection.SHAPE_STRAIGHT
|
||||||
|
Maneuver.TYPE_KEEP_RIGHT -> LaneDirection.SHAPE_STRAIGHT
|
||||||
else
|
else
|
||||||
-> LaneDirection.SHAPE_UNKNOWN
|
-> LaneDirection.SHAPE_UNKNOWN
|
||||||
}
|
}
|
||||||
@@ -136,7 +127,8 @@ class IconMapper() {
|
|||||||
|
|
||||||
"left_slight", "slight_left" -> {
|
"left_slight", "slight_left" -> {
|
||||||
when (stepData.currentManeuverType) {
|
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
|
else
|
||||||
-> LaneDirection.SHAPE_UNKNOWN
|
-> LaneDirection.SHAPE_UNKNOWN
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.kouros.navigation.model
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import androidx.car.app.CarContext
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.compose.runtime.toMutableStateList
|
import androidx.compose.runtime.toMutableStateList
|
||||||
import androidx.lifecycle.MutableLiveData
|
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.nominatim.SearchResult
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.data.overpass.Overpass
|
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.Levenshtein
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
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 com.kouros.navigation.utils.location
|
||||||
import io.objectbox.kotlin.boxFor
|
import io.objectbox.kotlin.boxFor
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.maplibre.geojson.FeatureCollection
|
import org.maplibre.geojson.FeatureCollection
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
class NavigationViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||||
|
|
||||||
val route: MutableLiveData<String> by lazy {
|
val route: MutableLiveData<String> by lazy {
|
||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
@@ -457,14 +458,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSearchFilter(context: Context): SearchFilter {
|
fun getSearchFilter(context: Context): SearchFilter {
|
||||||
val avoidMotorway = NavigationUtils.getBooleanKeyValue(
|
val repository = getSettingsRepository(context)
|
||||||
context = context,
|
val avoidMotorway = runBlocking { repository.avoidMotorwayFlow.first() }
|
||||||
Constants.AVOID_MOTORWAY
|
val avoidTollway = runBlocking { repository.avoidTollwayFlow.first() }
|
||||||
)
|
|
||||||
val avoidTollway = NavigationUtils.getBooleanKeyValue(
|
|
||||||
context = context,
|
|
||||||
Constants.AVOID_TOLLWAY
|
|
||||||
)
|
|
||||||
return SearchFilter(avoidMotorway, avoidTollway)
|
return SearchFilter(avoidMotorway, avoidTollway)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class RouteCalculator(var routeModel: RouteModel) {
|
|||||||
return nowUtcMillis + timeToDestinationMillis
|
return nowUtcMillis + timeToDestinationMillis
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
fun updateSpeedLimit(location: Location, viewModel: NavigationViewModel) {
|
||||||
if (routeModel.isNavigating()) {
|
if (routeModel.isNavigating()) {
|
||||||
// speed limit
|
// speed limit
|
||||||
val distance = lastSpeedLocation.distanceTo(location)
|
val distance = lastSpeedLocation.distanceTo(location)
|
||||||
|
|||||||
@@ -4,15 +4,19 @@ import android.content.Context
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.car.app.navigation.model.Maneuver
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
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.Place
|
||||||
import com.kouros.navigation.data.Route
|
import com.kouros.navigation.data.Route
|
||||||
import com.kouros.navigation.data.StepData
|
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.Lane
|
||||||
import com.kouros.navigation.data.route.Leg
|
import com.kouros.navigation.data.route.Leg
|
||||||
import com.kouros.navigation.data.route.Routes
|
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 com.kouros.navigation.utils.location
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
open class RouteModel {
|
open class RouteModel {
|
||||||
@@ -20,7 +24,7 @@ open class RouteModel {
|
|||||||
// Immutable Data Class
|
// Immutable Data Class
|
||||||
data class NavigationState(
|
data class NavigationState(
|
||||||
val route: Route = Route.Builder().buildEmpty(),
|
val route: Route = Route.Builder().buildEmpty(),
|
||||||
val iconMapper : IconMapper = IconMapper(),
|
val iconMapper: IconMapper = IconMapper(),
|
||||||
val navigating: Boolean = false,
|
val navigating: Boolean = false,
|
||||||
val arrived: Boolean = false,
|
val arrived: Boolean = false,
|
||||||
val travelMessage: String = "",
|
val travelMessage: String = "",
|
||||||
@@ -37,7 +41,7 @@ open class RouteModel {
|
|||||||
val route: Route
|
val route: Route
|
||||||
get() = navState.route
|
get() = navState.route
|
||||||
|
|
||||||
val routeCalculator : RouteCalculator = RouteCalculator(this)
|
val routeCalculator: RouteCalculator = RouteCalculator(this)
|
||||||
|
|
||||||
val curRoute: Routes
|
val curRoute: Routes
|
||||||
get() = navState.route.routes[navState.currentRouteIndex]
|
get() = navState.route.routes[navState.currentRouteIndex]
|
||||||
@@ -46,15 +50,17 @@ open class RouteModel {
|
|||||||
get() = navState.route.routes[navState.currentRouteIndex].legs.first()
|
get() = navState.route.routes[navState.currentRouteIndex].legs.first()
|
||||||
|
|
||||||
fun startNavigation(routeString: String, context: Context) {
|
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(
|
navState = navState.copy(
|
||||||
route = Route.Builder()
|
route = Route.Builder()
|
||||||
.routeEngine(routeEngine)
|
.routeEngine(routingEngine)
|
||||||
.route(routeString)
|
.route(routeString)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
if (hasLegs()) {
|
if (hasLegs()) {
|
||||||
navState = navState.copy(navigating = true)
|
navState = navState.copy(navigating = true)
|
||||||
|
//NavigationUtils.setStringKeyValue(context, routeString, LAST_ROUTE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,22 +68,80 @@ open class RouteModel {
|
|||||||
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(
|
navState = navState.copy(
|
||||||
route = Route.Builder().buildEmpty(),
|
route = Route.Builder().buildEmpty(),
|
||||||
navigating = false,
|
navigating = false,
|
||||||
arrived = false,
|
arrived = false,
|
||||||
maneuverType = Maneuver.TYPE_UNKNOWN
|
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)
|
navState = navState.copy(currentLocation = curLocation)
|
||||||
routeCalculator.findStep(curLocation)
|
routeCalculator.findStep(curLocation)
|
||||||
|
val repository = getSettingsRepository(context)
|
||||||
|
val carLocation = runBlocking { repository.carLocationFlow.first() }
|
||||||
|
if (carLocation) {
|
||||||
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||||
|
}
|
||||||
navState = navState.copy(lastLocation = navState.currentLocation)
|
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> {
|
private fun currentLanes(): List<Lane> {
|
||||||
var lanes = emptyList<Lane>()
|
var lanes = emptyList<Lane>()
|
||||||
if (navState.route.legs().isNotEmpty()) {
|
if (navState.route.legs().isNotEmpty()) {
|
||||||
@@ -87,7 +151,7 @@ open class RouteModel {
|
|||||||
navState.lastLocation.distanceTo(location(it.location[0], it.location[1]))
|
navState.lastLocation.distanceTo(location(it.location[0], it.location[1]))
|
||||||
val sectionBearing =
|
val sectionBearing =
|
||||||
navState.lastLocation.bearingTo(location(it.location[0], it.location[1]))
|
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
|
lanes = it.lane
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,61 +160,6 @@ open class RouteModel {
|
|||||||
return lanes
|
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 {
|
fun isNavigating(): Boolean {
|
||||||
return navState.navigating
|
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.content.Context
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
import androidx.car.app.CarContext
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
import com.kouros.navigation.data.Constants.ROUTING_ENGINE
|
||||||
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
||||||
import com.kouros.navigation.data.RouteEngine
|
import com.kouros.navigation.data.RouteEngine
|
||||||
|
import com.kouros.navigation.data.datastore.DataStoreManager
|
||||||
import com.kouros.navigation.data.osrm.OsrmRepository
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||||
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.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.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@@ -26,58 +32,13 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
object NavigationUtils {
|
object NavigationUtils {
|
||||||
|
|
||||||
fun getViewModel(context: Context): ViewModel {
|
fun getViewModel(context: Context): NavigationViewModel {
|
||||||
val routeEngine = getIntKeyValue(context = context, ROUTING_ENGINE)
|
val repository = getSettingsRepository(context)
|
||||||
|
val routeEngine = runBlocking { repository.routingEngineFlow.first() }
|
||||||
return when (routeEngine) {
|
return when (routeEngine) {
|
||||||
RouteEngine.VALHALLA.ordinal -> ViewModel(ValhallaRepository())
|
RouteEngine.VALHALLA.ordinal -> NavigationViewModel(ValhallaRepository())
|
||||||
RouteEngine.OSRM.ordinal -> ViewModel(OsrmRepository())
|
RouteEngine.OSRM.ordinal -> NavigationViewModel(OsrmRepository())
|
||||||
else -> ViewModel(TomTomRepository())
|
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,8 +51,8 @@ fun calculateZoom(speed: Double?): Double {
|
|||||||
val zoom = when (speedKmh) {
|
val zoom = when (speedKmh) {
|
||||||
in 0..10 -> 18.0
|
in 0..10 -> 18.0
|
||||||
in 11..30 -> 17.5
|
in 11..30 -> 17.5
|
||||||
in 31..50 -> 17.0
|
in 31..65 -> 17.0
|
||||||
in 61..70 -> 16.5
|
in 66..70 -> 16.5
|
||||||
else -> 16.0
|
else -> 16.0
|
||||||
}
|
}
|
||||||
return zoom
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BIN
common/data/src/main/res/drawable/ic_turn_merge_symmetrical.png
Normal file
BIN
common/data/src/main/res/drawable/ic_turn_merge_symmetrical.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 832 B |
BIN
common/data/src/main/res/drawable/ic_turn_sharp_left.png
Normal file
BIN
common/data/src/main/res/drawable/ic_turn_sharp_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
BIN
common/data/src/main/res/drawable/ic_turn_sharp_right.png
Normal file
BIN
common/data/src/main/res/drawable/ic_turn_sharp_right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
BIN
common/data/src/main/res/drawable/ic_turn_u_turn_left.png
Normal file
BIN
common/data/src/main/res/drawable/ic_turn_u_turn_left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 829 B |
BIN
common/data/src/main/res/drawable/ic_turn_u_turn_right.png
Normal file
BIN
common/data/src/main/res/drawable/ic_turn_u_turn_right.png
Normal file
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
6668
common/data/src/main/res/raw/tomtom_traffic
Normal file
6668
common/data/src/main/res/raw/tomtom_traffic
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,33 +13,34 @@ junitVersion = "1.3.0"
|
|||||||
espressoCore = "3.7.0"
|
espressoCore = "3.7.0"
|
||||||
kotlinxSerializationJson = "1.10.0"
|
kotlinxSerializationJson = "1.10.0"
|
||||||
lifecycleRuntimeKtx = "2.10.0"
|
lifecycleRuntimeKtx = "2.10.0"
|
||||||
composeBom = "2026.01.01"
|
composeBom = "2026.02.00"
|
||||||
appcompat = "1.7.1"
|
appcompat = "1.7.1"
|
||||||
material = "1.13.0"
|
material = "1.13.0"
|
||||||
carApp = "1.7.0"
|
carApp = "1.7.0"
|
||||||
androidx-car = "1.7.0"
|
androidx-car = "1.7.0"
|
||||||
objectboxKotlin = "5.1.0"
|
objectboxKotlin = "5.2.0"
|
||||||
objectboxProcessor = "5.0.1"
|
objectboxProcessor = "5.2.0"
|
||||||
ui = "1.10.0"
|
ui = "1.10.0"
|
||||||
material3 = "1.4.0"
|
material3 = "1.4.0"
|
||||||
runtimeLivedata = "1.10.2"
|
runtimeLivedata = "1.10.3"
|
||||||
foundation = "1.10.0"
|
foundation = "1.10.3"
|
||||||
maplibre-composeMaterial3 = "0.12.2"
|
maplibre-composeMaterial3 = "0.12.2"
|
||||||
maplibre-compose = "0.12.1"
|
maplibre-compose = "0.12.1"
|
||||||
playServicesLocation = "21.3.0"
|
playServicesLocation = "21.3.0"
|
||||||
runtime = "1.10.2"
|
runtime = "1.10.3"
|
||||||
accompanist = "0.37.3"
|
accompanist = "0.37.3"
|
||||||
uiVersion = "1.10.2"
|
uiVersion = "1.10.3"
|
||||||
uiText = "1.10.2"
|
uiText = "1.10.3"
|
||||||
navigationCompose = "2.9.7"
|
navigationCompose = "2.9.7"
|
||||||
uiToolingPreview = "1.10.2"
|
uiToolingPreview = "1.10.3"
|
||||||
uiTooling = "1.10.2"
|
uiTooling = "1.10.3"
|
||||||
material3WindowSizeClass = "1.4.0"
|
material3WindowSizeClass = "1.4.0"
|
||||||
uiGraphics = "1.10.2"
|
uiGraphics = "1.10.3"
|
||||||
window = "1.5.1"
|
window = "1.5.1"
|
||||||
foundationLayout = "1.10.2"
|
foundationLayout = "1.10.3"
|
||||||
foundationLayoutVersion = "1.10.2"
|
foundationLayoutVersion = "1.10.3"
|
||||||
|
datastorePreferences = "1.2.0"
|
||||||
|
datastoreCore = "1.2.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
android-sdk-turf = { module = "org.maplibre.gl:android-sdk-turf", version.ref = "androidSdkTurf" }
|
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-window = { group = "androidx.window", name = "window", version.ref = "window" }
|
||||||
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" }
|
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-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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user