App DrawItem and Search

This commit is contained in:
Dimitris
2025-12-09 15:52:27 +01:00
parent f70ca6e8fe
commit aeca6ff237
25 changed files with 868 additions and 654 deletions

View File

@@ -151,8 +151,8 @@ class NavigationSession : Session(), NavigationScreen.Listener {
updateLocation(location)
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
/* minTimeMs= */ 500,
/* minDistanceM= */ 0f,
/* minTimeMs= */ 1000,
/* minDistanceM= */ 5f,
mLocationListener
)
}

View File

@@ -18,6 +18,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
@@ -25,6 +27,11 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.kouros.navigation.car.map.BuildingLayer
import com.kouros.navigation.car.map.DrawImage
import com.kouros.navigation.car.map.RouteLayer
import com.kouros.navigation.car.map.cameraState
import com.kouros.navigation.car.map.getPaddingValues
import com.kouros.navigation.car.navigation.RouteCarModel
import com.kouros.navigation.data.Constants
import com.kouros.navigation.data.Constants.SHOW_THREED_BUILDING
@@ -33,9 +40,12 @@ import com.kouros.navigation.model.RouteModel
import com.kouros.navigation.utils.NavigationUtils.getBooleanKeyValue
import com.kouros.navigation.utils.bearing
import com.kouros.navigation.utils.calculateZoom
import com.kouros.navigation.utils.previewZoom
import org.maplibre.compose.camera.CameraPosition
import org.maplibre.compose.camera.CameraState
import org.maplibre.compose.map.MapOptions
import org.maplibre.compose.map.MaplibreMap
import org.maplibre.compose.map.OrnamentOptions
import org.maplibre.compose.sources.getBaseSource
import org.maplibre.compose.style.BaseStyle
import org.maplibre.spatialk.geojson.Position
@@ -77,7 +87,7 @@ class SurfaceRenderer(
lateinit var mapView: ComposeView
var panView = false
val tilt = 55.0
var tilt = 55.0
var previewDistance = 0.0
val mSurfaceCallback: SurfaceCallback = object : SurfaceCallback {
@@ -164,11 +174,12 @@ class SurfaceRenderer(
}
fun onConnectionStateUpdated(connectionState: Int) {
when(connectionState) {
when (connectionState) {
CarConnection.CONNECTION_TYPE_NATIVE -> ObjectBox.init(carContext)
}
}
@Composable
fun MapView() {
val stateWidth = visibleArea.observeAsState()
@@ -178,21 +189,29 @@ class SurfaceRenderer(
val paddingValues = getPaddingValues(width - stateWidth.value!!.width(), height, preview)
val cameraState = cameraState(paddingValues, position, tilt)
val baseStyle =if (isSystemInDarkTheme()) BaseStyle.Uri(Constants.STYLE_DARK) else BaseStyle.Uri(
Constants.STYLE
)
MaplibreMap(
cameraState = cameraState,
baseStyle = baseStyle,
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
if (!getBooleanKeyValue(context = carContext, SHOW_THREED_BUILDING)) {
BuildingLayer(tiles)
}
RouteLayer(route, previewRoute)
}
//Puck(cameraState, lastLocation)
val baseStyle = remember {
mutableStateOf(BaseStyle.Uri(Constants.STYLE))
}
baseStyle.value =
(if (isSystemInDarkTheme()) BaseStyle.Uri(Constants.STYLE_DARK) else BaseStyle.Uri(
Constants.STYLE
))
MaplibreMap(
options = MapOptions(
ornamentOptions =
OrnamentOptions(isScaleBarEnabled = false)),
cameraState = cameraState,
baseStyle = baseStyle.value
) {
getBaseSource(id = "openmaptiles")?.let { tiles ->
if (!getBooleanKeyValue(context = carContext, SHOW_THREED_BUILDING)) {
BuildingLayer(tiles)
}
RouteLayer(route, previewRoute, position!!.zoom)
}
//Puck(cameraState, lastLocation)
}
ShowPosition(cameraState, position, paddingValues)
}
@@ -215,7 +234,7 @@ class SurfaceRenderer(
}
} else {
bearing = 0.0
zoom = previewZoom()
zoom = previewZoom(previewDistance)
target = Position(centerLocation.longitude, centerLocation.latitude)
localTilt = 0.0
}
@@ -261,11 +280,19 @@ class SurfaceRenderer(
} else {
cameraPosition.value!!.zoom + 1.0
}
cameraPosition.postValue(
cameraPosition.value!!.copy(
zoom = newZoom,
target = cameraPosition.value!!.target
)
tilt = if (newZoom < 13) {
0.0
} else {
if (tilt == 0.0) {
55.0
} else {
tilt
}
}
updateCameraPosition(
cameraPosition.value!!.bearing,
newZoom,
cameraPosition.value!!.target,
)
}
}
@@ -273,7 +300,7 @@ class SurfaceRenderer(
fun updateLocation(location: Location) {
synchronized(this) {
if (!preview) {
val bearing = bearing(lastLocation, location)
val bearing = bearing(lastLocation, location, cameraPosition.value!!.bearing)
val zoom = if (!panView) {
calculateZoom(location.speed.toDouble())
} else {
@@ -287,10 +314,9 @@ class SurfaceRenderer(
lastBearing = cameraPosition.value!!.bearing
lastLocation = location
} else {
val zoom = previewZoom()
updateCameraPosition(
0.0,
zoom,
previewZoom(previewDistance),
Position(centerLocation.longitude, centerLocation.latitude)
)
}
@@ -302,7 +328,7 @@ class SurfaceRenderer(
cameraPosition.value!!.copy(
bearing = bearing,
zoom = zoom,
tilt = 0.0,
tilt = tilt,
padding = getPaddingValues(width - visibleArea.value!!.width(), height, preview),
target = target
)
@@ -323,28 +349,6 @@ class SurfaceRenderer(
previewDistance = routeModel.route.distance
}
private fun previewZoom(): 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
}
}
return 9.0
}
fun setPreViewDistance(): Double {
return previewDistance
}
companion
object {
private const val TAG = "MapRenderer"

View File

@@ -1,4 +1,4 @@
package com.kouros.navigation.car
package com.kouros.navigation.car.map
import android.location.Location
import androidx.compose.runtime.Composable

View File

@@ -1,4 +1,4 @@
package com.kouros.navigation.car
package com.kouros.navigation.car.map
import android.location.Location
import androidx.compose.foundation.Canvas
@@ -17,9 +17,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
@@ -67,7 +66,8 @@ fun cameraState(
}
@Composable
fun RouteLayer(routeData: String?, previewRoute: String?) {
fun RouteLayer(routeData: String?, previewRoute: String?, zoom: Double) {
val width = zoom - 2
if (routeData!!.isNotEmpty()) {
val routes =
rememberGeoJsonSource(GeoJsonData.JsonString(routeData))
@@ -75,13 +75,13 @@ fun RouteLayer(routeData: String?, previewRoute: String?) {
id = "routes-casing",
source = routes,
color = const(Color.White),
width = const(16.dp),
width = const((width+2).dp),
)
LineLayer(
id = "routes",
source = routes,
color = const(RouteColor),
width = const(14.dp),
width = const(width.dp),
)
}
if (previewRoute!!.isNotEmpty()) {
@@ -122,10 +122,7 @@ fun DrawImage(padding: PaddingValues, location: Location, width: Int, height: In
@Composable
fun NavigationImage(padding: PaddingValues, width: Int, height: Int, street: String) {
val imageSize = (height/6)
println("Image Size: $imageSize")
val vector = ImageVector.vectorResource(id = R.drawable.assistant_navigation_48px)
val color = remember { NavigationColor }
BadgedBox(
modifier = Modifier
@@ -134,19 +131,18 @@ fun NavigationImage(padding: PaddingValues, width: Int, height: Int, street: Str
Badge()
}
) {
Canvas(modifier =Modifier
.size(imageSize.dp, imageSize.dp)) {
scale(scaleX = 1f, scaleY = 0.7f) {
drawCircle(Color.DarkGray.copy(alpha = 0.2f))
}
}
Icon(
painter = painterResource(id = R.drawable.navigation),
"Navigation",
tint = color,
tint = color.copy(alpha = 1f),
modifier = Modifier.size(imageSize.dp, imageSize.dp),
)
// Icon(
// modifier = Modifier.size(72.dp, 72.dp),
// imageVector = vector,
// contentDescription = "Navigation",
// tint = color
// )
if (street.isNotEmpty())
Text(text = street)
}

View File

@@ -81,7 +81,7 @@ class RouteCarModel() : RouteModel() {
val maneuverType = maneuver.type
val routing = routingData(maneuverType, carContext)
var text = ""
val distanceLeft = leftStepDistance() * 1000
val distanceLeft = leftStepDistance()
when (distanceLeft) {
in 0.0..NEXT_STEP_THRESHOLD -> {
@@ -178,7 +178,7 @@ class RouteCarModel() : RouteModel() {
|| maneuverType == ManeuverType.DestinationLeft.value
}
fun travelEstimate(carContext: CarContext): TravelEstimate {
fun travelEstimate(): TravelEstimate {
val timeLeft = travelLeftTime()
// Calculate the time to destination from the current time.
val nowUtcMillis = System.currentTimeMillis()

View File

@@ -29,6 +29,7 @@ import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.car.NavigationCarAppService
import com.kouros.navigation.car.SurfaceRenderer
import com.kouros.navigation.car.navigation.RouteCarModel
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.model.ViewModel
@@ -102,7 +103,7 @@ class NavigationScreen(
.setNavigationInfo(
getRoutingInfo()
)
.setDestinationTravelEstimate(routeModel.travelEstimate(carContext))
.setDestinationTravelEstimate(routeModel.travelEstimate())
.setActionStrip(actionStripBuilder.build())
.setMapActionStrip(mapActionStripBuilder().build())
.setBackgroundColor(CarColor.GREEN)
@@ -182,27 +183,11 @@ class NavigationScreen(
return builder.build()
}
fun navigationRerouteTemplate(actionStripBuilder: ActionStrip.Builder): NavigationTemplate {
fun navigationRerouteTemplate(actionStripBuilder: ActionStrip.Builder): Template {
return NavigationTemplate.Builder()
.setNavigationInfo(
MessageInfo.Builder(
carContext.getString(R.string.new_route)
)
.setText(routeModel.destination.street.toString())
.setImage(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.navigation_48px
)
)
.build()
)
.build()
)
.setBackgroundColor(CarColor.SECONDARY)
.setNavigationInfo(RoutingInfo.Builder().setLoading(true).build())
.setActionStrip(actionStripBuilder.build())
.setMapActionStrip(mapActionStripBuilder().build())
.setBackgroundColor(CarColor.GREEN)
.build()
}
@@ -447,8 +432,12 @@ class NavigationScreen(
}
fun updateTrip(location: Location) {
val start = System.currentTimeMillis()
routeModel.updateLocation(location)
if (routeModel.maneuverType == Maneuver.TYPE_DESTINATION && routeModel.leftStepDistance() * 1000 < 25.0) {
val end = System.currentTimeMillis()
println("Time ${end-start}")
if (routeModel.maneuverType == Maneuver.TYPE_DESTINATION
&& routeModel.leftStepDistance() < DESTINATION_ARRIVAL_DISTANCE) {
routeModel.arrived = true
routeModel.stopNavigation()
}

View File

@@ -2,11 +2,8 @@ package com.kouros.navigation.car.screen
import android.location.Location
import android.net.Uri
import android.os.Build
import android.text.Spannable
import android.text.SpannableString
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
@@ -25,7 +22,6 @@ import com.kouros.android.cars.carappservice.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.TAG
import com.kouros.navigation.data.NavigationRepository
import com.kouros.navigation.data.Place
import com.kouros.navigation.model.ViewModel
@@ -66,7 +62,7 @@ class PlaceListScreen(
fun loadPlaces() {
if (category == Constants.RECENT) {
viewModel.loadPlaces(carContext, location)
viewModel.loadRecentPlaces(carContext, location)
}
if (category == Constants.CONTACTS) {
viewModel.loadContacts(carContext, location)

View File

@@ -2,14 +2,17 @@ package com.kouros.navigation.car.screen
import android.annotation.SuppressLint
import android.location.Location
import android.net.Uri
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarIcon
import androidx.car.app.model.ItemList
import androidx.car.app.model.Row
import androidx.car.app.model.SearchTemplate
import androidx.car.app.model.SearchTemplate.SearchCallback
import androidx.car.app.model.Template
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.Observer
import com.kouros.android.cars.carappservice.R
import com.kouros.navigation.car.SurfaceRenderer
@@ -61,6 +64,7 @@ class SearchScreen(
itemListBuilder.addItem(
Row.Builder()
.setTitle(it.name)
.setImage(categoryIcon(it.id))
.setOnClickListener {
screenManager
.pushForResult(
@@ -104,6 +108,25 @@ class SearchScreen(
.build()
}
fun categoryIcon(category: String?): CarIcon {
val resId : Int = when (category) {
Constants.RECENT -> {
R.drawable.ic_place_white_24dp
}
Constants.FAVORITES -> {
R.drawable.ic_favorite_white_24dp
}
else -> {
R.drawable.navigation
}
}
return CarIcon.Builder(
IconCompat.createWithResource(
carContext, resId
)
).build()
}
@SuppressLint("DefaultLocale")
fun doSearch(searchItemListBuilder: ItemList.Builder) {
searchResult.forEach {