Speed radar
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 = 10
|
versionCode = 11
|
||||||
versionName = "0.1.3.10"
|
versionName = "0.1.3.11"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
|
<!-- <uses-permission android:name="android.permission.READ_CONTACTS"/>-->
|
||||||
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
|
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
|
||||||
tools:ignore="MockLocation" />
|
tools:ignore="MockLocation" />
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:theme="@style/Theme.Navigation">
|
android:theme="@style/Theme.Navigation">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import com.google.android.gms.location.FusedLocationProviderClient
|
|||||||
import com.google.android.gms.location.LocationServices
|
import com.google.android.gms.location.LocationServices
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
|
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
|
||||||
@@ -87,8 +88,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (newRoute.isNotEmpty()) {
|
if (newRoute.isNotEmpty()) {
|
||||||
routeModel.startNavigation(newRoute)
|
routeModel.startNavigation(newRoute)
|
||||||
routeData.value = routeModel.route.routeGeoJson
|
routeData.value = routeModel.route.routeGeoJson
|
||||||
//mock.setMockLocation(homeLocation.latitude, homeLocation.longitude)
|
|
||||||
simulate()
|
simulate()
|
||||||
|
//test()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val cameraPosition = MutableLiveData(
|
val cameraPosition = MutableLiveData(
|
||||||
@@ -237,10 +238,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
&& lastLocation.latitude != location.position.latitude
|
&& lastLocation.latitude != location.position.latitude
|
||||||
&& lastLocation.longitude != location.position.longitude
|
&& lastLocation.longitude != location.position.longitude
|
||||||
) {
|
) {
|
||||||
if (lastLocation.latitude != 0.0 && !overpass) {
|
|
||||||
//viewModel.getAmenities(Constants.CHARGING_STATION, lastLocation)
|
|
||||||
//overpass = true
|
|
||||||
}
|
|
||||||
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()) {
|
||||||
@@ -249,6 +246,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
|
if (route.currentManeuverIndex + 1 <= route.maneuvers.size) {
|
||||||
nextStepData.value = nextStep()
|
nextStepData.value = nextStep()
|
||||||
}
|
}
|
||||||
|
if (routeState.maneuverType == 39
|
||||||
|
&& leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE
|
||||||
|
) {
|
||||||
|
stopNavigation()
|
||||||
|
routeState = routeState.copy(arrived = true)
|
||||||
|
routeData.value = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
val bearing = bearing(lastLocation, currentLocation, cameraPosition.value!!.bearing)
|
||||||
@@ -312,4 +316,17 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
for ((index, loc) in routeModel.route.waypoints.withIndex()) {
|
||||||
|
if (index > 300) {
|
||||||
|
routeModel.updateLocation(location(loc[0], loc[1]))
|
||||||
|
routeModel.currentStep()
|
||||||
|
if (routeModel.route.currentManeuverIndex + 1 <= routeModel.route.maneuvers.size) {
|
||||||
|
nextStepData.value = routeModel.nextStep()
|
||||||
|
}
|
||||||
|
println(routeModel.routeState.maneuverType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
@@ -32,8 +34,10 @@ android {
|
|||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlin {
|
||||||
jvmTarget = "11"
|
compilerOptions {
|
||||||
|
jvmTarget = JvmTarget.JVM_11
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
|
|||||||
routeModel.stopNavigation()
|
routeModel.stopNavigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var uriHost: String = "navigation"
|
var uriHost: String = "navigation"
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import com.kouros.navigation.data.Constants
|
|||||||
import com.kouros.navigation.data.ObjectBox
|
import com.kouros.navigation.data.ObjectBox
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.utils.bearing
|
import com.kouros.navigation.utils.bearing
|
||||||
import com.kouros.navigation.utils.calcTilt
|
import com.kouros.navigation.utils.calculateTilt
|
||||||
import com.kouros.navigation.utils.calculateZoom
|
import com.kouros.navigation.utils.calculateZoom
|
||||||
import com.kouros.navigation.utils.duration
|
import com.kouros.navigation.utils.duration
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
@@ -225,7 +225,7 @@ class SurfaceRenderer(
|
|||||||
} else {
|
} else {
|
||||||
cameraPosition.value!!.zoom + 1.0
|
cameraPosition.value!!.zoom + 1.0
|
||||||
}
|
}
|
||||||
tilt = calcTilt(newZoom, tilt)
|
tilt = calculateTilt(newZoom, tilt)
|
||||||
updateCameraPosition(
|
updateCameraPosition(
|
||||||
cameraPosition.value!!.bearing,
|
cameraPosition.value!!.bearing,
|
||||||
newZoom,
|
newZoom,
|
||||||
@@ -310,7 +310,7 @@ class SurfaceRenderer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setCategories(location: Location, route: String) {
|
fun setCategories(location: Location, route: String) {
|
||||||
viewStyle = ViewStyle.SEARCH_VIEW
|
viewStyle = ViewStyle.AMENITY_VIEW
|
||||||
routeData.value = route
|
routeData.value = route
|
||||||
updateCameraPosition(
|
updateCameraPosition(
|
||||||
0.0,
|
0.0,
|
||||||
@@ -319,14 +319,13 @@ class SurfaceRenderer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCategoryLocation(location: Location) {
|
fun setCategoryLocation(location: Location, category: String) {
|
||||||
viewStyle = ViewStyle.SEARCH_VIEW
|
viewStyle = ViewStyle.AMENITY_VIEW
|
||||||
cameraPosition.postValue(
|
cameraPosition.postValue(
|
||||||
cameraPosition.value!!.copy(
|
cameraPosition.value!!.copy(
|
||||||
target = Position(location.longitude, location.latitude)
|
target = Position(location.longitude, location.latitude)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion
|
companion
|
||||||
@@ -337,6 +336,6 @@ class SurfaceRenderer(
|
|||||||
|
|
||||||
|
|
||||||
enum class ViewStyle {
|
enum class ViewStyle {
|
||||||
VIEW, PREVIEW, PAN_VIEW, SEARCH_VIEW
|
VIEW, PREVIEW, PAN_VIEW, AMENITY_VIEW
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -33,16 +33,18 @@ 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
|
||||||
import org.maplibre.compose.expressions.dsl.zoom
|
import org.maplibre.compose.expressions.dsl.zoom
|
||||||
import org.maplibre.compose.layers.Anchor
|
import org.maplibre.compose.layers.Anchor
|
||||||
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.layers.SymbolLayer
|
import org.maplibre.compose.layers.SymbolLayer
|
||||||
@@ -58,9 +60,15 @@ 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.Feature
|
||||||
import org.maplibre.spatialk.geojson.FeatureCollection
|
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
|
||||||
@@ -103,19 +111,22 @@ fun MapLibre(
|
|||||||
if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) {
|
if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) {
|
||||||
BuildingLayer(tiles)
|
BuildingLayer(tiles)
|
||||||
}
|
}
|
||||||
RouteLayer(route, viewStyle)
|
if (viewStyle == ViewStyle.AMENITY_VIEW) {
|
||||||
|
AmenityLayer(route)
|
||||||
|
} else {
|
||||||
|
RouteLayer(route)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//Puck(cameraState, lastLocation)
|
//Puck(cameraState, lastLocation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
|
fun RouteLayer(routeData: String?) {
|
||||||
if (routeData != null && routeData.isNotEmpty()) {
|
if (routeData != null && routeData.isNotEmpty()) {
|
||||||
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
||||||
if (viewStyle == ViewStyle.VIEW) {
|
|
||||||
LineLayer(
|
LineLayer(
|
||||||
id = "routes-casing$viewStyle",
|
id = "routes-casing",
|
||||||
source = routes,
|
source = routes,
|
||||||
color = const(Color.White),
|
color = const(Color.White),
|
||||||
width =
|
width =
|
||||||
@@ -129,7 +140,7 @@ fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
LineLayer(
|
LineLayer(
|
||||||
id = "routes$viewStyle",
|
id = "routes",
|
||||||
source = routes,
|
source = routes,
|
||||||
color = const(RouteColor),
|
color = const(RouteColor),
|
||||||
width =
|
width =
|
||||||
@@ -142,21 +153,27 @@ fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
|
|||||||
20 to const(22.dp),
|
20 to const(22.dp),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
SymbolLayer(
|
|
||||||
id = "my-symbol-layer",
|
|
||||||
source = routes,
|
|
||||||
// Convert a drawable resource to a MapLibre image
|
|
||||||
// drawAsSdf = true allows us to tint the image programmatically
|
|
||||||
iconImage = image(painterResource(com.kouros.android.cars.carappservice.R.drawable.ev_station_24px), drawAsSdf = true),
|
|
||||||
// Now we can apply any color we want!
|
|
||||||
iconColor = const(Color.Red),
|
|
||||||
iconSize = const(5.0f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AmenityLayer(routeData: String?) {
|
||||||
|
if (routeData != null && routeData.isNotEmpty()) {
|
||||||
|
val color = if (routeData.contains(Constants.PHARMACY)) {
|
||||||
|
const(Color.Red)
|
||||||
|
} else {
|
||||||
|
const(Color.Green)
|
||||||
|
}
|
||||||
|
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
|
||||||
|
SymbolLayer(
|
||||||
|
id = "amenity-layer",
|
||||||
|
source = routes,
|
||||||
|
iconImage = image(painterResource(R.drawable.ev_station_48px), drawAsSdf = true),
|
||||||
|
iconColor = color,
|
||||||
|
iconSize = const(3.0f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@Composable
|
@Composable
|
||||||
fun BuildingLayer(tiles: Source) {
|
fun BuildingLayer(tiles: Source) {
|
||||||
Anchor.Replace("building-3d") {
|
Anchor.Replace("building-3d") {
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ package com.kouros.navigation.car.navigation
|
|||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.car.app.AppManager
|
||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
|
import androidx.car.app.model.Action
|
||||||
|
import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||||
|
import androidx.car.app.model.Alert
|
||||||
|
import androidx.car.app.model.AlertCallback
|
||||||
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.CarText
|
||||||
@@ -28,8 +33,7 @@ import androidx.car.app.navigation.model.Maneuver
|
|||||||
import androidx.car.app.navigation.model.Step
|
import androidx.car.app.navigation.model.Step
|
||||||
import androidx.car.app.navigation.model.TravelEstimate
|
import androidx.car.app.navigation.model.TravelEstimate
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.data.ManeuverType
|
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -117,4 +121,43 @@ class RouteCarModel() : RouteModel() {
|
|||||||
fun createCarIcon(carContext: CarContext, @DrawableRes iconRes: Int): CarIcon {
|
fun createCarIcon(carContext: CarContext, @DrawableRes iconRes: Int): CarIcon {
|
||||||
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
|
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) {
|
||||||
|
carContext.getCarService<AppManager?>(AppManager::class.java)
|
||||||
|
.showAlert(createAlert(carContext, distance, maxSpeed))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAlert(carContext: CarContext, distance: Double, maxSpeed: String?): Alert {
|
||||||
|
val title = createCarText(carContext,R.string.speed_camera)
|
||||||
|
val subtitle = CarText.create(maxSpeed!!)
|
||||||
|
val icon = CarIcon.ALERT
|
||||||
|
|
||||||
|
val dismissAction: Action = createToastAction(
|
||||||
|
carContext,
|
||||||
|
R.string.speed_camera, R.string.exit_action_title,
|
||||||
|
FLAG_DEFAULT
|
||||||
|
)
|
||||||
|
|
||||||
|
return Alert.Builder( /* alertId: */0, title, /* durationMillis: */10000)
|
||||||
|
.setSubtitle(subtitle)
|
||||||
|
.setIcon(icon)
|
||||||
|
.addAction(dismissAction).setCallback(object : AlertCallback {
|
||||||
|
override fun onCancel(reason: Int) {
|
||||||
|
}
|
||||||
|
override fun onDismiss() {
|
||||||
|
}
|
||||||
|
}).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createToastAction(
|
||||||
|
carContext: CarContext,
|
||||||
|
@StringRes titleRes: Int, @StringRes toastStringRes: Int,
|
||||||
|
flags: Int
|
||||||
|
): Action {
|
||||||
|
return Action.Builder()
|
||||||
|
.setOnClickListener { }
|
||||||
|
.setTitle(createCarText(carContext,titleRes))
|
||||||
|
.setFlags(flags)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.data.Category
|
import com.kouros.navigation.data.Category
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants.CHARGING_STATION
|
||||||
|
import com.kouros.navigation.data.Constants.FUEL_STATION
|
||||||
|
import com.kouros.navigation.data.Constants.PHARMACY
|
||||||
|
|
||||||
class CategoriesScreen(
|
class CategoriesScreen(
|
||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
@@ -24,9 +26,9 @@ class CategoriesScreen(
|
|||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
var categories: List<Category> = listOf(
|
var categories: List<Category> = listOf(
|
||||||
Category(id = Constants.FUEL_STATION, name = carContext.getString(R.string.fuel_station)),
|
Category(id = FUEL_STATION, name = carContext.getString(R.string.fuel_station)),
|
||||||
Category(id = Constants.PHARMACY, name = carContext.getString(R.string.pharmacy)),
|
Category(id = PHARMACY, name = carContext.getString(R.string.pharmacy)),
|
||||||
Category(id = Constants.CHARGING_STATION, name = carContext.getString(R.string.charging_station))
|
Category(id = CHARGING_STATION, name = carContext.getString(R.string.charging_station))
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
@@ -36,13 +38,7 @@ class CategoriesScreen(
|
|||||||
itemListBuilder.addItem(
|
itemListBuilder.addItem(
|
||||||
Row.Builder()
|
Row.Builder()
|
||||||
.setTitle(it.name)
|
.setTitle(it.name)
|
||||||
.setImage(CarIcon.Builder(
|
.setImage(carIcon(carContext,it.id))
|
||||||
IconCompat.createWithResource(
|
|
||||||
carContext,
|
|
||||||
com.kouros.android.cars.carappservice.R.drawable.ev_station_24px
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build())
|
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
screenManager
|
screenManager
|
||||||
.pushForResult(
|
.pushForResult(
|
||||||
@@ -64,7 +60,7 @@ class CategoriesScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
surfaceRenderer.viewStyle = ViewStyle.SEARCH_VIEW
|
surfaceRenderer.viewStyle = ViewStyle.AMENITY_VIEW
|
||||||
|
|
||||||
val header = Header.Builder()
|
val header = Header.Builder()
|
||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
@@ -77,3 +73,19 @@ class CategoriesScreen(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun carIcon(context: CarContext, id: String): CarIcon {
|
||||||
|
val resId = when (id) {
|
||||||
|
FUEL_STATION -> R.drawable.local_gas_station_48px
|
||||||
|
PHARMACY -> R.drawable.local_pharmacy_48px
|
||||||
|
CHARGING_STATION -> R.drawable.ev_station_48px
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
return CarIcon.Builder(
|
||||||
|
IconCompat.createWithResource(
|
||||||
|
context,
|
||||||
|
resId as Int
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
@@ -3,12 +3,10 @@ package com.kouros.navigation.car.screen
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.CarToast
|
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.constraints.ConstraintManager
|
import androidx.car.app.constraints.ConstraintManager
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
import androidx.car.app.model.ActionStrip
|
import androidx.car.app.model.ActionStrip
|
||||||
import androidx.car.app.model.CarIcon
|
|
||||||
import androidx.car.app.model.CarText
|
import androidx.car.app.model.CarText
|
||||||
import androidx.car.app.model.Header
|
import androidx.car.app.model.Header
|
||||||
import androidx.car.app.model.ItemList
|
import androidx.car.app.model.ItemList
|
||||||
@@ -17,24 +15,24 @@ import androidx.car.app.model.Row
|
|||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import androidx.car.app.navigation.model.MapController
|
import androidx.car.app.navigation.model.MapController
|
||||||
import androidx.car.app.navigation.model.MapWithContentTemplate
|
import androidx.car.app.navigation.model.MapWithContentTemplate
|
||||||
import androidx.car.app.versioning.CarAppApiLevels
|
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.navigation.NavigationMessage
|
import com.kouros.navigation.car.navigation.NavigationMessage
|
||||||
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
import com.kouros.navigation.utils.GeoUtils.createPointCollection
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
|
import com.kouros.navigation.utils.round
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class CategoryScreen(
|
class CategoryScreen(
|
||||||
private val carContext: CarContext,
|
private val carContext: CarContext,
|
||||||
private val surfaceRenderer: SurfaceRenderer,
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
location: Location,
|
location: Location,
|
||||||
category: String,
|
private val category: String,
|
||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
val viewModel = ViewModel(NavigationRepository())
|
||||||
@@ -52,7 +50,7 @@ class CategoryScreen(
|
|||||||
coordinates.add(listOf(it.lon!!, it.lat!!))
|
coordinates.add(listOf(it.lon!!, it.lat!!))
|
||||||
}
|
}
|
||||||
if (elements.isNotEmpty()) {
|
if (elements.isNotEmpty()) {
|
||||||
val route = createPointCollection(coordinates)
|
val route = createPointCollection(coordinates, category)
|
||||||
surfaceRenderer.setCategories(loc, route)
|
surfaceRenderer.setCategories(loc, route)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
@@ -65,41 +63,25 @@ class CategoryScreen(
|
|||||||
|
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
|
||||||
val listBuilder = ItemList.Builder()
|
val listBuilder = ItemList.Builder()
|
||||||
if (carContext.getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
|
var index = 0
|
||||||
var index = 0
|
val listLimit = min(
|
||||||
val listLimit = min(
|
50,
|
||||||
100,
|
carContext.getCarService(ConstraintManager::class.java)
|
||||||
carContext.getCarService(ConstraintManager::class.java)
|
.getContentLimit(
|
||||||
.getContentLimit(
|
ConstraintManager.CONTENT_LIMIT_TYPE_LIST
|
||||||
ConstraintManager.CONTENT_LIMIT_TYPE_LIST
|
)
|
||||||
)
|
)
|
||||||
)
|
elements.forEach {
|
||||||
elements.forEach {
|
if (index++ < listLimit) {
|
||||||
if (index++ < listLimit) {
|
if (it.tags.operator != null) {
|
||||||
listBuilder.addItem(
|
listBuilder.addItem(
|
||||||
Row.Builder()
|
createItem(it, category)
|
||||||
.setOnClickListener {
|
|
||||||
val location = location(it.lon!!, it.lat!!)
|
|
||||||
surfaceRenderer.setCategoryLocation(location)
|
|
||||||
}
|
|
||||||
.setTitle(it.tags.operator.toString())
|
|
||||||
.setImage(
|
|
||||||
CarIcon.Builder(
|
|
||||||
IconCompat.createWithResource(
|
|
||||||
carContext,
|
|
||||||
com.kouros.android.cars.carappservice.R.drawable.ev_station_24px
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.addText(it.tags.network.toString())
|
|
||||||
.build()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val header = Header.Builder()
|
val header = Header.Builder()
|
||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
.setTitle(carContext.getString(R.string.charging_station))
|
.setTitle(carContext.getString(R.string.charging_station))
|
||||||
@@ -116,11 +98,39 @@ class CategoryScreen(
|
|||||||
getMapActionStrip()
|
getMapActionStrip()
|
||||||
).build()
|
).build()
|
||||||
)
|
)
|
||||||
|
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun secondText(sText: String): CarText {
|
private fun createItem(it: Elements, category: String): Row {
|
||||||
|
var name = ""
|
||||||
|
if (it.tags.name != null) {
|
||||||
|
name = it.tags.name.toString()
|
||||||
|
}
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = it.tags.operator.toString()
|
||||||
|
}
|
||||||
|
val row = Row.Builder()
|
||||||
|
.setOnClickListener {
|
||||||
|
val location = location(it.lon!!, it.lat!!)
|
||||||
|
surfaceRenderer.setCategoryLocation(location, category)
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
.setTitle(name)
|
||||||
|
.setImage(carIcon(carContext, category))
|
||||||
|
if (it.distance < 1000) {
|
||||||
|
row.addText("${(it.distance).toInt()} m")
|
||||||
|
} else {
|
||||||
|
row.addText("${(it.distance / 1000).round(1)} km")
|
||||||
|
}
|
||||||
|
if (category == Constants.CHARGING_STATION) {
|
||||||
|
row.addText("${it.tags.socketType2} X Typ 2 ${it.tags.socketType2Output}")
|
||||||
|
} else {
|
||||||
|
row.addText(carText("${it.tags.openingHours}"))
|
||||||
|
}
|
||||||
|
return row.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun carText(sText: String): CarText {
|
||||||
val secondText =
|
val secondText =
|
||||||
CarText.Builder(
|
CarText.Builder(
|
||||||
"================= " + sText + " ================"
|
"================= " + sText + " ================"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Intent
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@@ -9,6 +7,7 @@ import android.os.Handler
|
|||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
|
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
|
||||||
@@ -21,18 +20,18 @@ import androidx.car.app.navigation.model.MapWithContentTemplate
|
|||||||
import androidx.car.app.navigation.model.MessageInfo
|
import androidx.car.app.navigation.model.MessageInfo
|
||||||
import androidx.car.app.navigation.model.NavigationTemplate
|
import androidx.car.app.navigation.model.NavigationTemplate
|
||||||
import androidx.car.app.navigation.model.RoutingInfo
|
import androidx.car.app.navigation.model.RoutingInfo
|
||||||
import androidx.car.app.notification.CarPendingIntent
|
|
||||||
import androidx.car.app.suggestion.model.Suggestion
|
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
import com.kouros.navigation.car.NavigationCarAppService
|
|
||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.ViewStyle
|
import com.kouros.navigation.car.ViewStyle
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
|
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.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
|
import com.kouros.navigation.data.nominatim.SearchResult
|
||||||
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
|
|
||||||
@@ -41,7 +40,6 @@ class NavigationScreen(
|
|||||||
private var surfaceRenderer: SurfaceRenderer,
|
private var surfaceRenderer: SurfaceRenderer,
|
||||||
private var routeModel: RouteCarModel,
|
private var routeModel: RouteCarModel,
|
||||||
private var listener: Listener
|
private var listener: Listener
|
||||||
|
|
||||||
) :
|
) :
|
||||||
Screen(carContext) {
|
Screen(carContext) {
|
||||||
|
|
||||||
@@ -52,20 +50,14 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
var recentPlace = Place()
|
||||||
lateinit var recentPlace: Place
|
var navigationType = NavigationType.VIEW
|
||||||
|
|
||||||
var recentPlaceFound = false
|
|
||||||
|
|
||||||
var recentPlaceActive = true
|
|
||||||
|
|
||||||
var calculateNewRoute = false
|
|
||||||
val viewModel = ViewModel(NavigationRepository())
|
val viewModel = ViewModel(NavigationRepository())
|
||||||
val observer = Observer<String> { route ->
|
val observer = Observer<String> { route ->
|
||||||
if (route.isNotEmpty()) {
|
if (route.isNotEmpty()) {
|
||||||
|
navigationType = NavigationType.NAVIGATION
|
||||||
routeModel.startNavigation(route)
|
routeModel.startNavigation(route)
|
||||||
surfaceRenderer.setRouteData()
|
surfaceRenderer.setRouteData()
|
||||||
recentPlaceActive = false
|
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,26 +65,48 @@ class NavigationScreen(
|
|||||||
val recentObserver = Observer<Place> { lastPlace ->
|
val recentObserver = Observer<Place> { lastPlace ->
|
||||||
if (!routeModel.isNavigating()) {
|
if (!routeModel.isNavigating()) {
|
||||||
recentPlace = lastPlace
|
recentPlace = lastPlace
|
||||||
recentPlaceFound = true
|
navigationType = NavigationType.RECENT
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val placeObserver = Observer<SearchResult> { searchResult ->
|
||||||
|
val place = Place(
|
||||||
|
name = searchResult.displayName,
|
||||||
|
street = searchResult.address.road,
|
||||||
|
city = searchResult.address.city,
|
||||||
|
latitude = searchResult.lat.toDouble(),
|
||||||
|
longitude = searchResult.lon.toDouble(),
|
||||||
|
category = Constants.CONTACTS,
|
||||||
|
postalCode = searchResult.address.postcode
|
||||||
|
)
|
||||||
|
navigateToPlace(place)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastCameraSearch = 0
|
||||||
|
|
||||||
|
var speedCameras = listOf<Elements>()
|
||||||
|
val speedObserver = Observer<List<Elements>> { cameras ->
|
||||||
|
speedCameras = cameras
|
||||||
|
println("Speed cameras ${speedCameras.size}")
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.route.observe(this, observer)
|
viewModel.route.observe(this, observer)
|
||||||
viewModel.recentPlace.observe(this, recentObserver)
|
viewModel.recentPlace.observe(this, recentObserver)
|
||||||
viewModel.loadRecentPlace(location = surfaceRenderer.lastLocation)
|
viewModel.loadRecentPlace(location = surfaceRenderer.lastLocation)
|
||||||
|
viewModel.placeLocation.observe(this, placeObserver)
|
||||||
|
viewModel.speedCameras.observe(this, speedObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
val actionStripBuilder = createActionStripBuilder()
|
val actionStripBuilder = createActionStripBuilder()
|
||||||
if (calculateNewRoute) {
|
return when (navigationType) {
|
||||||
return navigationRerouteTemplate(actionStripBuilder)
|
NavigationType.NAVIGATION -> navigationTemplate(actionStripBuilder)
|
||||||
}
|
NavigationType.RECENT -> navigationRecentPlaceTemplate()
|
||||||
return if (routeModel.isNavigating()) {
|
NavigationType.REROUTE -> navigationRerouteTemplate(actionStripBuilder)
|
||||||
navigationTemplate(actionStripBuilder)
|
NavigationType.ARRIVAL -> navigationEndTemplate(actionStripBuilder)
|
||||||
} else {
|
else -> navigationViewTemplate(actionStripBuilder)
|
||||||
navigationEndTemplate(actionStripBuilder)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,30 +125,37 @@ class NavigationScreen(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun navigationViewTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||||
|
return NavigationTemplate.Builder()
|
||||||
|
.setBackgroundColor(CarColor.SECONDARY)
|
||||||
|
.setActionStrip(actionStripBuilder.build())
|
||||||
|
.setMapActionStrip(mapActionStripBuilder().build())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun navigationEndTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
private fun navigationEndTemplate(actionStripBuilder: ActionStrip.Builder): Template {
|
||||||
if (routeModel.routeState.arrived) {
|
if (routeModel.routeState.arrived) {
|
||||||
val timer = object : CountDownTimer(10000, 10000) {
|
val timer = object : CountDownTimer(8000, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {}
|
override fun onTick(millisUntilFinished: Long) {}
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
routeModel.routeState = routeModel.routeState.copy(arrived = false)
|
routeModel.routeState = routeModel.routeState.copy(arrived = false)
|
||||||
|
navigationType = NavigationType.VIEW
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer.start()
|
timer.start()
|
||||||
return navigationArrivedTemplate(actionStripBuilder)
|
return navigationArrivedTemplate(actionStripBuilder)
|
||||||
} else {
|
} else {
|
||||||
return if (recentPlaceFound && recentPlaceActive) {
|
return NavigationTemplate.Builder()
|
||||||
return recentPlaceTemplate()
|
.setBackgroundColor(CarColor.SECONDARY)
|
||||||
} else {
|
.setActionStrip(actionStripBuilder.build())
|
||||||
NavigationTemplate.Builder()
|
.setMapActionStrip(mapActionStripBuilder().build())
|
||||||
.setBackgroundColor(CarColor.SECONDARY)
|
.build()
|
||||||
.setActionStrip(actionStripBuilder.build())
|
|
||||||
.setMapActionStrip(mapActionStripBuilder().build())
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
|
||||||
var street = ""
|
var street = ""
|
||||||
if (routeModel.routeState.destination.street != null) {
|
if (routeModel.routeState.destination.street != null) {
|
||||||
@@ -163,7 +184,7 @@ class NavigationScreen(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recentPlaceTemplate(): Template {
|
fun navigationRecentPlaceTemplate(): Template {
|
||||||
val messageTemplate = MessageTemplate.Builder(
|
val messageTemplate = MessageTemplate.Builder(
|
||||||
recentPlace.name + "\n"
|
recentPlace.name + "\n"
|
||||||
+ recentPlace.city
|
+ recentPlace.city
|
||||||
@@ -266,6 +287,7 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateAction(): Action {
|
private fun navigateAction(): Action {
|
||||||
|
navigationType = NavigationType.NAVIGATION
|
||||||
return Action.Builder()
|
return Action.Builder()
|
||||||
.setIcon(
|
.setIcon(
|
||||||
CarIcon.Builder(
|
CarIcon.Builder(
|
||||||
@@ -296,9 +318,10 @@ class NavigationScreen(
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
recentPlaceActive = false
|
navigationType = NavigationType.VIEW
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
.setFlags(FLAG_DEFAULT)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,28 +391,6 @@ class NavigationScreen(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSuggestion(title: Int, subtitle: Int, icon: CarIcon): Suggestion {
|
|
||||||
return Suggestion.Builder()
|
|
||||||
.setIdentifier("0")
|
|
||||||
.setTitle(carContext.getString(title))
|
|
||||||
.setSubtitle(carContext.getString(subtitle))
|
|
||||||
.setIcon(icon)
|
|
||||||
.setAction(
|
|
||||||
CarPendingIntent.getCarApp(
|
|
||||||
carContext, 0,
|
|
||||||
Intent().setComponent(
|
|
||||||
ComponentName(
|
|
||||||
carContext,
|
|
||||||
NavigationCarAppService::class.java
|
|
||||||
)
|
|
||||||
),
|
|
||||||
//.setAction(NavigationSession.EXECUTE_SCRIPT),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startSearchScreen() {
|
private fun startSearchScreen() {
|
||||||
screenManager
|
screenManager
|
||||||
.pushForResult(
|
.pushForResult(
|
||||||
@@ -397,26 +398,39 @@ class NavigationScreen(
|
|||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
val place = obj as Place
|
val place = obj as Place
|
||||||
val location = Location(LocationManager.GPS_PROVIDER)
|
if (place.longitude == 0.0) {
|
||||||
location.latitude = place.latitude
|
viewModel.findAddress(
|
||||||
location.longitude = place.longitude
|
"${obj.city} ${obj.street}},",
|
||||||
viewModel.saveRecent(place)
|
currentNavigationLocation
|
||||||
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
|
)
|
||||||
currentNavigationLocation = location
|
// result see observer
|
||||||
routeModel.routeState.destination = place
|
} else {
|
||||||
invalidate()
|
navigateToPlace(place)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun navigateToPlace(place: Place) {
|
||||||
|
navigationType = NavigationType.VIEW
|
||||||
|
val location = location(place.longitude, place.latitude)
|
||||||
|
viewModel.saveRecent(place)
|
||||||
|
currentNavigationLocation = location
|
||||||
|
viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
|
||||||
|
routeModel.routeState.destination = place
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
fun stopNavigation() {
|
fun stopNavigation() {
|
||||||
|
navigationType = NavigationType.VIEW
|
||||||
listener.stopNavigation()
|
listener.stopNavigation()
|
||||||
surfaceRenderer.routeData.value = ""
|
surfaceRenderer.routeData.value = ""
|
||||||
|
lastCameraSearch = 0
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateNewRoute(destination: Place) {
|
fun calculateNewRoute(destination: Place) {
|
||||||
calculateNewRoute = true
|
navigationType = NavigationType.REROUTE
|
||||||
stopNavigation()
|
stopNavigation()
|
||||||
invalidate()
|
invalidate()
|
||||||
val mainThreadHandler = Handler(carContext.mainLooper)
|
val mainThreadHandler = Handler(carContext.mainLooper)
|
||||||
@@ -424,7 +438,7 @@ class NavigationScreen(
|
|||||||
object : CountDownTimer(3000, 1000) {
|
object : CountDownTimer(3000, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {}
|
override fun onTick(millisUntilFinished: Long) {}
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
calculateNewRoute = false
|
navigationType = NavigationType.NAVIGATION
|
||||||
reRoute(destination)
|
reRoute(destination)
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
@@ -437,6 +451,12 @@ class NavigationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateTrip(location: Location) {
|
fun updateTrip(location: Location) {
|
||||||
|
if (lastCameraSearch++ % 100 == 0) {
|
||||||
|
viewModel.getSpeedCameras(location)
|
||||||
|
}
|
||||||
|
if (speedCameras.isNotEmpty()) {
|
||||||
|
updateDistance(location)
|
||||||
|
}
|
||||||
with(routeModel) {
|
with(routeModel) {
|
||||||
updateLocation(location)
|
updateLocation(location)
|
||||||
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
|
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
|
||||||
@@ -445,8 +465,33 @@ class NavigationScreen(
|
|||||||
stopNavigation()
|
stopNavigation()
|
||||||
routeState = routeState.copy(arrived = true)
|
routeState = routeState.copy(arrived = true)
|
||||||
surfaceRenderer.routeData.value = ""
|
surfaceRenderer.routeData.value = ""
|
||||||
|
navigationType = NavigationType.ARRIVAL
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateDistance(
|
||||||
|
location: Location,
|
||||||
|
) {
|
||||||
|
val updatedCameras = mutableListOf<Elements>()
|
||||||
|
speedCameras.forEach {
|
||||||
|
val plLocation =
|
||||||
|
location(longitude = it.lon!!, latitude = it.lat!!)
|
||||||
|
val distance = plLocation.distanceTo(location)
|
||||||
|
it.distance = distance.toDouble()
|
||||||
|
updatedCameras.add(it)
|
||||||
|
}
|
||||||
|
val sortedList = updatedCameras.sortedWith(compareBy { it.distance })
|
||||||
|
val camera = sortedList.first()
|
||||||
|
if (camera.distance < 100) {
|
||||||
|
routeModel.showSpeedCamera(carContext, camera.distance, camera.tags.maxspeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class NavigationType {
|
||||||
|
VIEW, NAVIGATION, REROUTE, RECENT, ARRIVAL
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import android.text.SpannableString
|
|||||||
import androidx.car.app.CarContext
|
import androidx.car.app.CarContext
|
||||||
import androidx.car.app.CarToast
|
import androidx.car.app.CarToast
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
|
import androidx.car.app.constraints.ConstraintManager
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
import androidx.car.app.model.CarIcon
|
import androidx.car.app.model.CarIcon
|
||||||
import androidx.car.app.model.Distance
|
import androidx.car.app.model.Distance
|
||||||
@@ -22,10 +23,14 @@ import com.kouros.data.R
|
|||||||
import com.kouros.navigation.car.SurfaceRenderer
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
import com.kouros.navigation.car.navigation.RouteCarModel
|
import com.kouros.navigation.car.navigation.RouteCarModel
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
|
import com.kouros.navigation.data.Constants.CONTACTS
|
||||||
|
import com.kouros.navigation.data.Constants.FAVORITES
|
||||||
|
import com.kouros.navigation.data.Constants.RECENT
|
||||||
import com.kouros.navigation.data.Constants.categories
|
import com.kouros.navigation.data.Constants.categories
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.model.ViewModel
|
import com.kouros.navigation.model.ViewModel
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
|
||||||
class PlaceListScreen(
|
class PlaceListScreen(
|
||||||
@@ -62,73 +67,74 @@ class PlaceListScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loadPlaces() {
|
fun loadPlaces() {
|
||||||
if (category == Constants.RECENT) {
|
if (category == RECENT) {
|
||||||
viewModel.loadRecentPlaces(carContext, location)
|
viewModel.loadRecentPlaces(carContext, location)
|
||||||
}
|
}
|
||||||
if (category == Constants.CONTACTS) {
|
if (category == CONTACTS) {
|
||||||
viewModel.loadContacts(carContext, location)
|
viewModel.loadContacts(carContext)
|
||||||
}
|
}
|
||||||
if (category == Constants.FAVORITES) {
|
if (category == FAVORITES) {
|
||||||
viewModel.loadFavorites(carContext, location)
|
viewModel.loadFavorites(carContext, location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
val itemListBuilder = ItemList.Builder()
|
val itemListBuilder = ItemList.Builder()
|
||||||
.setNoItemsMessage(carContext.getString(R.string.no_places))
|
.setNoItemsMessage(carContext.getString(R.string.no_places))
|
||||||
places.forEach {
|
places.forEach {
|
||||||
itemListBuilder.addItem(
|
val row = Row.Builder()
|
||||||
Row.Builder()
|
.setImage(contactIcon(it.avatar, it.category))
|
||||||
.addAction(
|
.setTitle(it.name!!)
|
||||||
deleteAction(it)
|
.setOnClickListener {
|
||||||
|
val place = Place(
|
||||||
|
0,
|
||||||
|
it.name,
|
||||||
|
it.category,
|
||||||
|
it.latitude,
|
||||||
|
it.longitude,
|
||||||
|
it.postalCode,
|
||||||
|
it.city,
|
||||||
|
it.street,
|
||||||
|
avatar = null
|
||||||
)
|
)
|
||||||
.setImage(contactIcon(it.avatar, it.category))
|
screenManager
|
||||||
.setTitle(it.name!!)
|
.pushForResult(
|
||||||
.addText(SpannableString(" ").apply {
|
RoutePreviewScreen(
|
||||||
setSpan(
|
carContext,
|
||||||
DistanceSpan.create(
|
surfaceRenderer,
|
||||||
Distance.create(
|
place
|
||||||
it.distance.toDouble(),
|
)
|
||||||
Distance.UNIT_KILOMETERS
|
) { obj: Any? ->
|
||||||
)
|
if (obj != null) {
|
||||||
), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
|
setResult(obj)
|
||||||
)
|
finish()
|
||||||
})
|
}
|
||||||
.setOnClickListener {
|
}
|
||||||
val place = Place(
|
}
|
||||||
0,
|
if (category != CONTACTS) {
|
||||||
it.name,
|
row.addText(SpannableString(" ").apply {
|
||||||
it.category,
|
setSpan(
|
||||||
it.latitude,
|
DistanceSpan.create(
|
||||||
it.longitude,
|
Distance.create(
|
||||||
it.postalCode,
|
it.distance.toDouble(),
|
||||||
it.city,
|
Distance.UNIT_KILOMETERS
|
||||||
it.street,
|
)
|
||||||
avatar = null
|
), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
|
||||||
)
|
)
|
||||||
setResult(place)
|
})
|
||||||
finish()
|
row.addAction(
|
||||||
// screenManager
|
deleteAction(it)
|
||||||
// .pushForResult(
|
)
|
||||||
// RoutePreviewScreen(
|
}
|
||||||
// carContext,
|
itemListBuilder.addItem(
|
||||||
// surfaceRenderer,
|
row.build()
|
||||||
// place
|
|
||||||
// )
|
|
||||||
// ) { obj: Any? ->
|
|
||||||
// if (obj != null) {
|
|
||||||
// setResult(obj)
|
|
||||||
// finish()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
var title = ""
|
var title = ""
|
||||||
when(category) {
|
when (category) {
|
||||||
Constants.RECENT -> title = carContext.getString(R.string.recent_destinations)
|
RECENT -> title = carContext.getString(R.string.recent_destinations)
|
||||||
Constants.CONTACTS -> title = carContext.getString(R.string.contacts)
|
CONTACTS -> title = carContext.getString(R.string.contacts)
|
||||||
Constants.FAVORITES -> title = carContext.getString(R.string.favorites)
|
FAVORITES -> title = carContext.getString(R.string.favorites)
|
||||||
}
|
}
|
||||||
val header = Header.Builder()
|
val header = Header.Builder()
|
||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
|
|||||||
@@ -1,18 +1,3 @@
|
|||||||
/*
|
|
||||||
* Copyright 2024 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.kouros.navigation.car.screen
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
@@ -22,18 +7,23 @@ import androidx.car.app.CarContext
|
|||||||
import androidx.car.app.CarToast
|
import androidx.car.app.CarToast
|
||||||
import androidx.car.app.Screen
|
import androidx.car.app.Screen
|
||||||
import androidx.car.app.model.Action
|
import androidx.car.app.model.Action
|
||||||
|
import androidx.car.app.model.Action.FLAG_DEFAULT
|
||||||
import androidx.car.app.model.Action.FLAG_PRIMARY
|
import androidx.car.app.model.Action.FLAG_PRIMARY
|
||||||
import androidx.car.app.model.ActionStrip
|
import androidx.car.app.model.ActionStrip
|
||||||
|
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.CarText
|
||||||
import androidx.car.app.model.DurationSpan
|
import androidx.car.app.model.DurationSpan
|
||||||
import androidx.car.app.model.Header
|
import androidx.car.app.model.Header
|
||||||
import androidx.car.app.model.ItemList
|
import androidx.car.app.model.ItemList
|
||||||
import androidx.car.app.model.ListTemplate
|
import androidx.car.app.model.ListTemplate
|
||||||
|
import androidx.car.app.model.MessageTemplate
|
||||||
import androidx.car.app.model.Row
|
import androidx.car.app.model.Row
|
||||||
import androidx.car.app.model.Template
|
import androidx.car.app.model.Template
|
||||||
import androidx.car.app.navigation.model.MapController
|
import androidx.car.app.navigation.model.MapController
|
||||||
import androidx.car.app.navigation.model.MapWithContentTemplate
|
import androidx.car.app.navigation.model.MapWithContentTemplate
|
||||||
|
import androidx.car.app.navigation.model.NavigationTemplate
|
||||||
|
import androidx.car.app.navigation.model.RoutingInfo
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
@@ -78,24 +68,20 @@ class RoutePreviewScreen(
|
|||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
val navigateActionIcon: CarIcon = CarIcon.Builder(
|
val navigateActionIcon: CarIcon = CarIcon.Builder(
|
||||||
IconCompat.createWithResource(
|
IconCompat.createWithResource(
|
||||||
carContext, R.drawable.baseline_assistant_navigation_24
|
carContext, R.drawable.navigation_48px
|
||||||
)
|
)
|
||||||
).build()
|
).build()
|
||||||
val navigateAction = Action.Builder()
|
val navigateAction = Action.Builder()
|
||||||
.setFlags(FLAG_PRIMARY)
|
.setFlags(FLAG_DEFAULT)
|
||||||
.setIcon(navigateActionIcon)
|
.setIcon(navigateActionIcon)
|
||||||
.setOnClickListener { this.onNavigate() }
|
.setOnClickListener { this.onNavigate() }
|
||||||
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val itemListBuilder = ItemList.Builder()
|
|
||||||
|
|
||||||
if (routeModel.isNavigating() && routeModel.route.waypoints.isNotEmpty()) {
|
|
||||||
itemListBuilder.addItem(createRow(0, navigateAction))
|
|
||||||
}
|
|
||||||
|
|
||||||
val header = Header.Builder()
|
val header = Header.Builder()
|
||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
.setTitle(carContext.getString(R.string.route_preview))
|
.setTitle(carContext.getString(R.string.route_preview))
|
||||||
|
//.addEndHeaderAction(navigateAction)
|
||||||
.addEndHeaderAction(
|
.addEndHeaderAction(
|
||||||
favoriteAction()
|
favoriteAction()
|
||||||
)
|
)
|
||||||
@@ -104,21 +90,30 @@ class RoutePreviewScreen(
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val timer = object : CountDownTimer(10000, 15000) {
|
val message = if (routeModel.isNavigating() && routeModel.route.waypoints.isNotEmpty()) {
|
||||||
|
createRouteText()
|
||||||
|
} else {
|
||||||
|
CarText.Builder("Wait")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
val messageTemplate = MessageTemplate.Builder(
|
||||||
|
message
|
||||||
|
)
|
||||||
|
.setHeader(header)
|
||||||
|
.addAction(navigateAction)
|
||||||
|
.setLoading(message.toString() == "Wait")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val timer = object : CountDownTimer(5000, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {}
|
override fun onTick(millisUntilFinished: Long) {}
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
onNavigate()
|
//onNavigate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer.start()
|
timer.start()
|
||||||
|
|
||||||
return MapWithContentTemplate.Builder()
|
return MapWithContentTemplate.Builder()
|
||||||
.setContentTemplate(
|
.setContentTemplate(messageTemplate)
|
||||||
ListTemplate.Builder()
|
|
||||||
.setHeader(header)
|
|
||||||
.setSingleList(itemListBuilder.build())
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.setMapController(
|
.setMapController(
|
||||||
MapController.Builder().setMapActionStrip(
|
MapController.Builder().setMapActionStrip(
|
||||||
getMapActionStrip()
|
getMapActionStrip()
|
||||||
@@ -177,18 +172,8 @@ class RoutePreviewScreen(
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
private fun createRow(index: Int, action: Action): Row {
|
|
||||||
val route: CarText = createRouteText(index)
|
|
||||||
return Row.Builder()
|
|
||||||
.setTitle(route)
|
|
||||||
.setOnClickListener { onRouteSelected(index) }
|
|
||||||
.addText("${destination.street!!} ${destination.postalCode} ${destination.city}")
|
|
||||||
.addAction(action)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private fun createRouteText(): CarText {
|
||||||
private fun createRouteText(index: Int): CarText {
|
|
||||||
val time = routeModel.route.summary.time
|
val time = routeModel.route.summary.time
|
||||||
val length = BigDecimal(routeModel.route.distance).setScale(1, RoundingMode.HALF_EVEN)
|
val length = BigDecimal(routeModel.route.distance).setScale(1, RoundingMode.HALF_EVEN)
|
||||||
val firstRoute = SpannableString(" \u00b7 $length km")
|
val firstRoute = SpannableString(" \u00b7 $length km")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class SearchScreen(
|
|||||||
|
|
||||||
var categories: List<Category> = listOf(
|
var categories: List<Category> = listOf(
|
||||||
Category(id = Constants.RECENT, name = carContext.getString(R.string.recent_destinations)),
|
Category(id = Constants.RECENT, name = carContext.getString(R.string.recent_destinations)),
|
||||||
//Category(id = Constants.CONTACTS, name = carContext.getString(R.string.contacts)),
|
Category(id = Constants.CONTACTS, name = carContext.getString(R.string.contacts)),
|
||||||
Category(id = Constants.CATEGORIES, name = carContext.getString(R.string.category_title)),
|
Category(id = Constants.CATEGORIES, name = carContext.getString(R.string.category_title)),
|
||||||
Category(id = Constants.FAVORITES, name = carContext.getString(R.string.favorites))
|
Category(id = Constants.FAVORITES, name = carContext.getString(R.string.favorites))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ class SettingsScreen(
|
|||||||
) : Screen(carContext) {
|
) : Screen(carContext) {
|
||||||
|
|
||||||
override fun onGetTemplate(): Template {
|
override fun onGetTemplate(): Template {
|
||||||
|
|
||||||
val listBuilder = ItemList.Builder()
|
val listBuilder = ItemList.Builder()
|
||||||
listBuilder.addItem(
|
listBuilder.addItem(
|
||||||
buildRowForTemplate(
|
buildRowForTemplate(
|
||||||
|
|||||||
@@ -24,9 +24,11 @@ class ViewModelTest {
|
|||||||
fun routeViewModelTest() {
|
fun routeViewModelTest() {
|
||||||
|
|
||||||
val fromLocation = Location(LocationManager.GPS_PROVIDER)
|
val fromLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
fromLocation.isMock = true
|
||||||
fromLocation.latitude = homeLocation.latitude
|
fromLocation.latitude = homeLocation.latitude
|
||||||
fromLocation.longitude = homeLocation.longitude
|
fromLocation.longitude = homeLocation.longitude
|
||||||
val toLocation = Location(LocationManager.GPS_PROVIDER)
|
val toLocation = Location(LocationManager.GPS_PROVIDER)
|
||||||
|
toLocation.isMock = true
|
||||||
toLocation.latitude = home2Location.latitude
|
toLocation.latitude = home2Location.latitude
|
||||||
toLocation.longitude = home2Location.longitude
|
toLocation.longitude = home2Location.longitude
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ data class Elements (
|
|||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id" ) var id : Long? = null,
|
||||||
@SerializedName("lat" ) var lat : Double? = null,
|
@SerializedName("lat" ) var lat : Double? = null,
|
||||||
@SerializedName("lon" ) var lon : Double? = null,
|
@SerializedName("lon" ) var lon : Double? = null,
|
||||||
@SerializedName("tags" ) var tags : Tags = Tags()
|
@SerializedName("tags" ) var tags : Tags = Tags(),
|
||||||
|
var distance : Double = 0.0
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -12,21 +12,22 @@ 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(category: String, location: Location) : List<Elements> {
|
fun getAmenities(type: String, category: String, location: Location) : List<Elements> {
|
||||||
val boundingBox = getOverpassBbox(location, 2.0)
|
val boundingBox = getOverpassBbox(location, 5.0)
|
||||||
val bb = getBoundingBox2(location, 2.0)
|
|
||||||
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
|
||||||
httpURLConnection.requestMethod = "POST"
|
httpURLConnection.requestMethod = "POST"
|
||||||
httpURLConnection.setRequestProperty(
|
httpURLConnection.setRequestProperty(
|
||||||
"Accept",
|
"Accept",
|
||||||
"application/json"
|
"application/json"
|
||||||
)
|
)
|
||||||
|
// node["highway"="speed_camera"]
|
||||||
|
// node[amenity=$category]
|
||||||
httpURLConnection.setDoOutput(true);
|
httpURLConnection.setDoOutput(true);
|
||||||
// define a query
|
// define search query
|
||||||
val test = """
|
val searchQuery = """
|
||||||
|[out:json];
|
|[out:json];
|
||||||
|(
|
|(
|
||||||
| node[amenity=$category]
|
| node[$type=$category]
|
||||||
| ($boundingBox);
|
| ($boundingBox);
|
||||||
|);
|
|);
|
||||||
|out body;
|
|out body;
|
||||||
@@ -34,7 +35,7 @@ class Overpass {
|
|||||||
|
|
||||||
// Send the JSON we created
|
// Send the JSON we created
|
||||||
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||||
outputStreamWriter.write(test)
|
outputStreamWriter.write(searchQuery)
|
||||||
outputStreamWriter.flush()
|
outputStreamWriter.flush()
|
||||||
// Check if the connection is successful
|
// Check if the connection is successful
|
||||||
val responseCode = httpURLConnection.responseCode
|
val responseCode = httpURLConnection.responseCode
|
||||||
@@ -43,7 +44,7 @@ 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: $response")
|
println("Overpass: $type $response")
|
||||||
return overpass.elements
|
return overpass.elements
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|||||||
@@ -3,20 +3,22 @@ package com.kouros.navigation.data.overpass
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
data class Tags (
|
data class Tags(
|
||||||
|
@SerializedName("name") var name: String? = null,
|
||||||
@SerializedName("amenity" ) var amenity : String? = null,
|
@SerializedName("amenity") var amenity: String? = null,
|
||||||
@SerializedName("authentication:none" ) var authenticationNone : String? = null,
|
@SerializedName("authentication:none") var authenticationNone: String? = null,
|
||||||
@SerializedName("capacity" ) var capacity : String? = null,
|
@SerializedName("capacity") var capacity: String? = null,
|
||||||
@SerializedName("motorcar" ) var motorcar : String? = null,
|
@SerializedName("motorcar") var motorcar: String? = null,
|
||||||
@SerializedName("network" ) var network : String? = null,
|
@SerializedName("network") var network: String? = null,
|
||||||
@SerializedName("opening_hours" ) var openingHours : String? = null,
|
@SerializedName("opening_hours") var openingHours: String? = null,
|
||||||
@SerializedName("operator" ) var operator : String? = null,
|
@SerializedName("operator") var operator: String? = null,
|
||||||
@SerializedName("operator:short" ) var operatorShort : String? = null,
|
@SerializedName("operator:short") var operatorShort: String? = null,
|
||||||
@SerializedName("operator:wikidata" ) var operatorWikidata : String? = null,
|
@SerializedName("operator:wikidata") var operatorWikidata: String? = null,
|
||||||
@SerializedName("operator:wikipedia" ) var operatorWikipedia : String? = null,
|
@SerializedName("operator:wikipedia") var operatorWikipedia: String? = null,
|
||||||
@SerializedName("ref" ) var ref : String? = null,
|
@SerializedName("ref") var ref: String? = null,
|
||||||
@SerializedName("socket:type2" ) var socketType2 : String? = null,
|
@SerializedName("socket:type2") var socketType2: String? = null,
|
||||||
@SerializedName("socket:type2:output" ) var socketType2Output : String? = null
|
@SerializedName("socket:type2:output") var socketType2Output: String? = null,
|
||||||
|
@SerializedName("maxspeed") var maxspeed: String? = null,
|
||||||
|
@SerializedName("direction") var direction: String? = null,
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -36,7 +36,7 @@ class Contacts(private var context: Context) {
|
|||||||
if (name.contains("Jola")
|
if (name.contains("Jola")
|
||||||
|| name.contains("Dominic")
|
|| name.contains("Dominic")
|
||||||
|| name.contains("Martha")
|
|| name.contains("Martha")
|
||||||
|| name.contains("Rena")
|
|| name.contains("Groth")
|
||||||
|| name.contains("David")) {
|
|| name.contains("David")) {
|
||||||
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
|
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
|
||||||
if (mimeType == ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) {
|
if (mimeType == ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) {
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ open class RouteModel() {
|
|||||||
maneuverType = relevantManeuver.type
|
maneuverType = relevantManeuver.type
|
||||||
}
|
}
|
||||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
val maneuverIconPair = maneuverIcon(maneuverType)
|
||||||
|
routeState.maneuverType = maneuverIconPair.first
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
streetName,
|
streetName,
|
||||||
@@ -138,40 +139,7 @@ open class RouteModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun currentStepOld(): StepData {
|
|
||||||
val maneuver = route.currentManeuver()
|
|
||||||
var text = ""
|
|
||||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
|
||||||
text = maneuver.streetNames[0]
|
|
||||||
}
|
|
||||||
val distanceStepLeft = leftStepDistance()
|
|
||||||
when (distanceStepLeft) {
|
|
||||||
in 0.0..Constants.NEXT_STEP_THRESHOLD -> {
|
|
||||||
if (route.currentManeuverIndex < route.maneuvers.size) {
|
|
||||||
val maneuver = route.nextManeuver()
|
|
||||||
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
|
|
||||||
text = maneuver.streetNames[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val type = if (hasArrived(maneuver.type)) {
|
|
||||||
maneuver.type
|
|
||||||
} else {
|
|
||||||
ManeuverType.None.value
|
|
||||||
}
|
|
||||||
var routing: (Pair<Int, Int>) = maneuverIcon(type)
|
|
||||||
when (distanceStepLeft) {
|
|
||||||
in 0.0..NEXT_STEP_THRESHOLD -> {
|
|
||||||
if (route.currentManeuverIndex < route.maneuvers.size) {
|
|
||||||
val maneuver = route.nextManeuver()
|
|
||||||
val maneuverType = maneuver.type
|
|
||||||
routing = maneuverIcon(maneuverType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return StepData(text, distanceStepLeft, routing.first, routing.second, arrivalTime(), travelLeftDistance())
|
|
||||||
}
|
|
||||||
fun nextStep(): StepData {
|
fun nextStep(): StepData {
|
||||||
val maneuver = route.nextManeuver()
|
val maneuver = route.nextManeuver()
|
||||||
val maneuverType = maneuver.type
|
val maneuverType = maneuver.type
|
||||||
@@ -181,7 +149,6 @@ 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]
|
||||||
@@ -319,7 +286,7 @@ open class RouteModel() {
|
|||||||
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
currentTurnIcon = R.drawable.ic_roundabout_ccw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeState.maneuverType = type
|
//routeState.maneuverType = type
|
||||||
return Pair(type, currentTurnIcon)
|
return Pair(type, currentTurnIcon)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +298,7 @@ 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
|
||||||
|| routeState.maneuverType == ManeuverType.Destination.value
|
|| type == ManeuverType.Destination.value
|
||||||
|| routeState.maneuverType == ManeuverType.DestinationLeft.value
|
|| type == ManeuverType.DestinationLeft.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.kouros.navigation.model
|
package com.kouros.navigation.model
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.location.Geocoder
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.compose.runtime.toMutableStateList
|
import androidx.compose.runtime.toMutableStateList
|
||||||
@@ -10,16 +9,15 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.kouros.navigation.data.Constants
|
import com.kouros.navigation.data.Constants
|
||||||
import com.kouros.navigation.data.Locations
|
|
||||||
import com.kouros.navigation.data.NavigationRepository
|
import com.kouros.navigation.data.NavigationRepository
|
||||||
import com.kouros.navigation.data.ObjectBox.boxStore
|
import com.kouros.navigation.data.ObjectBox.boxStore
|
||||||
import com.kouros.navigation.data.overpass.Overpass
|
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
import com.kouros.navigation.data.Place_
|
import com.kouros.navigation.data.Place_
|
||||||
import com.kouros.navigation.data.SearchFilter
|
import com.kouros.navigation.data.SearchFilter
|
||||||
import com.kouros.navigation.data.nominatim.Search
|
import com.kouros.navigation.data.nominatim.Search
|
||||||
import com.kouros.navigation.data.nominatim.SearchResult
|
import com.kouros.navigation.data.nominatim.SearchResult
|
||||||
import com.kouros.navigation.data.overpass.Elements
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
|
import com.kouros.navigation.data.overpass.Overpass
|
||||||
import com.kouros.navigation.utils.NavigationUtils
|
import com.kouros.navigation.utils.NavigationUtils
|
||||||
import com.kouros.navigation.utils.location
|
import com.kouros.navigation.utils.location
|
||||||
import io.objectbox.kotlin.boxFor
|
import io.objectbox.kotlin.boxFor
|
||||||
@@ -31,34 +29,42 @@ import java.time.ZoneOffset
|
|||||||
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||||
|
|
||||||
val route: MutableLiveData<String> by lazy {
|
val route: MutableLiveData<String> by lazy {
|
||||||
MutableLiveData<String>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val previewRoute: MutableLiveData<String> by lazy {
|
val previewRoute: MutableLiveData<String> by lazy {
|
||||||
MutableLiveData<String>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val recentPlace: MutableLiveData<Place> by lazy {
|
val recentPlace: MutableLiveData<Place> by lazy {
|
||||||
MutableLiveData<Place>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
val places: MutableLiveData<List<Place>> by lazy {
|
val places: MutableLiveData<List<Place>> by lazy {
|
||||||
MutableLiveData<List<Place>>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val favorites: MutableLiveData<List<Place>> by lazy {
|
val favorites: MutableLiveData<List<Place>> by lazy {
|
||||||
MutableLiveData<List<Place>>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val searchPlaces: MutableLiveData<List<SearchResult>> by lazy {
|
val searchPlaces: MutableLiveData<List<SearchResult>> by lazy {
|
||||||
MutableLiveData<List<SearchResult>>()
|
MutableLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
val placeLocation: MutableLiveData<SearchResult> by lazy {
|
||||||
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val contactAddress: MutableLiveData<List<Place>> by lazy {
|
val contactAddress: MutableLiveData<List<Place>> by lazy {
|
||||||
MutableLiveData<List<Place>>()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
val elements: MutableLiveData<List<Elements>> by lazy {
|
val elements: MutableLiveData<List<Elements>> by lazy {
|
||||||
MutableLiveData<List<Elements>>()
|
MutableLiveData()
|
||||||
|
}
|
||||||
|
|
||||||
|
val speedCameras: MutableLiveData<List<Elements>> by lazy {
|
||||||
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadRecentPlace(location: Location) {
|
fun loadRecentPlace(location: Location) {
|
||||||
@@ -98,9 +104,15 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
query.close()
|
query.close()
|
||||||
for (place in results) {
|
for (place in results) {
|
||||||
val plLocation = location(place.longitude, place.latitude)
|
val plLocation = location(place.longitude, place.latitude)
|
||||||
val distance =
|
if (place.latitude != 0.0) {
|
||||||
repository.getRouteDistance(location, plLocation, getSearchFilter(context))
|
val distance =
|
||||||
place.distance = distance.toFloat()
|
repository.getRouteDistance(
|
||||||
|
location,
|
||||||
|
plLocation,
|
||||||
|
getSearchFilter(context)
|
||||||
|
)
|
||||||
|
place.distance = distance.toFloat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
places.postValue(results)
|
places.postValue(results)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -164,52 +176,54 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadContacts(context: Context, currentLocation: Location) {
|
fun loadContacts(context: Context) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
val contactList = mutableListOf<Place>()
|
||||||
val geocoder = Geocoder(context)
|
val contacts = Contacts(context = context)
|
||||||
val contactList = mutableListOf<Place>()
|
val addresses = contacts.retrieveContacts()
|
||||||
val contacts = Contacts(context = context)
|
for (address in addresses) {
|
||||||
val addresses = contacts.retrieveContacts()
|
val addressLines = address.address.split("\n")
|
||||||
for (address in addresses) {
|
if (addressLines.size > 1) {
|
||||||
val addressLines = address.address.split("\n")
|
contactList.add(
|
||||||
geocoder.getFromLocationName(
|
Place(
|
||||||
address.address, 5
|
id = address.contactId,
|
||||||
) {
|
name = address.name + " " + addressLines[0] + " " + addressLines[1],
|
||||||
for (adr in it) {
|
Constants.CONTACTS,
|
||||||
if (addressLines.size > 1) {
|
street = addressLines[0],
|
||||||
val plLocation = location(adr.longitude, adr.latitude)
|
city = addressLines[1],
|
||||||
val distance =
|
avatar = address.avatar,
|
||||||
repository.getRouteDistance(
|
longitude = 0.0,
|
||||||
currentLocation,
|
latitude = 0.0,
|
||||||
plLocation,
|
distance = 0F,
|
||||||
getSearchFilter(context)
|
)
|
||||||
)
|
)
|
||||||
contactList.add(
|
|
||||||
Place(
|
|
||||||
id = address.contactId,
|
|
||||||
name = address.name + " " + addressLines[0] + " " + addressLines[1],
|
|
||||||
Constants.CONTACTS,
|
|
||||||
street = addressLines[0],
|
|
||||||
city = addressLines[1],
|
|
||||||
latitude = adr.latitude,
|
|
||||||
longitude = adr.longitude,
|
|
||||||
avatar = address.avatar,
|
|
||||||
distance = distance.toFloat()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
contactAddress.postValue(contactList)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
contactAddress.postValue(contactList)
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun findAddress(search: String, location: Location) {
|
||||||
|
var sortedList: List<SearchResult>
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val placesJson = repository.searchPlaces(search, location)
|
||||||
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
|
val places = gson.fromJson(placesJson, Search::class.java)
|
||||||
|
val distPlaces = mutableListOf<SearchResult>()
|
||||||
|
places.forEach {
|
||||||
|
val plLocation =
|
||||||
|
location(longitude = it.lon.toDouble(), latitude = it.lat.toDouble())
|
||||||
|
val distance = plLocation.distanceTo(location)
|
||||||
|
it.distance = distance
|
||||||
|
distPlaces.add(it)
|
||||||
|
}
|
||||||
|
sortedList = distPlaces.sortedWith(compareBy { it.distance })
|
||||||
|
if (sortedList.isNotEmpty()) {
|
||||||
|
placeLocation.postValue(sortedList.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fun searchPlaces(search: String, location: Location) {
|
fun searchPlaces(search: String, location: Location) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val placesJson = repository.searchPlaces(search, location)
|
val placesJson = repository.searchPlaces(search, location)
|
||||||
@@ -223,7 +237,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
it.distance = distance
|
it.distance = distance
|
||||||
distPlaces.add(it)
|
distPlaces.add(it)
|
||||||
}
|
}
|
||||||
val sortedList = distPlaces.sortedWith(compareBy({ it.distance }))
|
val sortedList = distPlaces.sortedWith(compareBy { it.distance })
|
||||||
searchPlaces.postValue(sortedList)
|
searchPlaces.postValue(sortedList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,8 +251,33 @@ 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(category, location)
|
val amenities = Overpass().getAmenities("amenity", category, location)
|
||||||
elements.postValue(amenities)
|
val distAmenities = mutableListOf<Elements>()
|
||||||
|
amenities.forEach {
|
||||||
|
val plLocation =
|
||||||
|
location(longitude = it.lon!!, latitude = it.lat!!)
|
||||||
|
val distance = plLocation.distanceTo(location)
|
||||||
|
it.distance = distance.toDouble()
|
||||||
|
distAmenities.add(it)
|
||||||
|
}
|
||||||
|
val sortedList = distAmenities.sortedWith(compareBy { it.distance })
|
||||||
|
elements.postValue(sortedList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSpeedCameras(location: Location) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val amenities = Overpass().getAmenities("highway", "speed_camera", location)
|
||||||
|
val distAmenities = mutableListOf<Elements>()
|
||||||
|
amenities.forEach {
|
||||||
|
val plLocation =
|
||||||
|
location(longitude = it.lon!!, latitude = it.lat!!)
|
||||||
|
val distance = plLocation.distanceTo(location)
|
||||||
|
it.distance = distance.toDouble()
|
||||||
|
distAmenities.add(it)
|
||||||
|
}
|
||||||
|
val sortedList = distAmenities.sortedWith(compareBy { it.distance })
|
||||||
|
speedCameras.postValue(sortedList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.kouros.navigation.utils
|
|||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import com.kouros.navigation.data.BoundingBox
|
import com.kouros.navigation.data.BoundingBox
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
import org.maplibre.geojson.FeatureCollection
|
import org.maplibre.geojson.FeatureCollection
|
||||||
import org.maplibre.geojson.Point
|
import org.maplibre.geojson.Point
|
||||||
import org.maplibre.spatialk.geojson.Feature
|
import org.maplibre.spatialk.geojson.Feature
|
||||||
@@ -102,12 +104,12 @@ object GeoUtils {
|
|||||||
return featureCollection.toJson()
|
return featureCollection.toJson()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createPointCollection(lineCoordinates: List<List<Double>>): String {
|
fun createPointCollection(lineCoordinates: List<List<Double>>, category: String): String {
|
||||||
val featureCollection = buildFeatureCollection {
|
val featureCollection = buildFeatureCollection {
|
||||||
lineCoordinates.forEach {
|
lineCoordinates.forEach {
|
||||||
addFeature {
|
addFeature {
|
||||||
geometry = org.maplibre.spatialk.geojson.Point(it[0], it[1])
|
geometry = org.maplibre.spatialk.geojson.Point(it[0], it[1])
|
||||||
properties = null
|
properties = buildJsonObject { put("category", category) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,7 @@ import android.content.Context
|
|||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.kouros.navigation.data.BoundingBox
|
|
||||||
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
import com.kouros.navigation.data.Constants.SHARED_PREF_KEY
|
||||||
import org.maplibre.geojson.FeatureCollection
|
|
||||||
import org.maplibre.geojson.Point
|
|
||||||
import org.maplibre.spatialk.geojson.Feature
|
|
||||||
import org.maplibre.spatialk.geojson.dsl.addFeature
|
|
||||||
import org.maplibre.spatialk.geojson.dsl.buildFeatureCollection
|
|
||||||
import org.maplibre.spatialk.geojson.dsl.buildLineString
|
|
||||||
import org.maplibre.spatialk.geojson.toJson
|
|
||||||
import org.maplibre.turf.TurfMeasurement
|
|
||||||
import org.maplibre.turf.TurfMisc
|
|
||||||
import java.lang.Math.toDegrees
|
|
||||||
import java.lang.Math.toRadians
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@@ -24,7 +12,6 @@ import java.time.ZonedDateTime
|
|||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.cos
|
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
@@ -33,7 +20,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
object NavigationUtils {
|
object NavigationUtils {
|
||||||
|
|
||||||
fun getBooleanKeyValue(context: Context, key: String) : Boolean {
|
fun getBooleanKeyValue(context: Context, key: String): Boolean {
|
||||||
return context
|
return context
|
||||||
.getSharedPreferences(
|
.getSharedPreferences(
|
||||||
SHARED_PREF_KEY,
|
SHARED_PREF_KEY,
|
||||||
@@ -56,7 +43,7 @@ object NavigationUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIntKeyValue(context: Context, key: String) : Int {
|
fun getIntKeyValue(context: Context, key: String): Int {
|
||||||
return context
|
return context
|
||||||
.getSharedPreferences(
|
.getSharedPreferences(
|
||||||
SHARED_PREF_KEY,
|
SHARED_PREF_KEY,
|
||||||
@@ -88,9 +75,8 @@ fun calculateZoom(speed: Double?): Double {
|
|||||||
val zoom = when (speedKmh) {
|
val zoom = when (speedKmh) {
|
||||||
in 0..10 -> 18.0
|
in 0..10 -> 18.0
|
||||||
in 11..30 -> 17.0
|
in 11..30 -> 17.0
|
||||||
in 21..40 -> 16.0
|
in 31..50 -> 16.0
|
||||||
in 31..50 -> 15.0
|
in 61..70 -> 15.0
|
||||||
in 51..60 -> 15.0
|
|
||||||
else -> 14
|
else -> 14
|
||||||
}
|
}
|
||||||
return zoom.toDouble()
|
return zoom.toDouble()
|
||||||
@@ -98,39 +84,34 @@ fun calculateZoom(speed: Double?): Double {
|
|||||||
|
|
||||||
fun previewZoom(previewDistance: Double): Double {
|
fun previewZoom(previewDistance: Double): Double {
|
||||||
when (previewDistance) {
|
when (previewDistance) {
|
||||||
in 0.0..10.0 -> {
|
in 0.0..10.0 -> return 13.0
|
||||||
return 13.0
|
in 10.0..20.0 -> return 11.0
|
||||||
}
|
in 20.0..30.0 -> return 10.0
|
||||||
in 10.0..20.0 -> {
|
|
||||||
return 11.0
|
|
||||||
}
|
|
||||||
in 20.0..30.0 -> {
|
|
||||||
return 10.0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 9.0
|
return 9.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateTilt(newZoom: Double, tilt: Double): Double =
|
||||||
fun calcTilt(newZoom: Double, tilt: Double): Double = if (newZoom < 13) {
|
if (newZoom < 13) {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
|
||||||
if (tilt == 0.0) {
|
|
||||||
55.0
|
|
||||||
} else {
|
} else {
|
||||||
tilt
|
if (tilt == 0.0) {
|
||||||
|
55.0
|
||||||
|
} else {
|
||||||
|
tilt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fun bearing(fromLocation: Location, toLocation: Location, oldBearing: Double) : Double {
|
fun bearing(fromLocation: Location, toLocation: Location, oldBearing: Double): Double {
|
||||||
val distance = fromLocation.distanceTo(toLocation)
|
val distance = fromLocation.distanceTo(toLocation)
|
||||||
if (distance < 1.0) {
|
if (distance < 1.0) {
|
||||||
return oldBearing
|
return oldBearing
|
||||||
}
|
}
|
||||||
val bearing = fromLocation.bearingTo(toLocation).toInt().toDouble()
|
val bearing = fromLocation.bearingTo(toLocation).toInt().toDouble()
|
||||||
return bearing
|
return bearing
|
||||||
}
|
}
|
||||||
|
|
||||||
fun location(longitude : Double, latitude: Double): Location {
|
fun location(longitude: Double, latitude: Double): Location {
|
||||||
val location = Location(LocationManager.GPS_PROVIDER)
|
val location = Location(LocationManager.GPS_PROVIDER)
|
||||||
location.longitude = longitude
|
location.longitude = longitude
|
||||||
location.latitude = latitude
|
location.latitude = latitude
|
||||||
@@ -138,7 +119,7 @@ fun location(longitude : Double, latitude: Double): Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun formatDateTime(time: Long): String {
|
fun formatDateTime(time: Long): String {
|
||||||
val dateFormatter = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT)
|
val dateFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||||
val dateTime = LocalDateTime.ofEpochSecond(time / 1000, 0, ZoneOffset.UTC)
|
val dateTime = LocalDateTime.ofEpochSecond(time / 1000, 0, ZoneOffset.UTC)
|
||||||
val zdt = ZonedDateTime.of(dateTime, ZoneId.of("Europe/Berlin"))
|
val zdt = ZonedDateTime.of(dateTime, ZoneId.of("Europe/Berlin"))
|
||||||
return zdt.format(dateFormatter)
|
return zdt.format(dateFormatter)
|
||||||
|
|||||||
10
common/data/src/main/res/drawable/ev_station_48px.xml
Normal file
10
common/data/src/main/res/drawable/ev_station_48px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M220,408L489,408L489,180Q489,180 489,180Q489,180 489,180L220,180Q220,180 220,180Q220,180 220,180L220,408ZM160,840L160,180Q160,156 178,138Q196,120 220,120L489,120Q513,120 531,138Q549,156 549,180L549,468L614,468Q634.71,468 649.36,482.64Q664,497.29 664,518L664,737Q664,759 681.5,773.5Q699,788 722,788Q745,788 765,773.5Q785,759 785,737L785,350L770,350Q757.25,350 748.63,341.37Q740,332.75 740,320L740,230L760,230L760,180L790,180L790,230L830,230L830,180L860,180L860,230L880,230L880,320Q880,332.75 871.38,341.37Q862.75,350 850,350L835,350L835,736.69Q835,780 801,810Q767,840 721.82,840Q677.66,840 645.83,810Q614,780 614,737L614,518Q614,518 614,518Q614,518 614,518L549,518L549,840L160,840ZM337,746L425,606L372,606L372,501L285,641L337,641L337,746Z"/>
|
||||||
|
</vector>
|
||||||
10
common/data/src/main/res/drawable/local_gas_station_24px.xml
Normal file
10
common/data/src/main/res/drawable/local_gas_station_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="M160,840L160,200Q160,167 183.5,143.5Q207,120 240,120L480,120Q513,120 536.5,143.5Q560,167 560,200L560,480L600,480Q633,480 656.5,503.5Q680,527 680,560L680,740Q680,757 691.5,768.5Q703,780 720,780Q737,780 748.5,768.5Q760,757 760,740L760,452Q751,457 741,458.5Q731,460 720,460Q678,460 649,431Q620,402 620,360Q620,328 637.5,302.5Q655,277 684,266L600,182L642,140L790,284Q805,299 812.5,319Q820,339 820,360L820,740Q820,782 791,811Q762,840 720,840Q678,840 649,811Q620,782 620,740L620,540Q620,540 620,540Q620,540 620,540L560,540L560,840L160,840ZM240,400L480,400L480,200Q480,200 480,200Q480,200 480,200L240,200Q240,200 240,200Q240,200 240,200L240,400ZM720,400Q737,400 748.5,388.5Q760,377 760,360Q760,343 748.5,331.5Q737,320 720,320Q703,320 691.5,331.5Q680,343 680,360Q680,377 691.5,388.5Q703,400 720,400ZM240,760L480,760L480,480L240,480L240,760ZM480,760L240,760L240,760L480,760Z"/>
|
||||||
|
</vector>
|
||||||
10
common/data/src/main/res/drawable/local_gas_station_48px.xml
Normal file
10
common/data/src/main/res/drawable/local_gas_station_48px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M160,840L160,180Q160,156 178,138Q196,120 220,120L489,120Q513,120 531,138Q549,156 549,180L549,468L614,468Q634.63,468 649.31,482.69Q664,497.37 664,518L664,737Q664,758.68 679.5,773.34Q695,788 717,788Q739,788 754.5,773.34Q770,758.68 770,737L770,442Q759,448 747,451Q735,454 723,454Q683.52,454 656.26,426.74Q629,399.48 629,360Q629,328.39 647,303.19Q665,278 695,270L600,175L636,140L789,293Q803,307 811.5,323.5Q820,340 820,360L820,737Q820,780.26 790.18,810.13Q760.37,840 717.18,840Q674,840 644,810.13Q614,780.26 614,737L614,518Q614,518 614,518Q614,518 614,518L549,518L549,840L160,840ZM220,408L489,408L489,180Q489,180 489,180Q489,180 489,180L220,180Q220,180 220,180Q220,180 220,180L220,408ZM723,404Q741,404 754,391Q767,378 767,360Q767,342 754,329Q741,316 723,316Q705,316 692,329Q679,342 679,360Q679,378 692,391Q705,404 723,404Z"/>
|
||||||
|
</vector>
|
||||||
10
common/data/src/main/res/drawable/local_pharmacy_24px.xml
Normal file
10
common/data/src/main/res/drawable/local_pharmacy_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="M120,840L120,760L200,520L120,280L120,200L628,200L686,40L780,74L734,200L840,200L840,280L760,520L840,760L840,840L120,840ZM440,680L520,680L520,560L640,560L640,480L520,480L520,360L440,360L440,480L320,480L320,560L440,560L440,680ZM204,760L756,760L676,520L756,280L204,280L284,520L204,760ZM480,520L480,520L480,520L480,520L480,520L480,520Z"/>
|
||||||
|
</vector>
|
||||||
10
common/data/src/main/res/drawable/local_pharmacy_48px.xml
Normal file
10
common/data/src/main/res/drawable/local_pharmacy_48px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M120,840L120,780L207,525L120,270L120,210L647,210L709,40L777,67L725,210L840,210L840,270L752,525L840,780L840,840L120,840ZM452,679L512,679L512,555L636,555L636,495L512,495L512,371L452,371L452,495L328,495L328,555L452,555L452,679Z"/>
|
||||||
|
</vector>
|
||||||
@@ -6,7 +6,7 @@ koinAndroid = "4.1.1"
|
|||||||
koinAndroidxCompose = "4.1.1"
|
koinAndroidxCompose = "4.1.1"
|
||||||
koinComposeViewmodel = "4.1.1"
|
koinComposeViewmodel = "4.1.1"
|
||||||
koinCore = "4.1.1"
|
koinCore = "4.1.1"
|
||||||
kotlin = "2.2.21"
|
kotlin = "2.3.0"
|
||||||
coreKtx = "1.17.0"
|
coreKtx = "1.17.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.3.0"
|
junitVersion = "1.3.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user