OSM: adjustments for AA/AAOS app

This commit is contained in:
johan12345
2023-11-12 15:14:54 +01:00
parent 8b02660c34
commit cc40b7e988
6 changed files with 130 additions and 35 deletions

View File

@@ -62,6 +62,7 @@ import net.vonforst.evmap.storage.ChargeLocationsRepository
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.ChargerIconGenerator
import net.vonforst.evmap.ui.getMarkerTint
import net.vonforst.evmap.utils.formatDMS
import net.vonforst.evmap.viewmodel.Status
import net.vonforst.evmap.viewmodel.awaitFinished
import java.time.ZoneId
@@ -258,7 +259,7 @@ class ChargerDetailScreen(
// Row 1: address + chargepoints
rows.add(Row.Builder().apply {
setTitle(charger.address.toString())
setTitle(charger.address?.toString() ?: charger.coordinates.formatDMS())
if (photo == null) {
// show just the icon

View File

@@ -6,6 +6,7 @@ import android.location.Location
import androidx.activity.OnBackPressedCallback
import androidx.car.app.AppManager
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.annotations.RequiresCarApi
@@ -30,6 +31,8 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.car2go.maps.AnyMap
import com.car2go.maps.OnMapReadyCallback
@@ -56,6 +59,8 @@ import net.vonforst.evmap.storage.ChargeLocationsRepository
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.MarkerManager
import net.vonforst.evmap.utils.distanceBetween
import net.vonforst.evmap.utils.headingDiff
import net.vonforst.evmap.viewmodel.Resource
import net.vonforst.evmap.viewmodel.Status
import net.vonforst.evmap.viewmodel.await
import net.vonforst.evmap.viewmodel.awaitFinished
@@ -67,6 +72,9 @@ import java.time.Instant
import java.time.ZonedDateTime
import kotlin.collections.set
import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.time.DurationUnit
import kotlin.time.TimeSource
/**
* Main map screen showing either nearby chargers or favorites.
@@ -425,12 +433,15 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
}
this@MapScreen.chargers = chargers
} else {
val response = repo.getChargepoints(
val responseLiveData = repo.getChargepoints(
map.projection.visibleRegion.latLngBounds,
map.cameraPosition.zoom,
filtersWithValue,
false
).awaitFinished()
)
val observer = setupProgressToasts(responseLiveData)
val response = responseLiveData.awaitFinished()
responseLiveData.removeObserver(observer)
if (response.status == Status.ERROR || response.data == null) {
loadingError = true
this@MapScreen.chargers = null
@@ -454,6 +465,31 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
}
}
private fun setupProgressToasts(
responseLiveData: LiveData<Resource<List<ChargepointListItem>>>
): Observer<Resource<List<ChargepointListItem>>> {
var lastTime = TimeSource.Monotonic.markNow()
val observer =
Observer<Resource<List<ChargepointListItem>>> { value ->
if (value.progress != null && lastTime.elapsedNow().toDouble(
DurationUnit.SECONDS
) > 2
) {
CarToast.makeText(
carContext,
carContext.getString(
R.string.downloading_chargers_percent,
value.progress * 100
),
CarToast.LENGTH_SHORT
).show()
lastTime = TimeSource.Monotonic.markNow()
}
}
responseLiveData.observe(this@MapScreen, observer)
return observer
}
private fun onEnergyLevelUpdated(energyLevel: EnergyLevel) {
val isUpdate = this.energyLevel == null
this.energyLevel = energyLevel

View File

@@ -30,6 +30,8 @@ import androidx.car.app.model.Toggle
import androidx.core.content.IntentCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.core.text.HtmlCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kotlinx.coroutines.launch
@@ -157,7 +159,7 @@ class SettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
}
@ExperimentalCarApi
class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx), DefaultLifecycleObserver {
val prefs = PreferenceDataSource(ctx)
val encryptedPrefs = EncryptedPreferenceDataStore(ctx)
val db = AppDatabase.getInstance(ctx)
@@ -175,11 +177,15 @@ class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ct
var teslaLoggingIn = false
init {
lifecycle.addObserver(this)
}
override fun onGetTemplate(): Template {
return ListTemplate.Builder().apply {
setTitle(carContext.getString(R.string.settings_data_sources))
setHeaderAction(Action.BACK)
setSingleList(ItemList.Builder().apply {
addSectionedList(SectionedItemList.create(ItemList.Builder().apply {
addItem(Row.Builder().apply {
setTitle(carContext.getString(R.string.pref_data_source))
setBrowsable(true)
@@ -195,6 +201,41 @@ class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ct
)
}
}.build())
/*addItem(
Row.Builder()
.setTitle(carContext.getString(R.string.pref_prediction_enabled))
.addText(carContext.getString(R.string.pref_prediction_enabled_summary))
.setToggle(Toggle.Builder {
prefs.predictionEnabled = it
}.setChecked(prefs.predictionEnabled).build())
.build()
)*/
addItem(Row.Builder().apply {
setTitle(carContext.getString(R.string.pref_tesla_account))
addText(
if (encryptedPrefs.teslaRefreshToken != null) {
carContext.getString(
R.string.pref_tesla_account_enabled,
encryptedPrefs.teslaEmail
)
} else if (teslaLoggingIn) {
carContext.getString(R.string.logging_in)
} else {
carContext.getString(R.string.pref_tesla_account_disabled)
}
)
if (encryptedPrefs.teslaRefreshToken != null) {
setOnClickListener {
teslaLogout()
}
} else {
setOnClickListener(ParkedOnlyOnClickListener.create {
teslaLogin()
})
}
}.build())
}.build(), carContext.getString(R.string.settings_charger_data)))
addSectionedList(SectionedItemList.create(ItemList.Builder().apply {
addItem(Row.Builder().apply {
setTitle(carContext.getString(R.string.pref_search_provider))
setBrowsable(true)
@@ -243,43 +284,54 @@ class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ct
}
}
}.build())
/*addItem(
Row.Builder()
.setTitle(carContext.getString(R.string.pref_prediction_enabled))
.addText(carContext.getString(R.string.pref_prediction_enabled_summary))
.setToggle(Toggle.Builder {
prefs.predictionEnabled = it
}.setChecked(prefs.predictionEnabled).build())
.build()
)*/
}.build(), carContext.getString(R.string.settings_map)))
addSectionedList(SectionedItemList.create(ItemList.Builder().apply {
addItem(Row.Builder().apply {
setTitle(carContext.getString(R.string.pref_tesla_account))
addText(
if (encryptedPrefs.teslaRefreshToken != null) {
carContext.getString(
R.string.pref_tesla_account_enabled,
encryptedPrefs.teslaEmail
setTitle(carContext.getString(R.string.settings_cache_count))
cacheCount?.let { count ->
cacheSize?.let { size ->
val sizeMb = size.toFloat() / 1024 / 1024
addText(
carContext.getString(
R.string.settings_cache_count_summary,
count,
sizeMb
)
)
} else if (teslaLoggingIn) {
carContext.getString(R.string.logging_in)
} else {
carContext.getString(R.string.pref_tesla_account_disabled)
}
)
if (encryptedPrefs.teslaRefreshToken != null) {
setOnClickListener {
teslaLogout()
}
} else {
setOnClickListener(ParkedOnlyOnClickListener.create {
teslaLogin()
})
}
}.build())
}.build())
addItem(Row.Builder().apply {
setTitle(carContext.getString(R.string.settings_cache_clear))
addText(carContext.getString(R.string.settings_cache_clear_summary))
setOnClickListener {
lifecycleScope.launch {
db.savedRegionDao().deleteAll()
db.chargeLocationsDao().deleteAllIfNotFavorite()
loadCacheSize()
}
}
}.build())
}.build(), carContext.getString(R.string.settings_caching)))
}.build()
}
var cacheCount: Long? = null
var cacheSize: Long? = null
private suspend fun loadCacheSize() {
cacheCount = db.chargeLocationsDao().getCountAsync()
cacheSize = db.chargeLocationsDao().getSize()
invalidate()
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
lifecycleScope.launch {
loadCacheSize()
}
}
private fun teslaLogin() {
val codeVerifier = TeslaAuthenticationApi.generateCodeVerifier()
val codeChallenge = TeslaAuthenticationApi.generateCodeChallenge(codeVerifier)
@@ -399,7 +451,8 @@ class ChooseDataSourceScreen(
val descriptions = when (type) {
Type.CHARGER_DATA_SOURCE -> listOf(
carContext.getString(R.string.data_source_goingelectric_desc),
carContext.getString(R.string.data_source_openchargemap_desc)
carContext.getString(R.string.data_source_openchargemap_desc),
carContext.getString(R.string.data_source_openstreetmap_desc)
)
Type.SEARCH_PROVIDER -> null
Type.MAP_PROVIDER -> null

View File

@@ -102,6 +102,9 @@ abstract class ChargeLocationsDao {
@Query("SELECT COUNT(*) FROM chargelocation")
abstract fun getCount(): LiveData<Long>
@Query("SELECT COUNT(*) FROM chargelocation")
abstract suspend fun getCountAsync(): Long
@SkipQueryVerification
@Query("SELECT SUM(pgsize) FROM dbstat WHERE name == \"ChargeLocation\"")
abstract suspend fun getSize(): Long

View File

@@ -364,6 +364,7 @@
<string name="referral_tesla">Tesla</string>
<string name="generic_connection_error">Daten konnten nicht geladen werden</string>
<string name="copied">In Zwischenablage kopiert</string>
<string name="downloading_chargers_percent">Lade Ladestations-Datenbank herunter… %.0f%%</string>
<string name="status_available">Verfügbar</string>
<string name="status_occupied">Besetzt</string>
<string name="status_charging">Lädt</string>

View File

@@ -364,6 +364,7 @@
<string name="referral_tesla">Tesla</string>
<string name="generic_connection_error">Could not load data</string>
<string name="copied">Copied to clipboard</string>
<string name="downloading_chargers_percent">Downloading charger database… %.0f%%</string>
<string name="status_available">Available</string>
<string name="status_occupied">Occupied</string>
<string name="status_charging">Charging</string>