From 558d12bb8aa2c34c6558bc2d4d1c4b7b416fd865 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 16 Apr 2022 21:35:13 +0200 Subject: [PATCH] Add preference to disable currency converter --- .../currencies/CurrencyRepository.kt | 35 ++++++++--- i18n/src/main/res/values/strings.xml | 2 + .../de/mm20/launcher2/preferences/Defaults.kt | 1 + .../preferences/migrations/Migration_5_6.kt | 3 + preferences/src/main/proto/settings.proto | 1 + .../launcher2/ui/settings/SettingsActivity.kt | 4 ++ .../settings/search/SearchSettingsScreen.kt | 9 ++- .../UnitConverterSettingsScreen.kt | 46 ++++++++++++++ .../UnitConverterSettingsScreenVM.kt | 43 +++++++++++++ .../de/mm20/launcher2/unitconverter/Module.kt | 2 +- .../unitconverter/UnitConverterRepository.kt | 62 ++++++++++++------- .../converters/CurrencyConverter.kt | 7 +-- 12 files changed, 176 insertions(+), 39 deletions(-) create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreen.kt create mode 100644 ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt diff --git a/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt b/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt index 2a4028ac..72ab8084 100644 --- a/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt +++ b/currencies/src/main/java/de/mm20/launcher2/currencies/CurrencyRepository.kt @@ -4,24 +4,36 @@ import android.content.Context import android.util.Log import androidx.work.* import de.mm20.launcher2.database.AppDatabase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import java.util.concurrent.TimeUnit -class CurrencyRepository(val context: Context) { +class CurrencyRepository( + private val context: Context, +) { - init { - val currencyWorker = PeriodicWorkRequest.Builder(ExchangeRateWorker::class.java, 60, TimeUnit.MINUTES) + fun enableCurrencyUpdateWorker() { + val currencyWorker = + PeriodicWorkRequest.Builder(ExchangeRateWorker::class.java, 60, TimeUnit.MINUTES) .build() - WorkManager.getInstance(context).enqueueUniquePeriodicWork("ExchangeRates", - ExistingPeriodicWorkPolicy.REPLACE, currencyWorker) + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + "ExchangeRates", + ExistingPeriodicWorkPolicy.REPLACE, currencyWorker + ) } - suspend fun convertCurrency(fromCurrency: String, value: Double, toCurrency: String? = null): List> { + fun disableCurrencyUpdateWorker() { + WorkManager.getInstance(context).cancelUniqueWork("ExchangeRates") + } + + suspend fun convertCurrency( + fromCurrency: String, + value: Double, + toCurrency: String? = null + ): List> { return withContext>>(Dispatchers.IO) { val dao = AppDatabase.getInstance(context) - .currencyDao() + .currencyDao() val from = Currency(dao.getCurrency(fromCurrency) ?: return@withContext emptyList()) @@ -38,7 +50,10 @@ class CurrencyRepository(val context: Context) { } else { val to = Currency(dao.getCurrency(toCurrency) ?: return@withContext emptyList()) if (from.lastUpdate != to.lastUpdate) { - Log.w("MM20", "Exchange rate update dates do not match: $fromCurrency, $toCurrency") + Log.w( + "MM20", + "Exchange rate update dates do not match: $fromCurrency, $toCurrency" + ) return@withContext emptyList() } listOf(toCurrency to value * to.value / from.value) diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index c17c9056..14418bfa 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -525,6 +525,8 @@ Evaluate mathematical terms Unit converter Usage: 1.5 kg or 4 cm >> in + Currency converter + Periodically download exchange rates to convert currencies Wikipedia Search the free encyclopedia Websites diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt index 05fbb045..30d92836 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt @@ -87,6 +87,7 @@ fun createFactorySettings(context: Context): Settings { Settings.UnitConverterSearchSettings .newBuilder() .setEnabled(true) + .setCurrencies(true) ) .setWikipediaSearch( Settings.WikipediaSearchSettings diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt index 5d73cd05..fa94cf67 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt @@ -15,6 +15,9 @@ class Migration_5_6: VersionedMigration(5, 6) { .setLightScheme(DefaultLightCustomColorScheme) .setDarkScheme(DefaultDarkCustomColorScheme) ) + ).setUnitConverterSearch( + builder.unitConverterSearch.toBuilder() + .setCurrencies(true) ) } } \ No newline at end of file diff --git a/preferences/src/main/proto/settings.proto b/preferences/src/main/proto/settings.proto index f7240e02..fc6f1442 100644 --- a/preferences/src/main/proto/settings.proto +++ b/preferences/src/main/proto/settings.proto @@ -136,6 +136,7 @@ message Settings { message UnitConverterSearchSettings { bool enabled = 1; + bool currencies = 2; } UnitConverterSearchSettings unit_converter_search = 13; diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt index 2c824331..8c2c3cd9 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt @@ -38,6 +38,7 @@ import de.mm20.launcher2.ui.settings.license.LicenseScreen import de.mm20.launcher2.ui.settings.main.MainSettingsScreen import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen +import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterSettingsScreen import de.mm20.launcher2.ui.settings.weatherwidget.WeatherWidgetSettingsScreen import de.mm20.launcher2.ui.settings.websearch.WebSearchSettingsScreen import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen @@ -98,6 +99,9 @@ class SettingsActivity : BaseActivity() { composable("settings/search") { SearchSettingsScreen() } + composable("settings/search/unitconverter") { + UnitConverterSettingsScreen() + } composable("settings/search/wikipedia") { WikipediaSettingsScreen() } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt index 1cefde45..524735e5 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt @@ -128,13 +128,16 @@ fun SearchSettingsScreen() { ) val unitConverter by viewModel.unitConverter.observeAsState() - SwitchPreference( + PreferenceWithSwitch( title = stringResource(R.string.preference_search_unitconverter), summary = stringResource(R.string.preference_search_unitconverter_summary), icon = Icons.Rounded.Loop, - value = unitConverter == true, - onValueChanged = { + switchValue = unitConverter == true, + onSwitchChanged = { viewModel.setUnitConverter(it) + }, + onClick = { + navController?.navigate("settings/search/unitconverter") } ) diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreen.kt new file mode 100644 index 00000000..c9f9b8da --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreen.kt @@ -0,0 +1,46 @@ +package de.mm20.launcher2.ui.settings.unitconverter + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Loop +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.preferences.PreferenceCategory +import de.mm20.launcher2.ui.component.preferences.PreferenceScreen +import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch +import de.mm20.launcher2.ui.component.preferences.SwitchPreference +import de.mm20.launcher2.ui.settings.search.SearchSettingsScreenVM + +@Composable +fun UnitConverterSettingsScreen() { + val viewModel: UnitConverterSettingsScreenVM = viewModel() + PreferenceScreen(title = stringResource(R.string.preference_search_unitconverter)) { + item { + PreferenceCategory { + val unitConverter by viewModel.unitConverter.observeAsState() + SwitchPreference( + title = stringResource(R.string.preference_search_unitconverter), + summary = stringResource(R.string.preference_search_unitconverter_summary), + value = unitConverter == true, + onValueChanged = { + viewModel.setUnitConverter(it) + } + ) + val currencyConverter by viewModel.currencyConverter.observeAsState() + SwitchPreference( + title = stringResource(R.string.preference_search_currencyconverter), + summary = stringResource(R.string.preference_search_currencyconverter_summary), + enabled = unitConverter != false, + value = currencyConverter == true, + onValueChanged = { + viewModel.setCurrencyConverter(it) + } + ) + } + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt new file mode 100644 index 00000000..4cec43a5 --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt @@ -0,0 +1,43 @@ +package de.mm20.launcher2.ui.settings.unitconverter + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent { + + private val dataStore: LauncherDataStore by inject() + + val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }.asLiveData() + fun setUnitConverter(unitConverter: Boolean) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setUnitConverterSearch( + it.unitConverterSearch.toBuilder() + .setEnabled(unitConverter) + ) + .build() + } + } + } + + val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies }.asLiveData() + fun setCurrencyConverter(currencyConverter: Boolean) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setUnitConverterSearch( + it.unitConverterSearch.toBuilder() + .setCurrencies(currencyConverter) + ) + .build() + } + } + } +} \ No newline at end of file diff --git a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt index 4a207456..b272f2b6 100644 --- a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt +++ b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt @@ -7,5 +7,5 @@ import org.koin.dsl.module val unitConverterModule = module { single { CurrencyRepository(androidContext()) } - single { UnitConverterRepositoryImpl(androidContext()) } + single { UnitConverterRepositoryImpl(androidContext(), get()) } } \ No newline at end of file diff --git a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt index dad6c403..185899a8 100644 --- a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt +++ b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt @@ -2,40 +2,59 @@ package de.mm20.launcher2.unitconverter import android.content.Context import androidx.lifecycle.MutableLiveData +import de.mm20.launcher2.currencies.CurrencyRepository import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.search.data.UnitConverter import de.mm20.launcher2.unitconverter.converters.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject interface UnitConverterRepository { - fun search(query:String): Flow + fun search(query: String): Flow } -internal class UnitConverterRepositoryImpl(val context: Context) : UnitConverterRepository, KoinComponent { +internal class UnitConverterRepositoryImpl( + private val context: Context, + private val currencyRepository: CurrencyRepository, +) : UnitConverterRepository, KoinComponent { private val dataStore: LauncherDataStore by inject() + private val scope = CoroutineScope(Job() + Dispatchers.Default) + val unitConverter = MutableLiveData() + init { + scope.launch { + dataStore.data.map { it.unitConverterSearch }.distinctUntilChanged().collectLatest { + if (it.enabled && it.currencies) currencyRepository.enableCurrencyUpdateWorker() + else currencyRepository.disableCurrencyUpdateWorker() + } + } + } + override fun search(query: String): Flow = channelFlow { if (query.isBlank()) { send(null) return@channelFlow } - dataStore.data.map { it.unitConverterSearch.enabled }.collectLatest { - if (it) { - send(queryUnitConverter(query)) + dataStore.data.map { it.unitConverterSearch }.collectLatest { + if (it.enabled) { + send(queryUnitConverter(query, it.currencies)) } else { send(null) } } } - private suspend fun queryUnitConverter(query: String): UnitConverter? { + private suspend fun queryUnitConverter( + query: String, + includeCurrencies: Boolean + ): UnitConverter? { if (!query.matches(Regex("[0-9,.:]+ [^\\s]+")) && !query.matches(Regex("[0-9,.:]+ [^\\s]+ >> [^\\s]+"))) return null val valueStr: String val unitStr: String @@ -49,18 +68,19 @@ internal class UnitConverterRepositoryImpl(val context: Context) : UnitConverter val value = valueStr.toDoubleOrNull() ?: valueStr.replace(',', '.').toDoubleOrNull() ?: return null - val converters = listOf( - lazy { MassConverter(context) }, - lazy { LengthConverter(context) }, - lazy { CurrencyConverter() }, - lazy { DataConverter(context) }, - lazy { TimeConverter(context) }, - lazy { VelocityConverter(context) }, - lazy { AreaConverter(context) }, - lazy { TemperatureConverter(context) } + val converters = mutableListOf( + MassConverter(context), + LengthConverter(context), + DataConverter(context), + TimeConverter(context), + VelocityConverter(context), + AreaConverter(context), + TemperatureConverter(context) ) - for (conv in converters) { - val converter = conv.value + + if (includeCurrencies) converters.add(CurrencyConverter(currencyRepository)) + + for (converter in converters) { if (!converter.isValidUnit(unitStr)) continue if (targetUnitStr != null && !converter.isValidUnit(targetUnitStr)) continue return converter.convert(context, unitStr, value, targetUnitStr) diff --git a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/CurrencyConverter.kt b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/CurrencyConverter.kt index c63ed2ac..21d5de35 100644 --- a/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/CurrencyConverter.kt +++ b/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/CurrencyConverter.kt @@ -12,14 +12,13 @@ import java.text.DecimalFormat import java.util.Locale import java.util.Currency as JCurrency import kotlin.math.abs -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -class CurrencyConverter : Converter, KoinComponent { +class CurrencyConverter( + private val repository: CurrencyRepository, +) : Converter { override val dimension: Dimension = Dimension.Currency - private val repository: CurrencyRepository by inject() private val topCurrencies = arrayOf("USD", "EUR", "JPY", "GBP", "AUD")