Speed limit
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 = 11
|
versionCode = 13
|
||||||
versionName = "0.1.3.11"
|
versionName = "0.1.3.13"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ package com.kouros.navigation
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.di.appModule
|
import com.kouros.navigation.di.appModule
|
||||||
|
import com.kouros.navigation.model.ViewModel
|
||||||
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
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
@@ -26,5 +30,7 @@ class MainApplication : Application() {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
var useContacts = false
|
var useContacts = false
|
||||||
|
|
||||||
|
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.kouros.navigation.di
|
package com.kouros.navigation.di
|
||||||
|
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.osrm.OsrmRepository
|
||||||
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.core.module.dsl.viewModelOf
|
import org.koin.core.module.dsl.viewModelOf
|
||||||
@@ -8,5 +10,5 @@ import org.koin.dsl.module
|
|||||||
|
|
||||||
val appModule = module {
|
val appModule = module {
|
||||||
viewModelOf(::ViewModel)
|
viewModelOf(::ViewModel)
|
||||||
singleOf(::NavigationRepository)
|
singleOf(::ValhallaRepository)
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ class MockLocation (private var locationManager: LocationManager) {
|
|||||||
this.longitude = longitude
|
this.longitude = longitude
|
||||||
this.altitude = 0.0
|
this.altitude = 0.0
|
||||||
this.accuracy = 1.0f
|
this.accuracy = 1.0f
|
||||||
this.speed = 0f
|
this.speed = 10f
|
||||||
this.time = System.currentTimeMillis()
|
this.time = System.currentTimeMillis()
|
||||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class MockLocation (private var locationManager: LocationManager) {
|
|||||||
this.longitude = longitude
|
this.longitude = longitude
|
||||||
this.altitude = 0.0
|
this.altitude = 0.0
|
||||||
this.accuracy = 1.0f
|
this.accuracy = 1.0f
|
||||||
this.speed = 0f
|
this.speed = 10f
|
||||||
this.time = System.currentTimeMillis()
|
this.time = System.currentTimeMillis()
|
||||||
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
this.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
|
||||||
|
|
||||||
|
|||||||
@@ -15,18 +15,13 @@ import androidx.annotation.RequiresPermission
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material3.BottomSheetScaffold
|
import androidx.compose.material3.BottomSheetScaffold
|
||||||
import androidx.compose.material3.BottomSheetScaffoldState
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
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
|
||||||
@@ -37,19 +32,18 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
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.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
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.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.homeLocation
|
import com.kouros.navigation.data.Constants.homeLocation
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
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.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
@@ -73,7 +67,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
var tilt = 50.0
|
var tilt = 50.0
|
||||||
val useMock = true
|
val useMock = true
|
||||||
@@ -109,8 +103,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
private var overpass = false
|
private var overpass = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.route.observe(this, observer)
|
navigationViewModel.route.observe(this, observer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||||
@@ -221,7 +214,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
closeSheet: () -> Unit
|
closeSheet: () -> Unit
|
||||||
) {
|
) {
|
||||||
if (!routeModel.isNavigating()) {
|
if (!routeModel.isNavigating()) {
|
||||||
SearchSheet(applicationContext, viewModel, lastLocation) { closeSheet() }
|
SearchSheet(applicationContext, navigationViewModel, lastLocation) { closeSheet() }
|
||||||
} else {
|
} else {
|
||||||
NavigationSheet(
|
NavigationSheet(
|
||||||
routeModel, step!!, nextStep!!,
|
routeModel, step!!, nextStep!!,
|
||||||
@@ -241,7 +234,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val currentLocation = location(location.position.longitude, location.position.latitude)
|
val currentLocation = location(location.position.longitude, location.position.latitude)
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
if (isNavigating()) {
|
if (isNavigating()) {
|
||||||
updateLocation(currentLocation)
|
updateLocation(currentLocation, navigationViewModel)
|
||||||
stepData.value = currentStep()
|
stepData.value = currentStep()
|
||||||
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
|
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
|
||||||
nextStepData.value = nextStep()
|
nextStepData.value = nextStep()
|
||||||
@@ -266,7 +259,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
lastLocation = currentLocation
|
lastLocation = currentLocation
|
||||||
if (!loadRecentPlaces) {
|
if (!loadRecentPlaces) {
|
||||||
viewModel.loadRecentPlaces(applicationContext, lastLocation)
|
navigationViewModel.loadRecentPlaces(applicationContext, lastLocation)
|
||||||
loadRecentPlaces = true
|
loadRecentPlaces = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,7 +313,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
fun test() {
|
fun test() {
|
||||||
for ((index, loc) in routeModel.route.waypoints.withIndex()) {
|
for ((index, loc) in routeModel.route.waypoints.withIndex()) {
|
||||||
if (index > 300) {
|
if (index > 300) {
|
||||||
routeModel.updateLocation(location(loc[0], loc[1]))
|
routeModel.updateLocation(location(loc[0], loc[1]), navigationViewModel)
|
||||||
routeModel.currentStep()
|
routeModel.currentStep()
|
||||||
if (routeModel.route.currentManeuverIndex + 1 <= routeModel.route.maneuvers.size) {
|
if (routeModel.route.currentManeuverIndex + 1 <= routeModel.route.maneuvers.size) {
|
||||||
nextStepData.value = routeModel.nextStep()
|
nextStepData.value = routeModel.nextStep()
|
||||||
|
|||||||
@@ -1,62 +1,28 @@
|
|||||||
package com.kouros.navigation.ui
|
package com.kouros.navigation.ui
|
||||||
|
|
||||||
import android.R.attr.x
|
|
||||||
import android.R.attr.y
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
|
||||||
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.foundation.layout.absoluteOffset
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
|
||||||
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.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.unit.DpOffset
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
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.BuildingLayer
|
|
||||||
import com.kouros.navigation.car.map.DarkMode
|
import com.kouros.navigation.car.map.DarkMode
|
||||||
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.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
|
|
||||||
import com.kouros.navigation.data.RouteColor
|
|
||||||
import com.kouros.navigation.data.StepData
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.camera.CameraState
|
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
import org.maplibre.compose.expressions.dsl.const
|
|
||||||
import org.maplibre.compose.expressions.dsl.exponential
|
|
||||||
import org.maplibre.compose.expressions.dsl.interpolate
|
|
||||||
import org.maplibre.compose.expressions.dsl.zoom
|
|
||||||
import org.maplibre.compose.layers.CircleLayer
|
|
||||||
import org.maplibre.compose.layers.LineLayer
|
|
||||||
import org.maplibre.compose.location.LocationTrackingEffect
|
import org.maplibre.compose.location.LocationTrackingEffect
|
||||||
import org.maplibre.compose.location.UserLocationState
|
import org.maplibre.compose.location.UserLocationState
|
||||||
import org.maplibre.compose.map.MapOptions
|
|
||||||
import org.maplibre.compose.map.MaplibreMap
|
|
||||||
import org.maplibre.compose.map.OrnamentOptions
|
|
||||||
import org.maplibre.compose.sources.GeoJsonData
|
|
||||||
import org.maplibre.compose.sources.GeoJsonSource
|
|
||||||
import org.maplibre.compose.sources.getBaseSource
|
|
||||||
import org.maplibre.compose.sources.rememberGeoJsonSource
|
|
||||||
import org.maplibre.compose.style.BaseStyle
|
import org.maplibre.compose.style.BaseStyle
|
||||||
import org.maplibre.spatialk.geojson.Position
|
import org.maplibre.spatialk.geojson.Position
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ import com.kouros.navigation.car.screen.SearchScreen
|
|||||||
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
import com.kouros.navigation.data.Constants.MAXIMAL_ROUTE_DEVIATION
|
||||||
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
import com.kouros.navigation.data.Constants.MAXIMAL_SNAP_CORRECTION
|
||||||
import com.kouros.navigation.data.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.valhalla.ValhallaRepository
|
||||||
|
import com.kouros.navigation.model.ViewModel
|
||||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||||
|
|
||||||
class NavigationSession : Session(), NavigationScreen.Listener {
|
class NavigationSession : Session(), NavigationScreen.Listener {
|
||||||
@@ -67,6 +69,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val navigationViewModel = ViewModel(ValhallaRepository())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val lifecycle: Lifecycle = lifecycle
|
val lifecycle: Lifecycle = lifecycle
|
||||||
@@ -78,7 +81,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
|
|
||||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
||||||
|
|
||||||
navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel, this)
|
navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel, this, navigationViewModel)
|
||||||
|
|
||||||
if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
== PackageManager.PERMISSION_GRANTED && !useContacts
|
== PackageManager.PERMISSION_GRANTED && !useContacts
|
||||||
@@ -116,6 +119,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
location,
|
location,
|
||||||
|
navigationViewModel,
|
||||||
// TODO: Uri
|
// TODO: Uri
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||||
import com.kouros.navigation.car.map.DarkMode
|
import com.kouros.navigation.car.map.DarkMode
|
||||||
import com.kouros.navigation.car.map.DrawImage
|
import com.kouros.navigation.car.map.DrawNavigationImages
|
||||||
import com.kouros.navigation.car.map.MapLibre
|
import com.kouros.navigation.car.map.MapLibre
|
||||||
import com.kouros.navigation.car.map.cameraState
|
import com.kouros.navigation.car.map.cameraState
|
||||||
import com.kouros.navigation.car.map.getPaddingValues
|
import com.kouros.navigation.car.map.getPaddingValues
|
||||||
@@ -191,7 +191,7 @@ class SurfaceRenderer(
|
|||||||
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
duration(viewStyle == ViewStyle.PREVIEW, position!!.bearing, lastBearing)
|
||||||
val currentSpeed: Float? by speed.observeAsState()
|
val currentSpeed: Float? by speed.observeAsState()
|
||||||
if (viewStyle == ViewStyle.VIEW) {
|
if (viewStyle == ViewStyle.VIEW) {
|
||||||
DrawImage(paddingValues, currentSpeed, width, height)
|
DrawNavigationImages(paddingValues, currentSpeed, routeModel.routeState.maxSpeed, width, height)
|
||||||
}
|
}
|
||||||
LaunchedEffect(position, viewStyle) {
|
LaunchedEffect(position, viewStyle) {
|
||||||
cameraState.animateTo(
|
cameraState.animateTo(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.drawscope.scale
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.drawText
|
import androidx.compose.ui.text.drawText
|
||||||
|
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
|
||||||
@@ -33,13 +34,10 @@ import com.kouros.navigation.data.RouteColor
|
|||||||
import com.kouros.navigation.data.SpeedColor
|
import com.kouros.navigation.data.SpeedColor
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
import com.kouros.navigation.utils.NavigationUtils.getIntKeyValue
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
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
|
||||||
import org.maplibre.compose.expressions.dsl.const
|
import org.maplibre.compose.expressions.dsl.const
|
||||||
import org.maplibre.compose.expressions.dsl.contains
|
|
||||||
import org.maplibre.compose.expressions.dsl.exponential
|
import org.maplibre.compose.expressions.dsl.exponential
|
||||||
import org.maplibre.compose.expressions.dsl.image
|
import org.maplibre.compose.expressions.dsl.image
|
||||||
import org.maplibre.compose.expressions.dsl.interpolate
|
import org.maplibre.compose.expressions.dsl.interpolate
|
||||||
@@ -60,15 +58,7 @@ import org.maplibre.compose.sources.Source
|
|||||||
import org.maplibre.compose.sources.getBaseSource
|
import org.maplibre.compose.sources.getBaseSource
|
||||||
import org.maplibre.compose.sources.rememberGeoJsonSource
|
import org.maplibre.compose.sources.rememberGeoJsonSource
|
||||||
import org.maplibre.compose.style.BaseStyle
|
import org.maplibre.compose.style.BaseStyle
|
||||||
import org.maplibre.geojson.FeatureCollection
|
|
||||||
import org.maplibre.spatialk.geojson.BoundingBox.Companion.serializer
|
|
||||||
import org.maplibre.spatialk.geojson.Feature
|
|
||||||
import org.maplibre.spatialk.geojson.GeoJson
|
|
||||||
import org.maplibre.spatialk.geojson.Geometry
|
|
||||||
import org.maplibre.spatialk.geojson.Position
|
import org.maplibre.spatialk.geojson.Position
|
||||||
import org.maplibre.spatialk.geojson.dsl.FeatureBuilder
|
|
||||||
import org.maplibre.spatialk.geojson.dsl.FeatureCollectionBuilder
|
|
||||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -187,9 +177,12 @@ fun BuildingLayer(tiles: Source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrawImage(padding: PaddingValues, speed: Float?, width: Int, height: Int) {
|
fun DrawNavigationImages(padding: PaddingValues, speed: Float?, maxSpeed: Int, width: Int, height: Int) {
|
||||||
NavigationImage(padding, width, height)
|
NavigationImage(padding, width, height)
|
||||||
Speed(width, height, speed)
|
CurrentSpeed(width, height, speed)
|
||||||
|
if (speed != null && maxSpeed > 0 && (speed * 3.6) > maxSpeed) {
|
||||||
|
MaxSpeed(width, height, maxSpeed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -215,7 +208,7 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Speed(
|
private fun CurrentSpeed(
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
speed: Float?
|
speed: Float?
|
||||||
@@ -235,6 +228,7 @@ private fun Speed(
|
|||||||
val kmh = "km/h"
|
val kmh = "km/h"
|
||||||
val styleSpeed = TextStyle(
|
val styleSpeed = TextStyle(
|
||||||
fontSize = 22.sp,
|
fontSize = 22.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
)
|
)
|
||||||
val styleKm = TextStyle(
|
val styleKm = TextStyle(
|
||||||
@@ -278,6 +272,61 @@ private fun Speed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MaxSpeed(
|
||||||
|
width: Int,
|
||||||
|
height: Int,
|
||||||
|
maxSpeed: Int,
|
||||||
|
) {
|
||||||
|
val radius = 20
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
start = width.dp - 350.dp,
|
||||||
|
top = height.dp - 80.dp
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
val textMeasurerSpeed = rememberTextMeasurer()
|
||||||
|
val speed = maxSpeed.toString()
|
||||||
|
val styleSpeed = TextStyle(
|
||||||
|
fontSize = 26.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = Color.Black,
|
||||||
|
)
|
||||||
|
val textLayoutSpeed = remember(speed) {
|
||||||
|
textMeasurerSpeed.measure(speed, styleSpeed)
|
||||||
|
}
|
||||||
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
|
drawCircle(
|
||||||
|
center = Offset(
|
||||||
|
x = center.x,
|
||||||
|
y = center.y
|
||||||
|
),
|
||||||
|
radius = radius * 1.3.toFloat(),
|
||||||
|
color = Color.Red,
|
||||||
|
)
|
||||||
|
drawCircle(
|
||||||
|
center = Offset(
|
||||||
|
x = center.x,
|
||||||
|
y = center.y
|
||||||
|
),
|
||||||
|
radius = radius.toFloat(),
|
||||||
|
color = Color.White,
|
||||||
|
)
|
||||||
|
drawText(
|
||||||
|
textMeasurer = textMeasurerSpeed,
|
||||||
|
text = speed,
|
||||||
|
style = styleSpeed,
|
||||||
|
topLeft = Offset(
|
||||||
|
x = center.x - textLayoutSpeed.size.width / 2,
|
||||||
|
y = center.y - textLayoutSpeed.size.height / 2,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DarkMode(context: Context, baseStyle: MutableState<BaseStyle.Uri>) {
|
fun DarkMode(context: Context, baseStyle: MutableState<BaseStyle.Uri>) {
|
||||||
val darkMode = getIntKeyValue(context, Constants.DARK_MODE_SETTINGS)
|
val darkMode = getIntKeyValue(context, Constants.DARK_MODE_SETTINGS)
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class RouteCarModel() : RouteModel() {
|
|||||||
return step
|
return step
|
||||||
}
|
}
|
||||||
|
|
||||||
fun travelEstimate(): TravelEstimate {
|
fun travelEstimate(carContext: CarContext): TravelEstimate {
|
||||||
val timeLeft = travelLeftTime()
|
val timeLeft = travelLeftTime()
|
||||||
val timeToDestinationMillis =
|
val timeToDestinationMillis =
|
||||||
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
TimeUnit.SECONDS.toMillis(timeLeft.toLong())
|
||||||
@@ -88,7 +88,7 @@ class RouteCarModel() : RouteModel() {
|
|||||||
arrivalTime(),
|
arrivalTime(),
|
||||||
TimeZone.getTimeZone("Europe/Berlin")
|
TimeZone.getTimeZone("Europe/Berlin")
|
||||||
)
|
)
|
||||||
return TravelEstimate.Builder( // The estimated distance to the destination.
|
val travelBuilder = TravelEstimate.Builder( // The estimated distance to the destination.
|
||||||
Distance.create(
|
Distance.create(
|
||||||
leftDistance,
|
leftDistance,
|
||||||
displayUnit
|
displayUnit
|
||||||
@@ -102,9 +102,12 @@ class RouteCarModel() : RouteModel() {
|
|||||||
)
|
)
|
||||||
.setRemainingTimeColor(CarColor.YELLOW)
|
.setRemainingTimeColor(CarColor.YELLOW)
|
||||||
.setRemainingDistanceColor(CarColor.RED)
|
.setRemainingDistanceColor(CarColor.RED)
|
||||||
//.setTripText(createCarText(carContext,R.string.navigate))
|
|
||||||
//.setTripIcon(createCarIcon(carContext, R.drawable.navigation_48px))
|
if (routeState.travelMessage.isNotEmpty()) {
|
||||||
.build()
|
travelBuilder.setTripIcon(createCarIcon(carContext, R.drawable.warning_24px))
|
||||||
|
travelBuilder.setTripText(CarText.create(routeState.travelMessage))
|
||||||
|
}
|
||||||
|
return travelBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createString(
|
fun createString(
|
||||||
@@ -123,12 +126,12 @@ class RouteCarModel() : RouteModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) {
|
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) {
|
||||||
carContext.getCarService<AppManager?>(AppManager::class.java)
|
carContext.getCarService<AppManager?>(AppManager::class.java)
|
||||||
.showAlert(createAlert(carContext, distance, maxSpeed))
|
.showAlert(createAlert(carContext, distance, maxSpeed))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAlert(carContext: CarContext, distance: Double, maxSpeed: String?): Alert {
|
fun createAlert(carContext: CarContext, distance: Double, maxSpeed: String?): Alert {
|
||||||
val title = createCarText(carContext,R.string.speed_camera)
|
val title = createCarText(carContext, R.string.speed_camera)
|
||||||
val subtitle = CarText.create(maxSpeed!!)
|
val subtitle = CarText.create(maxSpeed!!)
|
||||||
val icon = CarIcon.ALERT
|
val icon = CarIcon.ALERT
|
||||||
|
|
||||||
@@ -144,6 +147,7 @@ class RouteCarModel() : RouteModel() {
|
|||||||
.addAction(dismissAction).setCallback(object : AlertCallback {
|
.addAction(dismissAction).setCallback(object : AlertCallback {
|
||||||
override fun onCancel(reason: Int) {
|
override fun onCancel(reason: Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDismiss() {
|
override fun onDismiss() {
|
||||||
}
|
}
|
||||||
}).build()
|
}).build()
|
||||||
@@ -155,8 +159,8 @@ class RouteCarModel() : RouteModel() {
|
|||||||
flags: Int
|
flags: Int
|
||||||
): Action {
|
): Action {
|
||||||
return Action.Builder()
|
return Action.Builder()
|
||||||
.setOnClickListener { }
|
.setOnClickListener { }
|
||||||
.setTitle(createCarText(carContext,titleRes))
|
.setTitle(createCarText(carContext, titleRes))
|
||||||
.setFlags(flags)
|
.setFlags(flags)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ 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
|
||||||
|
|
||||||
class CategoriesScreen(
|
class CategoriesScreen(
|
||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
private val location: Location,
|
private val location: Location,
|
||||||
|
private val viewModel: ViewModel
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var categories: List<Category> = listOf(
|
var categories: List<Category> = listOf(
|
||||||
@@ -46,7 +48,8 @@ class CategoriesScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
location,
|
location,
|
||||||
it.id
|
it.id,
|
||||||
|
viewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ class CategoryScreen(
|
|||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
location: Location,
|
location: Location,
|
||||||
private val category: String,
|
private val category: String,
|
||||||
|
private val viewModel: ViewModel
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
|
||||||
var elements = listOf<Elements>()
|
var elements = listOf<Elements>()
|
||||||
|
|
||||||
val observer = Observer<List<Elements>> { newElements ->
|
val observer = Observer<List<Elements>> { newElements ->
|
||||||
@@ -113,7 +113,6 @@ class CategoryScreen(
|
|||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
val location = location(it.lon!!, it.lat!!)
|
val location = location(it.lon!!, it.lat!!)
|
||||||
surfaceRenderer.setCategoryLocation(location, category)
|
surfaceRenderer.setCategoryLocation(location, category)
|
||||||
println(it)
|
|
||||||
}
|
}
|
||||||
.setTitle(name)
|
.setTitle(name)
|
||||||
.setImage(carIcon(carContext, category))
|
.setImage(carIcon(carContext, category))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.car.app.model.Action.FLAG_DEFAULT
|
|||||||
import androidx.car.app.model.ActionStrip
|
import androidx.car.app.model.ActionStrip
|
||||||
import androidx.car.app.model.CarColor
|
import androidx.car.app.model.CarColor
|
||||||
import androidx.car.app.model.CarIcon
|
import androidx.car.app.model.CarIcon
|
||||||
|
import androidx.car.app.model.CarText
|
||||||
import androidx.car.app.model.Distance
|
import androidx.car.app.model.Distance
|
||||||
import androidx.car.app.model.Header
|
import androidx.car.app.model.Header
|
||||||
import androidx.car.app.model.MessageTemplate
|
import androidx.car.app.model.MessageTemplate
|
||||||
@@ -39,7 +40,8 @@ class NavigationScreen(
|
|||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
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
|
||||||
) :
|
) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ class NavigationScreen(
|
|||||||
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
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
|
||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
navigationType = NavigationType.NAVIGATION
|
navigationType = NavigationType.NAVIGATION
|
||||||
@@ -88,7 +90,6 @@ class NavigationScreen(
|
|||||||
var speedCameras = listOf<Elements>()
|
var speedCameras = listOf<Elements>()
|
||||||
val speedObserver = Observer<List<Elements>> { cameras ->
|
val speedObserver = Observer<List<Elements>> { cameras ->
|
||||||
speedCameras = cameras
|
speedCameras = cameras
|
||||||
println("Speed cameras ${speedCameras.size}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -118,7 +119,7 @@ class NavigationScreen(
|
|||||||
.setNavigationInfo(
|
.setNavigationInfo(
|
||||||
getRoutingInfo()
|
getRoutingInfo()
|
||||||
)
|
)
|
||||||
.setDestinationTravelEstimate(routeModel.travelEstimate())
|
.setDestinationTravelEstimate(routeModel.travelEstimate(carContext))
|
||||||
.setActionStrip(actionStripBuilder.build())
|
.setActionStrip(actionStripBuilder.build())
|
||||||
.setMapActionStrip(mapActionStripBuilder().build())
|
.setMapActionStrip(mapActionStripBuilder().build())
|
||||||
.setBackgroundColor(CarColor.GREEN)
|
.setBackgroundColor(CarColor.GREEN)
|
||||||
@@ -301,7 +302,7 @@ class NavigationScreen(
|
|||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
val navigateTo = location(recentPlace.longitude, recentPlace.latitude)
|
||||||
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, navigateTo)
|
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, navigateTo)
|
||||||
routeModel.routeState.destination = recentPlace
|
routeModel.routeState = routeModel.routeState.copy(destination = recentPlace)
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -394,7 +395,7 @@ class NavigationScreen(
|
|||||||
private fun startSearchScreen() {
|
private fun startSearchScreen() {
|
||||||
screenManager
|
screenManager
|
||||||
.pushForResult(
|
.pushForResult(
|
||||||
SearchScreen(carContext, surfaceRenderer, surfaceRenderer.lastLocation)
|
SearchScreen(carContext, surfaceRenderer, surfaceRenderer.lastLocation, viewModel)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
val place = obj as Place
|
val place = obj as Place
|
||||||
@@ -417,7 +418,7 @@ class NavigationScreen(
|
|||||||
viewModel.saveRecent(place)
|
viewModel.saveRecent(place)
|
||||||
currentNavigationLocation = location
|
currentNavigationLocation = location
|
||||||
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
|
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||||
routeModel.routeState.destination = place
|
routeModel.routeState = routeModel.routeState.copy(destination = place)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,8 +431,8 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun calculateNewRoute(destination: Place) {
|
fun calculateNewRoute(destination: Place) {
|
||||||
navigationType = NavigationType.REROUTE
|
|
||||||
stopNavigation()
|
stopNavigation()
|
||||||
|
navigationType = NavigationType.REROUTE
|
||||||
invalidate()
|
invalidate()
|
||||||
val mainThreadHandler = Handler(carContext.mainLooper)
|
val mainThreadHandler = Handler(carContext.mainLooper)
|
||||||
mainThreadHandler.post {
|
mainThreadHandler.post {
|
||||||
@@ -451,14 +452,9 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateTrip(location: Location) {
|
fun updateTrip(location: Location) {
|
||||||
if (lastCameraSearch++ % 100 == 0) {
|
updateSpeedCamera(location)
|
||||||
viewModel.getSpeedCameras(location)
|
|
||||||
}
|
|
||||||
if (speedCameras.isNotEmpty()) {
|
|
||||||
updateDistance(location)
|
|
||||||
}
|
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
updateLocation(location)
|
updateLocation(location, viewModel)
|
||||||
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
|
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
|
||||||
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
) {
|
) {
|
||||||
@@ -472,6 +468,15 @@ class NavigationScreen(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateSpeedCamera(location: Location) {
|
||||||
|
if (lastCameraSearch++ % 100 == 0) {
|
||||||
|
viewModel.getSpeedCameras(location)
|
||||||
|
}
|
||||||
|
if (speedCameras.isNotEmpty()) {
|
||||||
|
updateDistance(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateDistance(
|
private fun updateDistance(
|
||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) {
|
||||||
@@ -485,10 +490,9 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
||||||
val camera = sortedList.first()
|
val camera = sortedList.first()
|
||||||
if (camera.distance < 100) {
|
if (camera.distance < 80) {
|
||||||
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ class PlaceListScreen(
|
|||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
private val location: Location,
|
private val location: Location,
|
||||||
private val category: String
|
private val category: String,
|
||||||
|
private val viewModel: ViewModel
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
|
||||||
var places = listOf<Place>()
|
var places = listOf<Place>()
|
||||||
|
|
||||||
val observer = Observer<List<Place>> { newPlaces ->
|
val observer = Observer<List<Place>> { newPlaces ->
|
||||||
@@ -102,7 +102,8 @@ class PlaceListScreen(
|
|||||||
RoutePreviewScreen(
|
RoutePreviewScreen(
|
||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
place
|
place,
|
||||||
|
viewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
|
|||||||
@@ -41,13 +41,12 @@ import java.math.RoundingMode
|
|||||||
class RoutePreviewScreen(
|
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
|
||||||
) :
|
) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
private var isFavorite = false
|
private var isFavorite = false
|
||||||
|
|
||||||
val vieModel = ViewModel(NavigationRepository())
|
|
||||||
|
|
||||||
val routeModel = RouteCarModel()
|
val routeModel = RouteCarModel()
|
||||||
|
|
||||||
val navigationMessage = NavigationMessage(carContext)
|
val navigationMessage = NavigationMessage(carContext)
|
||||||
@@ -60,9 +59,9 @@ class RoutePreviewScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
vieModel.previewRoute.observe(this, observer)
|
viewModel.previewRoute.observe(this, observer)
|
||||||
val location = location(destination.longitude, destination.latitude)
|
val location = location(destination.longitude, destination.latitude)
|
||||||
vieModel.loadPreviewRoute(carContext, surfaceRenderer.lastLocation, location)
|
viewModel.loadPreviewRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
@@ -149,7 +148,7 @@ class RoutePreviewScreen(
|
|||||||
CarToast.LENGTH_SHORT
|
CarToast.LENGTH_SHORT
|
||||||
)
|
)
|
||||||
.show()
|
.show()
|
||||||
vieModel.saveFavorite(destination)
|
viewModel.saveFavorite(destination)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
@@ -157,7 +156,7 @@ class RoutePreviewScreen(
|
|||||||
private fun deleteFavoriteAction(): Action = Action.Builder()
|
private fun deleteFavoriteAction(): Action = Action.Builder()
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
vieModel.deleteFavorite(destination)
|
viewModel.deleteFavorite(destination)
|
||||||
}
|
}
|
||||||
isFavorite = !isFavorite
|
isFavorite = !isFavorite
|
||||||
finish()
|
finish()
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import com.kouros.navigation.model.ViewModel
|
|||||||
class SearchScreen(
|
class SearchScreen(
|
||||||
carContext: CarContext,
|
carContext: CarContext,
|
||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private var location: Location
|
private var location: Location,
|
||||||
|
private val viewModel: ViewModel
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var isSearchComplete: Boolean = false
|
var isSearchComplete: Boolean = false
|
||||||
@@ -47,8 +48,6 @@ class SearchScreen(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.searchPlaces.observe(this, observer)
|
viewModel.searchPlaces.observe(this, observer)
|
||||||
}
|
}
|
||||||
@@ -75,6 +74,7 @@ class SearchScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
location,
|
location,
|
||||||
|
viewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
surfaceRenderer.viewStyle = ViewStyle.VIEW
|
||||||
@@ -90,7 +90,8 @@ class SearchScreen(
|
|||||||
carContext,
|
carContext,
|
||||||
surfaceRenderer,
|
surfaceRenderer,
|
||||||
location,
|
location,
|
||||||
it.id
|
it.id,
|
||||||
|
viewModel
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
|
|||||||
@@ -34,6 +34,5 @@ class ViewModelTest {
|
|||||||
|
|
||||||
val route = repo.getRoute(fromLocation, toLocation, SearchFilter())
|
val route = repo.getRoute(fromLocation, toLocation, SearchFilter())
|
||||||
model.startNavigation(route)
|
model.startNavigation(route)
|
||||||
println(route)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ val RouteColor = Color(0xFF5582D0)
|
|||||||
|
|
||||||
val SpeedColor = Color(0xFF262525)
|
val SpeedColor = Color(0xFF262525)
|
||||||
|
|
||||||
|
val MaxSpeedColor = Color(0xFF262525)
|
||||||
|
|
||||||
val PlaceColor = Color(0xFF868005)
|
val PlaceColor = Color(0xFF868005)
|
||||||
@@ -27,34 +27,14 @@ import java.net.URL
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
|
||||||
class NavigationRepository {
|
abstract class NavigationRepository {
|
||||||
|
|
||||||
private val placesUrl = "https://kouros-online.de/maps/placespwd";
|
private val placesUrl = "https://kouros-online.de/maps/placespwd";
|
||||||
|
|
||||||
private val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
|
||||||
|
|
||||||
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
private val nominatimUrl = "https://nominatim.openstreetmap.org/"
|
||||||
|
|
||||||
// Road classes from highest to lowest are:
|
|
||||||
// motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other.
|
|
||||||
|
|
||||||
// exclude_toll
|
abstract fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String
|
||||||
fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
|
||||||
SearchFilter
|
|
||||||
val vLocation = listOf(
|
|
||||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
|
||||||
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
|
||||||
)
|
|
||||||
val valhallaLocation = ValhallaLocation(
|
|
||||||
locations = vLocation,
|
|
||||||
costing = "auto",
|
|
||||||
units = "km",
|
|
||||||
id = "my_work_route",
|
|
||||||
language = "de-DE"
|
|
||||||
)
|
|
||||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
|
||||||
return fetchUrl(routeUrl + routeLocation, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter): Double {
|
fun getRouteDistance(currentLocation: Location, location: Location, searchFilter: SearchFilter): Double {
|
||||||
val route = getRoute(currentLocation, location, searchFilter)
|
val route = getRoute(currentLocation, location, searchFilter)
|
||||||
@@ -98,7 +78,7 @@ class NavigationRepository {
|
|||||||
return places
|
return places
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchUrl(url: String, authenticator : Boolean): String {
|
fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||||
try {
|
try {
|
||||||
if (authenticator) {
|
if (authenticator) {
|
||||||
Authenticator.setDefault(object : Authenticator() {
|
Authenticator.setDefault(object : Authenticator() {
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ data class Route(
|
|||||||
fun route(route: String) = apply {
|
fun route(route: String) = apply {
|
||||||
if (route.isNotEmpty() && route != "[]") {
|
if (route.isNotEmpty() && route != "[]") {
|
||||||
val gson = GsonBuilder().serializeNulls().create()
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
val valhalla = gson.fromJson(route, ValhallaJson::class.java)
|
val routeJson = gson.fromJson(route, ValhallaJson::class.java)
|
||||||
trip = valhalla.trip
|
trip = routeJson.trip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Intersections(
|
||||||
|
|
||||||
|
@SerializedName("out") var out: Int? = null,
|
||||||
|
@SerializedName("entry") var entry: ArrayList<Boolean> = arrayListOf(),
|
||||||
|
@SerializedName("bearings") var bearings: ArrayList<Int> = arrayListOf(),
|
||||||
|
@SerializedName("location") var location: ArrayList<Double> = arrayListOf(),
|
||||||
|
@SerializedName("lanes") var lanes: ArrayList<Lane> = arrayListOf(),
|
||||||
|
)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class Lane(
|
||||||
|
@SerializedName("valid" ) var valid: Boolean,
|
||||||
|
@SerializedName("indications" ) var indications: List<String>,
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Legs (
|
||||||
|
|
||||||
|
@SerializedName("steps" ) var steps : ArrayList<Steps> = arrayListOf(),
|
||||||
|
@SerializedName("weight" ) var weight : Double? = null,
|
||||||
|
@SerializedName("summary" ) var summary : String? = null,
|
||||||
|
@SerializedName("duration" ) var duration : Double? = null,
|
||||||
|
@SerializedName("distance" ) var distance : Double? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Maneuver (
|
||||||
|
|
||||||
|
@SerializedName("bearing_after" ) var bearingAfter : Int? = null,
|
||||||
|
@SerializedName("bearing_before" ) var bearingBefore : Int? = null,
|
||||||
|
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||||
|
@SerializedName("modifier" ) var modifier : String? = null,
|
||||||
|
@SerializedName("type" ) var type : String? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class OsrmJson (
|
||||||
|
|
||||||
|
@SerializedName("code" ) var code : String? = null,
|
||||||
|
@SerializedName("routes" ) var routes : ArrayList<Routes> = arrayListOf(),
|
||||||
|
@SerializedName("waypoints" ) var waypoints : ArrayList<Waypoints> = arrayListOf()
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.SearchFilter
|
||||||
|
|
||||||
|
private const val routeUrl = "https://router.project-osrm.org/route/v1/driving/"
|
||||||
|
|
||||||
|
class OsrmRepository : NavigationRepository() {
|
||||||
|
override fun getRoute(
|
||||||
|
currentLocation: Location,
|
||||||
|
location: Location,
|
||||||
|
searchFilter: SearchFilter
|
||||||
|
): String {
|
||||||
|
val routeLocation = "${currentLocation.latitude},${currentLocation.longitude};${location.latitude},${location.longitude}?steps=true"
|
||||||
|
return fetchUrl(routeUrl + routeLocation, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Routes (
|
||||||
|
|
||||||
|
@SerializedName("legs" ) var legs : ArrayList<Legs> = arrayListOf(),
|
||||||
|
@SerializedName("weight_name" ) var weightName : String? = null,
|
||||||
|
@SerializedName("geometry" ) var geometry : String? = null,
|
||||||
|
@SerializedName("weight" ) var weight : Double? = null,
|
||||||
|
@SerializedName("duration" ) var duration : Double? = null,
|
||||||
|
@SerializedName("distance" ) var distance : Double? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Steps (
|
||||||
|
|
||||||
|
@SerializedName("intersections" ) var intersections : ArrayList<Intersections> = arrayListOf(),
|
||||||
|
@SerializedName("driving_side" ) var drivingSide : String? = null,
|
||||||
|
@SerializedName("geometry" ) var geometry : String? = null,
|
||||||
|
@SerializedName("maneuver" ) var maneuver : Maneuver? = Maneuver(),
|
||||||
|
@SerializedName("name" ) var name : String? = null,
|
||||||
|
@SerializedName("mode" ) var mode : String? = null,
|
||||||
|
@SerializedName("weight" ) var weight : Double? = null,
|
||||||
|
@SerializedName("duration" ) var duration : Double? = null,
|
||||||
|
@SerializedName("distance" ) var distance : Double? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.kouros.navigation.data.osrm
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Waypoints (
|
||||||
|
|
||||||
|
@SerializedName("hint" ) var hint : String? = null,
|
||||||
|
@SerializedName("location" ) var location : ArrayList<Double> = arrayListOf(),
|
||||||
|
@SerializedName("name" ) var name : String? = null,
|
||||||
|
@SerializedName("distance" ) var distance : Double? = null
|
||||||
|
|
||||||
|
)
|
||||||
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
1020
common/data/src/main/java/com/kouros/navigation/data/osrm/osrm.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,8 @@ package com.kouros.navigation.data.overpass
|
|||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.kouros.navigation.utils.GeoUtils.getBoundingBox2
|
|
||||||
import com.kouros.navigation.utils.GeoUtils.getOverpassBbox
|
import com.kouros.navigation.utils.GeoUtils.getOverpassBbox
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@@ -12,8 +11,33 @@ import java.net.URL
|
|||||||
class Overpass {
|
class Overpass {
|
||||||
|
|
||||||
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
|
||||||
fun getAmenities(type: String, category: String, location: Location) : List<Elements> {
|
fun getAround(radius: Int, linestring: String) : List<Elements> {
|
||||||
val boundingBox = getOverpassBbox(location, 5.0)
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
|
httpURLConnection.requestMethod = "POST"
|
||||||
|
httpURLConnection.setRequestProperty(
|
||||||
|
"Accept",
|
||||||
|
"application/json"
|
||||||
|
)
|
||||||
|
httpURLConnection.setDoOutput(true);
|
||||||
|
// define search query
|
||||||
|
val searchQuery = """
|
||||||
|
|[out:json];
|
||||||
|
|(
|
||||||
|
| way[highway](around:$radius,$linestring)
|
||||||
|
| ;
|
||||||
|
|);
|
||||||
|
|out body;
|
||||||
|
""".trimMargin()
|
||||||
|
return overpassApi(httpURLConnection, searchQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAmenities(
|
||||||
|
type: String,
|
||||||
|
category: String,
|
||||||
|
location: Location,
|
||||||
|
radius: Double
|
||||||
|
): List<Elements> {
|
||||||
|
val boundingBox = getOverpassBbox(location, radius)
|
||||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
httpURLConnection.requestMethod = "POST"
|
httpURLConnection.requestMethod = "POST"
|
||||||
httpURLConnection.setRequestProperty(
|
httpURLConnection.setRequestProperty(
|
||||||
@@ -32,8 +56,11 @@ class Overpass {
|
|||||||
|);
|
|);
|
||||||
|out body;
|
|out body;
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
|
return overpassApi(httpURLConnection, searchQuery)
|
||||||
|
}
|
||||||
|
|
||||||
// Send the JSON we created
|
fun overpassApi(httpURLConnection: HttpURLConnection, searchQuery: String) : List<Elements> {
|
||||||
|
// Send the JSON we created
|
||||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||||
outputStreamWriter.write(searchQuery)
|
outputStreamWriter.write(searchQuery)
|
||||||
outputStreamWriter.flush()
|
outputStreamWriter.flush()
|
||||||
@@ -44,9 +71,9 @@ class Overpass {
|
|||||||
.use { it.readText() } // defaults to UTF-8
|
.use { it.readText() } // defaults to UTF-8
|
||||||
val gson = GsonBuilder().serializeNulls().create()
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
val overpass = gson.fromJson(response, Amenity::class.java)
|
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||||
println("Overpass: $type $response")
|
//println("Overpass: $response")
|
||||||
return overpass.elements
|
return overpass.elements
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.kouros.navigation.data.valhalla
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import com.kouros.navigation.data.Locations
|
||||||
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.SearchFilter
|
||||||
|
import com.kouros.navigation.data.ValhallaLocation
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
private const val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||||
|
class ValhallaRepository : NavigationRepository() {
|
||||||
|
|
||||||
|
override fun getRoute(currentLocation: Location, location: Location, searchFilter: SearchFilter): String {
|
||||||
|
SearchFilter
|
||||||
|
val vLocation = listOf(
|
||||||
|
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude, search_filter = searchFilter),
|
||||||
|
Locations(lat = location.latitude, lon = location.longitude, search_filter = searchFilter)
|
||||||
|
)
|
||||||
|
val valhallaLocation = ValhallaLocation(
|
||||||
|
locations = vLocation,
|
||||||
|
costing = "auto",
|
||||||
|
units = "km",
|
||||||
|
id = "my_work_route",
|
||||||
|
language = "de-DE"
|
||||||
|
)
|
||||||
|
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||||
|
return fetchUrl(routeUrl + routeLocation, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,16 +4,16 @@ import android.location.Location
|
|||||||
import androidx.car.app.navigation.model.Maneuver
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
import androidx.car.app.navigation.model.Step
|
import androidx.car.app.navigation.model.Step
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants
|
|
||||||
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
import com.kouros.navigation.data.ManeuverType
|
import com.kouros.navigation.data.ManeuverType
|
||||||
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.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import org.maplibre.turf.TurfMeasurement
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import org.maplibre.turf.TurfMisc
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@@ -21,14 +21,19 @@ open class RouteModel() {
|
|||||||
data class RouteState(
|
data class RouteState(
|
||||||
val route: Route? = null,
|
val route: Route? = null,
|
||||||
val isNavigating: Boolean = false,
|
val isNavigating: Boolean = false,
|
||||||
var destination: Place = Place(),
|
val destination: Place = Place(),
|
||||||
val arrived: Boolean = false,
|
val arrived: Boolean = false,
|
||||||
var maneuverType: Int = 0,
|
val maneuverType: Int = 0,
|
||||||
var currentShapeIndex: Int = 0,
|
var currentShapeIndex: Int = 0,
|
||||||
var distanceToStepEnd: Float = 0F,
|
var distanceToStepEnd: Float = 0F,
|
||||||
var beginIndex: Int = 0,
|
var beginIndex: Int = 0,
|
||||||
var endIndex: Int = 0
|
var endIndex: Int = 0,
|
||||||
)
|
val travelMessage: String = "",
|
||||||
|
// max speed for street (maneuver)
|
||||||
|
val lastSpeedIndex: Int = 0,
|
||||||
|
val maxSpeed: Int = 0,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
var routeState = RouteState()
|
var routeState = RouteState()
|
||||||
|
|
||||||
@@ -61,7 +66,8 @@ open class RouteModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLocation(location: Location) {
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
fun updateLocation(location: Location, viewModel: ViewModel) {
|
||||||
var nearestDistance = 100000.0f
|
var nearestDistance = 100000.0f
|
||||||
var newShapeIndex = -1
|
var newShapeIndex = -1
|
||||||
// find nearest waypoint and current shape index
|
// find nearest waypoint and current shape index
|
||||||
@@ -77,6 +83,24 @@ open class RouteModel() {
|
|||||||
// find maneuver
|
// find maneuver
|
||||||
// calculate distance to step end
|
// calculate distance to step end
|
||||||
findManeuver(newShapeIndex)
|
findManeuver(newShapeIndex)
|
||||||
|
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
updateSpeedLimit(location, viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSpeedLimit(location: Location, viewModel: ViewModel) {
|
||||||
|
// speed limit for each maneuver index
|
||||||
|
if (routeState.lastSpeedIndex < route.currentManeuverIndex) {
|
||||||
|
routeState = routeState.copy(lastSpeedIndex = route.currentManeuverIndex)
|
||||||
|
val elements = viewModel.getMaxSpeed(location)
|
||||||
|
elements.forEach {
|
||||||
|
if (it.tags.name != null && it.tags.maxspeed != null) {
|
||||||
|
val speed = it.tags.maxspeed!!.toInt()
|
||||||
|
routeState = routeState.copy(maxSpeed = speed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findManeuver(newShapeIndex: Int) {
|
private fun findManeuver(newShapeIndex: Int) {
|
||||||
@@ -127,7 +151,7 @@ open class RouteModel() {
|
|||||||
maneuverType = relevantManeuver.type
|
maneuverType = relevantManeuver.type
|
||||||
}
|
}
|
||||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
val maneuverIconPair = maneuverIcon(maneuverType)
|
||||||
routeState.maneuverType = maneuverIconPair.first
|
routeState = routeState.copy(maneuverType = maneuverIconPair.first)
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
streetName,
|
streetName,
|
||||||
@@ -149,6 +173,7 @@ open class RouteModel() {
|
|||||||
when (distanceLeft) {
|
when (distanceLeft) {
|
||||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
in 0.0..NEXT_STEP_THRESHOLD -> {
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
||||||
text = maneuver.streetNames[0]
|
text = maneuver.streetNames[0]
|
||||||
@@ -296,8 +321,8 @@ open class RouteModel() {
|
|||||||
|
|
||||||
|
|
||||||
fun hasArrived(type: Int): Boolean {
|
fun hasArrived(type: Int): Boolean {
|
||||||
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
|
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
|
||||||
return type == ManeuverType.DestinationRight.value
|
return type == ManeuverType.DestinationRight.value
|
||||||
|| type == ManeuverType.Destination.value
|
|| type == ManeuverType.Destination.value
|
||||||
|| type == ManeuverType.DestinationLeft.value
|
|| type == ManeuverType.DestinationLeft.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
|
|
||||||
fun getAmenities(category: String, location: Location) {
|
fun getAmenities(category: String, location: Location) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val amenities = Overpass().getAmenities("amenity", category, location)
|
val amenities = Overpass().getAmenities("amenity", category, location, 5.0)
|
||||||
val distAmenities = mutableListOf<Elements>()
|
val distAmenities = mutableListOf<Elements>()
|
||||||
amenities.forEach {
|
amenities.forEach {
|
||||||
val plLocation =
|
val plLocation =
|
||||||
@@ -267,7 +267,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
|
|
||||||
fun getSpeedCameras(location: Location) {
|
fun getSpeedCameras(location: Location) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val amenities = Overpass().getAmenities("highway", "speed_camera", location)
|
val amenities = Overpass().getAmenities("highway", "speed_camera", location, 5.0)
|
||||||
val distAmenities = mutableListOf<Elements>()
|
val distAmenities = mutableListOf<Elements>()
|
||||||
amenities.forEach {
|
amenities.forEach {
|
||||||
val plLocation =
|
val plLocation =
|
||||||
@@ -281,6 +281,12 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMaxSpeed(location: Location) : List<Elements> {
|
||||||
|
val lineString = "${location.latitude},${location.longitude}"
|
||||||
|
val amenities = Overpass().getAround(10, lineString)
|
||||||
|
return amenities
|
||||||
|
}
|
||||||
|
|
||||||
fun saveFavorite(place: Place) {
|
fun saveFavorite(place: Place) {
|
||||||
place.category = Constants.FAVORITES
|
place.category = Constants.FAVORITES
|
||||||
savePlace(place)
|
savePlace(place)
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ fun calculateZoom(speed: Double?): Double {
|
|||||||
val speedKmh = (speed * 3.6).toInt()
|
val speedKmh = (speed * 3.6).toInt()
|
||||||
val zoom = when (speedKmh) {
|
val zoom = when (speedKmh) {
|
||||||
in 0..10 -> 18.0
|
in 0..10 -> 18.0
|
||||||
in 11..30 -> 17.0
|
in 11..30 -> 17.5
|
||||||
in 31..50 -> 16.0
|
in 31..50 -> 17.0
|
||||||
in 61..70 -> 15.0
|
in 61..70 -> 16.5
|
||||||
else -> 14
|
else -> 16.0
|
||||||
}
|
}
|
||||||
return zoom.toDouble()
|
return zoom.toDouble()
|
||||||
}
|
}
|
||||||
|
|||||||
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
10
common/data/src/main/res/drawable/warning_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500Z"/>
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user