From 29a952098fbbd638eea157937bedac3119bdf616 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Jan 2022 00:53:40 +0100 Subject: [PATCH] better display of exchange rate information --- .../salomax/currencies/model/ApiProvider.kt | 48 +++++++++++++++++++ .../salomax/currencies/model/ExchangeRates.kt | 4 +- .../de/salomax/currencies/model/Timeline.kt | 4 +- .../salomax/currencies/repository/Database.kt | 18 ++++--- .../repository/ExchangeRatesRepository.kt | 14 +----- .../repository/ExchangeRatesService.kt | 19 ++++---- .../SharedPreferenceExchangeRatesLiveData.kt | 4 +- .../currencies/view/main/MainActivity.kt | 24 ++++++---- .../view/preference/PreferenceFragment.kt | 10 ++-- .../preference/PreferenceViewModel.kt | 7 +-- app/src/main/res/layout/main_display.xml | 28 ++++++----- app/src/main/res/values-de/strings.xml | 4 +- app/src/main/res/values-el/strings.xml | 4 +- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-fr/strings.xml | 4 +- app/src/main/res/values-it/strings.xml | 4 +- app/src/main/res/values-nb/strings.xml | 4 +- app/src/main/res/values-tr/strings.xml | 4 +- app/src/main/res/values-vi/strings.xml | 4 +- app/src/main/res/values/strings.xml | 4 +- 20 files changed, 129 insertions(+), 87 deletions(-) create mode 100644 app/src/main/kotlin/de/salomax/currencies/model/ApiProvider.kt diff --git a/app/src/main/kotlin/de/salomax/currencies/model/ApiProvider.kt b/app/src/main/kotlin/de/salomax/currencies/model/ApiProvider.kt new file mode 100644 index 00000000..af7fd751 --- /dev/null +++ b/app/src/main/kotlin/de/salomax/currencies/model/ApiProvider.kt @@ -0,0 +1,48 @@ +package de.salomax.currencies.model + +import android.content.Context +import de.salomax.currencies.R + +enum class ApiProvider( + val number: Int, // safer ordinal; DON'T CHANGE! + val baseUrl: String, +) { + EXCHANGERATE_HOST(0, "https://api.exchangerate.host"), + FRANKFURTER_APP(1, "https://api.frankfurter.app"), + FER_EE(2, "https://api.fer.ee"); + + companion object { + fun fromNumber(value: Int): ApiProvider? = values().firstOrNull { it.number == value } + } + + fun getName(context: Context): CharSequence? { + return context.resources.getTextArray(R.array.api_names)[ + when (this) { + EXCHANGERATE_HOST -> 0 + FRANKFURTER_APP -> 1 + FER_EE -> 2 + } + ] + } + + fun getDescription(context: Context): CharSequence? { + return context.resources.getTextArray(R.array.api_about_summary)[ + when (this) { + EXCHANGERATE_HOST -> 0 + FRANKFURTER_APP -> 1 + FER_EE -> 2 + } + ] + } + + fun getUpdateIntervalDescription(context: Context): CharSequence? { + return context.resources.getTextArray(R.array.api_refreshPeriod_summary)[ + when (this) { + EXCHANGERATE_HOST -> 0 + FRANKFURTER_APP -> 1 + FER_EE -> 2 + } + ] + } + +} diff --git a/app/src/main/kotlin/de/salomax/currencies/model/ExchangeRates.kt b/app/src/main/kotlin/de/salomax/currencies/model/ExchangeRates.kt index 855a4df3..ba2bf173 100644 --- a/app/src/main/kotlin/de/salomax/currencies/model/ExchangeRates.kt +++ b/app/src/main/kotlin/de/salomax/currencies/model/ExchangeRates.kt @@ -11,5 +11,7 @@ data class ExchangeRates( @field:Json(name = "base") val base: Currency?, @field:Json(name = "date") val date: LocalDate?, - @field:Json(name = "rates") val rates: List? + @field:Json(name = "rates") val rates: List?, + + val provider: ApiProvider? ) diff --git a/app/src/main/kotlin/de/salomax/currencies/model/Timeline.kt b/app/src/main/kotlin/de/salomax/currencies/model/Timeline.kt index 7ee414be..685d7cec 100644 --- a/app/src/main/kotlin/de/salomax/currencies/model/Timeline.kt +++ b/app/src/main/kotlin/de/salomax/currencies/model/Timeline.kt @@ -12,5 +12,7 @@ data class Timeline( @field:Json(name = "base") val base: String?, @field:Json(name = "start_date") val startDate: LocalDate?, @field:Json(name = "end_date") val endDate: LocalDate?, - @field:Json(name = "rates") val rates: Map? + @field:Json(name = "rates") val rates: Map?, + + val provider: ApiProvider? ) diff --git a/app/src/main/kotlin/de/salomax/currencies/repository/Database.kt b/app/src/main/kotlin/de/salomax/currencies/repository/Database.kt index 566b2fff..38206f3a 100644 --- a/app/src/main/kotlin/de/salomax/currencies/repository/Database.kt +++ b/app/src/main/kotlin/de/salomax/currencies/repository/Database.kt @@ -4,7 +4,9 @@ import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import androidx.lifecycle.map +import de.salomax.currencies.model.ApiProvider import de.salomax.currencies.model.Currency import de.salomax.currencies.model.ExchangeRates import de.salomax.currencies.util.* @@ -20,6 +22,7 @@ class Database(context: Context) { private val keyDate = "_date" private val keyBaseRate = "_base" + private val keyProvider = "_provider" fun insertExchangeRates(items: ExchangeRates) { // don't insert null-values. this would clear the cache @@ -31,6 +34,7 @@ class Database(context: Context) { // apply new ones editor.putString(keyDate, items.date.toString()) editor.putString(keyBaseRate, items.base?.iso4217Alpha()) + editor.putInt(keyProvider, items.provider?.number ?: -1) items.rates?.forEach { rate -> editor.putFloat(rate.currency.iso4217Alpha(), rate.value) } @@ -151,18 +155,20 @@ class Database(context: Context) { /* api */ - fun setApiProvider(api: Int) { + fun setApiProvider(api: ApiProvider) { prefs.apply { - edit().putInt(keyApi, api).apply() + edit().putInt(keyApi, api.number).apply() } } - fun getApiProvider(): Int { - return prefs.getInt(keyApi, 0) + fun getApiProvider(): ApiProvider { + return ApiProvider.fromNumber(prefs.getInt(keyApi, 0))!! } - fun getApiProviderAsync(): LiveData { - return SharedPreferenceIntLiveData(prefs, keyApi, 0) + fun getApiProviderAsync(): LiveData { + return Transformations.map(SharedPreferenceIntLiveData(prefs, keyApi, 0)) { + ApiProvider.fromNumber(it) + } } /* theme */ diff --git a/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesRepository.kt b/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesRepository.kt index 61febb2a..96b10f30 100644 --- a/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesRepository.kt +++ b/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesRepository.kt @@ -31,12 +31,7 @@ class ExchangeRatesRepository(private val context: Context) { // call api ExchangeRatesService.getRates( // use the right api - when (Database(context).getApiProvider()) { - 0 -> ExchangeRatesService.ApiProvider.EXCHANGERATE_HOST - 1 -> ExchangeRatesService.ApiProvider.FRANKFURTER_APP - 2 -> ExchangeRatesService.ApiProvider.FER_EE - else -> throw UnsupportedOperationException() - } + Database(context).getApiProvider() ).run { val rates = component1() val fuelError = component2() @@ -72,12 +67,7 @@ class ExchangeRatesRepository(private val context: Context) { // call api ExchangeRatesService.getTimeline( // use the right api - apiProvider = when (Database(context).getApiProvider()) { - 0 -> ExchangeRatesService.ApiProvider.EXCHANGERATE_HOST - 1 -> ExchangeRatesService.ApiProvider.FRANKFURTER_APP - 2 -> ExchangeRatesService.ApiProvider.FER_EE - else -> throw UnsupportedOperationException() - }, + apiProvider = Database(context).getApiProvider(), base = base, symbol = symbol ).run { diff --git a/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesService.kt b/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesService.kt index 33c699f2..87e3e892 100644 --- a/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesService.kt +++ b/app/src/main/kotlin/de/salomax/currencies/repository/ExchangeRatesService.kt @@ -1,16 +1,15 @@ package de.salomax.currencies.repository import com.github.kittinunf.fuel.Fuel -import com.github.kittinunf.fuel.core.* +import com.github.kittinunf.fuel.core.FuelError +import com.github.kittinunf.fuel.core.awaitResult import com.github.kittinunf.fuel.moshi.moshiDeserializerOf import com.github.kittinunf.result.Result import com.github.kittinunf.result.map import com.squareup.moshi.* import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import de.salomax.currencies.model.* import de.salomax.currencies.model.Currency -import de.salomax.currencies.model.ExchangeRates -import de.salomax.currencies.model.Timeline -import de.salomax.currencies.model.Rate import java.io.IOException import java.time.LocalDate import java.time.format.DateTimeFormatter @@ -18,12 +17,6 @@ import java.util.* object ExchangeRatesService { - enum class ApiProvider(val baseUrl: String) { - EXCHANGERATE_HOST("https://api.exchangerate.host"), - FRANKFURTER_APP("https://api.frankfurter.app"), - FER_EE("https://api.fer.ee") - } - /** * Get all the current exchange rates from the given api provider. Base will be Euro. */ @@ -51,7 +44,9 @@ object ExchangeRatesService { .build() .adapter(ExchangeRates::class.java) ) - ) + ).map { timeline -> + timeline.copy(provider = apiProvider) + } } /** @@ -105,6 +100,8 @@ object ExchangeRatesService { Currency.FOK -> timeline.copy(base = base.iso4217Alpha()) else -> timeline } + }.map { timeline -> + timeline.copy(provider = apiProvider) } } diff --git a/app/src/main/kotlin/de/salomax/currencies/util/SharedPreferenceExchangeRatesLiveData.kt b/app/src/main/kotlin/de/salomax/currencies/util/SharedPreferenceExchangeRatesLiveData.kt index 6ad472a0..62e01d72 100644 --- a/app/src/main/kotlin/de/salomax/currencies/util/SharedPreferenceExchangeRatesLiveData.kt +++ b/app/src/main/kotlin/de/salomax/currencies/util/SharedPreferenceExchangeRatesLiveData.kt @@ -3,6 +3,7 @@ package de.salomax.currencies.util import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import androidx.lifecycle.LiveData +import de.salomax.currencies.model.ApiProvider import de.salomax.currencies.model.Currency import de.salomax.currencies.model.ExchangeRates import de.salomax.currencies.model.Rate @@ -23,7 +24,8 @@ class SharedPreferenceExchangeRatesLiveData(private val sharedPrefs: SharedPrefe .filter { !it.key.startsWith("_") } .sortedBy { it.key } .mapNotNull { Currency.fromString(it.key!!)?.let { currency -> Rate(currency, (it.value as Float)) } } - .toList() + .toList(), + sharedPrefs.getInt("_provider", -1).let { ApiProvider.fromNumber(it) } ) } diff --git a/app/src/main/kotlin/de/salomax/currencies/view/main/MainActivity.kt b/app/src/main/kotlin/de/salomax/currencies/view/main/MainActivity.kt index 9cc89a67..227d2443 100644 --- a/app/src/main/kotlin/de/salomax/currencies/view/main/MainActivity.kt +++ b/app/src/main/kotlin/de/salomax/currencies/view/main/MainActivity.kt @@ -203,16 +203,22 @@ class MainActivity : BaseActivity() { val dateString = date ?.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)) ?.replace("\u200F", "") // remove rtl-mark (fixes broken arab date) + val providerString = it.provider?.getName(this) + + // show rate age and rate source + tvDate.text = if (dateString != null && providerString != null) + HtmlCompat.fromHtml( + getString( + R.string.last_updated, + dateString, + providerString + ), + HtmlCompat.FROM_HTML_MODE_LEGACY + ) + else + null - tvDate.text = getString(R.string.last_updated, dateString) - // today - if (date?.isEqual(LocalDate.now()) == true) - tvDate.append(" (${getString(R.string.today)})") - // yesterday - else if (date?.isEqual(LocalDate.now().minusDays(1)) == true) - tvDate.append(" (${getString(R.string.yesterday)})") - - // paint text in red in case the data is old + // paint text in red in case the data is old (at least 3 days) tvDate.setTextColor( if (date?.isBefore(LocalDate.now().minusDays(3)) == true) getColor(android.R.color.holo_red_light) diff --git a/app/src/main/kotlin/de/salomax/currencies/view/preference/PreferenceFragment.kt b/app/src/main/kotlin/de/salomax/currencies/view/preference/PreferenceFragment.kt index b0692be5..61ea241b 100644 --- a/app/src/main/kotlin/de/salomax/currencies/view/preference/PreferenceFragment.kt +++ b/app/src/main/kotlin/de/salomax/currencies/view/preference/PreferenceFragment.kt @@ -11,6 +11,7 @@ import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import de.salomax.currencies.BuildConfig import de.salomax.currencies.R +import de.salomax.currencies.model.ApiProvider import de.salomax.currencies.util.toHumanReadableNumber import de.salomax.currencies.viewmodel.preference.PreferenceViewModel import de.salomax.currencies.widget.EditTextSwitchPreference @@ -67,7 +68,8 @@ class PreferenceFragment: PreferenceFragmentCompat() { // api provider findPreference(getString(R.string.api_key))?.apply { setOnPreferenceChangeListener { _, newValue -> - viewModel.setApiProvider(newValue.toString().toInt()) + ApiProvider.fromNumber(newValue.toString().toInt()) + ?.let { viewModel.setApiProvider(it) } true } } @@ -75,12 +77,12 @@ class PreferenceFragment: PreferenceFragmentCompat() { viewModel.getApiProvider().observe(this, { findPreference(getString(R.string.key_apiProvider))?.apply { title = - resources.getString(R.string.api_about_title, resources.getTextArray(R.array.api_names)[it]) + resources.getString(R.string.api_about_title, it.getName(context)) summary = - resources.getTextArray(R.array.api_about_summary)[it] + it.getDescription(context) } findPreference(getString(R.string.key_refreshPeriod))?.summary = - resources.getTextArray(R.array.api_refreshPeriod_summary)[it] + it.getUpdateIntervalDescription(requireContext()) }) // donate diff --git a/app/src/main/kotlin/de/salomax/currencies/viewmodel/preference/PreferenceViewModel.kt b/app/src/main/kotlin/de/salomax/currencies/viewmodel/preference/PreferenceViewModel.kt index ee76a8da..77de8906 100644 --- a/app/src/main/kotlin/de/salomax/currencies/viewmodel/preference/PreferenceViewModel.kt +++ b/app/src/main/kotlin/de/salomax/currencies/viewmodel/preference/PreferenceViewModel.kt @@ -8,6 +8,7 @@ import androidx.core.app.TaskStackBuilder import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import de.salomax.currencies.R +import de.salomax.currencies.model.ApiProvider import de.salomax.currencies.repository.Database import de.salomax.currencies.repository.ExchangeRatesRepository import de.salomax.currencies.view.main.MainActivity @@ -15,17 +16,17 @@ import de.salomax.currencies.view.preference.PreferenceActivity class PreferenceViewModel(private val app: Application) : AndroidViewModel(app) { - private var apiProvider: LiveData = Database(app).getApiProviderAsync() + private var apiProvider: LiveData = Database(app).getApiProviderAsync() private var isPreviewConversionEnabled: LiveData = Database(app).isPreviewConversionEnabled() - fun setApiProvider(api: Int) { + fun setApiProvider(api: ApiProvider) { // first put provider to db... Database(app).setApiProvider(api) // ...after that, fetch the new exchange rates ExchangeRatesRepository(app).getExchangeRates() } - fun getApiProvider(): LiveData { + fun getApiProvider(): LiveData { return apiProvider } diff --git a/app/src/main/res/layout/main_display.xml b/app/src/main/res/layout/main_display.xml index fff9c336..88e097f9 100644 --- a/app/src/main/res/layout/main_display.xml +++ b/app/src/main/res/layout/main_display.xml @@ -15,6 +15,22 @@ android:orientation="horizontal" app:layout_constraintGuide_percent=".5" /> + + + + - - , . tausche Währungen - Wechselkurs vom %s - heute - gestern + Kurse vom <b>%1$s</b> via <b>%2$s</b> <b>%s</b> in die Zwischenablage kopiert Fehler: %s Unbekanntes Problem. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index d95d22ba..4001f980 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -3,9 +3,7 @@ ,  .  εναλλαγή νομισμάτων - Ισοτιμία της %s - σήμερον - χθες + Το %s αντιγράφηκε στο πρόχειρο Σφάλμα: %s Άγνωστο πρόβλημα. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 84926931..403e5102 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -3,9 +3,7 @@ , \u2009 alternar monedas - Tipo de cambio de %s - hoy - ayer + Copiado <b>%s</b> al portapapeles Error: %s Problema desconocido. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4d75048f..2102dabb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,9 +3,7 @@ , \u2009 toggle les devises - Taux de change de %s - aujourd\'hui - hier + Copie de <b>%s</b> au presse-papiers Erreur : %s Problème inconnu. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index da151819..8f3a94c8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -3,9 +3,7 @@ , \u2009 togliere le valute - Tasso di cambio da: %s - oggi - ieri + Copiato <b>%s</b> negli appunti Errore: %s Problema sconosciuto. diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 23ff176d..f1b321e9 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -3,9 +3,7 @@ , \u2009 veksle valuta - Valutakurs fra %s - i dag - i går + Kurser fra <b>%1$s</b> via <b>%2$s</b> Kopierte <b>%s</b> til utklippstavlen Feil: %s Ukjent problem. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index ecaab026..656d496a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -3,9 +3,7 @@ ,  .  para birimlerini değiştir - %s tarihli döviz kuru - bugün - dün + %s panoya kopyalandı Hata: %s Bilinmeyen bir sorunla karşılaşıldı. diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 2743fcc8..ee26f38b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -3,9 +3,7 @@ . \u2009 chuyển tiền tệ - Tỉ lệ chuyển đổi từ %s - hôm nay - hôm qua + Đã sao chép %s vào bộ nhớ tạm Lỗi: %s Vấn đề không xác định. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 03be44b9..b2af2845 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,9 +7,7 @@ \u2009 toggle currencies - Exchange rate from %s - today - yesterday + Rates as of <b>%1$s</b> by <b>%2$s</b> Copied <b>%s</b> to clipboard Error: %s