Skip to content

Commit

Permalink
better display of exchange rate information
Browse files Browse the repository at this point in the history
  • Loading branch information
sal0max committed Jan 23, 2022
1 parent eb6b249 commit 29a9520
Show file tree
Hide file tree
Showing 20 changed files with 129 additions and 87 deletions.
48 changes: 48 additions & 0 deletions app/src/main/kotlin/de/salomax/currencies/model/ApiProvider.kt
Original file line number Diff line number Diff line change
@@ -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
}
]
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Rate>?
@field:Json(name = "rates") val rates: List<Rate>?,

val provider: ApiProvider?
)
4 changes: 3 additions & 1 deletion app/src/main/kotlin/de/salomax/currencies/model/Timeline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocalDate, Rate>?
@field:Json(name = "rates") val rates: Map<LocalDate, Rate>?,

val provider: ApiProvider?
)
18 changes: 12 additions & 6 deletions app/src/main/kotlin/de/salomax/currencies/repository/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand All @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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<Int> {
return SharedPreferenceIntLiveData(prefs, keyApi, 0)
fun getApiProviderAsync(): LiveData<ApiProvider> {
return Transformations.map(SharedPreferenceIntLiveData(prefs, keyApi, 0)) {
ApiProvider.fromNumber(it)
}
}

/* theme */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
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
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.
*/
Expand Down Expand Up @@ -51,7 +44,9 @@ object ExchangeRatesService {
.build()
.adapter(ExchangeRates::class.java)
)
)
).map { timeline ->
timeline.copy(provider = apiProvider)
}
}

/**
Expand Down Expand Up @@ -105,6 +100,8 @@ object ExchangeRatesService {
Currency.FOK -> timeline.copy(base = base.iso4217Alpha())
else -> timeline
}
}.map { timeline ->
timeline.copy(provider = apiProvider)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) }
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -67,20 +68,21 @@ class PreferenceFragment: PreferenceFragmentCompat() {
// api provider
findPreference<ListPreference>(getString(R.string.api_key))?.apply {
setOnPreferenceChangeListener { _, newValue ->
viewModel.setApiProvider(newValue.toString().toInt())
ApiProvider.fromNumber(newValue.toString().toInt())
?.let { viewModel.setApiProvider(it) }
true
}
}
// change text according to selected api
viewModel.getApiProvider().observe(this, {
findPreference<LongSummaryPreference>(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<LongSummaryPreference>(getString(R.string.key_refreshPeriod))?.summary =
resources.getTextArray(R.array.api_refreshPeriod_summary)[it]
it.getUpdateIntervalDescription(requireContext())
})

// donate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@ 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
import de.salomax.currencies.view.preference.PreferenceActivity

class PreferenceViewModel(private val app: Application) : AndroidViewModel(app) {

private var apiProvider: LiveData<Int> = Database(app).getApiProviderAsync()
private var apiProvider: LiveData<ApiProvider> = Database(app).getApiProviderAsync()
private var isPreviewConversionEnabled: LiveData<Boolean> = 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<Int> {
fun getApiProvider(): LiveData<ApiProvider> {
return apiProvider
}

Expand Down
28 changes: 16 additions & 12 deletions app/src/main/res/layout/main_display.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@
android:orientation="horizontal"
app:layout_constraintGuide_percent=".5" />

<!-- exchange rate info text -->

<TextView
android:id="@+id/textRefreshed"
style="@style/TextAppearance.MaterialComponents.Subtitle1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin1x"
android:alpha=".5"
android:gravity="end"
android:textColor="?android:attr/textColorTertiary"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="Rates as of 1/21/22 by Exchangerate.host" />

<!-- toggle button & separator -->

<com.google.android.material.floatingactionbutton.FloatingActionButton
Expand Down Expand Up @@ -55,18 +71,6 @@
app:layout_constraintStart_toEndOf="@id/btn_toggle"
app:layout_constraintTop_toTopOf="@id/guideline_display" />

<TextView
android:id="@+id/textRefreshed"
style="@style/TextAppearance.MaterialComponents.Subtitle1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin1x"
android:alpha=".33"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="Exchange rate age: 2020-10-28" />

<!-- top main_display -->

<de.salomax.currencies.view.main.spinner.SearchableSpinner
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<string name="decimal_separator">,</string>
<string name="thousands_separator">.</string>
<string name="desc_toggle_currencies">tausche Währungen</string>
<string name="last_updated">Wechselkurs vom %s</string>
<string name="today">heute</string>
<string name="yesterday">gestern</string>
<string name="last_updated">Kurse vom &lt;b>%1$s&lt;/b> via &lt;b>%2$s&lt;/b></string>
<string name="copied_to_clipboard">&lt;b&gt;%s&lt;/b&gt; in die Zwischenablage kopiert</string>
<string name="error">Fehler: %s</string>
<string name="error_generic">Unbekanntes Problem.</string>
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/res/values-el/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<string name="decimal_separator">,</string>
<string name="thousands_separator"> . </string>
<string name="desc_toggle_currencies">εναλλαγή νομισμάτων</string>
<string name="last_updated">Ισοτιμία της %s</string>
<string name="today">σήμερον</string>
<string name="yesterday">χθες</string>

<string name="copied_to_clipboard">Το <b>%s</b> αντιγράφηκε στο πρόχειρο</string>
<string name="error">Σφάλμα: %s</string>
<string name="error_generic">Άγνωστο πρόβλημα.</string>
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<string name="decimal_separator">,</string>
<string name="thousands_separator">\u2009</string>
<string name="desc_toggle_currencies">alternar monedas</string>
<string name="last_updated">Tipo de cambio de %s</string>
<string name="today">hoy</string>
<string name="yesterday">ayer</string>

<string name="copied_to_clipboard">Copiado &lt;b&gt;%s&lt;/b&gt; al portapapeles</string>
<string name="error">Error: %s</string>
<string name="error_generic">Problema desconocido.</string>
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
<string name="decimal_separator">,</string>
<string name="thousands_separator">\u2009</string>
<string name="desc_toggle_currencies">toggle les devises</string>
<string name="last_updated">Taux de change de %s</string>
<string name="today">aujourd\'hui</string>
<string name="yesterday">hier</string>

<string name="copied_to_clipboard">Copie de &lt;b&gt;%s&lt;/b&gt; au presse-papiers</string>
<string name="error">Erreur : %s</string>
<string name="error_generic">Problème inconnu.</string>
Expand Down
Loading

0 comments on commit 29a9520

Please sign in to comment.