diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 78b8d7d..e13da31 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -14,8 +14,8 @@ android {
applicationId = "com.kouros.navigation"
minSdk = 33
targetSdk = 36
- versionCode = 10
- versionName = "0.1.3.10"
+ versionCode = 11
+ versionName = "0.1.3.11"
base.archivesName = "navi-$versionName"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ed14074..527c9da 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
@@ -18,6 +19,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.Navigation">
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)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts
index fbe1331..a879b46 100644
--- a/automotive/build.gradle.kts
+++ b/automotive/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
@@ -32,8 +34,10 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
- kotlinOptions {
- jvmTarget = "11"
+ kotlin {
+ compilerOptions {
+ jvmTarget = JvmTarget.JVM_11
+ }
}
}
diff --git a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt
index 888f50b..6f413f0 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/NavigationSession.kt
@@ -180,6 +180,7 @@ class NavigationSession : Session(), NavigationScreen.Listener {
routeModel.stopNavigation()
}
+
companion object {
var uriHost: String = "navigation"
diff --git a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt
index ff0eeca..5e1ff71 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/SurfaceRenderer.kt
@@ -37,7 +37,7 @@ import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.ObjectBox
import com.kouros.navigation.model.RouteModel
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.duration
import com.kouros.navigation.utils.location
@@ -225,7 +225,7 @@ class SurfaceRenderer(
} else {
cameraPosition.value!!.zoom + 1.0
}
- tilt = calcTilt(newZoom, tilt)
+ tilt = calculateTilt(newZoom, tilt)
updateCameraPosition(
cameraPosition.value!!.bearing,
newZoom,
@@ -310,7 +310,7 @@ class SurfaceRenderer(
}
fun setCategories(location: Location, route: String) {
- viewStyle = ViewStyle.SEARCH_VIEW
+ viewStyle = ViewStyle.AMENITY_VIEW
routeData.value = route
updateCameraPosition(
0.0,
@@ -319,14 +319,13 @@ class SurfaceRenderer(
)
}
- fun setCategoryLocation(location: Location) {
- viewStyle = ViewStyle.SEARCH_VIEW
+ fun setCategoryLocation(location: Location, category: String) {
+ viewStyle = ViewStyle.AMENITY_VIEW
cameraPosition.postValue(
cameraPosition.value!!.copy(
target = Position(location.longitude, location.latitude)
)
)
-
}
companion
@@ -337,6 +336,6 @@ class SurfaceRenderer(
enum class ViewStyle {
- VIEW, PREVIEW, PAN_VIEW, SEARCH_VIEW
+ VIEW, PREVIEW, PAN_VIEW, AMENITY_VIEW
}
\ No newline at end of file
diff --git a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt
index 8f1fd6b..28050ec 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/map/MapView.kt
@@ -33,16 +33,18 @@ import com.kouros.navigation.data.RouteColor
import com.kouros.navigation.data.SpeedColor
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
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.CameraState
import org.maplibre.compose.camera.rememberCameraState
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.image
import org.maplibre.compose.expressions.dsl.interpolate
import org.maplibre.compose.expressions.dsl.zoom
import org.maplibre.compose.layers.Anchor
-import org.maplibre.compose.layers.CircleLayer
import org.maplibre.compose.layers.FillLayer
import org.maplibre.compose.layers.LineLayer
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.rememberGeoJsonSource
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.FeatureCollection
+import org.maplibre.spatialk.geojson.GeoJson
+import org.maplibre.spatialk.geojson.Geometry
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
@@ -103,19 +111,22 @@ fun MapLibre(
if (!getBooleanKeyValue(context = context, SHOW_THREED_BUILDING)) {
BuildingLayer(tiles)
}
- RouteLayer(route, viewStyle)
+ if (viewStyle == ViewStyle.AMENITY_VIEW) {
+ AmenityLayer(route)
+ } else {
+ RouteLayer(route)
+ }
}
//Puck(cameraState, lastLocation)
}
}
@Composable
-fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
+fun RouteLayer(routeData: String?) {
if (routeData != null && routeData.isNotEmpty()) {
val routes = rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
- if (viewStyle == ViewStyle.VIEW) {
LineLayer(
- id = "routes-casing$viewStyle",
+ id = "routes-casing",
source = routes,
color = const(Color.White),
width =
@@ -129,7 +140,7 @@ fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
),
)
LineLayer(
- id = "routes$viewStyle",
+ id = "routes",
source = routes,
color = const(RouteColor),
width =
@@ -142,21 +153,27 @@ fun RouteLayer(routeData: String?, viewStyle: ViewStyle) {
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
fun BuildingLayer(tiles: Source) {
Anchor.Replace("building-3d") {
diff --git a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt
index 44ad275..4001944 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/navigation/RouteCarModel.kt
@@ -18,7 +18,12 @@ package com.kouros.navigation.car.navigation
import android.text.SpannableString
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
+import androidx.car.app.AppManager
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.CarIcon
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.TravelEstimate
import androidx.core.graphics.drawable.IconCompat
-import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
-import com.kouros.navigation.data.ManeuverType
+import com.kouros.data.R
import com.kouros.navigation.model.RouteModel
import java.util.TimeZone
import java.util.concurrent.TimeUnit
@@ -117,4 +121,43 @@ class RouteCarModel() : RouteModel() {
fun createCarIcon(carContext: CarContext, @DrawableRes iconRes: Int): CarIcon {
return CarIcon.Builder(IconCompat.createWithResource(carContext, iconRes)).build()
}
+
+ fun showSpeedCamera(carContext: CarContext, distance: Double, maxSpeed: String?) {
+ carContext.getCarService(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()
+ }
}
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt
index 21b2bc4..28ce1d3 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoriesScreen.kt
@@ -15,7 +15,9 @@ import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.ViewStyle
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(
private val carContext: CarContext,
@@ -24,9 +26,9 @@ class CategoriesScreen(
) : Screen(carContext) {
var categories: List = listOf(
- Category(id = Constants.FUEL_STATION, name = carContext.getString(R.string.fuel_station)),
- Category(id = Constants.PHARMACY, name = carContext.getString(R.string.pharmacy)),
- Category(id = Constants.CHARGING_STATION, name = carContext.getString(R.string.charging_station))
+ Category(id = FUEL_STATION, name = carContext.getString(R.string.fuel_station)),
+ Category(id = PHARMACY, name = carContext.getString(R.string.pharmacy)),
+ Category(id = CHARGING_STATION, name = carContext.getString(R.string.charging_station))
)
override fun onGetTemplate(): Template {
@@ -36,13 +38,7 @@ class CategoriesScreen(
itemListBuilder.addItem(
Row.Builder()
.setTitle(it.name)
- .setImage(CarIcon.Builder(
- IconCompat.createWithResource(
- carContext,
- com.kouros.android.cars.carappservice.R.drawable.ev_station_24px
- )
- )
- .build())
+ .setImage(carIcon(carContext,it.id))
.setOnClickListener {
screenManager
.pushForResult(
@@ -64,7 +60,7 @@ class CategoriesScreen(
)
}
- surfaceRenderer.viewStyle = ViewStyle.SEARCH_VIEW
+ surfaceRenderer.viewStyle = ViewStyle.AMENITY_VIEW
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
@@ -76,4 +72,20 @@ class CategoriesScreen(
.setSingleList(itemListBuilder.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()
}
\ No newline at end of file
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt
index 22540ca..4966f08 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/CategoryScreen.kt
@@ -3,12 +3,10 @@ package com.kouros.navigation.car.screen
import android.location.Location
import androidx.annotation.DrawableRes
import androidx.car.app.CarContext
-import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.model.Action
import androidx.car.app.model.ActionStrip
-import androidx.car.app.model.CarIcon
import androidx.car.app.model.CarText
import androidx.car.app.model.Header
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.navigation.model.MapController
import androidx.car.app.navigation.model.MapWithContentTemplate
-import androidx.car.app.versioning.CarAppApiLevels
-import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.Observer
import com.kouros.data.R
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.navigation.NavigationMessage
+import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.overpass.Elements
import com.kouros.navigation.model.ViewModel
import com.kouros.navigation.utils.GeoUtils.createPointCollection
import com.kouros.navigation.utils.location
+import com.kouros.navigation.utils.round
import kotlin.math.min
class CategoryScreen(
private val carContext: CarContext,
private val surfaceRenderer: SurfaceRenderer,
location: Location,
- category: String,
+ private val category: String,
) : Screen(carContext) {
val viewModel = ViewModel(NavigationRepository())
@@ -52,7 +50,7 @@ class CategoryScreen(
coordinates.add(listOf(it.lon!!, it.lat!!))
}
if (elements.isNotEmpty()) {
- val route = createPointCollection(coordinates)
+ val route = createPointCollection(coordinates, category)
surfaceRenderer.setCategories(loc, route)
invalidate()
}
@@ -65,41 +63,25 @@ class CategoryScreen(
override fun onGetTemplate(): Template {
-
val listBuilder = ItemList.Builder()
- if (carContext.getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
- var index = 0
- val listLimit = min(
- 100,
- carContext.getCarService(ConstraintManager::class.java)
- .getContentLimit(
- ConstraintManager.CONTENT_LIMIT_TYPE_LIST
- )
- )
- elements.forEach {
- if (index++ < listLimit) {
+ var index = 0
+ val listLimit = min(
+ 50,
+ carContext.getCarService(ConstraintManager::class.java)
+ .getContentLimit(
+ ConstraintManager.CONTENT_LIMIT_TYPE_LIST
+ )
+ )
+ elements.forEach {
+ if (index++ < listLimit) {
+ if (it.tags.operator != null) {
listBuilder.addItem(
- Row.Builder()
- .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()
+ createItem(it, category)
)
}
}
}
+
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
.setTitle(carContext.getString(R.string.charging_station))
@@ -116,11 +98,39 @@ class CategoryScreen(
getMapActionStrip()
).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 =
CarText.Builder(
"================= " + sText + " ================"
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt
index 95aa63c..3da4d4f 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/NavigationScreen.kt
@@ -1,7 +1,5 @@
package com.kouros.navigation.car.screen
-import android.content.ComponentName
-import android.content.Intent
import android.location.Location
import android.location.LocationManager
import android.os.CountDownTimer
@@ -9,6 +7,7 @@ import android.os.Handler
import androidx.car.app.CarContext
import androidx.car.app.Screen
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.CarColor
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.NavigationTemplate
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.lifecycle.Observer
import com.kouros.data.R
-import com.kouros.navigation.car.NavigationCarAppService
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.ViewStyle
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.NavigationRepository
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.utils.location
@@ -41,7 +40,6 @@ class NavigationScreen(
private var surfaceRenderer: SurfaceRenderer,
private var routeModel: RouteCarModel,
private var listener: Listener
-
) :
Screen(carContext) {
@@ -52,20 +50,14 @@ class NavigationScreen(
}
var currentNavigationLocation = Location(LocationManager.GPS_PROVIDER)
-
- lateinit var recentPlace: Place
-
- var recentPlaceFound = false
-
- var recentPlaceActive = true
-
- var calculateNewRoute = false
+ var recentPlace = Place()
+ var navigationType = NavigationType.VIEW
val viewModel = ViewModel(NavigationRepository())
val observer = Observer { route ->
if (route.isNotEmpty()) {
+ navigationType = NavigationType.NAVIGATION
routeModel.startNavigation(route)
surfaceRenderer.setRouteData()
- recentPlaceActive = false
invalidate()
}
}
@@ -73,26 +65,48 @@ class NavigationScreen(
val recentObserver = Observer { lastPlace ->
if (!routeModel.isNavigating()) {
recentPlace = lastPlace
- recentPlaceFound = true
+ navigationType = NavigationType.RECENT
invalidate()
}
}
+ val placeObserver = Observer { 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()
+ val speedObserver = Observer> { cameras ->
+ speedCameras = cameras
+ println("Speed cameras ${speedCameras.size}")
+ }
+
init {
viewModel.route.observe(this, observer)
viewModel.recentPlace.observe(this, recentObserver)
viewModel.loadRecentPlace(location = surfaceRenderer.lastLocation)
+ viewModel.placeLocation.observe(this, placeObserver)
+ viewModel.speedCameras.observe(this, speedObserver)
}
override fun onGetTemplate(): Template {
val actionStripBuilder = createActionStripBuilder()
- if (calculateNewRoute) {
- return navigationRerouteTemplate(actionStripBuilder)
- }
- return if (routeModel.isNavigating()) {
- navigationTemplate(actionStripBuilder)
- } else {
- navigationEndTemplate(actionStripBuilder)
+ return when (navigationType) {
+ NavigationType.NAVIGATION -> navigationTemplate(actionStripBuilder)
+ NavigationType.RECENT -> navigationRecentPlaceTemplate()
+ NavigationType.REROUTE -> navigationRerouteTemplate(actionStripBuilder)
+ NavigationType.ARRIVAL -> navigationEndTemplate(actionStripBuilder)
+ else -> navigationViewTemplate(actionStripBuilder)
}
}
@@ -111,30 +125,37 @@ class NavigationScreen(
.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 {
if (routeModel.routeState.arrived) {
- val timer = object : CountDownTimer(10000, 10000) {
+ val timer = object : CountDownTimer(8000, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
routeModel.routeState = routeModel.routeState.copy(arrived = false)
+ navigationType = NavigationType.VIEW
invalidate()
}
}
timer.start()
return navigationArrivedTemplate(actionStripBuilder)
} else {
- return if (recentPlaceFound && recentPlaceActive) {
- return recentPlaceTemplate()
- } else {
- NavigationTemplate.Builder()
- .setBackgroundColor(CarColor.SECONDARY)
- .setActionStrip(actionStripBuilder.build())
- .setMapActionStrip(mapActionStripBuilder().build())
- .build()
- }
+ return NavigationTemplate.Builder()
+ .setBackgroundColor(CarColor.SECONDARY)
+ .setActionStrip(actionStripBuilder.build())
+ .setMapActionStrip(mapActionStripBuilder().build())
+ .build()
}
}
+
fun navigationArrivedTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
var street = ""
if (routeModel.routeState.destination.street != null) {
@@ -163,7 +184,7 @@ class NavigationScreen(
.build()
}
- fun recentPlaceTemplate(): Template {
+ fun navigationRecentPlaceTemplate(): Template {
val messageTemplate = MessageTemplate.Builder(
recentPlace.name + "\n"
+ recentPlace.city
@@ -266,6 +287,7 @@ class NavigationScreen(
}
private fun navigateAction(): Action {
+ navigationType = NavigationType.NAVIGATION
return Action.Builder()
.setIcon(
CarIcon.Builder(
@@ -296,9 +318,10 @@ class NavigationScreen(
.build()
)
.setOnClickListener {
- recentPlaceActive = false
+ navigationType = NavigationType.VIEW
invalidate()
}
+ .setFlags(FLAG_DEFAULT)
.build()
}
@@ -368,28 +391,6 @@ class NavigationScreen(
.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() {
screenManager
.pushForResult(
@@ -397,26 +398,39 @@ class NavigationScreen(
) { obj: Any? ->
if (obj != null) {
val place = obj as Place
- val location = Location(LocationManager.GPS_PROVIDER)
- location.latitude = place.latitude
- location.longitude = place.longitude
- viewModel.saveRecent(place)
- viewModel.loadRoute(carContext, surfaceRenderer.lastLocation, location)
- currentNavigationLocation = location
- routeModel.routeState.destination = place
- invalidate()
+ if (place.longitude == 0.0) {
+ viewModel.findAddress(
+ "${obj.city} ${obj.street}},",
+ currentNavigationLocation
+ )
+ // result see observer
+ } else {
+ 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() {
+ navigationType = NavigationType.VIEW
listener.stopNavigation()
surfaceRenderer.routeData.value = ""
+ lastCameraSearch = 0
invalidate()
}
fun calculateNewRoute(destination: Place) {
- calculateNewRoute = true
+ navigationType = NavigationType.REROUTE
stopNavigation()
invalidate()
val mainThreadHandler = Handler(carContext.mainLooper)
@@ -424,7 +438,7 @@ class NavigationScreen(
object : CountDownTimer(3000, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
- calculateNewRoute = false
+ navigationType = NavigationType.NAVIGATION
reRoute(destination)
}
}.start()
@@ -437,6 +451,12 @@ class NavigationScreen(
}
fun updateTrip(location: Location) {
+ if (lastCameraSearch++ % 100 == 0) {
+ viewModel.getSpeedCameras(location)
+ }
+ if (speedCameras.isNotEmpty()) {
+ updateDistance(location)
+ }
with(routeModel) {
updateLocation(location)
if (routeState.maneuverType == Maneuver.TYPE_DESTINATION
@@ -445,8 +465,33 @@ class NavigationScreen(
stopNavigation()
routeState = routeState.copy(arrived = true)
surfaceRenderer.routeData.value = ""
+ navigationType = NavigationType.ARRIVAL
+ invalidate()
}
}
invalidate()
}
+
+ private fun updateDistance(
+ location: Location,
+ ) {
+ val updatedCameras = mutableListOf()
+ 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
+}
\ No newline at end of file
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt
index 24447a0..a06fdb3 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/PlaceListScreen.kt
@@ -7,6 +7,7 @@ import android.text.SpannableString
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
+import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.model.Action
import androidx.car.app.model.CarIcon
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.navigation.RouteCarModel
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.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.model.ViewModel
+import kotlin.math.min
class PlaceListScreen(
@@ -62,73 +67,74 @@ class PlaceListScreen(
}
fun loadPlaces() {
- if (category == Constants.RECENT) {
+ if (category == RECENT) {
viewModel.loadRecentPlaces(carContext, location)
}
- if (category == Constants.CONTACTS) {
- viewModel.loadContacts(carContext, location)
+ if (category == CONTACTS) {
+ viewModel.loadContacts(carContext)
}
- if (category == Constants.FAVORITES) {
+ if (category == FAVORITES) {
viewModel.loadFavorites(carContext, location)
}
}
+
override fun onGetTemplate(): Template {
val itemListBuilder = ItemList.Builder()
.setNoItemsMessage(carContext.getString(R.string.no_places))
places.forEach {
- itemListBuilder.addItem(
- Row.Builder()
- .addAction(
- deleteAction(it)
+ val row = Row.Builder()
+ .setImage(contactIcon(it.avatar, it.category))
+ .setTitle(it.name!!)
+ .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))
- .setTitle(it.name!!)
- .addText(SpannableString(" ").apply {
- setSpan(
- DistanceSpan.create(
- Distance.create(
- it.distance.toDouble(),
- Distance.UNIT_KILOMETERS
- )
- ), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
- )
- })
- .setOnClickListener {
- val place = Place(
- 0,
- it.name,
- it.category,
- it.latitude,
- it.longitude,
- it.postalCode,
- it.city,
- it.street,
- avatar = null
- )
- setResult(place)
- finish()
-// screenManager
-// .pushForResult(
-// RoutePreviewScreen(
-// carContext,
-// surfaceRenderer,
-// place
-// )
-// ) { obj: Any? ->
-// if (obj != null) {
-// setResult(obj)
-// finish()
-// }
-// }
- }
- .build()
+ screenManager
+ .pushForResult(
+ RoutePreviewScreen(
+ carContext,
+ surfaceRenderer,
+ place
+ )
+ ) { obj: Any? ->
+ if (obj != null) {
+ setResult(obj)
+ finish()
+ }
+ }
+ }
+ if (category != CONTACTS) {
+ row.addText(SpannableString(" ").apply {
+ setSpan(
+ DistanceSpan.create(
+ Distance.create(
+ it.distance.toDouble(),
+ Distance.UNIT_KILOMETERS
+ )
+ ), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE
+ )
+ })
+ row.addAction(
+ deleteAction(it)
+ )
+ }
+ itemListBuilder.addItem(
+ row.build()
)
}
var title = ""
- when(category) {
- Constants.RECENT -> title = carContext.getString(R.string.recent_destinations)
- Constants.CONTACTS -> title = carContext.getString(R.string.contacts)
- Constants.FAVORITES -> title = carContext.getString(R.string.favorites)
+ when (category) {
+ RECENT -> title = carContext.getString(R.string.recent_destinations)
+ CONTACTS -> title = carContext.getString(R.string.contacts)
+ FAVORITES -> title = carContext.getString(R.string.favorites)
}
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt
index 576d7bb..f0a8ef0 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/RoutePreviewScreen.kt
@@ -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
import android.os.CountDownTimer
@@ -22,18 +7,23 @@ import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
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.ActionStrip
+import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.CarText
import androidx.car.app.model.DurationSpan
import androidx.car.app.model.Header
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
+import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.Template
import androidx.car.app.navigation.model.MapController
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.lifecycle.Observer
import com.kouros.data.R
@@ -78,24 +68,20 @@ class RoutePreviewScreen(
override fun onGetTemplate(): Template {
val navigateActionIcon: CarIcon = CarIcon.Builder(
IconCompat.createWithResource(
- carContext, R.drawable.baseline_assistant_navigation_24
+ carContext, R.drawable.navigation_48px
)
).build()
val navigateAction = Action.Builder()
- .setFlags(FLAG_PRIMARY)
+ .setFlags(FLAG_DEFAULT)
.setIcon(navigateActionIcon)
.setOnClickListener { this.onNavigate() }
+
.build()
- val itemListBuilder = ItemList.Builder()
-
- if (routeModel.isNavigating() && routeModel.route.waypoints.isNotEmpty()) {
- itemListBuilder.addItem(createRow(0, navigateAction))
- }
-
val header = Header.Builder()
.setStartHeaderAction(Action.BACK)
.setTitle(carContext.getString(R.string.route_preview))
+ //.addEndHeaderAction(navigateAction)
.addEndHeaderAction(
favoriteAction()
)
@@ -104,21 +90,30 @@ class RoutePreviewScreen(
)
.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 onFinish() {
- onNavigate()
+ //onNavigate()
}
}
timer.start()
return MapWithContentTemplate.Builder()
- .setContentTemplate(
- ListTemplate.Builder()
- .setHeader(header)
- .setSingleList(itemListBuilder.build())
- .build()
- )
+ .setContentTemplate(messageTemplate)
.setMapController(
MapController.Builder().setMapActionStrip(
getMapActionStrip()
@@ -177,18 +172,8 @@ class RoutePreviewScreen(
.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(index: Int): CarText {
+ private fun createRouteText(): CarText {
val time = routeModel.route.summary.time
val length = BigDecimal(routeModel.route.distance).setScale(1, RoundingMode.HALF_EVEN)
val firstRoute = SpannableString(" \u00b7 $length km")
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/SearchScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/SearchScreen.kt
index d1f6544..42e3999 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/SearchScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/SearchScreen.kt
@@ -35,7 +35,7 @@ class SearchScreen(
var categories: List = listOf(
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.FAVORITES, name = carContext.getString(R.string.favorites))
)
diff --git a/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt b/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt
index 46fca1d..644dad8 100644
--- a/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt
+++ b/common/car/src/main/java/com/kouros/navigation/car/screen/SettingsScreen.kt
@@ -31,7 +31,6 @@ class SettingsScreen(
) : Screen(carContext) {
override fun onGetTemplate(): Template {
-
val listBuilder = ItemList.Builder()
listBuilder.addItem(
buildRowForTemplate(
diff --git a/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt b/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt
index a0be751..cc2dca3 100644
--- a/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt
+++ b/common/car/src/test/java/com/kouros/navigation/car/UnitTest.kt
@@ -24,9 +24,11 @@ class ViewModelTest {
fun routeViewModelTest() {
val fromLocation = Location(LocationManager.GPS_PROVIDER)
+ fromLocation.isMock = true
fromLocation.latitude = homeLocation.latitude
fromLocation.longitude = homeLocation.longitude
val toLocation = Location(LocationManager.GPS_PROVIDER)
+ toLocation.isMock = true
toLocation.latitude = home2Location.latitude
toLocation.longitude = home2Location.longitude
diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt
index 3cb354b..50c372c 100644
--- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt
+++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Elements.kt
@@ -9,6 +9,7 @@ data class Elements (
@SerializedName("id" ) var id : Long? = null,
@SerializedName("lat" ) var lat : 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
)
\ No newline at end of file
diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt
index 61b47cb..c676cdc 100644
--- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt
+++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Overpass.kt
@@ -12,21 +12,22 @@ import java.net.URL
class Overpass {
val overpassUrl = "https://overpass.kumi.systems/api/interpreter"
- fun getAmenities(category: String, location: Location) : List {
- val boundingBox = getOverpassBbox(location, 2.0)
- val bb = getBoundingBox2(location, 2.0)
+ fun getAmenities(type: String, category: String, location: Location) : List {
+ val boundingBox = getOverpassBbox(location, 5.0)
val httpURLConnection = URL(overpassUrl).openConnection() as HttpURLConnection
httpURLConnection.requestMethod = "POST"
httpURLConnection.setRequestProperty(
"Accept",
"application/json"
)
+ // node["highway"="speed_camera"]
+ // node[amenity=$category]
httpURLConnection.setDoOutput(true);
- // define a query
- val test = """
+ // define search query
+ val searchQuery = """
|[out:json];
|(
- | node[amenity=$category]
+ | node[$type=$category]
| ($boundingBox);
|);
|out body;
@@ -34,7 +35,7 @@ class Overpass {
// Send the JSON we created
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
- outputStreamWriter.write(test)
+ outputStreamWriter.write(searchQuery)
outputStreamWriter.flush()
// Check if the connection is successful
val responseCode = httpURLConnection.responseCode
@@ -43,7 +44,7 @@ class Overpass {
.use { it.readText() } // defaults to UTF-8
val gson = GsonBuilder().serializeNulls().create()
val overpass = gson.fromJson(response, Amenity::class.java)
- println("Overpass: $response")
+ println("Overpass: $type $response")
return overpass.elements
}
return emptyList()
diff --git a/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt b/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt
index 7ae9104..064fbdb 100644
--- a/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt
+++ b/common/data/src/main/java/com/kouros/navigation/data/overpass/Tags.kt
@@ -3,20 +3,22 @@ package com.kouros.navigation.data.overpass
import com.google.gson.annotations.SerializedName
-data class Tags (
-
- @SerializedName("amenity" ) var amenity : String? = null,
- @SerializedName("authentication:none" ) var authenticationNone : String? = null,
- @SerializedName("capacity" ) var capacity : String? = null,
- @SerializedName("motorcar" ) var motorcar : String? = null,
- @SerializedName("network" ) var network : String? = null,
- @SerializedName("opening_hours" ) var openingHours : String? = null,
- @SerializedName("operator" ) var operator : String? = null,
- @SerializedName("operator:short" ) var operatorShort : String? = null,
- @SerializedName("operator:wikidata" ) var operatorWikidata : String? = null,
- @SerializedName("operator:wikipedia" ) var operatorWikipedia : String? = null,
- @SerializedName("ref" ) var ref : String? = null,
- @SerializedName("socket:type2" ) var socketType2 : String? = null,
- @SerializedName("socket:type2:output" ) var socketType2Output : String? = null
+data class Tags(
+ @SerializedName("name") var name: String? = null,
+ @SerializedName("amenity") var amenity: String? = null,
+ @SerializedName("authentication:none") var authenticationNone: String? = null,
+ @SerializedName("capacity") var capacity: String? = null,
+ @SerializedName("motorcar") var motorcar: String? = null,
+ @SerializedName("network") var network: String? = null,
+ @SerializedName("opening_hours") var openingHours: String? = null,
+ @SerializedName("operator") var operator: String? = null,
+ @SerializedName("operator:short") var operatorShort: String? = null,
+ @SerializedName("operator:wikidata") var operatorWikidata: String? = null,
+ @SerializedName("operator:wikipedia") var operatorWikipedia: String? = null,
+ @SerializedName("ref") var ref: String? = null,
+ @SerializedName("socket:type2") var socketType2: String? = null,
+ @SerializedName("socket:type2:output") var socketType2Output: String? = null,
+ @SerializedName("maxspeed") var maxspeed: String? = null,
+ @SerializedName("direction") var direction: String? = null,
)
\ No newline at end of file
diff --git a/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt b/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt
index e2799aa..a506ed8 100644
--- a/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt
+++ b/common/data/src/main/java/com/kouros/navigation/model/Contacts.kt
@@ -36,7 +36,7 @@ class Contacts(private var context: Context) {
if (name.contains("Jola")
|| name.contains("Dominic")
|| name.contains("Martha")
- || name.contains("Rena")
+ || name.contains("Groth")
|| name.contains("David")) {
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
if (mimeType == ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) {
diff --git a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt
index 79069f4..9a766b9 100644
--- a/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt
+++ b/common/data/src/main/java/com/kouros/navigation/model/RouteModel.kt
@@ -127,6 +127,7 @@ open class RouteModel() {
maneuverType = relevantManeuver.type
}
val maneuverIconPair = maneuverIcon(maneuverType)
+ routeState.maneuverType = maneuverIconPair.first
// Construct and return the final StepData object
return StepData(
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) = 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 {
val maneuver = route.nextManeuver()
val maneuverType = maneuver.type
@@ -181,7 +149,6 @@ open class RouteModel() {
when (distanceLeft) {
in 0.0..NEXT_STEP_THRESHOLD -> {
}
-
else -> {
if (maneuver.streetNames != null && maneuver.streetNames.isNotEmpty()) {
text = maneuver.streetNames[0]
@@ -319,7 +286,7 @@ open class RouteModel() {
currentTurnIcon = R.drawable.ic_roundabout_ccw
}
}
- routeState.maneuverType = type
+ //routeState.maneuverType = type
return Pair(type, currentTurnIcon)
}
@@ -331,7 +298,7 @@ open class RouteModel() {
fun hasArrived(type: Int): Boolean {
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
return type == ManeuverType.DestinationRight.value
- || routeState.maneuverType == ManeuverType.Destination.value
- || routeState.maneuverType == ManeuverType.DestinationLeft.value
+ || type == ManeuverType.Destination.value
+ || type == ManeuverType.DestinationLeft.value
}
}
\ No newline at end of file
diff --git a/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt b/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt
index 61dadbd..9635697 100644
--- a/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt
+++ b/common/data/src/main/java/com/kouros/navigation/model/ViewModel.kt
@@ -1,7 +1,6 @@
package com.kouros.navigation.model
import android.content.Context
-import android.location.Geocoder
import android.location.Location
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
@@ -10,16 +9,15 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.GsonBuilder
import com.kouros.navigation.data.Constants
-import com.kouros.navigation.data.Locations
import com.kouros.navigation.data.NavigationRepository
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.SearchFilter
import com.kouros.navigation.data.nominatim.Search
import com.kouros.navigation.data.nominatim.SearchResult
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.location
import io.objectbox.kotlin.boxFor
@@ -31,34 +29,42 @@ import java.time.ZoneOffset
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
val route: MutableLiveData by lazy {
- MutableLiveData()
+ MutableLiveData()
}
val previewRoute: MutableLiveData by lazy {
- MutableLiveData()
+ MutableLiveData()
}
val recentPlace: MutableLiveData by lazy {
- MutableLiveData()
+ MutableLiveData()
}
val places: MutableLiveData> by lazy {
- MutableLiveData>()
+ MutableLiveData()
}
val favorites: MutableLiveData> by lazy {
- MutableLiveData>()
+ MutableLiveData()
}
val searchPlaces: MutableLiveData> by lazy {
- MutableLiveData>()
+ MutableLiveData()
}
+ val placeLocation: MutableLiveData by lazy {
+ MutableLiveData()
+ }
+
val contactAddress: MutableLiveData> by lazy {
- MutableLiveData>()
+ MutableLiveData()
}
val elements: MutableLiveData> by lazy {
- MutableLiveData>()
+ MutableLiveData()
+ }
+
+ val speedCameras: MutableLiveData> by lazy {
+ MutableLiveData()
}
fun loadRecentPlace(location: Location) {
@@ -98,9 +104,15 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
query.close()
for (place in results) {
val plLocation = location(place.longitude, place.latitude)
- val distance =
- repository.getRouteDistance(location, plLocation, getSearchFilter(context))
- place.distance = distance.toFloat()
+ if (place.latitude != 0.0) {
+ val distance =
+ repository.getRouteDistance(
+ location,
+ plLocation,
+ getSearchFilter(context)
+ )
+ place.distance = distance.toFloat()
+ }
}
places.postValue(results)
} 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) {
- try {
- val geocoder = Geocoder(context)
- val contactList = mutableListOf()
- val contacts = Contacts(context = context)
- val addresses = contacts.retrieveContacts()
- for (address in addresses) {
- val addressLines = address.address.split("\n")
- geocoder.getFromLocationName(
- address.address, 5
- ) {
- for (adr in it) {
- if (addressLines.size > 1) {
- val plLocation = location(adr.longitude, adr.latitude)
- val distance =
- repository.getRouteDistance(
- currentLocation,
- plLocation,
- 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)
- }
+ val contactList = mutableListOf()
+ val contacts = Contacts(context = context)
+ val addresses = contacts.retrieveContacts()
+ for (address in addresses) {
+ val addressLines = address.address.split("\n")
+ if (addressLines.size > 1) {
+ contactList.add(
+ Place(
+ id = address.contactId,
+ name = address.name + " " + addressLines[0] + " " + addressLines[1],
+ Constants.CONTACTS,
+ street = addressLines[0],
+ city = addressLines[1],
+ avatar = address.avatar,
+ longitude = 0.0,
+ latitude = 0.0,
+ distance = 0F,
+ )
+ )
}
- } catch (e: Exception) {
- e.printStackTrace()
+ contactAddress.postValue(contactList)
}
}
}
+
+ fun findAddress(search: String, location: Location) {
+ var sortedList: List
+ 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()
+ 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) {
viewModelScope.launch(Dispatchers.IO) {
val placesJson = repository.searchPlaces(search, location)
@@ -223,7 +237,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
it.distance = distance
distPlaces.add(it)
}
- val sortedList = distPlaces.sortedWith(compareBy({ it.distance }))
+ val sortedList = distPlaces.sortedWith(compareBy { it.distance })
searchPlaces.postValue(sortedList)
}
}
@@ -237,8 +251,33 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
fun getAmenities(category: String, location: Location) {
viewModelScope.launch(Dispatchers.IO) {
- val amenities = Overpass().getAmenities(category, location)
- elements.postValue(amenities)
+ val amenities = Overpass().getAmenities("amenity", category, location)
+ val distAmenities = mutableListOf()
+ 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()
+ 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)
}
}
diff --git a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt
index e032ad7..3565541 100644
--- a/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt
+++ b/common/data/src/main/java/com/kouros/navigation/utils/GeoUtils.kt
@@ -2,6 +2,8 @@ package com.kouros.navigation.utils
import android.location.Location
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.Point
import org.maplibre.spatialk.geojson.Feature
@@ -102,12 +104,12 @@ object GeoUtils {
return featureCollection.toJson()
}
- fun createPointCollection(lineCoordinates: List>): String {
+ fun createPointCollection(lineCoordinates: List>, category: String): String {
val featureCollection = buildFeatureCollection {
lineCoordinates.forEach {
addFeature {
geometry = org.maplibre.spatialk.geojson.Point(it[0], it[1])
- properties = null
+ properties = buildJsonObject { put("category", category) }
}
}
}
diff --git a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt
index be869b8..c206822 100644
--- a/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt
+++ b/common/data/src/main/java/com/kouros/navigation/utils/NavigationUtils.kt
@@ -4,19 +4,7 @@ import android.content.Context
import android.location.Location
import android.location.LocationManager
import androidx.core.content.edit
-import com.kouros.navigation.data.BoundingBox
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.ZoneId
import java.time.ZoneOffset
@@ -24,7 +12,6 @@ import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import kotlin.math.absoluteValue
-import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.time.Duration
@@ -33,7 +20,7 @@ import kotlin.time.Duration.Companion.seconds
object NavigationUtils {
- fun getBooleanKeyValue(context: Context, key: String) : Boolean {
+ fun getBooleanKeyValue(context: Context, key: String): Boolean {
return context
.getSharedPreferences(
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
.getSharedPreferences(
SHARED_PREF_KEY,
@@ -88,9 +75,8 @@ fun calculateZoom(speed: Double?): Double {
val zoom = when (speedKmh) {
in 0..10 -> 18.0
in 11..30 -> 17.0
- in 21..40 -> 16.0
- in 31..50 -> 15.0
- in 51..60 -> 15.0
+ in 31..50 -> 16.0
+ in 61..70 -> 15.0
else -> 14
}
return zoom.toDouble()
@@ -98,39 +84,34 @@ fun calculateZoom(speed: Double?): Double {
fun previewZoom(previewDistance: Double): Double {
when (previewDistance) {
- in 0.0..10.0 -> {
- return 13.0
- }
- in 10.0..20.0 -> {
- return 11.0
- }
- in 20.0..30.0 -> {
- return 10.0
- }
+ in 0.0..10.0 -> return 13.0
+ in 10.0..20.0 -> return 11.0
+ in 20.0..30.0 -> return 10.0
}
return 9.0
}
-
-fun calcTilt(newZoom: Double, tilt: Double): Double = if (newZoom < 13) {
- 0.0
-} else {
- if (tilt == 0.0) {
- 55.0
+fun calculateTilt(newZoom: Double, tilt: Double): Double =
+ if (newZoom < 13) {
+ 0.0
} 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)
if (distance < 1.0) {
return oldBearing
}
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)
location.longitude = longitude
location.latitude = latitude
@@ -138,7 +119,7 @@ fun location(longitude : Double, latitude: Double): Location {
}
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 zdt = ZonedDateTime.of(dateTime, ZoneId.of("Europe/Berlin"))
return zdt.format(dateFormatter)
diff --git a/common/car/src/main/res/drawable/ev_station_24px.xml b/common/data/src/main/res/drawable/ev_station_24px.xml
similarity index 100%
rename from common/car/src/main/res/drawable/ev_station_24px.xml
rename to common/data/src/main/res/drawable/ev_station_24px.xml
diff --git a/common/data/src/main/res/drawable/ev_station_48px.xml b/common/data/src/main/res/drawable/ev_station_48px.xml
new file mode 100644
index 0000000..4681f9c
--- /dev/null
+++ b/common/data/src/main/res/drawable/ev_station_48px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/data/src/main/res/drawable/local_gas_station_24px.xml b/common/data/src/main/res/drawable/local_gas_station_24px.xml
new file mode 100644
index 0000000..3566c7d
--- /dev/null
+++ b/common/data/src/main/res/drawable/local_gas_station_24px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/data/src/main/res/drawable/local_gas_station_48px.xml b/common/data/src/main/res/drawable/local_gas_station_48px.xml
new file mode 100644
index 0000000..8efba2b
--- /dev/null
+++ b/common/data/src/main/res/drawable/local_gas_station_48px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/data/src/main/res/drawable/local_pharmacy_24px.xml b/common/data/src/main/res/drawable/local_pharmacy_24px.xml
new file mode 100644
index 0000000..17a1640
--- /dev/null
+++ b/common/data/src/main/res/drawable/local_pharmacy_24px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/common/data/src/main/res/drawable/local_pharmacy_48px.xml b/common/data/src/main/res/drawable/local_pharmacy_48px.xml
new file mode 100644
index 0000000..c1ba6ad
--- /dev/null
+++ b/common/data/src/main/res/drawable/local_pharmacy_48px.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 60bd242..8e780ee 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -6,7 +6,7 @@ koinAndroid = "4.1.1"
koinAndroidxCompose = "4.1.1"
koinComposeViewmodel = "4.1.1"
koinCore = "4.1.1"
-kotlin = "2.2.21"
+kotlin = "2.3.0"
coreKtx = "1.17.0"
junit = "4.13.2"
junitVersion = "1.3.0"