From cc40b7e988c0374da344cf55c70d2157bddf3e48 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 12 Nov 2023 15:14:54 +0100 Subject: [PATCH] OSM: adjustments for AA/AAOS app --- .../evmap/auto/ChargerDetailScreen.kt | 3 +- .../java/net/vonforst/evmap/auto/MapScreen.kt | 40 +++++- .../vonforst/evmap/auto/SettingsScreens.kt | 117 +++++++++++++----- .../evmap/storage/ChargeLocationsDao.kt | 3 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 130 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/net/vonforst/evmap/auto/ChargerDetailScreen.kt b/app/src/main/java/net/vonforst/evmap/auto/ChargerDetailScreen.kt index 8649ed65..003fb7ac 100644 --- a/app/src/main/java/net/vonforst/evmap/auto/ChargerDetailScreen.kt +++ b/app/src/main/java/net/vonforst/evmap/auto/ChargerDetailScreen.kt @@ -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 diff --git a/app/src/main/java/net/vonforst/evmap/auto/MapScreen.kt b/app/src/main/java/net/vonforst/evmap/auto/MapScreen.kt index cc438582..d8f58201 100644 --- a/app/src/main/java/net/vonforst/evmap/auto/MapScreen.kt +++ b/app/src/main/java/net/vonforst/evmap/auto/MapScreen.kt @@ -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>> + ): Observer>> { + var lastTime = TimeSource.Monotonic.markNow() + val observer = + Observer>> { 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 diff --git a/app/src/main/java/net/vonforst/evmap/auto/SettingsScreens.kt b/app/src/main/java/net/vonforst/evmap/auto/SettingsScreens.kt index c40debba..ee491d3d 100644 --- a/app/src/main/java/net/vonforst/evmap/auto/SettingsScreens.kt +++ b/app/src/main/java/net/vonforst/evmap/auto/SettingsScreens.kt @@ -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 diff --git a/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt b/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt index 25e7c39f..43d015f5 100644 --- a/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt +++ b/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt @@ -102,6 +102,9 @@ abstract class ChargeLocationsDao { @Query("SELECT COUNT(*) FROM chargelocation") abstract fun getCount(): LiveData + @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 diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b5538180..9cb6b892 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -364,6 +364,7 @@ Tesla Daten konnten nicht geladen werden In Zwischenablage kopiert + Lade Ladestations-Datenbank herunter… %.0f%% Verfügbar Besetzt Lädt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2649866..aa87eb85 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -364,6 +364,7 @@ Tesla Could not load data Copied to clipboard + Downloading charger database… %.0f%% Available Occupied Charging