Overpass
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 = 8
|
versionCode = 9
|
||||||
versionName = "0.1.3.8"
|
versionName = "0.1.3.9"
|
||||||
base.archivesName = "navi-$versionName"
|
base.archivesName = "navi-$versionName"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import androidx.lifecycle.Observer
|
|||||||
import com.google.android.gms.location.FusedLocationProviderClient
|
import com.google.android.gms.location.FusedLocationProviderClient
|
||||||
import com.google.android.gms.location.LocationServices
|
import com.google.android.gms.location.LocationServices
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.Constants
|
||||||
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
|
||||||
@@ -110,18 +111,19 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
private var loadRecentPlaces = false
|
private var loadRecentPlaces = false
|
||||||
|
|
||||||
|
private var overpass = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModel.route.observe(this, observer)
|
viewModel.route.observe(this, observer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
if (useMock) {
|
if (useMock) {
|
||||||
checkMockLocationEnabled()
|
checkMockLocationEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
|
||||||
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
||||||
if (useMock) {
|
if (useMock) {
|
||||||
@@ -241,6 +243,10 @@ 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()) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.livedata.observeAsState
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
@@ -64,27 +65,18 @@ class SurfaceRenderer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
var stableArea = Rect()
|
var stableArea = Rect()
|
||||||
|
|
||||||
var width = 0
|
var width = 0
|
||||||
|
|
||||||
var height = 0
|
var height = 0
|
||||||
|
|
||||||
var lastBearing = 0.0
|
var lastBearing = 0.0
|
||||||
val routeData = MutableLiveData("")
|
val routeData = MutableLiveData("")
|
||||||
|
|
||||||
val previewRouteData = MutableLiveData("")
|
|
||||||
|
|
||||||
val speed = MutableLiveData(0F)
|
val speed = MutableLiveData(0F)
|
||||||
lateinit var centerLocation: Location
|
lateinit var centerLocation: Location
|
||||||
var preview = false
|
var preview = false
|
||||||
|
var previewDistance = 0.0
|
||||||
|
val previewRouteData = MutableLiveData("")
|
||||||
lateinit var mapView: ComposeView
|
lateinit var mapView: ComposeView
|
||||||
|
|
||||||
var panView = false
|
var panView = false
|
||||||
var tilt = 55.0
|
var tilt = 55.0
|
||||||
|
|
||||||
var previewDistance = 0.0
|
|
||||||
|
|
||||||
var countDownTimerActive = false
|
var countDownTimerActive = false
|
||||||
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
|
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
|
||||||
|
|
||||||
@@ -341,6 +333,18 @@ class SurfaceRenderer(
|
|||||||
previewDistance = routeModel.route.distance
|
previewDistance = routeModel.route.distance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setLocation(location: Location) {
|
||||||
|
cameraPosition.postValue(
|
||||||
|
cameraPosition.value!!.copy(
|
||||||
|
bearing = 0.0,
|
||||||
|
zoom = 15.0,
|
||||||
|
tilt = 0.0,
|
||||||
|
padding = PaddingValues(start = 350.dp, bottom = 0.dp),
|
||||||
|
target = Position(location.longitude, location.latitude)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion
|
companion
|
||||||
object {
|
object {
|
||||||
private const val TAG = "MapRenderer"
|
private const val TAG = "MapRenderer"
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
|
|||||||
Canvas(modifier =Modifier
|
Canvas(modifier =Modifier
|
||||||
.size(imageSize.dp, imageSize.dp)) {
|
.size(imageSize.dp, imageSize.dp)) {
|
||||||
scale(scaleX = 1f, scaleY = 0.7f) {
|
scale(scaleX = 1f, scaleY = 0.7f) {
|
||||||
drawCircle(Color.DarkGray.copy(alpha = 0.4f))
|
drawCircle(Color.DarkGray.copy(alpha = 0.2f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import androidx.car.app.CarContext
|
||||||
|
import androidx.car.app.Screen
|
||||||
|
import androidx.car.app.model.Action
|
||||||
|
import androidx.car.app.model.Header
|
||||||
|
import androidx.car.app.model.ItemList
|
||||||
|
import androidx.car.app.model.ListTemplate
|
||||||
|
import androidx.car.app.model.Row
|
||||||
|
import androidx.car.app.model.Template
|
||||||
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.car.SurfaceRenderer
|
||||||
|
import com.kouros.navigation.data.Category
|
||||||
|
import com.kouros.navigation.data.Constants
|
||||||
|
|
||||||
|
class CategoriesScreen(
|
||||||
|
private val carContext: CarContext,
|
||||||
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
|
private val location: Location,
|
||||||
|
) : Screen(carContext) {
|
||||||
|
|
||||||
|
var categories: List<Category> = listOf(
|
||||||
|
Category(id = Constants.GAS_STATION, name = carContext.getString(R.string.gas_station)),
|
||||||
|
Category(id = Constants.PHARMACY, name = carContext.getString(R.string.pharmacy)),
|
||||||
|
Category(id = Constants.CHARGING_STATION, name = carContext.getString(R.string.charging_station))
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onGetTemplate(): Template {
|
||||||
|
val itemListBuilder = ItemList.Builder()
|
||||||
|
.setNoItemsMessage("No categories to show")
|
||||||
|
|
||||||
|
categories.forEach {
|
||||||
|
it.name
|
||||||
|
itemListBuilder.addItem(
|
||||||
|
Row.Builder()
|
||||||
|
.setTitle(it.name)
|
||||||
|
.setOnClickListener {
|
||||||
|
screenManager
|
||||||
|
.pushForResult(
|
||||||
|
CategoryScreen(
|
||||||
|
carContext,
|
||||||
|
surfaceRenderer,
|
||||||
|
location,
|
||||||
|
it.id
|
||||||
|
)
|
||||||
|
) { obj: Any? ->
|
||||||
|
if (obj != null) {
|
||||||
|
setResult(obj)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setBrowsable(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val header = Header.Builder()
|
||||||
|
.setStartHeaderAction(Action.BACK)
|
||||||
|
.setTitle("title")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return ListTemplate.Builder()
|
||||||
|
.setHeader(header)
|
||||||
|
.setSingleList(itemListBuilder.build())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package com.kouros.navigation.car.screen
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
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
|
||||||
|
import androidx.car.app.model.ListTemplate
|
||||||
|
import androidx.car.app.model.Row
|
||||||
|
import androidx.car.app.model.Template
|
||||||
|
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.data.NavigationRepository
|
||||||
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
|
import com.kouros.navigation.model.ViewModel
|
||||||
|
import com.kouros.navigation.utils.location
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class CategoryScreen(
|
||||||
|
private val carContext: CarContext,
|
||||||
|
private val surfaceRenderer: SurfaceRenderer,
|
||||||
|
location: Location,
|
||||||
|
category: String,
|
||||||
|
) : Screen(carContext) {
|
||||||
|
|
||||||
|
val viewModel = ViewModel(NavigationRepository())
|
||||||
|
var elements = listOf<Elements>()
|
||||||
|
|
||||||
|
val observer = Observer<List<Elements>> { newElements ->
|
||||||
|
elements = newElements
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModel.elements.observe(this, observer)
|
||||||
|
viewModel.getAmenities(category, location)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onGetTemplate(): Template {
|
||||||
|
val listBuilder = ItemList.Builder()
|
||||||
|
if (carContext.getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
|
||||||
|
val listLimit = min(
|
||||||
|
10,
|
||||||
|
carContext.getCarService(ConstraintManager::class.java)
|
||||||
|
.getContentLimit(
|
||||||
|
ConstraintManager.CONTENT_LIMIT_TYPE_LIST
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elements.forEach {
|
||||||
|
//listBuilder.addItem(createRow(it.tags!!.operator.toString(), it.tags?.capacity.toString()))
|
||||||
|
listBuilder.addItem(
|
||||||
|
Row.Builder()
|
||||||
|
.setOnClickListener({
|
||||||
|
val location = location(it.lon!!, it.lat!!)
|
||||||
|
surfaceRenderer.setLocation(location)
|
||||||
|
})
|
||||||
|
.setTitle(secondText(it.tags?.capacity.toString()))
|
||||||
|
.setImage(
|
||||||
|
CarIcon.Builder(
|
||||||
|
IconCompat.createWithResource(
|
||||||
|
carContext,
|
||||||
|
com.kouros.android.cars.carappservice.R.drawable.ev_station_24px
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.addText(it.tags!!.operator.toString())
|
||||||
|
.addText(secondText(it.tags?.capacity.toString()))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val header = Header.Builder()
|
||||||
|
.setStartHeaderAction(Action.BACK)
|
||||||
|
.setTitle(carContext.getString(R.string.charging_station))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val actionStrip = ActionStrip.Builder()
|
||||||
|
.addAction(
|
||||||
|
Action.Builder()
|
||||||
|
.setOnClickListener {
|
||||||
|
CarToast.makeText(
|
||||||
|
carContext,
|
||||||
|
carContext.getString(
|
||||||
|
R.string.notification_demo
|
||||||
|
),
|
||||||
|
CarToast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
.setIcon(
|
||||||
|
CarIcon.Builder(
|
||||||
|
IconCompat.createWithResource(
|
||||||
|
carContext,
|
||||||
|
R.drawable.ic_place_white_24dp
|
||||||
|
)
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
.setFlags(Action.FLAG_IS_PERSISTENT)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val builder = MapWithContentTemplate.Builder()
|
||||||
|
.setContentTemplate(
|
||||||
|
ListTemplate.Builder()
|
||||||
|
.setHeader(header)
|
||||||
|
.setSingleList(listBuilder.build())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.setActionStrip(actionStrip)
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun secondText(sText: String): CarText {
|
||||||
|
val secondText =
|
||||||
|
CarText.Builder(
|
||||||
|
"================= " + sText + " ================"
|
||||||
|
)
|
||||||
|
.addVariant(
|
||||||
|
("--------------------- " + sText
|
||||||
|
+ " ----------------------")
|
||||||
|
)
|
||||||
|
.addVariant(sText)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return secondText
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ 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.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
|
||||||
@@ -152,6 +153,7 @@ class PlaceListScreen(
|
|||||||
R.string.recent_Item_deleted, CarToast.LENGTH_LONG
|
R.string.recent_Item_deleted, CarToast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
loadPlaces()
|
loadPlaces()
|
||||||
|
invalidate()
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ 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.Action.FLAG_PRIMARY
|
||||||
import androidx.car.app.model.ActionStrip
|
import androidx.car.app.model.ActionStrip
|
||||||
import androidx.car.app.model.CarIcon
|
import androidx.car.app.model.CarIcon
|
||||||
import androidx.car.app.model.CarText
|
import androidx.car.app.model.CarText
|
||||||
@@ -94,6 +95,7 @@ class RoutePreviewScreen(
|
|||||||
)
|
)
|
||||||
).build()
|
).build()
|
||||||
val navigateAction = Action.Builder()
|
val navigateAction = Action.Builder()
|
||||||
|
.setFlags(FLAG_PRIMARY)
|
||||||
.setIcon(navigateActionIcon)
|
.setIcon(navigateActionIcon)
|
||||||
.setOnClickListener { this.onNavigate() }
|
.setOnClickListener { this.onNavigate() }
|
||||||
.build()
|
.build()
|
||||||
@@ -108,58 +110,10 @@ class RoutePreviewScreen(
|
|||||||
.setStartHeaderAction(Action.BACK)
|
.setStartHeaderAction(Action.BACK)
|
||||||
.setTitle(carContext.getString(R.string.route_preview))
|
.setTitle(carContext.getString(R.string.route_preview))
|
||||||
.addEndHeaderAction(
|
.addEndHeaderAction(
|
||||||
Action.Builder()
|
favoriteAction()
|
||||||
.setIcon(
|
|
||||||
CarIcon.Builder(
|
|
||||||
IconCompat.createWithResource(
|
|
||||||
carContext,
|
|
||||||
if (mIsFavorite)
|
|
||||||
R.drawable.ic_favorite_filled_white_24dp
|
|
||||||
else
|
|
||||||
R.drawable.ic_favorite_white_24dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.setOnClickListener {
|
|
||||||
mIsFavorite = !mIsFavorite
|
|
||||||
CarToast.makeText(
|
|
||||||
carContext,
|
|
||||||
if (mIsFavorite)
|
|
||||||
carContext
|
|
||||||
.getString(R.string.favorites)
|
|
||||||
else
|
|
||||||
carContext.getString(
|
|
||||||
R.string.not_favorite_toast_msg
|
|
||||||
),
|
|
||||||
CarToast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
vieModel.saveFavorite(destination)
|
|
||||||
println(destination)
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
)
|
)
|
||||||
.addEndHeaderAction(
|
.addEndHeaderAction(
|
||||||
Action.Builder()
|
deleteFavoriteAction()
|
||||||
.setOnClickListener {
|
|
||||||
if (mIsFavorite) {
|
|
||||||
vieModel.deleteFavorite(destination)
|
|
||||||
}
|
|
||||||
mIsFavorite = !mIsFavorite
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
.setIcon(
|
|
||||||
CarIcon.Builder(
|
|
||||||
IconCompat.createWithResource(
|
|
||||||
carContext,
|
|
||||||
R.drawable.ic_delete_foreground
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -186,6 +140,56 @@ class RoutePreviewScreen(
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun favoriteAction(): Action = Action.Builder()
|
||||||
|
.setIcon(
|
||||||
|
CarIcon.Builder(
|
||||||
|
IconCompat.createWithResource(
|
||||||
|
carContext,
|
||||||
|
if (mIsFavorite)
|
||||||
|
R.drawable.ic_favorite_filled_white_24dp
|
||||||
|
else
|
||||||
|
R.drawable.ic_favorite_white_24dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.setOnClickListener {
|
||||||
|
mIsFavorite = !mIsFavorite
|
||||||
|
CarToast.makeText(
|
||||||
|
carContext,
|
||||||
|
if (mIsFavorite)
|
||||||
|
carContext
|
||||||
|
.getString(R.string.favorites)
|
||||||
|
else
|
||||||
|
carContext.getString(
|
||||||
|
R.string.not_favorite_toast_msg
|
||||||
|
),
|
||||||
|
CarToast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
vieModel.saveFavorite(destination)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private fun deleteFavoriteAction(): Action = Action.Builder()
|
||||||
|
.setOnClickListener {
|
||||||
|
if (mIsFavorite) {
|
||||||
|
vieModel.deleteFavorite(destination)
|
||||||
|
}
|
||||||
|
mIsFavorite = !mIsFavorite
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
.setIcon(
|
||||||
|
CarIcon.Builder(
|
||||||
|
IconCompat.createWithResource(
|
||||||
|
carContext,
|
||||||
|
R.drawable.ic_delete_foreground
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
private fun createRow(index: Int, action: Action): Row {
|
private fun createRow(index: Int, action: Action): Row {
|
||||||
val route: CarText = createRouteText(index)
|
val route: CarText = createRouteText(index)
|
||||||
return Row.Builder()
|
return Row.Builder()
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ 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.FAVORITES, name = carContext.getString(R.string.favorites))
|
Category(id = Constants.FAVORITES, name = carContext.getString(R.string.favorites))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,20 +67,36 @@ class SearchScreen(
|
|||||||
.setTitle(it.name)
|
.setTitle(it.name)
|
||||||
.setImage(categoryIcon(it.id))
|
.setImage(categoryIcon(it.id))
|
||||||
.setOnClickListener {
|
.setOnClickListener {
|
||||||
screenManager
|
if (it.id == Constants.CATEGORIES) {
|
||||||
.pushForResult(
|
screenManager
|
||||||
PlaceListScreen(
|
.pushForResult(
|
||||||
carContext,
|
CategoriesScreen(
|
||||||
surfaceRenderer,
|
carContext,
|
||||||
location,
|
surfaceRenderer,
|
||||||
it.id
|
location,
|
||||||
)
|
)
|
||||||
) { obj: Any? ->
|
) { obj: Any? ->
|
||||||
if (obj != null) {
|
if (obj != null) {
|
||||||
setResult(obj)
|
setResult(obj)
|
||||||
finish()
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
screenManager
|
||||||
|
.pushForResult(
|
||||||
|
PlaceListScreen(
|
||||||
|
carContext,
|
||||||
|
surfaceRenderer,
|
||||||
|
location,
|
||||||
|
it.id
|
||||||
|
)
|
||||||
|
) { obj: Any? ->
|
||||||
|
if (obj != null) {
|
||||||
|
setResult(obj)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.setBrowsable(true)
|
.setBrowsable(true)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
10
common/car/src/main/res/drawable/ev_station_24px.xml
Normal file
10
common/car/src/main/res/drawable/ev_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="M340,760L440,600L380,600L380,480L280,640L340,640L340,760ZM240,400L480,400L480,200Q480,200 480,200Q480,200 480,200L240,200Q240,200 240,200Q240,200 240,200L240,400ZM240,760L480,760L480,480L240,480L240,760ZM160,840L160,200Q160,167 183.5,143.5Q207,120 240,120L480,120Q513,120 536.5,143.5Q560,167 560,200L560,480L610,480Q639,480 659.5,500.5Q680,521 680,550L680,735Q680,752 694,766Q708,780 725,780Q743,780 756.5,766Q770,752 770,735L770,360L760,360Q743,360 731.5,348.5Q720,337 720,320L720,240L740,240L740,180L780,180L780,240L820,240L820,180L860,180L860,240L880,240L880,320Q880,337 868.5,348.5Q857,360 840,360L830,360L830,735Q830,777 799.5,808.5Q769,840 725,840Q682,840 651,808.5Q620,777 620,735L620,550Q620,545 617.5,542.5Q615,540 610,540L560,540L560,840L160,840ZM480,760L240,760L240,760L480,760Z"/>
|
||||||
|
</vector>
|
||||||
@@ -57,8 +57,9 @@ dependencies {
|
|||||||
annotationProcessor(libs.objectbox.processor)
|
annotationProcessor(libs.objectbox.processor)
|
||||||
|
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|
||||||
implementation(libs.maplibre.compose)
|
implementation(libs.maplibre.compose)
|
||||||
|
implementation("hu.supercluster:overpasser:0.2.2")
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ data class ValhallaLocation (
|
|||||||
var language: String
|
var language: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class BoundingBox (
|
||||||
|
var southernLat : Double,
|
||||||
|
var westernLon: Double,
|
||||||
|
var northerLat : Double,
|
||||||
|
var easternLon : Double
|
||||||
|
)
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
|
|
||||||
const val STYLE: String = "https://kouros-online.de/liberty.json"
|
const val STYLE: String = "https://kouros-online.de/liberty.json"
|
||||||
@@ -141,12 +148,22 @@ object Constants {
|
|||||||
//const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
|
//const val STYLE: String = "https://tiles.openfreemap.org/styles/liberty"
|
||||||
const val TAG: String = "Navigation"
|
const val TAG: String = "Navigation"
|
||||||
|
|
||||||
|
const val CATEGORIES: String = "Categories"
|
||||||
|
|
||||||
const val CONTACTS: String = "Contacts"
|
const val CONTACTS: String = "Contacts"
|
||||||
|
|
||||||
const val RECENT: String = "Recent"
|
const val RECENT: String = "Recent"
|
||||||
|
|
||||||
const val FAVORITES: String = "Favorites"
|
const val FAVORITES: String = "Favorites"
|
||||||
|
|
||||||
|
const val GAS_STATION: String ="GasStation"
|
||||||
|
|
||||||
|
const val PHARMACY: String ="pharmacy"
|
||||||
|
|
||||||
|
const val CHARGING_STATION: String ="charging_station"
|
||||||
|
|
||||||
|
|
||||||
|
val categories = listOf("Tankstelle", "Apotheke", "Ladestationen")
|
||||||
/** The initial location to use as an anchor for searches. */
|
/** The initial location to use as an anchor for searches. */
|
||||||
val homeLocation: Location = Location(LocationManager.GPS_PROVIDER)
|
val homeLocation: Location = Location(LocationManager.GPS_PROVIDER)
|
||||||
val home2Location: Location = Location(LocationManager.GPS_PROVIDER)
|
val home2Location: Location = Location(LocationManager.GPS_PROVIDER)
|
||||||
@@ -176,7 +193,7 @@ object Constants {
|
|||||||
|
|
||||||
const val MAXIMAL_ROUTE_DEVIATION = 100.0
|
const val MAXIMAL_ROUTE_DEVIATION = 100.0
|
||||||
|
|
||||||
const val DESTINATION_ARRIVAL_DISTANCE = 20.0
|
const val DESTINATION_ARRIVAL_DISTANCE = 40.0
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
package com.kouros.navigation.data
|
package com.kouros.navigation.data
|
||||||
|
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
import com.kouros.navigation.model.RouteModel
|
import com.kouros.navigation.model.RouteModel
|
||||||
import com.kouros.navigation.utils.NavigationUtils.getBoundingBox
|
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
@@ -98,7 +98,6 @@ class NavigationRepository {
|
|||||||
return places
|
return places
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun fetchUrl(url: String, authenticator : Boolean): String {
|
private fun fetchUrl(url: String, authenticator : Boolean): String {
|
||||||
try {
|
try {
|
||||||
if (authenticator) {
|
if (authenticator) {
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.kouros.navigation.data
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.kouros.navigation.data.overpass.Amenity
|
||||||
|
import com.kouros.navigation.data.overpass.Elements
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getBoundingBox2
|
||||||
|
import com.kouros.navigation.utils.NavigationUtils.getOverpassBbox
|
||||||
|
import hu.supercluster.overpasser.library.output.OutputFormat
|
||||||
|
import hu.supercluster.overpasser.library.output.OutputModificator
|
||||||
|
import hu.supercluster.overpasser.library.output.OutputOrder
|
||||||
|
import hu.supercluster.overpasser.library.output.OutputVerbosity
|
||||||
|
import hu.supercluster.overpasser.library.query.OverpassQuery
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
|
||||||
|
class Overpass {
|
||||||
|
|
||||||
|
fun getAmenities(category: String, location: Location) : List<Elements> {
|
||||||
|
val boundingBox = getOverpassBbox(location, 2.0)
|
||||||
|
val bb = getBoundingBox2(location, 2.0)
|
||||||
|
val url = "https://overpass.kumi.systems/api/interpreter"
|
||||||
|
val httpURLConnection = URL(url).openConnection() as HttpURLConnection
|
||||||
|
httpURLConnection.requestMethod = "POST"
|
||||||
|
httpURLConnection.setRequestProperty(
|
||||||
|
"Accept",
|
||||||
|
"application/json"
|
||||||
|
)
|
||||||
|
httpURLConnection.setDoOutput(true);
|
||||||
|
val query = OverpassQuery()
|
||||||
|
.format(OutputFormat.JSON)
|
||||||
|
.timeout(30)
|
||||||
|
.filterQuery()
|
||||||
|
.node()
|
||||||
|
.amenity("charging_station")
|
||||||
|
.tagNot("access", "private")
|
||||||
|
.boundingBox(bb.southernLat, bb.westernLon, bb.northerLat, bb.easternLon)
|
||||||
|
.end()
|
||||||
|
.output(OutputVerbosity.BODY, OutputModificator.CENTER, OutputOrder.QT, 100)
|
||||||
|
.build()
|
||||||
|
// define a query
|
||||||
|
val test = """
|
||||||
|
|[out:json];
|
||||||
|
|(
|
||||||
|
| node[amenity=$category]
|
||||||
|
| ($boundingBox);
|
||||||
|
|);
|
||||||
|
|out body;
|
||||||
|
""".trimMargin()
|
||||||
|
|
||||||
|
// Send the JSON we created
|
||||||
|
val outputStreamWriter = OutputStreamWriter(httpURLConnection.outputStream)
|
||||||
|
outputStreamWriter.write(test)
|
||||||
|
outputStreamWriter.flush()
|
||||||
|
// Check if the connection is successful
|
||||||
|
val responseCode = httpURLConnection.responseCode
|
||||||
|
println("Overpass: $responseCode")
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
val response = httpURLConnection.inputStream.bufferedReader()
|
||||||
|
.use { it.readText() } // defaults to UTF-8
|
||||||
|
|
||||||
|
|
||||||
|
val gson = GsonBuilder().serializeNulls().create()
|
||||||
|
val overpass = gson.fromJson(response, Amenity::class.java)
|
||||||
|
println("Overpass: $response")
|
||||||
|
return overpass.elements
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.kouros.navigation.data.overpass
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Amenity (
|
||||||
|
|
||||||
|
@SerializedName("version" ) var version : Double? = null,
|
||||||
|
@SerializedName("generator" ) var generator : String? = null,
|
||||||
|
@SerializedName("osm3s" ) var osm3s : Osm3s? = Osm3s(),
|
||||||
|
@SerializedName("elements" ) var elements : ArrayList<Elements> = arrayListOf()
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.kouros.navigation.data.overpass
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Elements (
|
||||||
|
|
||||||
|
@SerializedName("type" ) var type : String? = null,
|
||||||
|
@SerializedName("id" ) var id : Long? = null,
|
||||||
|
@SerializedName("lat" ) var lat : Double? = null,
|
||||||
|
@SerializedName("lon" ) var lon : Double? = null,
|
||||||
|
@SerializedName("tags" ) var tags : Tags? = Tags()
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.kouros.navigation.data.overpass
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
data class Osm3s (
|
||||||
|
|
||||||
|
@SerializedName("timestamp_osm_base" ) var timestampOsmBase : String? = null,
|
||||||
|
@SerializedName("copyright" ) var copyright : String? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,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 socketType2Ooutput : String? = null
|
||||||
|
|
||||||
|
)
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"id": "background",
|
"id": "background",
|
||||||
"type": "background",
|
"type": "background",
|
||||||
"layout": {"visibility": "visible"},
|
"layout": {"visibility": "visible"},
|
||||||
"paint": {"background-color": "rgba(28, 28, 35, 1)"}
|
"paint": {"background-color": "rgba(39, 46, 57, 1)"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "natural_earth",
|
"id": "natural_earth",
|
||||||
@@ -903,7 +903,11 @@
|
|||||||
["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true],
|
["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true],
|
||||||
["match", ["get", "class"], ["service", "track"], true, false]
|
["match", ["get", "class"], ["service", "track"], true, false]
|
||||||
],
|
],
|
||||||
"layout": {"line-cap": "round", "line-join": "round"},
|
"layout": {
|
||||||
|
"line-cap": "round",
|
||||||
|
"line-join": "round",
|
||||||
|
"visibility": "visible"
|
||||||
|
},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
@@ -916,7 +920,7 @@
|
|||||||
20,
|
20,
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"line-color": "rgba(65, 74, 92, 1)"
|
"line-color": "rgba(77, 94, 123, 1)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1005,7 +1009,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-cap": "round", "line-join": "round"},
|
"layout": {"line-cap": "round", "line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#e9ac77",
|
"line-color": "rgba(65, 74, 92, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1029,7 +1033,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-join": "round"},
|
"layout": {"line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#e9ac77",
|
"line-color": "rgba(65, 74, 92, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1059,7 +1063,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-cap": "round", "line-join": "round"},
|
"layout": {"line-cap": "round", "line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#e9ac77",
|
"line-color": "rgba(65, 74, 92, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1245,7 +1249,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-cap": "round", "line-join": "round"},
|
"layout": {"line-cap": "round", "line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "rgba(105, 170, 87, 1)",
|
"line-color": "rgba(65, 74, 92, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1271,7 +1275,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-join": "round"},
|
"layout": {"line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#fea",
|
"line-color": "rgba(65, 74, 92, 1)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
@@ -1306,7 +1310,7 @@
|
|||||||
5,
|
5,
|
||||||
"hsl(26,87%,62%)",
|
"hsl(26,87%,62%)",
|
||||||
6,
|
6,
|
||||||
"#fc8"
|
"#ab9"
|
||||||
],
|
],
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
@@ -1847,7 +1851,7 @@
|
|||||||
],
|
],
|
||||||
"layout": {"line-join": "round"},
|
"layout": {"line-join": "round"},
|
||||||
"paint": {
|
"paint": {
|
||||||
"line-color": "#fea",
|
"line-color": "rgba(65, 74, 92, 34)",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["exponential", 1.2],
|
["exponential", 1.2],
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.location.Location
|
|||||||
import androidx.car.app.navigation.model.Maneuver
|
import androidx.car.app.navigation.model.Maneuver
|
||||||
import androidx.car.app.navigation.model.Step
|
import androidx.car.app.navigation.model.Step
|
||||||
import com.kouros.data.R
|
import com.kouros.data.R
|
||||||
|
import com.kouros.navigation.data.Constants
|
||||||
|
import com.kouros.navigation.data.Constants.DESTINATION_ARRIVAL_DISTANCE
|
||||||
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
import com.kouros.navigation.data.Constants.NEXT_STEP_THRESHOLD
|
||||||
import com.kouros.navigation.data.ManeuverType
|
import com.kouros.navigation.data.ManeuverType
|
||||||
import com.kouros.navigation.data.Place
|
import com.kouros.navigation.data.Place
|
||||||
@@ -35,6 +37,7 @@ open class RouteModel() {
|
|||||||
set(value) {
|
set(value) {
|
||||||
routeState = routeState.copy(route = value)
|
routeState = routeState.copy(route = value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startNavigation(routeString: String) {
|
fun startNavigation(routeString: String) {
|
||||||
val newRoute = Route.Builder()
|
val newRoute = Route.Builder()
|
||||||
.route(routeString)
|
.route(routeString)
|
||||||
@@ -49,7 +52,7 @@ open class RouteModel() {
|
|||||||
this.routeState = routeState.copy(
|
this.routeState = routeState.copy(
|
||||||
route = null,
|
route = null,
|
||||||
isNavigating = false,
|
isNavigating = false,
|
||||||
// destination = Place(),
|
// destination = Place(),
|
||||||
arrived = false,
|
arrived = false,
|
||||||
maneuverType = 0,
|
maneuverType = 0,
|
||||||
currentShapeIndex = 0,
|
currentShapeIndex = 0,
|
||||||
@@ -100,30 +103,31 @@ open class RouteModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun currentStep(): StepData {
|
fun currentStep(): StepData {
|
||||||
|
val currentManeuver = route.currentManeuver()
|
||||||
// Determine if we should display the current or the next maneuver
|
// Determine if we should display the current or the next maneuver
|
||||||
val distanceToNextStep = leftStepDistance()
|
val distanceToNextStep = leftStepDistance()
|
||||||
val isNearNextManeuver = distanceToNextStep in 0.0..NEXT_STEP_THRESHOLD
|
val isNearNextManeuver = distanceToNextStep in 0.0..NEXT_STEP_THRESHOLD
|
||||||
val shouldAdvance =
|
val shouldAdvance =
|
||||||
isNearNextManeuver && route.currentManeuverIndex < (route.maneuvers.size - 1)
|
isNearNextManeuver && route.currentManeuverIndex < (route.maneuvers.size)
|
||||||
|
|
||||||
|
// Determine the maneuver type and corresponding icon
|
||||||
|
var maneuverType = if (hasArrived(currentManeuver.type)) {
|
||||||
|
currentManeuver.type
|
||||||
|
} else {
|
||||||
|
ManeuverType.None.value
|
||||||
|
}
|
||||||
// Get the single, correct maneuver for this state
|
// Get the single, correct maneuver for this state
|
||||||
val relevantManeuver = if (shouldAdvance) {
|
val relevantManeuver = if (shouldAdvance) {
|
||||||
route.nextManeuver() // This advances the route's state
|
route.nextManeuver() // This advances the route's state
|
||||||
} else {
|
} else {
|
||||||
route.currentManeuver()
|
route.currentManeuver()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safely get the street name from the maneuver
|
// Safely get the street name from the maneuver
|
||||||
val streetName = relevantManeuver.streetNames?.firstOrNull() ?: ""
|
val streetName = relevantManeuver.streetNames?.firstOrNull() ?: ""
|
||||||
|
if (shouldAdvance) {
|
||||||
// Determine the maneuver type and corresponding icon
|
maneuverType = relevantManeuver.type
|
||||||
val maneuverType = if (hasArrived(relevantManeuver.type)) {
|
|
||||||
relevantManeuver.type
|
|
||||||
} else {
|
|
||||||
ManeuverType.None.value
|
|
||||||
}
|
}
|
||||||
val maneuverIconPair = maneuverIcon(maneuverType)
|
val maneuverIconPair = maneuverIcon(maneuverType)
|
||||||
|
|
||||||
// Construct and return the final StepData object
|
// Construct and return the final StepData object
|
||||||
return StepData(
|
return StepData(
|
||||||
streetName,
|
streetName,
|
||||||
@@ -135,6 +139,40 @@ 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
|
||||||
@@ -290,12 +328,10 @@ open class RouteModel() {
|
|||||||
return routeState.isNavigating
|
return routeState.isNavigating
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isArrived(): Boolean {
|
|
||||||
return routeState.arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasArrived(type: Int): Boolean {
|
fun hasArrived(type: Int): Boolean {
|
||||||
return type == ManeuverType.DestinationRight.value
|
// return leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE && type == ManeuverType.DestinationRight.value
|
||||||
|
return type == ManeuverType.DestinationRight.value
|
||||||
|| routeState.maneuverType == ManeuverType.Destination.value
|
|| routeState.maneuverType == ManeuverType.Destination.value
|
||||||
|| routeState.maneuverType == ManeuverType.DestinationLeft.value
|
|| routeState.maneuverType == ManeuverType.DestinationLeft.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ import com.kouros.navigation.data.Constants
|
|||||||
import com.kouros.navigation.data.Locations
|
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
|
||||||
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.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
|
||||||
@@ -55,6 +57,9 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
MutableLiveData<List<Place>>()
|
MutableLiveData<List<Place>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val elements: MutableLiveData<List<Elements>> by lazy {
|
||||||
|
MutableLiveData<List<Elements>>()
|
||||||
|
}
|
||||||
|
|
||||||
fun loadRecentPlace(location: Location) {
|
fun loadRecentPlace(location: Location) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
@@ -113,6 +118,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
.orderDesc(Place_.lastDate)
|
.orderDesc(Place_.lastDate)
|
||||||
.build()
|
.build()
|
||||||
val results = query.find()
|
val results = query.find()
|
||||||
|
println("Favorites $results")
|
||||||
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)
|
||||||
@@ -231,6 +237,13 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
return place.address.road
|
return place.address.road
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAmenities(category: String, location: Location) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val amenities = Overpass().getAmenities(category, location)
|
||||||
|
elements.postValue(amenities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun saveFavorite(place: Place) {
|
fun saveFavorite(place: Place) {
|
||||||
place.category = Constants.FAVORITES
|
place.category = Constants.FAVORITES
|
||||||
savePlace(place)
|
savePlace(place)
|
||||||
@@ -258,7 +271,7 @@ class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
|||||||
val current = LocalDateTime.now(ZoneOffset.UTC)
|
val current = LocalDateTime.now(ZoneOffset.UTC)
|
||||||
place.lastDate = current.atZone(ZoneOffset.UTC).toEpochSecond()
|
place.lastDate = current.atZone(ZoneOffset.UTC).toEpochSecond()
|
||||||
placeBox.put(place)
|
placeBox.put(place)
|
||||||
println("Save Recent $place")
|
println("Save Place $place")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +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 com.kouros.navigation.data.GeoJsonFeature
|
import com.kouros.navigation.data.GeoJsonFeature
|
||||||
import com.kouros.navigation.data.GeoJsonFeatureCollection
|
import com.kouros.navigation.data.GeoJsonFeatureCollection
|
||||||
@@ -158,6 +159,23 @@ object NavigationUtils {
|
|||||||
return jsonString
|
return jsonString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getOverpassBbox(location: Location, radius: Double): String {
|
||||||
|
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||||
|
val neLon = bbox["ne"]?.get("lon")
|
||||||
|
val neLat = bbox["ne"]?.get("lat")
|
||||||
|
val swLon = bbox["sw"]?.get("lon")
|
||||||
|
val swLat = bbox["sw"]?.get("lat")
|
||||||
|
return "$swLon,$swLat,$neLon,$neLat"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBoundingBox2(location: Location, radius: Double): BoundingBox {
|
||||||
|
val bbox = getBoundingBox(location.longitude, location.latitude, radius)
|
||||||
|
val neLon = bbox["ne"]?.get("lon")
|
||||||
|
val neLat = bbox["ne"]?.get("lat")
|
||||||
|
val swLon = bbox["sw"]?.get("lon")
|
||||||
|
val swLat = bbox["sw"]?.get("lat")
|
||||||
|
return BoundingBox(swLat ?: 0.0 , swLon ?: 0.0, neLat ?: 0.0, neLon ?: 0.0)
|
||||||
|
}
|
||||||
fun getBoundingBox(
|
fun getBoundingBox(
|
||||||
lat: Double,
|
lat: Double,
|
||||||
lon: Double,
|
lon: Double,
|
||||||
@@ -176,30 +194,6 @@ object NavigationUtils {
|
|||||||
"se" to mapOf("lat" to minLat, "lon" to maxLon)
|
"se" to mapOf("lat" to minLat, "lon" to maxLon)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun computeOffset(from: Location, distance: Double, heading: Double): Location {
|
|
||||||
val earthRadius = 6371009.0
|
|
||||||
var distance = distance
|
|
||||||
var heading = heading
|
|
||||||
distance /= earthRadius
|
|
||||||
heading = toRadians(heading)
|
|
||||||
val fromLat: Double = toRadians(from.latitude)
|
|
||||||
val fromLng: Double = toRadians(from.longitude)
|
|
||||||
val cosDistance: Double = cos(distance)
|
|
||||||
val sinDistance = sin(distance)
|
|
||||||
val sinFromLat = sin(fromLat)
|
|
||||||
val cosFromLat: Double = cos(fromLat)
|
|
||||||
val sinLat: Double = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading)
|
|
||||||
val dLng: Double = atan2(
|
|
||||||
sinDistance * cosFromLat * sin(heading),
|
|
||||||
cosDistance - sinFromLat * sinLat
|
|
||||||
)
|
|
||||||
val snap = Location(LocationManager.GPS_PROVIDER)
|
|
||||||
snap.latitude = toDegrees(asin(sinLat))
|
|
||||||
snap.longitude = toDegrees(fromLng + dLng)
|
|
||||||
return snap
|
|
||||||
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateZoom(speed: Double?): Double {
|
fun calculateZoom(speed: Double?): Double {
|
||||||
@@ -208,11 +202,12 @@ fun calculateZoom(speed: Double?): Double {
|
|||||||
}
|
}
|
||||||
val speedKmh = (speed * 3.6).toInt()
|
val speedKmh = (speed * 3.6).toInt()
|
||||||
val zoom = when (speedKmh) {
|
val zoom = when (speedKmh) {
|
||||||
in 0..10 -> 17.0
|
in 0..10 -> 18.0
|
||||||
in 11..30 -> 16.0
|
in 11..30 -> 17.0
|
||||||
in 31..50 -> 16.0
|
in 21..40 -> 16.0
|
||||||
|
in 31..50 -> 15.0
|
||||||
in 51..60 -> 15.0
|
in 51..60 -> 15.0
|
||||||
else -> 15
|
else -> 14
|
||||||
}
|
}
|
||||||
return zoom.toDouble()
|
return zoom.toDouble()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user