LocationPuck
This commit is contained in:
@@ -18,7 +18,6 @@ package com.kouros.navigation
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -34,23 +33,37 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalDrawerSheet
|
||||||
|
import androidx.compose.material3.ModalNavigationDrawer
|
||||||
|
import androidx.compose.material3.NavigationDrawerItem
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SegmentedButtonDefaults.Icon
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
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.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.location.LocationListenerCompat
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.example.places.ui.theme.PlacesTheme
|
import com.example.places.ui.theme.PlacesTheme
|
||||||
@@ -59,10 +72,12 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
|||||||
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.Constants.TAG
|
import com.kouros.navigation.data.Constants.TAG
|
||||||
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.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.maplibre.compose.camera.CameraPosition
|
import org.maplibre.compose.camera.CameraPosition
|
||||||
import org.maplibre.compose.camera.rememberCameraState
|
import org.maplibre.compose.camera.rememberCameraState
|
||||||
@@ -85,18 +100,24 @@ import org.maplibre.spatialk.geojson.Position
|
|||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
||||||
val geojson = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
val vieModel = ViewModel(NavigationRepository())
|
val vieModel = ViewModel(NavigationRepository())
|
||||||
val routeModel = RouteModel()
|
val routeModel = RouteModel()
|
||||||
|
|
||||||
|
var tilt = 0.0
|
||||||
|
|
||||||
|
val curLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
|
||||||
|
val instruction: MutableLiveData<StepData> by lazy {
|
||||||
|
MutableLiveData<StepData>()
|
||||||
|
}
|
||||||
|
|
||||||
val observer = Observer<String> { newRoute ->
|
val observer = Observer<String> { newRoute ->
|
||||||
routeModel.createNavigationRoute(newRoute)
|
routeModel.startNavigation(newRoute)
|
||||||
geojson.value = routeModel.geoJson
|
routeData.value = routeModel.route
|
||||||
homeLocation.latitude = 48.155782
|
|
||||||
homeLocation.longitude = 11.607921
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val cameraPosition = MutableLiveData(
|
val cameraPosition = MutableLiveData(
|
||||||
@@ -108,38 +129,19 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
vieModel.route.observe(this, observer)
|
vieModel.route.observe(this, observer)
|
||||||
vieModel.loadRoute(
|
|
||||||
homeLocation,
|
|
||||||
Constants.home2Location
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
fun updateLocation(location: org.maplibre.compose.location.Location?) {
|
||||||
updateLocation(location)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
fun requestLocationUpdates() {
|
|
||||||
val locationManager =
|
|
||||||
getSystemService(LOCATION_SERVICE) as LocationManager
|
|
||||||
val location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
|
||||||
updateLocation(location)
|
|
||||||
locationManager.requestLocationUpdates(
|
|
||||||
LocationManager.GPS_PROVIDER,
|
|
||||||
/* minTimeMs= */ 100,
|
|
||||||
/* minDistanceM= */ 0f,
|
|
||||||
mLocationListener
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateLocation(location: Location?) {
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
|
if (routeModel.isNavigating()) {
|
||||||
|
instruction.value = routeModel.currentStep()
|
||||||
|
}
|
||||||
|
val zoom = NavigationUtils().calculateZoom(location.speed)
|
||||||
cameraPosition.postValue(
|
cameraPosition.postValue(
|
||||||
cameraPosition.value!!.copy(
|
cameraPosition.value!!.copy(
|
||||||
zoom = 15.0,
|
zoom = zoom,
|
||||||
target = Position(location.longitude, location.latitude),
|
target = location.position
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,23 +166,64 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
PlacesTheme {
|
PlacesTheme {
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
ModalNavigationDrawer(
|
||||||
Column(modifier = Modifier.padding(innerPadding)) {
|
drawerContent = {
|
||||||
CheckPermission()
|
ModalDrawerSheet {
|
||||||
|
Text("Drawer title", modifier = Modifier.padding(16.dp))
|
||||||
|
HorizontalDivider()
|
||||||
|
NavigationDrawerItem(
|
||||||
|
label = { Text(text = "Drawer Item") },
|
||||||
|
selected = false,
|
||||||
|
onClick = { /*TODO*/ }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gesturesEnabled = false
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
text = {
|
||||||
|
Text("Navigate")
|
||||||
|
},
|
||||||
|
icon = { Icon(true) },
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
snackbarHostState.showSnackbar("Starte Navigation")
|
||||||
|
}
|
||||||
|
if (!routeModel.isNavigating()) {
|
||||||
|
tilt = 60.0
|
||||||
|
vieModel.loadRoute(
|
||||||
|
curLocation,
|
||||||
|
Constants.home2Location
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
tilt = 0.0
|
||||||
|
routeModel.stopNavigation()
|
||||||
|
routeData.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
CheckPermission()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
val locationManager =
|
|
||||||
getSystemService(LOCATION_SERVICE) as LocationManager
|
|
||||||
locationManager.removeUpdates(mLocationListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("PermissionLaunchedDuringComposition")
|
@SuppressLint("PermissionLaunchedDuringComposition")
|
||||||
@OptIn(ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -224,11 +267,40 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NavigationInfo(step: StepData?) {
|
||||||
|
Card {
|
||||||
|
Column {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(com.kouros.android.cars.carappservice.R.drawable.ic_turn_normal_right),
|
||||||
|
contentDescription = stringResource(id = com.kouros.android.cars.carappservice.R.string.accept_action_title)
|
||||||
|
)
|
||||||
|
if (step != null) {
|
||||||
|
Text(text = step.bearing.toString(), fontSize = 25.sp)
|
||||||
|
Text(text = step.instruction, fontSize = 25.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Map() {
|
fun Map() {
|
||||||
requestLocationUpdates()
|
val step: StepData? by instruction.observeAsState()
|
||||||
|
Column {
|
||||||
|
if (step != null) {
|
||||||
|
NavigationInfo(step)
|
||||||
|
}
|
||||||
|
MapView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MapView() {
|
||||||
|
val locationProvider = rememberDefaultLocationProvider()
|
||||||
|
val locationState = rememberUserLocationState(locationProvider)
|
||||||
|
updateLocation(locationState.location)
|
||||||
val position: CameraPosition? by cameraPosition.observeAsState()
|
val position: CameraPosition? by cameraPosition.observeAsState()
|
||||||
val geoJsonData: String? by geojson.observeAsState()
|
val route: String? by routeData.observeAsState()
|
||||||
val cameraState =
|
val cameraState =
|
||||||
rememberCameraState(
|
rememberCameraState(
|
||||||
firstPosition =
|
firstPosition =
|
||||||
@@ -241,9 +313,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val locationProvider = rememberDefaultLocationProvider()
|
if (locationState.location != null) {
|
||||||
val locationState = rememberUserLocationState(locationProvider)
|
curLocation.latitude = locationState.location?.position!!.latitude
|
||||||
|
curLocation.longitude = locationState.location?.position!!.longitude
|
||||||
|
}
|
||||||
MaplibreMap(
|
MaplibreMap(
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
|
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
|
||||||
@@ -270,7 +343,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
||||||
FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building")
|
FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building")
|
||||||
RouteLayer(geoJsonData)
|
RouteLayer(route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +353,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
bearing = position!!.bearing,
|
bearing = position!!.bearing,
|
||||||
zoom = position!!.zoom,
|
zoom = position!!.zoom,
|
||||||
target = position!!.target,
|
target = position!!.target,
|
||||||
|
tilt = tilt
|
||||||
),
|
),
|
||||||
duration = 3.seconds
|
duration = 3.seconds
|
||||||
)
|
)
|
||||||
@@ -287,21 +361,23 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RouteLayer(geoJsonData: String?) {
|
fun RouteLayer(routeData: String?) {
|
||||||
val routes =
|
if (routeData!!.isNotEmpty()) {
|
||||||
rememberGeoJsonSource(GeoJsonData.JsonString(geoJsonData!!))
|
val routes =
|
||||||
LineLayer(
|
rememberGeoJsonSource(GeoJsonData.JsonString(routeData!!))
|
||||||
id = "routes-casing",
|
LineLayer(
|
||||||
source = routes,
|
id = "routes-casing",
|
||||||
color = const(Color.White),
|
source = routes,
|
||||||
width = const(6.dp),
|
color = const(Color.White),
|
||||||
)
|
width = const(6.dp),
|
||||||
LineLayer(
|
)
|
||||||
id = "routes",
|
LineLayer(
|
||||||
source = routes,
|
id = "routes",
|
||||||
color = const(Color.Blue),
|
source = routes,
|
||||||
width = const(4.dp),
|
color = const(Color.Blue),
|
||||||
)
|
width = const(4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import androidx.car.app.CarContext
|
|||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.ScreenManager
|
import androidx.car.app.ScreenManager
|
||||||
import androidx.car.app.Session
|
import androidx.car.app.Session
|
||||||
import androidx.car.app.navigation.model.Maneuver
|
|
||||||
import androidx.core.location.LocationListenerCompat
|
import androidx.core.location.LocationListenerCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
@@ -25,19 +24,18 @@ import com.kouros.navigation.car.screen.RequestPermissionScreen
|
|||||||
import com.kouros.navigation.car.screen.SearchScreen
|
import com.kouros.navigation.car.screen.SearchScreen
|
||||||
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.ObjectBox
|
||||||
import com.kouros.navigation.model.RouteModel
|
|
||||||
|
|
||||||
class NavigationSession : Session() {
|
class NavigationSession : Session() {
|
||||||
val uriScheme = "samples";
|
val uriScheme = "samples";
|
||||||
|
|
||||||
val uriHost = "navigation";
|
val uriHost = "navigation";
|
||||||
|
|
||||||
lateinit var route: RouteCarModel;
|
lateinit var routeModel: RouteCarModel;
|
||||||
lateinit var navigationScreen: NavigationScreen
|
lateinit var navigationScreen: NavigationScreen
|
||||||
lateinit var surfaceRenderer: SurfaceRenderer
|
lateinit var surfaceRenderer: SurfaceRenderer
|
||||||
var locationIndex = 0
|
var locationIndex = 0
|
||||||
|
|
||||||
val test = true
|
val test = false
|
||||||
|
|
||||||
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
var mLocationListener: LocationListenerCompat = LocationListenerCompat { location: Location? ->
|
||||||
updateLocation(location)
|
updateLocation(location)
|
||||||
@@ -75,12 +73,12 @@ class NavigationSession : Session() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateScreen(intent: Intent): Screen {
|
override fun onCreateScreen(intent: Intent): Screen {
|
||||||
route = RouteCarModel()
|
routeModel = RouteCarModel()
|
||||||
ObjectBox.init(carContext);
|
ObjectBox.init(carContext);
|
||||||
|
|
||||||
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, route)
|
surfaceRenderer = SurfaceRenderer(carContext, lifecycle, routeModel)
|
||||||
|
|
||||||
navigationScreen = NavigationScreen(carContext, surfaceRenderer, route)
|
navigationScreen = NavigationScreen(carContext, surfaceRenderer, routeModel)
|
||||||
|
|
||||||
if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
if (carContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
== PackageManager.PERMISSION_GRANTED
|
== PackageManager.PERMISSION_GRANTED
|
||||||
@@ -152,7 +150,7 @@ class NavigationSession : Session() {
|
|||||||
updateLocation(location)
|
updateLocation(location)
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(
|
||||||
LocationManager.GPS_PROVIDER,
|
LocationManager.GPS_PROVIDER,
|
||||||
/* minTimeMs= */ 100,
|
/* minTimeMs= */ 1000,
|
||||||
/* minDistanceM= */ 0f,
|
/* minDistanceM= */ 0f,
|
||||||
mLocationListener
|
mLocationListener
|
||||||
)
|
)
|
||||||
@@ -169,14 +167,14 @@ class NavigationSession : Session() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test(location: Location?) {
|
fun test(location: Location?) {
|
||||||
if (route.isNavigating() && locationIndex < route.polylineLocations.size) {
|
if (routeModel.isNavigating() && locationIndex < routeModel.polylineLocations.size) {
|
||||||
val loc = route.polylineLocations[locationIndex]
|
val loc = routeModel.polylineLocations[locationIndex]
|
||||||
val curLocation = Location(LocationManager.GPS_PROVIDER)
|
val curLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
curLocation.longitude = loc[0]
|
curLocation.longitude = loc[0]
|
||||||
curLocation.latitude = loc[1]
|
curLocation.latitude = loc[1]
|
||||||
update(curLocation)
|
update(curLocation)
|
||||||
locationIndex += 1
|
locationIndex += 1
|
||||||
if (locationIndex > route.polylineLocations.size) {
|
if (locationIndex > routeModel.polylineLocations.size) {
|
||||||
val locationManager =
|
val locationManager =
|
||||||
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
locationManager.removeUpdates(mLocationListener)
|
locationManager.removeUpdates(mLocationListener)
|
||||||
@@ -188,12 +186,12 @@ class NavigationSession : Session() {
|
|||||||
|
|
||||||
fun update(location: Location) {
|
fun update(location: Location) {
|
||||||
surfaceRenderer.updateLocation(location)
|
surfaceRenderer.updateLocation(location)
|
||||||
if (route.isNavigating()) {
|
if (routeModel.isNavigating()) {
|
||||||
route.findManeuver(location)
|
routeModel.findManeuver(location)
|
||||||
// if (routingModel.distanceToRoute > 50) {
|
// if (routeModel.distanceToRoute > 50) {
|
||||||
// routingModel.stopNavigating()
|
// routeModel.stopNavigation()
|
||||||
// locationIndex = 0
|
// locationIndex = 0
|
||||||
// surfaceRenderer.setGeoJson()
|
// surfaceRenderer.setRouteData()
|
||||||
// navigationScreen.reRoute()
|
// navigationScreen.reRoute()
|
||||||
// }
|
// }
|
||||||
navigationScreen.updateTrip()
|
navigationScreen.updateTrip()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
|||||||
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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -27,11 +28,11 @@ 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.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson
|
import com.kouros.navigation.utils.NavigationUtils
|
||||||
|
import kotlinx.coroutines.flow.onSubscription
|
||||||
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.expressions.dsl.const
|
import org.maplibre.compose.expressions.dsl.const
|
||||||
import org.maplibre.compose.layers.CircleLayer
|
|
||||||
import org.maplibre.compose.layers.FillLayer
|
import org.maplibre.compose.layers.FillLayer
|
||||||
import org.maplibre.compose.layers.LineLayer
|
import org.maplibre.compose.layers.LineLayer
|
||||||
import org.maplibre.compose.location.LocationPuck
|
import org.maplibre.compose.location.LocationPuck
|
||||||
@@ -45,13 +46,13 @@ 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
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
|
|
||||||
|
|
||||||
class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
class SurfaceRenderer(
|
||||||
private var routeModel: RouteCarModel) : DefaultLifecycleObserver {
|
carContext: CarContext, lifecycle: Lifecycle,
|
||||||
var mVisibleArea: Rect? = null
|
private var routeModel: RouteCarModel
|
||||||
var mStableArea: Rect? = null
|
) : DefaultLifecycleObserver {
|
||||||
|
|
||||||
private val mCarContext: CarContext = carContext
|
private val mCarContext: CarContext = carContext
|
||||||
var lastLocation = Location(LocationManager.GPS_PROVIDER)
|
var lastLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
val cameraPosition = MutableLiveData(
|
val cameraPosition = MutableLiveData(
|
||||||
@@ -60,15 +61,15 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
target = Position(latitude = 48.1857475, longitude = 11.5793627)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val geojson = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
|
|
||||||
val previewGeojson = MutableLiveData("")
|
val previewRouteData = MutableLiveData("")
|
||||||
|
|
||||||
var preview = false
|
var preview = false
|
||||||
|
|
||||||
lateinit var mapView: ComposeView
|
lateinit var mapView: ComposeView
|
||||||
|
|
||||||
val tilt = 60.0
|
val tilt = 55.0
|
||||||
val padding = PaddingValues(start = 150.dp, top = 250.dp)
|
val padding = PaddingValues(start = 150.dp, top = 250.dp)
|
||||||
|
|
||||||
val prePadding = PaddingValues(start = 150.dp, bottom = 300.dp)
|
val prePadding = PaddingValues(start = 150.dp, bottom = 300.dp)
|
||||||
@@ -114,15 +115,14 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
presentation.show()
|
presentation.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVisibleAreaChanged(visibleArea: Rect) {
|
override fun onVisibleAreaChanged(visibleArea: Rect) {
|
||||||
synchronized(this@SurfaceRenderer) {
|
synchronized(this@SurfaceRenderer) {
|
||||||
mVisibleArea = visibleArea
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStableAreaChanged(stableArea: Rect) {
|
override fun onStableAreaChanged(stableArea: Rect) {
|
||||||
synchronized(this@SurfaceRenderer) {
|
synchronized(this@SurfaceRenderer) {
|
||||||
mStableArea = stableArea
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +155,9 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Map() {
|
fun Map() {
|
||||||
|
|
||||||
val position: CameraPosition? by cameraPosition.observeAsState()
|
val position: CameraPosition? by cameraPosition.observeAsState()
|
||||||
val geoJsonData: String? by geojson.observeAsState()
|
val route: String? by routeData.observeAsState()
|
||||||
val previewGeoJsonData: String? by previewGeojson.observeAsState()
|
val previewRoute: String? by previewRouteData.observeAsState()
|
||||||
val cameraState =
|
val cameraState =
|
||||||
rememberCameraState(
|
rememberCameraState(
|
||||||
firstPosition =
|
firstPosition =
|
||||||
@@ -172,15 +171,17 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
padding = getPaddingValues()
|
padding = getPaddingValues()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val variant = if (isSystemInDarkTheme()) "dark" else "light"
|
|
||||||
val locationProvider = rememberDefaultLocationProvider()
|
val locationProvider = rememberDefaultLocationProvider()
|
||||||
val locationState = rememberUserLocationState(locationProvider)
|
val locationState = rememberUserLocationState(locationProvider)
|
||||||
|
|
||||||
MaplibreMap(
|
MaplibreMap(
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
|
//baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
|
||||||
baseStyle = BaseStyle.Uri("https://kouros-online.de/liberty"),
|
baseStyle = BaseStyle.Uri("https://kouros-online.de/liberty"),
|
||||||
) {
|
) {
|
||||||
|
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
||||||
|
FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building")
|
||||||
|
RouteLayer(route, previewRoute)
|
||||||
|
}
|
||||||
LocationPuck(
|
LocationPuck(
|
||||||
idPrefix = "user-location",
|
idPrefix = "user-location",
|
||||||
locationState = locationState,
|
locationState = locationState,
|
||||||
@@ -188,18 +189,6 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
accuracyThreshold = 10f,
|
accuracyThreshold = 10f,
|
||||||
colors = LocationPuckColors(accuracyStrokeColor = Color.Green)
|
colors = LocationPuckColors(accuracyStrokeColor = Color.Green)
|
||||||
)
|
)
|
||||||
getBaseSource(id = "openmaptiles")?.let { tiles ->
|
|
||||||
FillLayer(id = "example", visible = false, source = tiles, sourceLayer = "building")
|
|
||||||
val coordinates = mutableListOf<List<Double>>()
|
|
||||||
coordinates.add(listOf(position!!.target.longitude, position!!.target.latitude))
|
|
||||||
coordinates.add(
|
|
||||||
listOf(
|
|
||||||
position!!.target.longitude + 0.00001,
|
|
||||||
position!!.target.latitude + 0.00001
|
|
||||||
)
|
|
||||||
)
|
|
||||||
RouteLayer(geoJsonData, previewGeoJsonData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(position) {
|
LaunchedEffect(position) {
|
||||||
@@ -217,26 +206,26 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RouteLayer(geoJsonData: String?, previewGeoJsonData: String?) {
|
fun RouteLayer(routeData: String?, previewRoute: String?) {
|
||||||
if (geoJsonData!!.isNotEmpty()) {
|
if (routeData!!.isNotEmpty()) {
|
||||||
val routes =
|
val routes =
|
||||||
rememberGeoJsonSource(GeoJsonData.JsonString(geoJsonData!!))
|
rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
||||||
LineLayer(
|
LineLayer(
|
||||||
id = "routes-casing",
|
id = "routes-casing",
|
||||||
source = routes,
|
source = routes,
|
||||||
color = const(Color.White),
|
color = const(Color.White),
|
||||||
width = const(12.dp),
|
width = const(16.dp),
|
||||||
)
|
)
|
||||||
LineLayer(
|
LineLayer(
|
||||||
id = "routes",
|
id = "routes",
|
||||||
source = routes,
|
source = routes,
|
||||||
color = const(Color.Blue),
|
color = const(Color.Blue),
|
||||||
width = const(10.dp),
|
width = const(14.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (previewGeoJsonData!!.isNotEmpty()) {
|
if (previewRoute!!.isNotEmpty()) {
|
||||||
val routes =
|
val routes =
|
||||||
rememberGeoJsonSource(GeoJsonData.JsonString(previewGeoJsonData!!))
|
rememberGeoJsonSource(GeoJsonData.JsonString(previewRoute))
|
||||||
LineLayer(
|
LineLayer(
|
||||||
id = "routes-casing-pre",
|
id = "routes-casing-pre",
|
||||||
source = routes,
|
source = routes,
|
||||||
@@ -251,6 +240,7 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(owner: LifecycleOwner) {
|
override fun onCreate(owner: LifecycleOwner) {
|
||||||
Log.i(TAG, "SurfaceRenderer created")
|
Log.i(TAG, "SurfaceRenderer created")
|
||||||
mCarContext.getCarService(AppManager::class.java)
|
mCarContext.getCarService(AppManager::class.java)
|
||||||
@@ -276,16 +266,22 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
|
|
||||||
fun updateLocation(location: Location) {
|
fun updateLocation(location: Location) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
var bearing = cameraPosition.value!!.bearing
|
var bearing: Double
|
||||||
if (lastLocation.latitude != location.latitude) {
|
if (routeModel.isNavigating()) {
|
||||||
if (lastLocation.distanceTo(location) > 10) {
|
bearing = routeModel.currentStep().bearing
|
||||||
bearing = lastLocation.bearingTo(location).toDouble()
|
} else {
|
||||||
|
bearing = cameraPosition.value!!.bearing
|
||||||
|
if (lastLocation.latitude != location.latitude) {
|
||||||
|
if (lastLocation.distanceTo(location) > 5) {
|
||||||
|
bearing = lastLocation.bearingTo(location).toDouble()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var zoom = NavigationUtils().calculateZoom(location.speed.toDouble())
|
||||||
if (preview) {
|
if (preview) {
|
||||||
bearing = 0.0
|
bearing = 0.0
|
||||||
|
zoom = 11.0
|
||||||
}
|
}
|
||||||
val zoom = calculateZoom(location)
|
|
||||||
cameraPosition.postValue(
|
cameraPosition.postValue(
|
||||||
cameraPosition.value!!.copy(
|
cameraPosition.value!!.copy(
|
||||||
bearing = bearing,
|
bearing = bearing,
|
||||||
@@ -298,30 +294,13 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateZoom(location: Location): Double {
|
fun setRouteData() {
|
||||||
if (preview) {
|
routeData.value = routeModel.route
|
||||||
return 11.0
|
|
||||||
}
|
|
||||||
//var zoom = cameraPosition.value!!.zoom
|
|
||||||
val zoom = when (location.speed.toInt()) {
|
|
||||||
in 0..10 -> 17.0
|
|
||||||
in 11..20 -> 16.0
|
|
||||||
in 21..30 -> 15.0
|
|
||||||
in 31..40 -> 14.0
|
|
||||||
in 41..50 -> 13.0
|
|
||||||
in 51..60 -> 12.0
|
|
||||||
else -> 11
|
|
||||||
}
|
|
||||||
return zoom.toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setGeoJson() {
|
|
||||||
geojson.value = routeModel.geoJson
|
|
||||||
preview = false
|
preview = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPreviewGeoJson(geoRoute: String) {
|
fun setPreviewRouteData(route: String) {
|
||||||
previewGeojson.value = geoRoute
|
previewRouteData.value = route
|
||||||
preview = true
|
preview = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +311,7 @@ class SurfaceRenderer(carContext: CarContext, lifecycle: Lifecycle,
|
|||||||
padding
|
padding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion
|
companion
|
||||||
object {
|
object {
|
||||||
private const val TAG = "MapRenderer"
|
private const val TAG = "MapRenderer"
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.kouros.navigation.car.navigation
|
package com.kouros.navigation.car.navigation
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -43,34 +42,27 @@ class RouteCarModel() : RouteModel() {
|
|||||||
fun currentStep(carContext: CarContext): Step {
|
fun currentStep(carContext: CarContext): Step {
|
||||||
val maneuver = (maneuvers[maneuverIndex] as JSONObject)
|
val maneuver = (maneuvers[maneuverIndex] as JSONObject)
|
||||||
val maneuverType = maneuver.getInt("type")
|
val maneuverType = maneuver.getInt("type")
|
||||||
val distanceStepLeft = leftStepDistance() * 1000
|
|
||||||
var text = ""
|
val stepData = currentStep()
|
||||||
|
|
||||||
var routing: (Pair<Int, CarIcon>)
|
var routing: (Pair<Int, CarIcon>)
|
||||||
routing = if (hasArrived(maneuverType)) {
|
routing = if (hasArrived(maneuverType)) {
|
||||||
routingData(maneuverType, carContext)
|
routingData(maneuverType, carContext)
|
||||||
} else {
|
} else {
|
||||||
routingData(ManeuverType.None.value, carContext)
|
routingData(ManeuverType.None.value, carContext)
|
||||||
}
|
}
|
||||||
when (distanceStepLeft) {
|
when (stepData.leftDistance) {
|
||||||
in 0.0..100.0 -> {
|
in 0.0..100.0 -> {
|
||||||
if (maneuverIndex < maneuvers.length()) {
|
if (maneuverIndex < maneuvers.length()) {
|
||||||
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
|
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
|
||||||
if (maneuver.optJSONArray("street_names") != null) {
|
|
||||||
text = maneuver.getJSONArray("street_names").get(0) as String
|
|
||||||
}
|
|
||||||
val maneuverType = maneuver.getInt("type")
|
val maneuverType = maneuver.getInt("type")
|
||||||
routing = routingData(maneuverType, carContext)
|
routing = routingData(maneuverType, carContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
|
||||||
if (maneuver.optJSONArray("street_names") != null) {
|
|
||||||
text = maneuver.getJSONArray("street_names").get(0) as String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentStepCueWithImage: SpannableString =
|
val currentStepCueWithImage: SpannableString =
|
||||||
createString(text)
|
createString(stepData.instruction)
|
||||||
val step =
|
val step =
|
||||||
Step.Builder(currentStepCueWithImage)
|
Step.Builder(currentStepCueWithImage)
|
||||||
.setManeuver(
|
.setManeuver(
|
||||||
@@ -88,8 +80,8 @@ class RouteCarModel() : RouteModel() {
|
|||||||
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
|
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
|
||||||
val maneuverType = maneuver.getInt("type")
|
val maneuverType = maneuver.getInt("type")
|
||||||
val routing = routingData(maneuverType, carContext)
|
val routing = routingData(maneuverType, carContext)
|
||||||
val distanceLeft = leftStepDistance() * 1000
|
|
||||||
var text = ""
|
var text = ""
|
||||||
|
val distanceLeft = leftStepDistance() * 1000
|
||||||
|
|
||||||
when (distanceLeft) {
|
when (distanceLeft) {
|
||||||
in 0.0..100.0 -> {
|
in 0.0..100.0 -> {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import androidx.lifecycle.Observer
|
|||||||
import com.kouros.android.cars.carappservice.R
|
import com.kouros.android.cars.carappservice.R
|
||||||
import com.kouros.navigation.car.NavigationCarAppService
|
import com.kouros.navigation.car.NavigationCarAppService
|
||||||
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.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
@@ -41,8 +40,8 @@ class NavigationScreen(
|
|||||||
val vieModel = ViewModel(NavigationRepository())
|
val vieModel = ViewModel(NavigationRepository())
|
||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
routeModel.createNavigationRoute(route)
|
routeModel.startNavigation(route)
|
||||||
surfaceRenderer.setGeoJson()
|
surfaceRenderer.setRouteData()
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +83,8 @@ class NavigationScreen(
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
surfaceRenderer.geojson.postValue("")
|
surfaceRenderer.routeData.postValue("")
|
||||||
routeModel.stopNavigating()
|
routeModel.stopNavigation()
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
@@ -241,7 +240,7 @@ class NavigationScreen(
|
|||||||
fun updateTrip() {
|
fun updateTrip() {
|
||||||
if (routeModel.maneuverType == Maneuver.TYPE_DESTINATION && routeModel.leftStepDistance() * 1000 < 25.0) {
|
if (routeModel.maneuverType == Maneuver.TYPE_DESTINATION && routeModel.leftStepDistance() * 1000 < 25.0) {
|
||||||
routeModel.arrived = true
|
routeModel.arrived = true
|
||||||
routeModel.stopNavigating()
|
routeModel.stopNavigation()
|
||||||
}
|
}
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ class RoutePreviewScreen(
|
|||||||
val navigationMessage = NavigationMessage(carContext)
|
val navigationMessage = NavigationMessage(carContext)
|
||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
routeModel.createNavigationRoute(route)
|
routeModel.startNavigation(route)
|
||||||
surfaceRenderer.setPreviewGeoJson(routeModel.geoJson)
|
surfaceRenderer.setPreviewRouteData(routeModel.route)
|
||||||
val geocoder = Geocoder(carContext)
|
val geocoder = Geocoder(carContext)
|
||||||
geocoder.getFromLocation(destination.latitude, destination.longitude, 1) {
|
geocoder.getFromLocation(destination.latitude, destination.longitude, 1) {
|
||||||
for (address in it) {
|
for (address in it) {
|
||||||
|
|||||||
@@ -47,26 +47,17 @@
|
|||||||
<string name="no_action_title" msgid="1452124604210014010">"Nein"</string>
|
<string name="no_action_title" msgid="1452124604210014010">"Nein"</string>
|
||||||
<string name="disable_all_rows" msgid="3003225080532928046">"Alle Zeilen deaktivieren"</string>
|
<string name="disable_all_rows" msgid="3003225080532928046">"Alle Zeilen deaktivieren"</string>
|
||||||
<string name="enable_all_rows" msgid="7274285275711872091">"Alle Zeilen aktivieren"</string>
|
<string name="enable_all_rows" msgid="7274285275711872091">"Alle Zeilen aktivieren"</string>
|
||||||
<string name="bug_reported_toast_msg" msgid="2487119172744644317">"Fehler wurde gemeldet!"</string>
|
|
||||||
<string name="zoomed_in_toast_msg" msgid="8915301497303842649">"Herangezoomt"</string>
|
<string name="zoomed_in_toast_msg" msgid="8915301497303842649">"Herangezoomt"</string>
|
||||||
<string name="zoomed_out_toast_msg" msgid="6260981223227212493">"Herausgezoomt"</string>
|
<string name="zoomed_out_toast_msg" msgid="6260981223227212493">"Herausgezoomt"</string>
|
||||||
<string name="triggered_toast_msg" msgid="3396166539208366382">"Ausgelöst"</string>
|
<string name="triggered_toast_msg" msgid="3396166539208366382">"Ausgelöst"</string>
|
||||||
<string name="primary_toast_msg" msgid="7153771322662005447">"Primäre Schaltfläche gedrückt"</string>
|
|
||||||
<string name="search_toast_msg" msgid="7826530065407699347">"Schaltfläche „Suchen“ gedrückt"</string>
|
|
||||||
<string name="options_toast_msg" msgid="2146223786877557730">"Optionsschaltfläche gedrückt"</string>
|
|
||||||
<string name="favorite_toast_msg" msgid="522064494016370117">"Favorit!"</string>
|
<string name="favorite_toast_msg" msgid="522064494016370117">"Favorit!"</string>
|
||||||
<string name="not_favorite_toast_msg" msgid="6831181108681007428">"Kein Favorit!"</string>
|
<string name="not_favorite_toast_msg" msgid="6831181108681007428">"Kein Favorit!"</string>
|
||||||
<string name="nav_requested_toast_msg" msgid="6696525973145493908">"Navigation angefragt"</string>
|
<string name="nav_requested_toast_msg" msgid="6696525973145493908">"Navigation angefragt"</string>
|
||||||
<string name="selected_route_toast_msg" msgid="3149189677200086656">"Ausgewählte Route"</string>
|
<string name="selected_route_toast_msg" msgid="3149189677200086656">"Ausgewählte Route"</string>
|
||||||
<string name="visible_routes_toast_msg" msgid="7065558153736024203">"Sichtbare Routen"</string>
|
<string name="visible_routes_toast_msg" msgid="7065558153736024203">"Sichtbare Routen"</string>
|
||||||
<string name="second_item_toast_msg" msgid="7210054709419608215">"Zweiter Eintrag angeklickt"</string>
|
|
||||||
<string name="third_item_checked_toast_msg" msgid="3022450599567347361">"Dritter Eintrag aktiviert"</string>
|
|
||||||
<string name="fifth_item_checked_toast_msg" msgid="1627599668504718594">"Fünfter Eintrag aktiviert"</string>
|
|
||||||
<string name="sixth_item_toast_msg" msgid="6117028866385793707">"Sechster Eintrag angeklickt"</string>
|
|
||||||
<string name="settings_toast_msg" msgid="7697794473002342727">"Einstellungen angeklickt"</string>
|
<string name="settings_toast_msg" msgid="7697794473002342727">"Einstellungen angeklickt"</string>
|
||||||
<string name="parked_toast_msg" msgid="2532422265890824446">"Aktion „Geparkt“"</string>
|
<string name="parked_toast_msg" msgid="2532422265890824446">"Aktion „Geparkt“"</string>
|
||||||
<string name="more_toast_msg" msgid="5938288138225509885">"„Mehr“ angeklickt"</string>
|
<string name="more_toast_msg" msgid="5938288138225509885">"„Mehr“ angeklickt"</string>
|
||||||
<string name="commute_toast_msg" msgid="4112684360647638688">"Schaltfläche für Arbeitsweg gedrückt"</string>
|
|
||||||
<string name="grant_location_permission_toast_msg" msgid="268046297444808010">"Standortermittlung erlauben, um aktuellen Standort anzuzeigen"</string>
|
<string name="grant_location_permission_toast_msg" msgid="268046297444808010">"Standortermittlung erlauben, um aktuellen Standort anzuzeigen"</string>
|
||||||
<string name="sign_in_with_google_toast_msg" msgid="5720947549233124775">"Über Google anmelden beginnt hier"</string>
|
<string name="sign_in_with_google_toast_msg" msgid="5720947549233124775">"Über Google anmelden beginnt hier"</string>
|
||||||
<string name="changes_selection_to_index_toast_msg_prefix" msgid="957766225794389167">"Auswahl auf Index geändert"</string>
|
<string name="changes_selection_to_index_toast_msg_prefix" msgid="957766225794389167">"Auswahl auf Index geändert"</string>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">Showcase</string>
|
<string name="app_name" translatable="false">Navigation</string>
|
||||||
|
|
||||||
<!-- Action Titles -->
|
<!-- Action Titles -->
|
||||||
<string name="back_caps_action_title">BACK</string>
|
<string name="back_caps_action_title">BACK</string>
|
||||||
@@ -50,27 +50,18 @@
|
|||||||
<string name="enable_all_rows">Enable All Rows</string>
|
<string name="enable_all_rows">Enable All Rows</string>
|
||||||
|
|
||||||
<!-- Toast Messages -->
|
<!-- Toast Messages -->
|
||||||
<string name="bug_reported_toast_msg">Bug reported!</string>
|
<string name="zoomed_in_toast_msg">Zoomed in</string>
|
||||||
<string name="zoomed_in_toast_msg">Zoomed in</string>
|
|
||||||
<string name="zoomed_out_toast_msg">Zoomed out</string>
|
<string name="zoomed_out_toast_msg">Zoomed out</string>
|
||||||
<string name="triggered_toast_msg">Triggered</string>
|
<string name="triggered_toast_msg">Triggered</string>
|
||||||
<string name="primary_toast_msg">Primary button pressed</string>
|
<string name="favorite_toast_msg">Favorite!</string>
|
||||||
<string name="search_toast_msg">Search button pressed</string>
|
|
||||||
<string name="options_toast_msg">Options button pressed</string>
|
|
||||||
<string name="favorite_toast_msg">Favorite!</string>
|
|
||||||
<string name="not_favorite_toast_msg">Not a favorite!</string>
|
<string name="not_favorite_toast_msg">Not a favorite!</string>
|
||||||
<string name="nav_requested_toast_msg">Navigation Requested</string>
|
<string name="nav_requested_toast_msg">Navigation Requested</string>
|
||||||
<string name="selected_route_toast_msg">Selected route</string>
|
<string name="selected_route_toast_msg">Selected route</string>
|
||||||
<string name="visible_routes_toast_msg">Visible routes</string>
|
<string name="visible_routes_toast_msg">Visible routes</string>
|
||||||
<string name="second_item_toast_msg">Clicked second item</string>
|
<string name="settings_toast_msg">Clicked Settings</string>
|
||||||
<string name="third_item_checked_toast_msg">Third item checked</string>
|
|
||||||
<string name="fifth_item_checked_toast_msg">Fifth item checked</string>
|
|
||||||
<string name="sixth_item_toast_msg">Clicked sixth item</string>
|
|
||||||
<string name="settings_toast_msg">Clicked Settings</string>
|
|
||||||
<string name="parked_toast_msg">Parked action</string>
|
<string name="parked_toast_msg">Parked action</string>
|
||||||
<string name="more_toast_msg">Clicked More</string>
|
<string name="more_toast_msg">Clicked More</string>
|
||||||
<string name="commute_toast_msg">Commute button pressed</string>
|
<string name="grant_location_permission_toast_msg">Grant location Permission to see current location</string>
|
||||||
<string name="grant_location_permission_toast_msg">Grant location Permission to see current location</string>
|
|
||||||
<string name="sign_in_with_google_toast_msg">Sign-in with Google starts here</string>
|
<string name="sign_in_with_google_toast_msg">Sign-in with Google starts here</string>
|
||||||
<string name="changes_selection_to_index_toast_msg_prefix">Changed selection to index</string>
|
<string name="changes_selection_to_index_toast_msg_prefix">Changed selection to index</string>
|
||||||
<string name="yes_action_toast_msg">Yes button pressed!</string>
|
<string name="yes_action_toast_msg">Yes button pressed!</string>
|
||||||
|
|||||||
255
common/car/src/test/java/com/kouros/navigation/car/Route.json
Normal file
255
common/car/src/test/java/com/kouros/navigation/car/Route.json
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
{
|
||||||
|
"trip": {
|
||||||
|
"locations": [
|
||||||
|
{
|
||||||
|
"type": "break",
|
||||||
|
"lat": 48.185749,
|
||||||
|
"lon": 11.579374,
|
||||||
|
"side_of_street": "right",
|
||||||
|
"original_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "break",
|
||||||
|
"lat": 48.116481,
|
||||||
|
"lon": 11.594322,
|
||||||
|
"street": "Hohenwaldeckstr. 27",
|
||||||
|
"side_of_street": "left",
|
||||||
|
"original_index": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"legs": [
|
||||||
|
{
|
||||||
|
"maneuvers": [
|
||||||
|
{
|
||||||
|
"type": 2,
|
||||||
|
"instruction": "Auf Vogelhartstraße Richtung Westen fahren.",
|
||||||
|
"verbal_succinct_transition_instruction": "Richtung Westen fahren. Dann Rechts auf Silcherstraße abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Auf Vogelhartstraße Richtung Westen fahren. Dann Rechts auf Silcherstraße abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "70 Meter weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Vogelhartstraße"
|
||||||
|
],
|
||||||
|
"bearing_after": 273,
|
||||||
|
"time": 16.965,
|
||||||
|
"length": 0.07,
|
||||||
|
"cost": 34.428,
|
||||||
|
"begin_shape_index": 0,
|
||||||
|
"end_shape_index": 6,
|
||||||
|
"verbal_multi_cue": true,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 10,
|
||||||
|
"instruction": "Rechts auf Silcherstraße abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Rechts auf Silcherstraße abbiegen.",
|
||||||
|
"verbal_succinct_transition_instruction": "Rechts abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Rechts auf Silcherstraße abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "200 Meter weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Silcherstraße"
|
||||||
|
],
|
||||||
|
"bearing_before": 273,
|
||||||
|
"bearing_after": 5,
|
||||||
|
"time": 43.25,
|
||||||
|
"length": 0.156,
|
||||||
|
"cost": 89.306,
|
||||||
|
"begin_shape_index": 6,
|
||||||
|
"end_shape_index": 13,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 10,
|
||||||
|
"instruction": "Rechts auf Schmalkaldener Straße abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Rechts auf Schmalkaldener Straße abbiegen.",
|
||||||
|
"verbal_succinct_transition_instruction": "Rechts abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Rechts auf Schmalkaldener Straße abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "400 Meter weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Schmalkaldener Straße"
|
||||||
|
],
|
||||||
|
"bearing_before": 2,
|
||||||
|
"bearing_after": 93,
|
||||||
|
"time": 108.947,
|
||||||
|
"length": 0.43,
|
||||||
|
"cost": 217.43,
|
||||||
|
"begin_shape_index": 13,
|
||||||
|
"end_shape_index": 29,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 10,
|
||||||
|
"instruction": "Rechts auf Ingolstädter Straße/B 13 abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Rechts auf Ingolstädter Straße abbiegen.",
|
||||||
|
"verbal_succinct_transition_instruction": "Rechts abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Rechts auf Ingolstädter Straße, B 13 abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "einen Kilometer weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"B 13"
|
||||||
|
],
|
||||||
|
"begin_street_names": [
|
||||||
|
"Ingolstädter Straße",
|
||||||
|
"B 13"
|
||||||
|
],
|
||||||
|
"bearing_before": 88,
|
||||||
|
"bearing_after": 178,
|
||||||
|
"time": 147.528,
|
||||||
|
"length": 1.064,
|
||||||
|
"cost": 230.646,
|
||||||
|
"begin_shape_index": 29,
|
||||||
|
"end_shape_index": 65,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 19,
|
||||||
|
"instruction": "Auf die Auffahrt nach links abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Auf die Auffahrt nach links abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Auf die Auffahrt nach links abbiegen.",
|
||||||
|
"street_names": [
|
||||||
|
"Schenkendorfstraße"
|
||||||
|
],
|
||||||
|
"bearing_before": 188,
|
||||||
|
"bearing_after": 98,
|
||||||
|
"time": 61.597,
|
||||||
|
"length": 0.374,
|
||||||
|
"cost": 117.338,
|
||||||
|
"begin_shape_index": 65,
|
||||||
|
"end_shape_index": 84,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 24,
|
||||||
|
"instruction": "Links halten auf B 2R.",
|
||||||
|
"verbal_transition_alert_instruction": "Links halten auf B 2R.",
|
||||||
|
"verbal_pre_transition_instruction": "Links halten auf B 2R.",
|
||||||
|
"verbal_post_transition_instruction": "6 Kilometer weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"B 2R"
|
||||||
|
],
|
||||||
|
"bearing_before": 117,
|
||||||
|
"bearing_after": 118,
|
||||||
|
"time": 509.658,
|
||||||
|
"length": 6.37,
|
||||||
|
"cost": 580.602,
|
||||||
|
"begin_shape_index": 84,
|
||||||
|
"end_shape_index": 240,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 20,
|
||||||
|
"instruction": "An der Ausfahrt rechts abfahren.",
|
||||||
|
"verbal_transition_alert_instruction": "An der Ausfahrt rechts abfahren.",
|
||||||
|
"verbal_pre_transition_instruction": "An der Ausfahrt rechts abfahren.",
|
||||||
|
"verbal_post_transition_instruction": "einen Kilometer weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Ampfingstraße"
|
||||||
|
],
|
||||||
|
"bearing_before": 191,
|
||||||
|
"bearing_after": 206,
|
||||||
|
"time": 133.661,
|
||||||
|
"length": 1.031,
|
||||||
|
"cost": 226.661,
|
||||||
|
"begin_shape_index": 240,
|
||||||
|
"end_shape_index": 280,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 10,
|
||||||
|
"instruction": "Rechts auf Anzinger Straße abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Rechts auf Anzinger Straße abbiegen.",
|
||||||
|
"verbal_succinct_transition_instruction": "Rechts abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Rechts auf Anzinger Straße abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "1.5 Kilometer weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Anzinger Straße"
|
||||||
|
],
|
||||||
|
"bearing_before": 182,
|
||||||
|
"bearing_after": 277,
|
||||||
|
"time": 211.637,
|
||||||
|
"length": 1.444,
|
||||||
|
"cost": 450.654,
|
||||||
|
"begin_shape_index": 280,
|
||||||
|
"end_shape_index": 334,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 15,
|
||||||
|
"instruction": "Links auf Hohenwaldeckstraße abbiegen.",
|
||||||
|
"verbal_transition_alert_instruction": "Links auf Hohenwaldeckstraße abbiegen.",
|
||||||
|
"verbal_succinct_transition_instruction": "Links abbiegen.",
|
||||||
|
"verbal_pre_transition_instruction": "Links auf Hohenwaldeckstraße abbiegen.",
|
||||||
|
"verbal_post_transition_instruction": "200 Meter weiter der Route folgen.",
|
||||||
|
"street_names": [
|
||||||
|
"Hohenwaldeckstraße"
|
||||||
|
],
|
||||||
|
"bearing_before": 249,
|
||||||
|
"bearing_after": 170,
|
||||||
|
"time": 45.365,
|
||||||
|
"length": 0.183,
|
||||||
|
"cost": 84.344,
|
||||||
|
"begin_shape_index": 334,
|
||||||
|
"end_shape_index": 342,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 6,
|
||||||
|
"instruction": "Hohenwaldeckstr. 27 befindet sich auf der linken Seite.",
|
||||||
|
"verbal_transition_alert_instruction": "Hohenwaldeckstr. 27 befindet sich auf der linken Seite.",
|
||||||
|
"verbal_pre_transition_instruction": "Hohenwaldeckstr. 27 befindet sich auf der linken Seite.",
|
||||||
|
"bearing_before": 184,
|
||||||
|
"time": 0,
|
||||||
|
"length": 0,
|
||||||
|
"cost": 0,
|
||||||
|
"begin_shape_index": 342,
|
||||||
|
"end_shape_index": 342,
|
||||||
|
"travel_mode": "drive",
|
||||||
|
"travel_type": "car"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": {
|
||||||
|
"level_changes": [
|
||||||
|
[220, -1]
|
||||||
|
],
|
||||||
|
"has_time_restrictions": false,
|
||||||
|
"has_toll": false,
|
||||||
|
"has_highway": false,
|
||||||
|
"has_ferry": false,
|
||||||
|
"min_lat": 48.116486,
|
||||||
|
"min_lon": 11.578422,
|
||||||
|
"max_lat": 48.186957,
|
||||||
|
"max_lon": 11.616382,
|
||||||
|
"time": 1278.611,
|
||||||
|
"length": 11.123,
|
||||||
|
"cost": 2031.412
|
||||||
|
},
|
||||||
|
"shape": "mk_|zA_}vaUA^MzKKhKMrKMrLEbEeLs@kGa@yV}AmIa@cJ]g@AgQc@TgTn@ak@\\}Y`@u~@NiZRss@Ekc@AcGAwE?iB@yN@mH@_L?sAOiVEiGrISbH[|s@kC`KY~Qw@dk@mBdBErH]bIa@pNk@pAGxgA}ErQw@f`@eB`AAhEOjDO~Kg@bh@cCpTcAtEUlBKtFMbk@cBpt@eDfScAlH]hHY`HTdATbBl@rAd@|Bz@xBr@|F`AzD\\l@mHHsCAeDcAkImBiLs@}Ii@wOh@a]vAu[bB{VjCmXjCuUtDoU~A}JjBmIvDmOvDgOfCiJdB}HvAsG|FwSzGaV`IgWdC_K\\cG~Pii@pUcr@dYaz@lEkMdDsPpMm]|Tqj@tQwc@jQsb@nVwm@vEmL`k@suAxHyPzFaKrBaD|AmBxCeDpC}BvDmCnEyBnDuAzFyAhDWdDW|D?~CPjFj@lHrB|QzI~O~Jb]lS`ZbR~NnIhCdBdDtBb`Azk@fhAdr@vN~I~l@|]vr@vVb]jElZw@xG{@jEw@`KiCfLiFrJ_GnPaNjJaJzJoNxMgUtU}g@d]_w@f_@ev@tO_YhRmYbHwIxG_I|NwN~AyAdTiP~b@iZ~J}GxScQ`JoIfGwGtEwFnCqDbEaG~CcFhHgMrGcNxHmR|FaQnCwIpAeEdCiIje@}}AnQil@pGySjCyIvSor@nJ{ZpHwVbQyk@zIkXlHkTrOcc@bPic@rUyk@vKoVnMaWfWed@rT_`@jQcZhBwCzCsEtCcElC}CzCiDrCiCnH{GzLwJlGwDfH_ElGeDhHwClHyBrG{BdK_CzMsBzJgAbIq@jI?nYGbSTfGDvEDnGRfWx@|i@lEvpBbUdRrBpLfAl_@jDr|Ghm@hu@jGrWrBpd@fAxG[raBgUl[{HhM}DzOeGdXiLpCuAzwAij@lEcAbF_AnEc@|DDzE[dAGrIh@|APfe@lLbc@lLpWbIbdAnYnKlBf]|Tzi@rZbl@|ZtSjJpOhG~HvB`AVvBl@hBf@vBl@`AXrJrCtZjHhRvCrKpAjAN|Gb@hLr@dLp@xYbB`CPlDNxBLlOv@n{@jE~F\\lDP|Ov@lSn@rGNrGPlHAxICnJCvJBhFBrQL~E?dC?zFJ[xIcB|_@}Add@kAvi@i@zg@AtGEd_@f@lq@lB`|@jApd@tA`l@XpFn@lHf@bFnAdMjA`HnDpQfEfSvElPfBdOvCrWjP|xANrA|@bH\\pC\\lCNnA~Hhn@dB|MnAtJrAlK`AzH~@hHvBvPj@pEl@zFx@zH^hD~BlQdEbZhF`_@rAbJ|AjK~AtKzBrNt@nFv@lFtB`OdCfQbMb_AlDnUrDvTvF|ZzIxh@jDm@zHgBhF{@lC[`L]jMr@bNj@~_@bB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": {
|
||||||
|
"has_time_restrictions": false,
|
||||||
|
"has_toll": false,
|
||||||
|
"has_highway": false,
|
||||||
|
"has_ferry": false,
|
||||||
|
"min_lat": 48.116486,
|
||||||
|
"min_lon": 11.578422,
|
||||||
|
"max_lat": 48.186957,
|
||||||
|
"max_lon": 11.616382,
|
||||||
|
"time": 1278.611,
|
||||||
|
"length": 11.123,
|
||||||
|
"cost": 2031.412
|
||||||
|
},
|
||||||
|
"status_message": "Found route between points",
|
||||||
|
"status": 0,
|
||||||
|
"units": "kilometers",
|
||||||
|
"language": "de-DE"
|
||||||
|
},
|
||||||
|
"id": "my_work_route"
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
package com.kouros.navigation.car
|
package com.kouros.navigation.car
|
||||||
|
|
||||||
import com.kouros.navigation.car.navigation.RoutingModel
|
import android.location.Location
|
||||||
|
import android.location.LocationManager
|
||||||
|
import com.kouros.navigation.data.Constants.home2Location
|
||||||
|
import com.kouros.navigation.data.Constants.homeLocation
|
||||||
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.model.RouteModel
|
||||||
|
import com.kouros.navigation.model.ViewModel
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@@ -14,5 +20,18 @@ class ExampleUnitTest {
|
|||||||
@Test
|
@Test
|
||||||
fun addition_isCorrect() {
|
fun addition_isCorrect() {
|
||||||
assertEquals(4, 2 + 2)
|
assertEquals(4, 2 + 2)
|
||||||
|
val model = RouteModel()
|
||||||
|
val repo = NavigationRepository()
|
||||||
|
val viewModel = ViewModel(repo)
|
||||||
|
val fromLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
fromLocation.latitude = homeLocation.latitude
|
||||||
|
fromLocation.longitude = homeLocation.longitude
|
||||||
|
val toLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
toLocation.latitude = home2Location.latitude
|
||||||
|
toLocation.longitude = home2Location.longitude
|
||||||
|
|
||||||
|
val route = repo.getRoute(fromLocation, toLocation)
|
||||||
|
model.startNavigation(route)
|
||||||
|
println(route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,6 @@ import android.location.LocationManager
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import io.objectbox.annotation.Entity
|
import io.objectbox.annotation.Entity
|
||||||
import io.objectbox.annotation.Id
|
import io.objectbox.annotation.Id
|
||||||
import io.objectbox.annotation.Index
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
data class Category(
|
data class Category(
|
||||||
@@ -52,6 +51,12 @@ data class ContactData(
|
|||||||
val avatar: Uri?
|
val avatar: Uri?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class StepData (
|
||||||
|
var instruction: String,
|
||||||
|
var leftDistance: Double,
|
||||||
|
var bearing: Double
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
//val places = mutableListOf<Place>()
|
//val places = mutableListOf<Place>()
|
||||||
/* Place(
|
/* Place(
|
||||||
@@ -33,7 +33,9 @@ class Contacts(private var context: Context) {
|
|||||||
while (moveToNext()) {
|
while (moveToNext()) {
|
||||||
val contactId = getLong(getColumnIndex(ContactsContract.Data.CONTACT_ID))
|
val contactId = getLong(getColumnIndex(ContactsContract.Data.CONTACT_ID))
|
||||||
val name = getString(getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
|
val name = getString(getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
|
||||||
if (name.contains("Jola") || name.contains("Dominic")
|
if (name.contains("Jola")
|
||||||
|
|| name.contains("Dominic")
|
||||||
|
|| name.contains("Martha")
|
||||||
|| name.contains("Μεντή")
|
|| name.contains("Μεντή")
|
||||||
|| name.contains("David")) {
|
|| name.contains("David")) {
|
||||||
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
|
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.kouros.navigation.model
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
|
import com.kouros.navigation.data.StepData
|
||||||
import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson
|
import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson
|
||||||
import com.kouros.navigation.utils.NavigationUtils.Utils.decodePolyline
|
import com.kouros.navigation.utils.NavigationUtils.Utils.decodePolyline
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
@@ -10,23 +11,19 @@ import org.json.JSONObject
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
open class RouteModel {
|
// Source - https://stackoverflow.com/a
|
||||||
|
// Posted by Dmitrii Bychkov
|
||||||
|
// Retrieved 2025-11-14, License - CC BY-SA 4.0
|
||||||
|
open class RouteModel () {
|
||||||
var polylineLocations: List<List<Double>> = emptyList()
|
var polylineLocations: List<List<Double>> = emptyList()
|
||||||
|
|
||||||
lateinit var maneuvers: JSONArray
|
lateinit var maneuvers: JSONArray
|
||||||
lateinit var locations: JSONArray
|
lateinit var locations: JSONArray
|
||||||
lateinit var summary: JSONObject
|
lateinit var summary: JSONObject
|
||||||
|
|
||||||
lateinit var destination: Place
|
lateinit var destination: Place
|
||||||
var navigating = false
|
var navigating = false
|
||||||
|
|
||||||
var arrived = false
|
var arrived = false
|
||||||
|
|
||||||
var maneuverIndex = 0
|
var maneuverIndex = 0
|
||||||
|
|
||||||
var maneuverType = 0
|
var maneuverType = 0
|
||||||
|
|
||||||
var currentIndex = 0
|
var currentIndex = 0
|
||||||
|
|
||||||
var distanceToStepEnd = 0F
|
var distanceToStepEnd = 0F
|
||||||
@@ -38,13 +35,23 @@ open class RouteModel {
|
|||||||
|
|
||||||
var distanceToRoute = 0F
|
var distanceToRoute = 0F
|
||||||
|
|
||||||
var geoJson = ""
|
var route = ""
|
||||||
|
|
||||||
private fun decodeValhallaRoute(route: String) {
|
data class Builder(
|
||||||
if (route.isEmpty() || route == "[]") {
|
var route: String? = null,
|
||||||
|
var fromLocation: Location? = null,
|
||||||
|
var toLocation: Location? = null) {
|
||||||
|
|
||||||
|
fun route(route: String) = apply { this.route = route }
|
||||||
|
fun fromLocation(fromLocation: Location) = apply { this.fromLocation = fromLocation }
|
||||||
|
fun toLocation(toLocation: Location) = apply { this.toLocation = toLocation }
|
||||||
|
//fun build() = RouteModel(route!!, fromLocation!!, toLocation!!)
|
||||||
|
}
|
||||||
|
private fun decodeValhallaRoute(valhallaRoute: String) {
|
||||||
|
if (valhallaRoute.isEmpty() || valhallaRoute == "[]") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
val jObject = JSONObject(route)
|
val jObject = JSONObject(valhallaRoute)
|
||||||
val trip = jObject.getJSONObject("trip")
|
val trip = jObject.getJSONObject("trip")
|
||||||
locations = trip.getJSONArray("locations")
|
locations = trip.getJSONArray("locations")
|
||||||
val legs = trip.getJSONArray("legs")
|
val legs = trip.getJSONArray("legs")
|
||||||
@@ -54,13 +61,13 @@ open class RouteModel {
|
|||||||
polylineLocations = decodePolyline(shape)
|
polylineLocations = decodePolyline(shape)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createNavigationRoute(route: String) {
|
fun startNavigation(valhallaRoute: String) {
|
||||||
decodeValhallaRoute(route)
|
decodeValhallaRoute(valhallaRoute)
|
||||||
for (i in 0..<maneuvers.length()) {
|
for (i in 0..<maneuvers.length()) {
|
||||||
val maneuver = (maneuvers[i] as JSONObject)
|
val maneuver = (maneuvers[i] as JSONObject)
|
||||||
routingManeuvers.add(maneuver)
|
routingManeuvers.add(maneuver)
|
||||||
}
|
}
|
||||||
geoJson = createGeoJson(polylineLocations)
|
route = createGeoJson(polylineLocations)
|
||||||
navigating = true
|
navigating = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +95,31 @@ open class RouteModel {
|
|||||||
distanceToRoute = nearestDistance
|
distanceToRoute = nearestDistance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun currentStep(): StepData {
|
||||||
|
var bearing = 0
|
||||||
|
val maneuver = (maneuvers[maneuverIndex] as JSONObject)
|
||||||
|
var text = ""
|
||||||
|
if (maneuver.optJSONArray("street_names") != null) {
|
||||||
|
text = maneuver.getJSONArray("street_names").get(0) as String
|
||||||
|
}
|
||||||
|
if (maneuver.has("bearing_after")) {
|
||||||
|
bearing = maneuver.getInt("bearing_after")
|
||||||
|
}
|
||||||
|
val distanceStepLeft = leftStepDistance() * 1000
|
||||||
|
when (distanceStepLeft) {
|
||||||
|
in 0.0..100.0 -> {
|
||||||
|
if (maneuverIndex < maneuvers.length()) {
|
||||||
|
val maneuver = (maneuvers[maneuverIndex + 1] as JSONObject)
|
||||||
|
if (maneuver.optJSONArray("street_names") != null) {
|
||||||
|
text = maneuver.getJSONArray("street_names").get(0) as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StepData(text, distanceStepLeft, bearing.toDouble())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Calculates the index in a maneuver. */
|
/** Calculates the index in a maneuver. */
|
||||||
private fun calculateCurrentIndex(
|
private fun calculateCurrentIndex(
|
||||||
@@ -159,8 +191,6 @@ open class RouteModel {
|
|||||||
val maneuver = routingManeuvers[maneuverIndex]
|
val maneuver = routingManeuvers[maneuverIndex]
|
||||||
var leftDistance = maneuver.getDouble("length")
|
var leftDistance = maneuver.getDouble("length")
|
||||||
if (endIndex > 0) {
|
if (endIndex > 0) {
|
||||||
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
|
|
||||||
//leftDistance = leftDistance * percent / 100
|
|
||||||
leftDistance = (distanceToStepEnd / 1000).toDouble()
|
leftDistance = (distanceToStepEnd / 1000).toDouble()
|
||||||
}
|
}
|
||||||
return leftDistance
|
return leftDistance
|
||||||
@@ -190,11 +220,11 @@ open class RouteModel {
|
|||||||
return arrived
|
return arrived
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopNavigating() {
|
fun stopNavigation() {
|
||||||
navigating = false
|
navigating = false
|
||||||
polylineLocations = mutableListOf()
|
polylineLocations = mutableListOf()
|
||||||
routingManeuvers = mutableListOf()
|
routingManeuvers = mutableListOf()
|
||||||
geoJson = ""
|
route = ""
|
||||||
maneuverIndex = 0
|
maneuverIndex = 0
|
||||||
currentIndex = 0
|
currentIndex = 0
|
||||||
distanceToStepEnd = 0F
|
distanceToStepEnd = 0F
|
||||||
|
|||||||
@@ -105,4 +105,20 @@ class NavigationUtils() {
|
|||||||
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
|
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateZoom(speed: Double?): Double {
|
||||||
|
if (speed == null) {
|
||||||
|
return 18.0
|
||||||
|
}
|
||||||
|
val zoom = when (speed.toInt()) {
|
||||||
|
in 0..10 -> 17.0
|
||||||
|
in 11..20 -> 17.0
|
||||||
|
in 21..30 -> 17.0
|
||||||
|
in 31..40 -> 16.0
|
||||||
|
in 41..50 -> 15.0
|
||||||
|
in 51..60 -> 14.0
|
||||||
|
else -> 11
|
||||||
|
}
|
||||||
|
return zoom.toDouble()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ ui = "1.9.4"
|
|||||||
material3 = "1.4.0"
|
material3 = "1.4.0"
|
||||||
runtimeLivedata = "1.9.4"
|
runtimeLivedata = "1.9.4"
|
||||||
foundation = "1.9.4"
|
foundation = "1.9.4"
|
||||||
|
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.9.4"
|
runtime = "1.9.4"
|
||||||
@@ -38,7 +39,7 @@ androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "ca
|
|||||||
#objectbox-kotlin = { module = "io.objectbox:objectbox-kotlin", version.ref = "objectboxKotlin" }
|
#objectbox-kotlin = { module = "io.objectbox:objectbox-kotlin", version.ref = "objectboxKotlin" }
|
||||||
ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
|
ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
|
||||||
maplibre-compose = { module = "org.maplibre.compose:maplibre-compose", version.ref = "maplibre-compose" }
|
maplibre-compose = { module = "org.maplibre.compose:maplibre-compose", version.ref = "maplibre-compose" }
|
||||||
maplibre-composeMaterial3 = { module = "org.maplibre.compose:maplibre-compose-material3", version = "maplibre-compose" }
|
maplibre-composeMaterial3 = { module = "org.maplibre.compose:maplibre-compose-material3", version = "maplibre-composeMaterial3" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||||
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
|
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
|
||||||
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
|
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
|
||||||
|
|||||||
Reference in New Issue
Block a user