CarHardwareInfoScreen
This commit is contained in:
@@ -17,8 +17,8 @@ android {
|
||||
applicationId = "com.kouros.navigation"
|
||||
minSdk = 33
|
||||
targetSdk = 37
|
||||
versionCode = 125
|
||||
versionName = "0.3.2.125"
|
||||
versionCode = 131
|
||||
versionName = "0.4.0.131"
|
||||
base.archivesName = "navi-$versionName"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ package com.kouros.navigation.car
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.kouros.navigation.car.navigation.Simulation
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.kouros.navigation.data.Constants.homeHohenwaldeck
|
||||
import com.kouros.navigation.data.RouteEngine
|
||||
import com.kouros.navigation.data.overpass.Overpass
|
||||
import com.kouros.navigation.data.route.ManeuverType
|
||||
import com.kouros.navigation.data.tomtom.TomTomRepository
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
@@ -15,18 +13,13 @@ import com.kouros.navigation.model.RouteModel
|
||||
import com.kouros.navigation.utils.GeoUtils.snapLocation
|
||||
import com.kouros.navigation.utils.getSettingsRepository
|
||||
import com.kouros.navigation.utils.location
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.maplibre.compose.expressions.dsl.step
|
||||
import org.maplibre.geojson.Point
|
||||
import kotlin.collections.forEach
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
@@ -635,5 +628,20 @@ class RouteModelTest {
|
||||
routeModel.updateLocation(location, NavigationViewModel(TomTomRepository()))
|
||||
//assertEquals(routeModel.currentStep().leftStepDistance, 0.0, 1.0)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `check deviation `() {
|
||||
val navigationViewModel = NavigationViewModel(TomTomRepository())
|
||||
// Schmalkaldener Straße
|
||||
val firstLocation = location(11.579903, 48.186906)
|
||||
routeModel.updateLocation(firstLocation, navigationViewModel)
|
||||
// Frankfurter Ring
|
||||
val location = location(11.579794, 48.187700)
|
||||
val snappedLocation = snapLocation(location, routeModel.route.maneuverLocations())
|
||||
val distance = snappedLocation.distanceTo(firstLocation)
|
||||
assertEquals(distance.toDouble(), 12.0, 1.0)
|
||||
assertEquals(snappedLocation.latitude, 48.18691500607897, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -458,6 +458,44 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the location deviation is acceptable.
|
||||
*/
|
||||
private fun checkLocationDeviation(
|
||||
location: Location,
|
||||
snappedLocation: Location,
|
||||
streetName: String
|
||||
): Boolean {
|
||||
var maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION
|
||||
val speed = surfaceRenderer.speed.value
|
||||
if (speed != null) {
|
||||
when (speed) {
|
||||
in 0.0..10.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION
|
||||
in 10.0..20.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 200
|
||||
in 20.0..30.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 400
|
||||
in 30.0..100.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 500
|
||||
}
|
||||
}
|
||||
val distance = location.distanceTo(snappedLocation)
|
||||
Log.d(TAG, "Distance: $distance $maximalRouteDeviation $speed")
|
||||
when {
|
||||
distance > maximalRouteDeviation -> {
|
||||
stopNavigation()
|
||||
navigationScreen.calculateNewRoute(routeModel.navState.destination, distance)
|
||||
return false
|
||||
}
|
||||
|
||||
distance < MAXIMAL_SNAP_CORRECTION -> {
|
||||
surfaceRenderer.updateLocation(snappedLocation, streetName)
|
||||
}
|
||||
|
||||
else -> {
|
||||
surfaceRenderer.updateLocation(location, streetName)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles location updates during active navigation.
|
||||
* Snaps location to route and checks for deviation requiring reroute.
|
||||
@@ -483,43 +521,6 @@ class NavigationSession : CarSession(), NavigationListener, NavigationObserverCa
|
||||
//Log.d(TAG, "UpdateLocation ${end-start} ms")
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the location deviation is acceptable.
|
||||
*/
|
||||
private fun checkLocationDeviation(
|
||||
location: Location,
|
||||
snappedLocation: Location,
|
||||
streetName: String
|
||||
): Boolean {
|
||||
var maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION
|
||||
val speed = surfaceRenderer.speed.value
|
||||
if (speed != null) {
|
||||
when (speed) {
|
||||
in 0.0..10.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION
|
||||
in 10.0..20.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 200
|
||||
in 20.0..30.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 400
|
||||
in 30.0..100.0 -> maximalRouteDeviation = MAXIMAL_ROUTE_DEVIATION + 500
|
||||
}
|
||||
}
|
||||
val distance = location.distanceTo(snappedLocation)
|
||||
when {
|
||||
distance > maximalRouteDeviation -> {
|
||||
stopNavigation()
|
||||
navigationScreen.calculateNewRoute(routeModel.navState.destination, distance)
|
||||
return false
|
||||
}
|
||||
|
||||
distance < MAXIMAL_SNAP_CORRECTION -> {
|
||||
surfaceRenderer.updateLocation(snappedLocation, streetName)
|
||||
}
|
||||
|
||||
else -> {
|
||||
surfaceRenderer.updateLocation(location, streetName)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the navigation screen with new trip information.
|
||||
*/
|
||||
|
||||
@@ -398,8 +398,11 @@ class SurfaceRenderer(
|
||||
synchronized(this) {
|
||||
street.value = streetName
|
||||
if (viewStyle == ViewStyle.VIEW || viewStyle == ViewStyle.PAN_VIEW) {
|
||||
val bearing = if (carOrientation == 999F) {
|
||||
if (location.hasBearing()) {
|
||||
//val bearing =
|
||||
// location.bearing.toDouble()
|
||||
//carOrientation = bearing.toFloat()
|
||||
// val bearing = if (carOrientation == 999F) {
|
||||
val bearing = if (location.hasBearing()) {
|
||||
location.bearing.toDouble()
|
||||
} else {
|
||||
bearingPositive(
|
||||
@@ -408,9 +411,10 @@ class SurfaceRenderer(
|
||||
cameraPosition.value!!.bearing
|
||||
)
|
||||
}
|
||||
} else {
|
||||
carOrientation.toDouble()
|
||||
}
|
||||
carOrientation = bearing.toFloat()
|
||||
// } else {
|
||||
// carOrientation.toDouble()
|
||||
// }
|
||||
val zoom = if (viewStyle == ViewStyle.VIEW) {
|
||||
calculateZoom(location.speed.toDouble())
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
package com.kouros.navigation.car.screen
|
||||
|
||||
import android.util.Log
|
||||
import androidx.annotation.GuardedBy
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.hardware.CarHardwareManager
|
||||
import androidx.car.app.hardware.common.CarValue
|
||||
import androidx.car.app.hardware.common.OnCarDataAvailableListener
|
||||
import androidx.car.app.hardware.info.CarHardwareLocation
|
||||
import androidx.car.app.hardware.info.CarSensors
|
||||
import androidx.car.app.hardware.info.Compass
|
||||
import androidx.car.app.hardware.info.EnergyProfile
|
||||
import androidx.car.app.hardware.info.ExteriorDimensions
|
||||
import androidx.car.app.hardware.info.Model
|
||||
import androidx.car.app.hardware.info.Speed
|
||||
import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.Header
|
||||
import androidx.car.app.model.Pane
|
||||
import androidx.car.app.model.PaneTemplate
|
||||
import androidx.car.app.model.Row
|
||||
import androidx.car.app.model.Template
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.kouros.data.R
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/**
|
||||
* Creates a screen that show the static information (such as model and energy profile) available
|
||||
* via CarHardware interfaces.
|
||||
*/
|
||||
class CarHardwareInfoScreen(carContext: CarContext) : Screen(carContext) {
|
||||
// Package private for inner class reference
|
||||
|
||||
var mHasLocationPermission: Boolean = false
|
||||
var mHasCompassPermission: Boolean = false
|
||||
|
||||
var mHasSpeedPermission: Boolean = false
|
||||
var mHasModelPermission: Boolean = false
|
||||
var mHasEnergyProfilePermission: Boolean = false
|
||||
var mHasExteriorDimensionsPermission: Boolean = false
|
||||
val mCarHardwareExecutor: Executor = ContextCompat.getMainExecutor(getCarContext())
|
||||
|
||||
@GuardedBy("this")
|
||||
var mLocation: CarHardwareLocation? = null
|
||||
|
||||
@GuardedBy("this")
|
||||
var mCompass: Compass? = null
|
||||
|
||||
@GuardedBy("this")
|
||||
var mSpeed: Speed? = null
|
||||
|
||||
/**
|
||||
* Value fetched from CarHardwareManager containing model information.
|
||||
*
|
||||
*
|
||||
* It is requested asynchronously and can be `null` until the response is
|
||||
* received.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
var mModel: Model? = null
|
||||
|
||||
/**
|
||||
* Value fetched from CarHardwareManager containing what type of fuel/ports the car has.
|
||||
*
|
||||
*
|
||||
* It is requested asynchronously and can be `null` until the response is
|
||||
* received.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
var mEnergyProfile: EnergyProfile? = null
|
||||
|
||||
@GuardedBy("this")
|
||||
var mExteriorDimensions: ExteriorDimensions? = null
|
||||
|
||||
val carLocationListener: OnCarDataAvailableListener<CarHardwareLocation?> =
|
||||
OnCarDataAvailableListener { data ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received locaction: " + data)
|
||||
mLocation = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
val mCompassListener: OnCarDataAvailableListener<Compass?> =
|
||||
OnCarDataAvailableListener { data: Compass? ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received compass: " + data)
|
||||
mCompass = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
var mModelListener: OnCarDataAvailableListener<Model?> =
|
||||
OnCarDataAvailableListener { data: Model? ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received model information: " + data)
|
||||
mModel = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
var mSpeedListener: OnCarDataAvailableListener<Speed?> =
|
||||
OnCarDataAvailableListener { data: Speed? ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received speed information: " + data)
|
||||
mSpeed = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
var mEnergyProfileListener: OnCarDataAvailableListener<EnergyProfile?> =
|
||||
OnCarDataAvailableListener { data: EnergyProfile? ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received energy profile information: " + data)
|
||||
mEnergyProfile = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
var mExteriorDimensionsListener: OnCarDataAvailableListener<ExteriorDimensions?> =
|
||||
OnCarDataAvailableListener { data: ExteriorDimensions? ->
|
||||
synchronized(this) {
|
||||
Log.i(TAG, "Received exterior dimensions: " + data)
|
||||
mExteriorDimensions = data
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
val carHardwareManager =
|
||||
getCarContext().getCarService(CarHardwareManager::class.java)
|
||||
val carInfo = carHardwareManager.carInfo
|
||||
val carSensors = carHardwareManager.carSensors
|
||||
|
||||
// Request any single shot values.
|
||||
synchronized(this@CarHardwareInfoScreen) {
|
||||
mLocation = null
|
||||
try {
|
||||
carSensors.addCarHardwareLocationListener(
|
||||
CarSensors.UPDATE_RATE_NORMAL, mCarHardwareExecutor,
|
||||
carLocationListener
|
||||
)
|
||||
mHasLocationPermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasLocationPermission = false
|
||||
}
|
||||
|
||||
mCompass = null
|
||||
try {
|
||||
carSensors.addCompassListener(
|
||||
CarSensors.UPDATE_RATE_NORMAL, mCarHardwareExecutor,
|
||||
mCompassListener
|
||||
)
|
||||
mHasCompassPermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasCompassPermission = false
|
||||
}
|
||||
|
||||
mSpeed = null
|
||||
try {
|
||||
carInfo.addSpeedListener(mCarHardwareExecutor, mSpeedListener)
|
||||
mHasSpeedPermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasSpeedPermission = false
|
||||
}
|
||||
|
||||
mModel = null
|
||||
try {
|
||||
carInfo.fetchModel(mCarHardwareExecutor, mModelListener)
|
||||
mHasModelPermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasModelPermission = false
|
||||
}
|
||||
|
||||
mEnergyProfile = null
|
||||
try {
|
||||
carInfo.fetchEnergyProfile(mCarHardwareExecutor, mEnergyProfileListener)
|
||||
mHasEnergyProfilePermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasEnergyProfilePermission = false
|
||||
}
|
||||
|
||||
mExteriorDimensions = null
|
||||
try {
|
||||
carInfo.fetchExteriorDimensions(
|
||||
mCarHardwareExecutor,
|
||||
mExteriorDimensionsListener
|
||||
)
|
||||
mHasExteriorDimensionsPermission = true
|
||||
} catch (_: SecurityException) {
|
||||
mHasExteriorDimensionsPermission = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
val paneBuilder = Pane.Builder()
|
||||
if (allInfoAvailable()) {
|
||||
val carInfoRowBuilder = Row.Builder()
|
||||
.setTitle(getCarContext().getString(R.string.car_sensors))
|
||||
if (!mHasCompassPermission) {
|
||||
carInfoRowBuilder.addText(getCarContext().getString(R.string.no_model_permission))
|
||||
} else {
|
||||
val info = StringBuilder()
|
||||
synchronized(this@CarHardwareInfoScreen) {
|
||||
if (mCompass != null) {
|
||||
if (mCompass!!.orientations.status == CarValue.STATUS_SUCCESS) {
|
||||
info.append(mCompass!!.orientations)
|
||||
}
|
||||
if (mCompass!!.orientations
|
||||
.status == CarValue.STATUS_UNAVAILABLE
|
||||
) {
|
||||
info.append("Compass unavailable")
|
||||
}
|
||||
if (mCompass!!.orientations
|
||||
.status == CarValue.STATUS_UNIMPLEMENTED
|
||||
) {
|
||||
info.append("Compass unimplemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
carInfoRowBuilder.addText(info)
|
||||
}
|
||||
|
||||
if (!mHasLocationPermission) {
|
||||
carInfoRowBuilder.addText(carContext.getString(R.string.no_model_permission))
|
||||
} else {
|
||||
val info = StringBuilder()
|
||||
synchronized(this@CarHardwareInfoScreen) {
|
||||
if (mLocation != null) {
|
||||
if (mLocation!!.location.status == CarValue.STATUS_SUCCESS) {
|
||||
info.append(mLocation!!.location)
|
||||
}
|
||||
if (mLocation!!.location.status
|
||||
== CarValue.STATUS_UNAVAILABLE
|
||||
) {
|
||||
info.append("Location unavailable")
|
||||
}
|
||||
if (mLocation!!.location
|
||||
.status == CarValue.STATUS_UNIMPLEMENTED
|
||||
) {
|
||||
info.append("Location unimplemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
carInfoRowBuilder.addText(info)
|
||||
}
|
||||
|
||||
paneBuilder.addRow(carInfoRowBuilder.build())
|
||||
|
||||
val modelRowBuilder = Row.Builder()
|
||||
.setTitle(getCarContext().getString(R.string.model_info))
|
||||
if (!mHasModelPermission) {
|
||||
modelRowBuilder.addText(getCarContext().getString(R.string.no_model_permission))
|
||||
} else {
|
||||
val info = StringBuilder()
|
||||
synchronized(this@CarHardwareInfoScreen) {
|
||||
checkNotNull(mModel)
|
||||
if (mModel!!.manufacturer.status != CarValue.STATUS_SUCCESS) {
|
||||
info.append(getCarContext().getString(R.string.manufacturer_unavailable))
|
||||
info.append(", ")
|
||||
} else {
|
||||
info.append(mModel!!.manufacturer.getValue())
|
||||
info.append(", ")
|
||||
}
|
||||
if (mModel!!.name.status != CarValue.STATUS_SUCCESS) {
|
||||
info.append(getCarContext().getString(R.string.model_unavailable))
|
||||
info.append(", ")
|
||||
} else {
|
||||
info.append(mModel!!.name.getValue())
|
||||
info.append(", ")
|
||||
}
|
||||
if (mModel!!.year.status != CarValue.STATUS_SUCCESS) {
|
||||
info.append(getCarContext().getString(R.string.year_unavailable))
|
||||
} else {
|
||||
info.append(mModel!!.year.getValue())
|
||||
}
|
||||
}
|
||||
modelRowBuilder.addText(info)
|
||||
}
|
||||
paneBuilder.addRow(modelRowBuilder.build())
|
||||
|
||||
val speedRowBuilder = Row.Builder()
|
||||
.setTitle(carContext.getString(R.string.speed))
|
||||
if (!mHasSpeedPermission) {
|
||||
speedRowBuilder.addText(carContext.getString(R.string.no_speed_permission))
|
||||
} else {
|
||||
if (mSpeed != null) {
|
||||
val info = StringBuilder()
|
||||
synchronized(this@CarHardwareInfoScreen) {
|
||||
if (mSpeed!!.displaySpeedMetersPerSecond.status != CarValue.STATUS_SUCCESS) {
|
||||
info.append(getCarContext().getString(R.string.manufacturer_unavailable))
|
||||
info.append(", ")
|
||||
} else {
|
||||
info.append(mSpeed!!.displaySpeedMetersPerSecond.value)
|
||||
info.append(", ")
|
||||
}
|
||||
}
|
||||
speedRowBuilder.addText(info)
|
||||
} else {
|
||||
val info = StringBuilder()
|
||||
info.append(carContext.getString(R.string.speed_unavailable))
|
||||
speedRowBuilder.addText(info)
|
||||
}
|
||||
}
|
||||
paneBuilder.addRow(speedRowBuilder.build())
|
||||
|
||||
val energyProfileRowBuilder = Row.Builder()
|
||||
.setTitle(getCarContext().getString(R.string.energy_profile))
|
||||
if (!mHasEnergyProfilePermission) {
|
||||
energyProfileRowBuilder.addText(
|
||||
getCarContext()
|
||||
.getString(R.string.no_energy_profile_permission)
|
||||
)
|
||||
} else {
|
||||
val fuelInfo = StringBuilder()
|
||||
|
||||
synchronized(this) {
|
||||
if (mEnergyProfile!!.fuelTypes.status != CarValue.STATUS_SUCCESS) {
|
||||
fuelInfo.append(getCarContext().getString(R.string.fuel_types))
|
||||
fuelInfo.append(": ")
|
||||
fuelInfo.append(getCarContext().getString(R.string.unavailable))
|
||||
} else {
|
||||
fuelInfo.append(getCarContext().getString(R.string.fuel_types))
|
||||
fuelInfo.append(": ")
|
||||
for (fuelType in mEnergyProfile!!.fuelTypes.getValue()!!) {
|
||||
fuelInfo.append(fuelTypeAsString(fuelType))
|
||||
fuelInfo.append(" ")
|
||||
}
|
||||
}
|
||||
energyProfileRowBuilder.addText(fuelInfo)
|
||||
val evInfo = StringBuilder()
|
||||
if (mEnergyProfile!!.evConnectorTypes.status
|
||||
!= CarValue.STATUS_SUCCESS
|
||||
) {
|
||||
evInfo.append(" ")
|
||||
evInfo.append(getCarContext().getString(R.string.ev_connector_types))
|
||||
evInfo.append(": ")
|
||||
evInfo.append(getCarContext().getString(R.string.unavailable))
|
||||
} else {
|
||||
evInfo.append(getCarContext().getString(R.string.ev_connector_types))
|
||||
evInfo.append(": ")
|
||||
for (connectorType in mEnergyProfile!!.evConnectorTypes.getValue()!!) {
|
||||
evInfo.append(evConnectorAsString(connectorType))
|
||||
evInfo.append(" ")
|
||||
}
|
||||
}
|
||||
energyProfileRowBuilder.addText(evInfo)
|
||||
}
|
||||
}
|
||||
paneBuilder.addRow(energyProfileRowBuilder.build())
|
||||
|
||||
synchronized(this) {
|
||||
paneBuilder.addRow(
|
||||
buildExteriorDimensionsRow(
|
||||
mExteriorDimensions,
|
||||
mHasExteriorDimensionsPermission
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
paneBuilder.setLoading(true)
|
||||
}
|
||||
return PaneTemplate.Builder(paneBuilder.build())
|
||||
.setHeader(
|
||||
Header.Builder()
|
||||
.setStartHeaderAction(Action.BACK)
|
||||
.setTitle(getCarContext().getString(R.string.car_hardware_info))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun allInfoAvailable(): Boolean {
|
||||
synchronized(this) {
|
||||
if (mHasModelPermission && mModel == null) {
|
||||
return false
|
||||
}
|
||||
if (mHasEnergyProfilePermission && mEnergyProfile == null) {
|
||||
return false
|
||||
}
|
||||
if (mHasExteriorDimensionsPermission && mExteriorDimensions == null) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "showcase"
|
||||
|
||||
private fun fuelTypeAsString(fuelType: Int): String {
|
||||
when (fuelType) {
|
||||
EnergyProfile.FUEL_TYPE_UNLEADED -> return "UNLEADED"
|
||||
EnergyProfile.FUEL_TYPE_LEADED -> return "LEADED"
|
||||
EnergyProfile.FUEL_TYPE_DIESEL_1 -> return "DIESEL_1"
|
||||
EnergyProfile.FUEL_TYPE_DIESEL_2 -> return "DIESEL_2"
|
||||
EnergyProfile.FUEL_TYPE_BIODIESEL -> return "BIODIESEL"
|
||||
EnergyProfile.FUEL_TYPE_E85 -> return "E85"
|
||||
EnergyProfile.FUEL_TYPE_LPG -> return "LPG"
|
||||
EnergyProfile.FUEL_TYPE_CNG -> return "CNG"
|
||||
EnergyProfile.FUEL_TYPE_LNG -> return "LNG"
|
||||
EnergyProfile.FUEL_TYPE_ELECTRIC -> return "ELECTRIC"
|
||||
EnergyProfile.FUEL_TYPE_HYDROGEN -> return "HYDROGEN"
|
||||
EnergyProfile.FUEL_TYPE_OTHER -> return "OTHER"
|
||||
EnergyProfile.FUEL_TYPE_UNKNOWN -> return "UNKNOWN"
|
||||
else -> return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
private fun evConnectorAsString(evConnectorType: Int): String {
|
||||
when (evConnectorType) {
|
||||
EnergyProfile.EVCONNECTOR_TYPE_J1772 -> return "J1772"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_MENNEKES -> return "MENNEKES"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_CHADEMO -> return "CHADEMO"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_COMBO_1 -> return "COMBO_1"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_COMBO_2 -> return "COMBO_2"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_TESLA_ROADSTER -> return "TESLA_ROADSTER"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_TESLA_HPWC -> return "TESLA_HPWC"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_TESLA_SUPERCHARGER -> return "TESLA_SUPERCHARGER"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_GBT -> return "GBT"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_GBT_DC -> return "GBT_DC"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_SCAME -> return "SCAME"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_OTHER -> return "OTHER"
|
||||
EnergyProfile.EVCONNECTOR_TYPE_UNKNOWN -> return "UNKNOWN"
|
||||
else -> return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildExteriorDimensionsRow(
|
||||
exteriorDimensions: ExteriorDimensions?, hasPermissions: Boolean
|
||||
): Row {
|
||||
val builder = Row.Builder().setTitle("Exterior dimensions")
|
||||
if (!hasPermissions) {
|
||||
builder.addText("Permissions not granted. This vehicle property requires CAR_INFO")
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
if (exteriorDimensions == null) {
|
||||
builder.addText("Pending callback from vehicle fetch request")
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
val carValue = exteriorDimensions.getExteriorDimensions()
|
||||
if (carValue.getStatus() != CarValue.STATUS_SUCCESS) {
|
||||
builder.addText("Fetch failed because the vehicle hasn't implemented this field")
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
val dimensionsArray = carValue.getValue()
|
||||
if (dimensionsArray == null || dimensionsArray.size != 8) {
|
||||
builder.addText(
|
||||
"Fetch succeeded, but the reply was not an int array of length 8: "
|
||||
+ dimensionsArray.contentToString()
|
||||
)
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
builder.addText(
|
||||
("Height: " + dimensionsArray[ExteriorDimensions.HEIGHT_INDEX]
|
||||
+ ", Length: " + dimensionsArray[ExteriorDimensions.LENGTH_INDEX]
|
||||
+ ", Width: " + dimensionsArray[ExteriorDimensions.WIDTH_INDEX]
|
||||
+ ", Width + mirrors: "
|
||||
+ dimensionsArray[ExteriorDimensions.WIDTH_INCLUDING_MIRRORS_INDEX])
|
||||
)
|
||||
builder.addText(
|
||||
("Wheel base: " + dimensionsArray[ExteriorDimensions.WHEEL_BASE_INDEX]
|
||||
+ ", Front width: " + dimensionsArray[ExteriorDimensions.TRACK_WIDTH_FRONT_INDEX]
|
||||
+ ", Rear width: " + dimensionsArray[ExteriorDimensions.TRACK_WIDTH_REAR_INDEX]
|
||||
+ ", Turning radius: "
|
||||
+ dimensionsArray[ExteriorDimensions.CURB_TO_CURB_TURNING_RADIUS_INDEX])
|
||||
)
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,7 +478,7 @@ open class NavigationScreen(
|
||||
val mainThreadHandler = Handler(carContext.mainLooper)
|
||||
mainThreadHandler.post {
|
||||
reRouteTimer?.cancel()
|
||||
reRouteTimer = object : CountDownTimer(5000, 1000) {
|
||||
reRouteTimer = object : CountDownTimer(3000, 1000) {
|
||||
override fun onTick(millisUntilFinished: Long) {}
|
||||
override fun onFinish() {
|
||||
navigationType = NavigationType.NAVIGATION
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.kouros.navigation.car.screen.settings
|
||||
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.Screen
|
||||
import androidx.car.app.annotations.ExperimentalCarApi
|
||||
import androidx.car.app.model.Action
|
||||
import androidx.car.app.model.CarIcon
|
||||
import androidx.car.app.model.Header
|
||||
@@ -16,6 +18,7 @@ import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.kouros.data.R
|
||||
import com.kouros.navigation.car.screen.CarHardwareInfoScreen
|
||||
import com.kouros.navigation.model.NavigationViewModel
|
||||
import com.kouros.navigation.utils.getSettingsViewModel
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -37,6 +40,7 @@ class SettingsScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCarApi::class)
|
||||
override fun onGetTemplate(): Template {
|
||||
settingsViewModel.guidanceAudio.asLiveData().observe(this, Observer {
|
||||
audioToggleState = settingsViewModel.guidanceAudio.value == 1
|
||||
@@ -94,6 +98,13 @@ class SettingsScreen(
|
||||
)
|
||||
)
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForTemplate(
|
||||
CarHardwareInfoScreen(carContext),
|
||||
R.string.model_info
|
||||
)
|
||||
)
|
||||
|
||||
listBuilder.addItem(
|
||||
buildRowForTemplate(
|
||||
CarSettings(carContext, navigationViewModel),
|
||||
|
||||
@@ -74,4 +74,22 @@
|
||||
<string name="alternative_routes">Alternative Routen</string>
|
||||
<string name="wait">Warten</string>
|
||||
<string name="restaurant">Restaurant</string>
|
||||
|
||||
<!-- CarHardwareInfoScreen -->
|
||||
<string name="car_hardware_info">Car Hardware Information</string>
|
||||
<string name="model_info">Model Information</string>
|
||||
<string name="no_model_permission">No Model Permission</string>
|
||||
<string name="no_speed_permission">No Speed Permission</string>
|
||||
<string name="manufacturer_unavailable">Manufacturer unavailable</string>
|
||||
<string name="model_unavailable">Model unavailable</string>
|
||||
<string name="year_unavailable">Year unavailable</string>
|
||||
<string name="energy_profile">Energy Profile</string>
|
||||
<string name="no_energy_profile_permission">No Energy Profile Permission</string>
|
||||
<string name="fuel_types">Fuel Types</string>
|
||||
<string name="unavailable">Unavailable</string>
|
||||
<string name="ev_connector_types">EV Connector Types</string>
|
||||
<string name="car_sensors">Car Sensors</string>
|
||||
<string name="speed">Speed</string>
|
||||
<string name="speed_unavailable">Speed unavailable</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -58,4 +58,22 @@
|
||||
<string name="alternative_routes">Εναλλακτικές διαδρομές</string>
|
||||
<string name="wait">Περιμένετε</string>
|
||||
<string name="restaurant">Restaurant</string>
|
||||
|
||||
<!-- CarHardwareInfoScreen -->
|
||||
<string name="car_hardware_info">Car Hardware Information</string>
|
||||
<string name="model_info">Model Information</string>
|
||||
<string name="no_model_permission">No Model Permission</string>
|
||||
<string name="no_speed_permission">No Speed Permission</string>
|
||||
<string name="manufacturer_unavailable">Manufacturer unavailable</string>
|
||||
<string name="model_unavailable">Model unavailable</string>
|
||||
<string name="year_unavailable">Year unavailable</string>
|
||||
<string name="energy_profile">Energy Profile</string>
|
||||
<string name="no_energy_profile_permission">No Energy Profile Permission</string>
|
||||
<string name="fuel_types">Fuel Types</string>
|
||||
<string name="unavailable">Unavailable</string>
|
||||
<string name="ev_connector_types">EV Connector Types</string>
|
||||
<string name="car_sensors">Car Sensors</string>
|
||||
<string name="speed">Speed</string>
|
||||
<string name="speed_unavailable">Speed unavailable</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -58,4 +58,22 @@
|
||||
<string name="alternative_routes">Alternatywne trasy</string>
|
||||
<string name="wait">Czekaj</string>
|
||||
<string name="restaurant">Restaurant</string>
|
||||
|
||||
<!-- CarHardwareInfoScreen -->
|
||||
<string name="car_hardware_info">Car Hardware Information</string>
|
||||
<string name="model_info">Model Information</string>
|
||||
<string name="no_speed_permission">No Speed Permission</string>
|
||||
<string name="no_model_permission">No Model Permission</string>
|
||||
<string name="manufacturer_unavailable">Manufacturer unavailable</string>
|
||||
<string name="model_unavailable">Model unavailable</string>
|
||||
<string name="year_unavailable">Year unavailable</string>
|
||||
<string name="energy_profile">Energy Profile</string>
|
||||
<string name="no_energy_profile_permission">No Energy Profile Permission</string>
|
||||
<string name="fuel_types">Fuel Types</string>
|
||||
<string name="unavailable">Unavailable</string>
|
||||
<string name="ev_connector_types">EV Connector Types</string>
|
||||
<string name="car_sensors">Car Sensors</string>
|
||||
<string name="speed">Speed</string>
|
||||
<string name="speed_unavailable">Speed unavailable</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -61,4 +61,22 @@
|
||||
<string name="alternative_routes">Alternative routes</string>
|
||||
<string name="wait">Wait</string>
|
||||
<string name="restaurant">Restaurant</string>
|
||||
|
||||
<!-- CarHardwareInfoScreen -->
|
||||
<string name="car_hardware_info">Car Hardware Information</string>
|
||||
<string name="model_info">Model Information</string>
|
||||
<string name="no_model_permission">No Model Permission</string>
|
||||
<string name="no_speed_permission">No Speed Permission</string>
|
||||
<string name="manufacturer_unavailable">Manufacturer unavailable</string>
|
||||
<string name="model_unavailable">Model unavailable</string>
|
||||
<string name="year_unavailable">Year unavailable</string>
|
||||
<string name="energy_profile">Energy Profile</string>
|
||||
<string name="no_energy_profile_permission">No Energy Profile Permission</string>
|
||||
<string name="fuel_types">Fuel Types</string>
|
||||
<string name="unavailable">Unavailable</string>
|
||||
<string name="ev_connector_types">EV Connector Types</string>
|
||||
<string name="car_sensors">Car Sensors</string>
|
||||
<string name="speed">Speed</string>
|
||||
<string name="speed_unavailable">Speed unavailable</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user