This commit is contained in:
Dimitris
2025-12-16 07:19:29 +01:00
parent 72872cddeb
commit d546ede0e5
22 changed files with 589 additions and 137 deletions

View File

@@ -21,6 +21,7 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -64,27 +65,18 @@ class SurfaceRenderer(
)
var stableArea = Rect()
var width = 0
var height = 0
var lastBearing = 0.0
val routeData = MutableLiveData("")
val previewRouteData = MutableLiveData("")
val speed = MutableLiveData(0F)
lateinit var centerLocation: Location
var preview = false
var previewDistance = 0.0
val previewRouteData = MutableLiveData("")
lateinit var mapView: ComposeView
var panView = false
var tilt = 55.0
var previewDistance = 0.0
var countDownTimerActive = false
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
@@ -341,6 +333,18 @@ class SurfaceRenderer(
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
object {
private const val TAG = "MapRenderer"

View File

@@ -166,7 +166,7 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int) {
Canvas(modifier =Modifier
.size(imageSize.dp, imageSize.dp)) {
scale(scaleX = 1f, scaleY = 0.7f) {
drawCircle(Color.DarkGray.copy(alpha = 0.4f))
drawCircle(Color.DarkGray.copy(alpha = 0.2f))
}
}
Icon(

View File

@@ -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()
}
}

View File

@@ -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
}
}

View File

@@ -22,6 +22,7 @@ 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.categories
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.model.ViewModel
@@ -152,6 +153,7 @@ class PlaceListScreen(
R.string.recent_Item_deleted, CarToast.LENGTH_LONG
).show()
loadPlaces()
invalidate()
}
.build()

View File

@@ -26,6 +26,7 @@ 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.Action.FLAG_PRIMARY
import androidx.car.app.model.ActionStrip
import androidx.car.app.model.CarIcon
import androidx.car.app.model.CarText
@@ -94,6 +95,7 @@ class RoutePreviewScreen(
)
).build()
val navigateAction = Action.Builder()
.setFlags(FLAG_PRIMARY)
.setIcon(navigateActionIcon)
.setOnClickListener { this.onNavigate() }
.build()
@@ -108,58 +110,10 @@ class RoutePreviewScreen(
.setStartHeaderAction(Action.BACK)
.setTitle(carContext.getString(R.string.route_preview))
.addEndHeaderAction(
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)
println(destination)
invalidate()
}
.build()
favoriteAction()
)
.addEndHeaderAction(
Action.Builder()
.setOnClickListener {
if (mIsFavorite) {
vieModel.deleteFavorite(destination)
}
mIsFavorite = !mIsFavorite
finish()
}
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_delete_foreground
)
)
.build()
)
.build()
deleteFavoriteAction()
)
.build()
@@ -186,6 +140,56 @@ class RoutePreviewScreen(
.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 {
val route: CarText = createRouteText(index)
return Row.Builder()

View File

@@ -34,7 +34,8 @@ class SearchScreen(
var categories: List<Category> = 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))
)
@@ -66,20 +67,36 @@ class SearchScreen(
.setTitle(it.name)
.setImage(categoryIcon(it.id))
.setOnClickListener {
screenManager
.pushForResult(
PlaceListScreen(
carContext,
surfaceRenderer,
location,
it.id
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
if (it.id == Constants.CATEGORIES) {
screenManager
.pushForResult(
CategoriesScreen(
carContext,
surfaceRenderer,
location,
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
}
}
}
} else {
screenManager
.pushForResult(
PlaceListScreen(
carContext,
surfaceRenderer,
location,
it.id
)
) { obj: Any? ->
if (obj != null) {
setResult(obj)
finish()
}
}
}
}
.setBrowsable(true)
.build()

View 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>