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,13 +296,15 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (!routeModel.isNavigating()) {
|
if (!routeModel.isNavigating()) {
|
||||||
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||||
} else {
|
} else {
|
||||||
NavigationSheet(
|
if (step != null && nextStep != null) {
|
||||||
applicationContext,
|
NavigationSheet(
|
||||||
routeModel,
|
applicationContext,
|
||||||
step!!,
|
routeModel,
|
||||||
nextStep!!,
|
step,
|
||||||
{ stopNavigation { closeSheet() } },
|
nextStep,
|
||||||
{ simulateNavigation() })
|
{ stopNavigation { closeSheet() } },
|
||||||
|
{ 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(
|
||||||
|
defaultElevation = 6.dp
|
||||||
|
|
||||||
|
), modifier = Modifier
|
||||||
|
.padding(top = 60.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(step.icon),
|
||||||
|
contentDescription = stringResource(id = R.string.accept_action_title),
|
||||||
|
modifier = Modifier.size(48.dp, 48.dp),
|
||||||
|
)
|
||||||
|
if (step.currentManeuverType == 46
|
||||||
|
|| step.currentManeuverType == 45
|
||||||
|
) {
|
||||||
|
Text(text = "Exit ${step.exitNumber}", fontSize = 18.sp)
|
||||||
|
}
|
||||||
Row {
|
Row {
|
||||||
Icon(
|
if (step.leftStepDistance < 1000) {
|
||||||
painter = painterResource(step.icon),
|
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
|
||||||
contentDescription = stringResource(id = R.string.accept_action_title),
|
} else {
|
||||||
modifier = Modifier.size(48.dp, 48.dp),
|
Text(
|
||||||
)
|
text = "${(step.leftStepDistance / 1000).round(1)} km",
|
||||||
if (step.currentManeuverType == 46
|
fontSize = 24.sp,
|
||||||
|| step.currentManeuverType == 45) {
|
color = MaterialTheme.colorScheme.primary
|
||||||
Text(text ="Exit ${step.exitNumber}", fontSize = 20.sp)
|
|
||||||
}
|
|
||||||
Column {
|
|
||||||
if (step.leftStepDistance < 1000) {
|
|
||||||
Text(text = "${step.leftStepDistance.toInt()} m", fontSize = 25.sp)
|
|
||||||
} else {
|
|
||||||
Text(
|
|
||||||
text = "${(step.leftStepDistance / 1000).round(1)} km",
|
|
||||||
fontSize = 25.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(text = step.instruction, fontSize = 20.sp)
|
|
||||||
}
|
|
||||||
if (nextStep != null && step.icon != nextStep.icon) {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(nextStep.icon),
|
|
||||||
contentDescription = stringResource(id = R.string.accept_action_title),
|
|
||||||
modifier = Modifier.size(48.dp, 48.dp),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier.padding(5.dp)
|
||||||
|
)
|
||||||
|
Text(text = step.instruction, fontSize = 24.sp, color = MaterialTheme.colorScheme.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fun NavigationSheet(
|
|||||||
val distance = (step.leftDistance / 1000).round(1)
|
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 = {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(R.drawable.ic_place_white_24dp),
|
|
||||||
contentDescription = stringResource(id = R.string.display_settings),
|
|
||||||
modifier = Modifier.size(48.dp, 48.dp),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
Button(onClick = { navController.navigate("nav_settings") }) {
|
||||||
SettingsMenuLink(
|
Text(stringResource(R.string.navigation_settings))
|
||||||
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() } }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-15
@@ -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)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+11
-22
@@ -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(
|
||||||
radioList,
|
SectionedItemList.create(
|
||||||
carContext.getString(R.string.dark_mode)
|
radioList,
|
||||||
))
|
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,28 +66,17 @@ class IconMapper() {
|
|||||||
|
|
||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
}
|
}
|
||||||
}
|
Maneuver.TYPE_U_TURN_LEFT -> {
|
||||||
return currentTurnIcon
|
currentTurnIcon = R.drawable.ic_turn_u_turn_left
|
||||||
}
|
}
|
||||||
|
Maneuver.TYPE_U_TURN_RIGHT -> {
|
||||||
|
currentTurnIcon = R.drawable.ic_turn_u_turn_right
|
||||||
fun addLanes(stepData: StepData) : Int {
|
}
|
||||||
stepData.lane.forEach {
|
Maneuver.TYPE_MERGE_LEFT -> {
|
||||||
if (it.indications.isNotEmpty() && it.valid) {
|
currentTurnIcon = R.drawable.ic_turn_merge_symmetrical
|
||||||
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
|
return currentTurnIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLanes(direction: String, stepData: StepData): Int {
|
fun addLanes(direction: String, stepData: StepData): Int {
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-13
@@ -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,38 +50,98 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasLegs(): Boolean {
|
private fun hasLegs(): Boolean {
|
||||||
return navState.route.routes.isNotEmpty() && navState.route.routes[0].legs.isNotEmpty()
|
return navState.route.routes.isNotEmpty() && navState.route.routes[0].legs.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopNavigation() {
|
fun stopNavigation(context: Context) {
|
||||||
navState = navState.copy(
|
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)
|
||||||
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
val repository = getSettingsRepository(context)
|
||||||
|
val carLocation = runBlocking { repository.carLocationFlow.first() }
|
||||||
|
if (carLocation) {
|
||||||
|
routeCalculator.updateSpeedLimit(curLocation, viewModel)
|
||||||
|
}
|
||||||
navState = navState.copy(lastLocation = navState.currentLocation)
|
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,60 +32,15 @@ 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateZoom(speed: Double?): Double {
|
fun calculateZoom(speed: Double?): Double {
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 832 B |
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
Binary file not shown.
|
After Width: | Height: | Size: 593 B |
Binary file not shown.
|
After Width: | Height: | Size: 829 B |
Binary file not shown.
|
After Width: | Height: | Size: 835 B |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+17
-14
@@ -13,33 +13,34 @@ junitVersion = "1.3.0"
|
|||||||
espressoCore = "3.7.0"
|
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