Compare commits

...

9 Commits
1.9.0 ... 1.9.1

Author SHA1 Message Date
johan12345
ce172bd4b0 Release 1.9.1 2024-05-25 13:07:22 +02:00
johan12345
b3bbca576f revert to MapLibre 10.x
fixes #345
2024-05-25 12:59:34 +02:00
johan12345
72ccd99c1f re-release 1.9.0 for Play Store 2024-05-24 23:09:37 +02:00
johan12345
dd5c8659df fix declaration of car app category in manifest 2024-05-24 23:08:13 +02:00
johan12345
8f7f9e5a09 re-release 1.9.0 for Play Store
Car App Library alpha version is not accepted
2024-05-21 23:38:33 +02:00
johan12345
8256981b8d workaround for car app library crash with invalid speed units 2024-05-21 23:38:33 +02:00
johan12345
5649ef202f fix Android Auto version check 2024-05-21 23:38:33 +02:00
johan12345
ed9c729684 Revert "update car app library to 1.7.0-alpha02"
This reverts commit d540faa179.
2024-05-21 22:14:55 +02:00
johan12345
add66811ae Export licenses to CSV
needed for Faurecia Aptoide store
2024-05-20 23:10:26 +02:00
11 changed files with 249 additions and 19 deletions

View File

@@ -34,3 +34,5 @@ jobs:
run: ./gradlew test${{ matrix.buildvariant }}DebugUnitTest --no-daemon
- name: Run Android Lint
run: ./gradlew lint${{ matrix.buildvariant }}Debug --no-daemon
- name: Check licenses
run: ./gradlew exportLibraryDefinitions --no-daemon

3
.gitignore vendored
View File

@@ -12,4 +12,5 @@ apikeys.xml
/app/**/*.apk
/_img/connectors/*.ai
api-7125266970515251116-798419-8e2dda660c80.json
output-metadata.json
output-metadata.json
licenses_*.csv

View File

@@ -0,0 +1,23 @@
import subprocess
import json
build_types = ["fossNormalRelease", "fossAutomotiveRelease"]
for build_type in build_types:
result = subprocess.run(["gradlew.bat", f"generateLibraryDefinitions{build_type.capitalize()}"],
capture_output=True)
data = json.load(
open(f"app/build/generated/aboutLibraries/{build_type}/res/raw/aboutlibraries.json"))
with open(f"licenses_{build_type}.csv", "w") as f:
f.write("component_name;license_title;license_url;public_repository;copyrights\n")
for lib in data["libraries"]:
license = data["licenses"][lib["licenses"][0]] if len(lib["licenses"]) > 0 else None
license_name = license["name"] if license is not None else " "
license_url = license["url"] if license is not None else " "
copyrights = ", ".join([dev["name"] for dev in lib["developers"] if "name" in dev])
if copyrights == "":
copyrights = " "
repo_url = lib['scm']['url'] if 'scm' in lib else ''
f.write(f"{lib['name']};{license_name};{license_url};\"{copyrights}\";{repo_url}\n")

View File

@@ -12,14 +12,16 @@ plugins {
android {
useLibrary("android.car")
defaultConfig {
applicationId = "net.vonforst.evmap"
compileSdk = 34
minSdk = 21
targetSdk = 34
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
versionCode = 214
versionName = "1.9.0"
versionCode = 220
versionName = "1.9.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -250,10 +252,14 @@ configurations {
aboutLibraries {
allowedLicenses = arrayOf(
"Apache-2.0", "mit", "BSD-2-Clause",
"Apache-2.0", "mit", "BSD-2-Clause", "BSD-3-Clause", "EPL-1.0",
"asdkl", // Android SDK
"Dual OpenSSL and SSLeay License", // Android NDK OpenSSL
"Google Maps Platform Terms of Service" // Google Maps SDK
"Google Maps Platform Terms of Service", // Google Maps SDK
"provided without support or warranty", // org.json
"Unicode/ICU License", // icu4j
"Bouncy Castle Licence", // bcprov
"CDDL + GPLv2 with classpath exception", // javax.annotation-api
)
strictMode = com.mikepenz.aboutlibraries.plugin.StrictMode.FAIL
}
@@ -302,17 +308,20 @@ dependencies {
implementation("com.github.romandanylyk:PageIndicatorView:b1bad589b5")
// Android Auto
val carAppVersion = "1.7.0-alpha02"
val carAppVersion = "1.4.0-rc02"
implementation("androidx.car.app:app:$carAppVersion")
normalImplementation("androidx.car.app:app-projected:$carAppVersion")
automotiveImplementation("androidx.car.app:app-automotive:$carAppVersion")
// AnyMaps
val anyMapsVersion = "c087b3e7c2"
val anyMapsVersion = "a5b9abca40"
implementation("com.github.ev-map.AnyMaps:anymaps-base:$anyMapsVersion")
googleImplementation("com.github.ev-map.AnyMaps:anymaps-google:$anyMapsVersion")
googleImplementation("com.google.android.gms:play-services-maps:18.2.0")
implementation("com.github.ev-map.AnyMaps:anymaps-maplibre:$anyMapsVersion")
implementation("com.github.ev-map.AnyMaps:anymaps-maplibre:$anyMapsVersion") {
// duplicates classes from mapbox-sdk-services
exclude("org.maplibre.gl", "android-sdk-geojson")
}
// Google Places
googleImplementation("com.google.android.libraries.places:places:3.3.0")

View File

@@ -0,0 +1,171 @@
import android.car.Car
import android.car.VehiclePropertyIds
import android.car.VehicleUnit
import android.car.hardware.CarPropertyValue
import android.car.hardware.property.CarPropertyManager
import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback
import androidx.annotation.OptIn
import androidx.car.app.CarContext
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.hardware.CarHardwareManager
import androidx.car.app.hardware.common.CarUnit
import androidx.car.app.hardware.common.CarValue
import androidx.car.app.hardware.common.OnCarDataAvailableListener
import androidx.car.app.hardware.info.CarInfo
import androidx.car.app.hardware.info.EnergyLevel
import androidx.car.app.hardware.info.EnergyProfile
import androidx.car.app.hardware.info.EvStatus
import androidx.car.app.hardware.info.Mileage
import androidx.car.app.hardware.info.Model
import androidx.car.app.hardware.info.Speed
import androidx.car.app.hardware.info.TollCard
import java.util.concurrent.Executor
val CarContext.patchedCarInfo: CarInfo
get() = CarInfoWrapper(this)
class CarInfoWrapper(ctx: CarContext) : CarInfo {
private val wrapped =
(ctx.getCarService(CarContext.HARDWARE_SERVICE) as CarHardwareManager).carInfo
private val carPropertyManager = try {
val car = Car.createCar(ctx)
car.getCarManager(Car.PROPERTY_SERVICE) as CarPropertyManager
} catch (e: NoClassDefFoundError) {
null
}
private val callbacks = mutableMapOf<OnCarDataAvailableListener<*>, CarPropertyEventCallback>()
override fun fetchModel(executor: Executor, listener: OnCarDataAvailableListener<Model>) =
wrapped.fetchModel(executor, listener)
override fun fetchEnergyProfile(
executor: Executor,
listener: OnCarDataAvailableListener<EnergyProfile>
) = wrapped.fetchEnergyProfile(executor, listener)
override fun addTollListener(
executor: Executor,
listener: OnCarDataAvailableListener<TollCard>
) = wrapped.addTollListener(executor, listener)
override fun removeTollListener(listener: OnCarDataAvailableListener<TollCard>) =
wrapped.removeTollListener(listener)
override fun addEnergyLevelListener(
executor: Executor,
listener: OnCarDataAvailableListener<EnergyLevel>
) = wrapped.addEnergyLevelListener(executor, listener)
override fun removeEnergyLevelListener(listener: OnCarDataAvailableListener<EnergyLevel>) =
wrapped.removeEnergyLevelListener(listener)
override fun addSpeedListener(executor: Executor, listener: OnCarDataAvailableListener<Speed>) {
// TODO: This is a emporary workaround until Car App Library 1.7.0 is released - previous versions would crash if the car reported an invalid speed display unit
carPropertyManager ?: return
val callback = object : CarPropertyEventCallback {
private var speedRaw: CarPropertyValue<Float>? = null
private var speedDisplay: CarPropertyValue<Float>? = null
private var speedUnit: CarPropertyValue<Int>? = null
override fun onChangeEvent(value: CarPropertyValue<*>?) {
when (value?.propertyId) {
VehiclePropertyIds.PERF_VEHICLE_SPEED -> speedRaw =
value as CarPropertyValue<Float>?
VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY -> speedDisplay =
value as CarPropertyValue<Float>?
VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS -> speedUnit =
value as CarPropertyValue<Int>?
}
executor.execute {
listener.onCarDataAvailable(Speed.Builder().apply {
speedRaw?.let {
setRawSpeedMetersPerSecond(
CarValue(
it.value,
it.timestamp,
if (it.value != null) CarValue.STATUS_SUCCESS else CarValue.STATUS_UNKNOWN
)
)
}
speedDisplay?.let {
setDisplaySpeedMetersPerSecond(
CarValue(
it.value,
it.timestamp,
if (it.value != null) CarValue.STATUS_SUCCESS else CarValue.STATUS_UNKNOWN
)
)
}
speedUnit?.let {
val unit = when (it.value) {
VehicleUnit.METER_PER_SEC -> CarUnit.METERS_PER_SEC
VehicleUnit.MILES_PER_HOUR -> CarUnit.MILES_PER_HOUR
VehicleUnit.KILOMETERS_PER_HOUR -> CarUnit.KILOMETERS_PER_HOUR
else -> null
}
setSpeedDisplayUnit(
CarValue(
unit,
it.timestamp,
if (unit != null) CarValue.STATUS_SUCCESS else CarValue.STATUS_UNKNOWN
)
)
}
}.build())
}
}
override fun onErrorEvent(propertyId: Int, areaId: Int) {
listener.onCarDataAvailable(
Speed.Builder()
.setRawSpeedMetersPerSecond(CarValue(null, 0, CarValue.STATUS_UNKNOWN))
.setDisplaySpeedMetersPerSecond(CarValue(null, 0, CarValue.STATUS_UNKNOWN))
.setSpeedDisplayUnit(CarValue(null, 0, CarValue.STATUS_UNKNOWN))
.build()
)
}
}
carPropertyManager.registerCallback(
callback,
VehiclePropertyIds.PERF_VEHICLE_SPEED,
CarPropertyManager.SENSOR_RATE_NORMAL
)
carPropertyManager.registerCallback(
callback,
VehiclePropertyIds.PERF_VEHICLE_SPEED_DISPLAY,
CarPropertyManager.SENSOR_RATE_NORMAL
)
carPropertyManager.registerCallback(
callback,
VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS,
CarPropertyManager.SENSOR_RATE_NORMAL
)
}
override fun removeSpeedListener(listener: OnCarDataAvailableListener<Speed>) {
val callback = callbacks[listener]
carPropertyManager?.unregisterCallback(callback)
}
override fun addMileageListener(
executor: Executor,
listener: OnCarDataAvailableListener<Mileage>
) = wrapped.addMileageListener(executor, listener)
override fun removeMileageListener(listener: OnCarDataAvailableListener<Mileage>) =
wrapped.removeMileageListener(listener)
@OptIn(ExperimentalCarApi::class)
override fun addEvStatusListener(
executor: Executor,
listener: OnCarDataAvailableListener<EvStatus>
) = wrapped.addEvStatusListener(executor, listener)
@OptIn(ExperimentalCarApi::class)
override fun removeEvStatusListener(listener: OnCarDataAvailableListener<EvStatus>) =
wrapped.removeEvStatusListener(listener)
}

View File

@@ -347,9 +347,8 @@
android:exported="true"
android:foregroundServiceType="location">
<intent-filter>
<action
android:name="androidx.car.app.CarAppService"
android:category="androidx.car.app.category.POI" />
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.POI" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View File

@@ -14,7 +14,11 @@ import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.hardware.common.CarUnit
import androidx.car.app.model.*
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.Distance
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Template
import androidx.car.app.versioning.CarAppApiLevels
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
@@ -26,7 +30,7 @@ import net.vonforst.evmap.getPackageInfoCompat
import net.vonforst.evmap.kmPerMile
import net.vonforst.evmap.shouldUseImperialUnits
import net.vonforst.evmap.ydPerMile
import java.util.*
import java.util.Locale
import kotlin.math.roundToInt
fun carAvailabilityColor(status: List<ChargepointStatus>): CarColor {
@@ -207,7 +211,9 @@ fun supportsCarApiLevel3(ctx: CarContext): Boolean {
val version = getAndroidAutoVersion(ctx)
// Android Auto 6.7 is required. 6.6 reports supporting API Level 3,
// but crashes when using it. See: https://issuetracker.google.com/issues/199509584
if (version[0] < "6" || version[0] == "6" && version[1] < "7") {
val major = version[0].toIntOrNull() ?: return false
val minor = version[1].toIntOrNull() ?: return false
if (major < 6 || major < 6 && minor < 7) {
return false
}
}

View File

@@ -6,9 +6,18 @@ import android.os.Handler
import android.os.Looper
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.hardware.CarHardwareManager
import androidx.car.app.hardware.info.*
import androidx.car.app.model.*
import androidx.car.app.hardware.info.CarSensors
import androidx.car.app.hardware.info.Compass
import androidx.car.app.hardware.info.EnergyLevel
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.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.GridItem
import androidx.car.app.model.GridTemplate
import androidx.car.app.model.ItemList
import androidx.car.app.model.Template
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.DefaultLifecycleObserver
@@ -18,14 +27,14 @@ import net.vonforst.evmap.R
import net.vonforst.evmap.ui.CompassNeedle
import net.vonforst.evmap.ui.Gauge
import net.vonforst.evmap.utils.formatDecimal
import patchedCarInfo
import kotlin.math.min
import kotlin.math.roundToInt
@androidx.car.app.annotations.ExperimentalCarApi
class VehicleDataScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx),
LocationAwareScreen, DefaultLifecycleObserver {
private val carInfo =
(ctx.getCarService(CarContext.HARDWARE_SERVICE) as CarHardwareManager).carInfo
private val carInfo = carContext.patchedCarInfo
private val carSensors = carContext.patchedCarSensors
private var model: Model? = null
private var energyLevel: EnergyLevel? = null

View File

@@ -0,0 +1,6 @@
import androidx.car.app.CarContext
import androidx.car.app.hardware.CarHardwareManager
import androidx.car.app.hardware.info.CarInfo
val CarContext.patchedCarInfo: CarInfo
get() = (this.getCarService(CarContext.HARDWARE_SERVICE) as CarHardwareManager).carInfo

View File

@@ -0,0 +1,2 @@
Fehler behoben:
- Unterstützung für Geräte mit OpenGL ES 2.0 wiederhergestellt

View File

@@ -0,0 +1,2 @@
Bugfixes:
- Restored support for devices with OpenGL ES 2.0