From 67db42c5f0dd54ea75ffb7c772dd79228478f4a9 Mon Sep 17 00:00:00 2001 From: Edric Date: Sun, 24 Nov 2024 19:53:28 +0800 Subject: [PATCH] UpdateSettingsFragment: Migrate to Jetpack Compose (#586) * Add `UpdateSettingsScreen` composable in `:features:settings` module (for #369) * Use `ViewModel` for data storage, handled by the `flow-preferences` library (https://github.com/tfcporciuncula/flow-preferences) * Add drawables and update pref strings from app module * Update fragment to use screen composable * Add `coreLibraryDesugaring` --- app/build.gradle.kts | 1 + .../fragment/UpdateSettingsFragment.kt | 204 ++++++------------ .../fragment/vm/UpdateSettingsViewModel.kt | 73 +++++++ features/settings/build.gradle.kts | 18 +- .../updates/model/CheckFrequencyCompat.kt | 32 +++ .../settings/updates/ui/UpdateSettings.kt | 136 ++++++++++++ .../drawable/ic_attach_money_outline_24dp.xml | 10 + .../ic_battery_charging_outline_24dp.xml | 10 + .../res/drawable/ic_system_update_24dp.xml | 10 + .../src/main/res/values/update_strings.xml | 42 ++++ gradle/libs.versions.toml | 1 + 11 files changed, 393 insertions(+), 144 deletions(-) create mode 100644 app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/vm/UpdateSettingsViewModel.kt create mode 100644 features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/model/CheckFrequencyCompat.kt create mode 100644 features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/ui/UpdateSettings.kt create mode 100644 features/settings/src/main/res/drawable/ic_attach_money_outline_24dp.xml create mode 100644 features/settings/src/main/res/drawable/ic_battery_charging_outline_24dp.xml create mode 100644 features/settings/src/main/res/drawable/ic_system_update_24dp.xml create mode 100644 features/settings/src/main/res/values/update_strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c4c78b2a..855d01bb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -222,6 +222,7 @@ dependencies { // Other dependencies implementation(libs.materialAbout) implementation(libs.appUpdater) + implementation(libs.flowPreferences) implementation(libs.migration) // Dagger dependencies diff --git a/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/UpdateSettingsFragment.kt b/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/UpdateSettingsFragment.kt index 958cf2f5..652623d7 100644 --- a/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/UpdateSettingsFragment.kt +++ b/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/UpdateSettingsFragment.kt @@ -1,150 +1,68 @@ package com.edricchan.studybuddy.ui.modules.settings.fragment -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences import android.os.Bundle -import android.text.format.DateUtils -import androidx.preference.Preference -import com.edricchan.studybuddy.R -import com.edricchan.studybuddy.constants.Constants -import com.edricchan.studybuddy.core.settings.updates.UpdateInfoPrefConstants -import com.edricchan.studybuddy.exts.android.getSerializableCompat -import com.edricchan.studybuddy.ui.modules.updates.UpdatesActivity -import com.edricchan.studybuddy.ui.preference.MaterialPreferenceFragment -import java.time.Instant - -class UpdateSettingsFragment : MaterialPreferenceFragment(), - SharedPreferences.OnSharedPreferenceChangeListener { - - private lateinit var updateInfoPreferences: SharedPreferences - private var lastUpdatedInstant: Instant? = null - private var lastCheckedForUpdatesInstant: Instant? = null - - private fun setLastCheckedForUpdates(lastCheckedForUpdatesMs: Long) { - lastCheckedForUpdatesInstant = - lastCheckedForUpdatesMs.takeIf { it <= DEFAULT_INSTANT } - ?.let { Instant.ofEpochMilli(it) } - } - - private fun setLastUpdated(lastUpdatedMs: Long) { - lastUpdatedInstant = - lastUpdatedMs.takeIf { it <= DEFAULT_INSTANT } - ?.let { Instant.ofEpochMilli(it) } - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) { - if (key == UpdateInfoPrefConstants.PREF_LAST_CHECKED_FOR_UPDATES_DATE || key == UpdateInfoPrefConstants.PREF_LAST_UPDATED_DATE) { - when (key) { - UpdateInfoPrefConstants.PREF_LAST_CHECKED_FOR_UPDATES_DATE -> { - setLastCheckedForUpdates( - updateInfoPreferences.getLong( - UpdateInfoPrefConstants.PREF_LAST_UPDATED_DATE, - DEFAULT_INSTANT - ) - ) - } - - UpdateInfoPrefConstants.PREF_LAST_UPDATED_DATE -> { - setLastUpdated( - updateInfoPreferences.getLong( - UpdateInfoPrefConstants.PREF_LAST_CHECKED_FOR_UPDATES_DATE, - DEFAULT_INSTANT - ) - ) - } - } - updateUpdatesPreferenceSummary() - } - } - - override fun onSaveInstanceState(outState: Bundle) { - outState.putSerializable( - LAST_CHECK_FOR_UPDATES_DATE_TAG, - lastCheckedForUpdatesInstant - ) - outState.putSerializable(LAST_UPDATED_DATE_TAG, lastUpdatedInstant) - super.onSaveInstanceState(outState) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - setPreferencesFromResource(R.xml.pref_updates, rootKey) - - updateInfoPreferences = requireContext().getSharedPreferences( - UpdateInfoPrefConstants.FILE_UPDATE_INFO, - Context.MODE_PRIVATE - ).apply { - registerOnSharedPreferenceChangeListener(this@UpdateSettingsFragment) - } - - savedInstanceState?.run { - lastCheckedForUpdatesInstant = getSerializableCompat( - LAST_CHECK_FOR_UPDATES_DATE_TAG - ) - lastUpdatedInstant = getSerializableCompat( - LAST_UPDATED_DATE_TAG - ) - } ?: run { - setLastCheckedForUpdates( - updateInfoPreferences - .getLong( - UpdateInfoPrefConstants.PREF_LAST_CHECKED_FOR_UPDATES_DATE, - DEFAULT_INSTANT - ) - ) - setLastUpdated( - updateInfoPreferences - .getLong( - UpdateInfoPrefConstants.PREF_LAST_UPDATED_DATE, - DEFAULT_INSTANT - ) +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.fragment.app.viewModels +import androidx.fragment.compose.content +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.edricchan.studybuddy.core.compat.navigation.navigateToUpdates +import com.edricchan.studybuddy.features.settings.updates.model.CheckFrequencyCompat +import com.edricchan.studybuddy.features.settings.updates.ui.UpdateSettingsScreen +import com.edricchan.studybuddy.ui.common.fragment.BaseFragment +import com.edricchan.studybuddy.ui.modules.settings.fragment.vm.UpdateSettingsViewModel +import com.edricchan.studybuddy.ui.theming.compose.StudyBuddyTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class UpdateSettingsFragment : BaseFragment() { + + private val viewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = content { + val nestedScrollInterop = rememberNestedScrollInteropConnection() + + val checkFrequency by viewModel.prefCheckFrequency.asFlow() + .collectAsStateWithLifecycle(initialValue = CheckFrequencyCompat.SixHours) + + val canDownloadMetered by viewModel.prefCanDownloadMetered.asFlow() + .collectAsStateWithLifecycle(initialValue = false) + + val onlyDownloadCharging by viewModel.prefOnlyDownloadCharging.asFlow() + .collectAsStateWithLifecycle(initialValue = false) + + val lastChecked by viewModel.lastChecked.asFlow() + .collectAsStateWithLifecycle(initialValue = null) + + val lastUpdated by viewModel.lastUpdated.asFlow() + .collectAsStateWithLifecycle(initialValue = null) + + StudyBuddyTheme { + UpdateSettingsScreen( + modifier = Modifier + .nestedScroll(nestedScrollInterop) + .windowInsetsPadding(WindowInsets.navigationBars), + onUpdatesClick = navController::navigateToUpdates, + lastChecked = lastChecked, + lastUpdated = lastUpdated, + checkFrequency = checkFrequency, + onCheckFrequencyChange = viewModel.prefCheckFrequency::set, + canDownloadMetered = canDownloadMetered, + onCanDownloadMeteredChange = viewModel.prefCanDownloadMetered::set, + onlyDownloadCharging = onlyDownloadCharging, + onOnlyDownloadCharging = viewModel.prefOnlyDownloadCharging::set, ) } - - findPreference(Constants.prefUpdates)?.intent = - Intent(context, UpdatesActivity::class.java) - - updateUpdatesPreferenceSummary() } - - override fun onPause() { - super.onPause() - updateInfoPreferences.unregisterOnSharedPreferenceChangeListener(this) - } - - override fun onResume() { - super.onResume() - updateInfoPreferences.registerOnSharedPreferenceChangeListener(this) - } - - private fun updateUpdatesPreferenceSummary() { - findPreference(Constants.prefUpdates)?.apply { - val lastCheckedForUpdates = - lastCheckedForUpdatesInstant?.let { getRelativeDateTimeString(it) } - ?: getString(R.string.pref_updates_summary_never) - val lastUpdated = lastUpdatedInstant?.let { getRelativeDateTimeString(it) } - ?: getString(R.string.pref_updates_summary_never) - summary = getString(R.string.pref_updates_summary, lastCheckedForUpdates, lastUpdated) - } - } - - private fun getRelativeDateTimeString(instant: Instant): CharSequence = - DateUtils.getRelativeDateTimeString( - context, - instant.toEpochMilli(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.WEEK_IN_MILLIS, - 0 - ) - - companion object { - // Indicates when the app was last updated - private const val LAST_UPDATED_DATE_TAG = "lastUpdatedDate" - - // Indicates when the app last checked for updates - private const val LAST_CHECK_FOR_UPDATES_DATE_TAG = "lastCheckForUpdatesDate" - - private const val DEFAULT_INSTANT = -1L - } - } diff --git a/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/vm/UpdateSettingsViewModel.kt b/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/vm/UpdateSettingsViewModel.kt new file mode 100644 index 00000000..9c6237d8 --- /dev/null +++ b/app/src/main/java/com/edricchan/studybuddy/ui/modules/settings/fragment/vm/UpdateSettingsViewModel.kt @@ -0,0 +1,73 @@ +package com.edricchan.studybuddy.ui.modules.settings.fragment.vm + +import android.content.Context +import androidx.lifecycle.ViewModel +import com.edricchan.studybuddy.core.settings.updates.UpdateInfoPrefConstants +import com.edricchan.studybuddy.core.settings.updates.keyPrefCanDownloadMetered +import com.edricchan.studybuddy.core.settings.updates.keyPrefOnlyDownloadCharging +import com.edricchan.studybuddy.core.settings.updates.keyPrefUpdatesFrequency +import com.edricchan.studybuddy.exts.androidx.preference.defaultSharedPreferences +import com.edricchan.studybuddy.features.settings.updates.model.CheckFrequencyCompat +import com.edricchan.studybuddy.features.settings.updates.model.hourValue +import com.fredporciuncula.flow.preferences.FlowSharedPreferences +import com.fredporciuncula.flow.preferences.Preference +import com.fredporciuncula.flow.preferences.map +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import java.time.Instant +import javax.inject.Inject + +@HiltViewModel +class UpdateSettingsViewModel @Inject constructor( + @ApplicationContext context: Context +) : ViewModel() { + private val updateInfoPreferences = FlowSharedPreferences( + context.getSharedPreferences( + UpdateInfoPrefConstants.FILE_UPDATE_INFO, + Context.MODE_PRIVATE + ) + ) + + private val appPreferences = FlowSharedPreferences( + context.defaultSharedPreferences + ) + + val prefCheckFrequency: Preference = appPreferences + // ListPreference uses a string to persist its data, see + // https://stackoverflow.com/q/11346916 + .getString( + keyPrefUpdatesFrequency, + defaultValue = CheckFrequencyCompat.SixHours.hourValue.toString() + ).map( + mapper = { + CheckFrequencyCompat.fromHoursOrNull(it.toInt()) ?: CheckFrequencyCompat.SixHours + }, + reverse = { it.hourValue.toString() } + ) + + val prefCanDownloadMetered: Preference = + appPreferences.getBoolean(keyPrefCanDownloadMetered) + + val prefOnlyDownloadCharging: Preference = + appPreferences.getBoolean(keyPrefOnlyDownloadCharging) + + val lastChecked: Preference = updateInfoPreferences.getLong( + UpdateInfoPrefConstants.PREF_LAST_CHECKED_FOR_UPDATES_DATE, + Long.MIN_VALUE + ).map( + mapper = { valLong -> + valLong.takeIf { it >= Instant.EPOCH.toEpochMilli() }?.let { Instant.ofEpochMilli(it) } + }, + reverse = { it?.toEpochMilli() ?: Long.MIN_VALUE } + ) + + val lastUpdated: Preference = updateInfoPreferences.getLong( + UpdateInfoPrefConstants.PREF_LAST_UPDATED_DATE, + Long.MIN_VALUE + ).map( + mapper = { valLong -> + valLong.takeIf { it >= Instant.EPOCH.toEpochMilli() }?.let { Instant.ofEpochMilli(it) } + }, + reverse = { it?.toEpochMilli() ?: Long.MIN_VALUE } + ) +} diff --git a/features/settings/build.gradle.kts b/features/settings/build.gradle.kts index fcff7d89..a9e013bd 100644 --- a/features/settings/build.gradle.kts +++ b/features/settings/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - com.edricchan.studybuddy.library.android + com.edricchan.studybuddy.library.`android-compose` } android { @@ -18,17 +18,33 @@ android { ) } } + + compileOptions.isCoreLibraryDesugaringEnabled = true } dependencies { + implementation(projects.core.resources.temporal) + implementation(projects.core.settings.updates) + api(projects.ui.preference) implementation(projects.exts.common) + implementation(projects.ui.theming.compose) implementation(projects.ui.theming.views) + implementation(projects.ui.preference.compose) api(libs.androidx.preference.ktx) + // Compose + implementation(libs.bundles.androidx.compose) + debugImplementation(libs.bundles.androidx.compose.tooling) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.test.espresso.core) + + // Compose rule support + androidTestImplementation(libs.androidx.compose.ui.test.junit4) + + coreLibraryDesugaring(libs.android.desugar) } diff --git a/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/model/CheckFrequencyCompat.kt b/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/model/CheckFrequencyCompat.kt new file mode 100644 index 00000000..f9f522d5 --- /dev/null +++ b/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/model/CheckFrequencyCompat.kt @@ -0,0 +1,32 @@ +package com.edricchan.studybuddy.features.settings.updates.model + +import androidx.annotation.StringRes +import com.edricchan.studybuddy.features.settings.R + +private val hourToEnumMap = mapOf( + CheckFrequencyCompat.Manual to 0, + CheckFrequencyCompat.ThreeHours to 3, + CheckFrequencyCompat.SixHours to 6, + CheckFrequencyCompat.TwelveHours to 12, + CheckFrequencyCompat.Daily to 24, + CheckFrequencyCompat.Weekly to 24 * 7 +) + +enum class CheckFrequencyCompat(@StringRes val stringResource: Int) { + Manual(R.string.pref_check_for_update_freq_manual), + ThreeHours(R.string.pref_check_for_update_freq_three_hour), + SixHours(R.string.pref_check_for_update_freq_six_hour), + TwelveHours(R.string.pref_check_for_update_freq_twelve_hour), + Daily(R.string.pref_check_for_update_freq_daily), + Weekly(R.string.pref_check_for_update_freq_weekly); + + companion object { + fun fromHoursOrNull(hours: Int): CheckFrequencyCompat? = + hourToEnumMap.entries + .find { entry -> entry.value == hours } + ?.key + } +} + +val CheckFrequencyCompat.hourValue: Int + get() = hourToEnumMap[this] ?: 0 diff --git a/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/ui/UpdateSettings.kt b/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/ui/UpdateSettings.kt new file mode 100644 index 00000000..cf98ab22 --- /dev/null +++ b/features/settings/src/main/kotlin/com/edricchan/studybuddy/features/settings/updates/ui/UpdateSettings.kt @@ -0,0 +1,136 @@ +package com.edricchan.studybuddy.features.settings.updates.ui + +import android.content.Context +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Refresh +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.edricchan.studybuddy.core.resources.temporal.relative.formatRelativeTimeSpan +import com.edricchan.studybuddy.features.settings.R +import com.edricchan.studybuddy.features.settings.updates.model.CheckFrequencyCompat +import com.edricchan.studybuddy.ui.preference.compose.ListDialogPreference +import com.edricchan.studybuddy.ui.preference.compose.Preference +import com.edricchan.studybuddy.ui.preference.compose.PreferenceCategory +import com.edricchan.studybuddy.ui.preference.compose.twostate.SwitchPreference +import com.edricchan.studybuddy.ui.theming.compose.StudyBuddyTheme +import java.time.Duration +import java.time.Instant + +@Composable +private fun Instant.formatRelativeTimeSpan( + context: Context = LocalContext.current, + now: Instant = Instant.now() +): String = context.formatRelativeTimeSpan(time = this, now = now) + +@Composable +fun UpdateSettingsScreen( + modifier: Modifier = Modifier, + onUpdatesClick: () -> Unit, + lastUpdated: Instant?, + lastChecked: Instant?, + checkFrequency: CheckFrequencyCompat, + onCheckFrequencyChange: (CheckFrequencyCompat) -> Unit, + canDownloadMetered: Boolean, + onCanDownloadMeteredChange: (Boolean) -> Unit, + onlyDownloadCharging: Boolean, + onOnlyDownloadCharging: (Boolean) -> Unit +) = Column( + modifier = modifier.verticalScroll(rememberScrollState()) +) { + Preference( + icon = { + Icon( + painterResource(R.drawable.ic_system_update_24dp), + contentDescription = null + ) + }, + title = { Text(text = stringResource(R.string.pref_updates_title)) }, + subtitle = { + Text( + text = stringResource( + R.string.pref_updates_summary, + lastChecked?.formatRelativeTimeSpan() + ?: stringResource(R.string.pref_updates_summary_never), + lastUpdated?.formatRelativeTimeSpan() + ?: stringResource(R.string.pref_updates_summary_never) + ) + ) + }, + onClick = onUpdatesClick + ) + + PreferenceCategory( + title = { Text(text = stringResource(R.string.pref_updates_options_title)) } + ) { + ListDialogPreference( + icon = { Icon(Icons.Outlined.Refresh, contentDescription = null) }, + title = { Text(text = stringResource(R.string.pref_check_for_update_frequency_title)) }, + subtitle = { Text(text = stringResource(checkFrequency.stringResource)) }, + values = CheckFrequencyCompat.entries, + valueLabel = { Text(text = stringResource(it.stringResource)) }, + value = checkFrequency, + onValueChanged = onCheckFrequencyChange + ) + SwitchPreference( + icon = { + Icon( + painterResource(R.drawable.ic_attach_money_outline_24dp), + contentDescription = null + ) + }, + title = { Text(text = stringResource(R.string.pref_updates_download_over_metered_title)) }, + subtitle = { Text(text = stringResource(R.string.pref_updates_download_over_metered_summary)) }, + checked = canDownloadMetered, + onCheckedChange = onCanDownloadMeteredChange + ) + SwitchPreference( + icon = { + Icon( + painterResource(R.drawable.ic_battery_charging_outline_24dp), + contentDescription = null + ) + }, + title = { Text(text = stringResource(R.string.pref_updates_download_only_when_charging_title)) }, + subtitle = { Text(text = stringResource(R.string.pref_updates_download_only_when_charging_summary)) }, + checked = onlyDownloadCharging, + onCheckedChange = onOnlyDownloadCharging + ) + } +} + +@Preview +@Composable +private fun UpdateSettingsScreenPreview() { + var checkFrequency by remember { mutableStateOf(CheckFrequencyCompat.Manual) } + var canDownloadMetered by remember { mutableStateOf(false) } + var onlyDownloadCharging by remember { mutableStateOf(false) } + + val now = Instant.now() + + StudyBuddyTheme { + UpdateSettingsScreen( + onUpdatesClick = {}, + lastUpdated = now - Duration.ofDays(2), + lastChecked = now, + checkFrequency = checkFrequency, + onCheckFrequencyChange = { checkFrequency = it }, + canDownloadMetered = canDownloadMetered, + onCanDownloadMeteredChange = { canDownloadMetered = it }, + onlyDownloadCharging = onlyDownloadCharging, + onOnlyDownloadCharging = { onlyDownloadCharging = it }, + ) + } +} diff --git a/features/settings/src/main/res/drawable/ic_attach_money_outline_24dp.xml b/features/settings/src/main/res/drawable/ic_attach_money_outline_24dp.xml new file mode 100644 index 00000000..e112eb66 --- /dev/null +++ b/features/settings/src/main/res/drawable/ic_attach_money_outline_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/features/settings/src/main/res/drawable/ic_battery_charging_outline_24dp.xml b/features/settings/src/main/res/drawable/ic_battery_charging_outline_24dp.xml new file mode 100644 index 00000000..6f72e231 --- /dev/null +++ b/features/settings/src/main/res/drawable/ic_battery_charging_outline_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/features/settings/src/main/res/drawable/ic_system_update_24dp.xml b/features/settings/src/main/res/drawable/ic_system_update_24dp.xml new file mode 100644 index 00000000..47cda13f --- /dev/null +++ b/features/settings/src/main/res/drawable/ic_system_update_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/features/settings/src/main/res/values/update_strings.xml b/features/settings/src/main/res/values/update_strings.xml new file mode 100644 index 00000000..e61d0185 --- /dev/null +++ b/features/settings/src/main/res/values/update_strings.xml @@ -0,0 +1,42 @@ + + + + Switching this on will allow + downloads on cellular as well + as other metered networks. For more info, check your network settings + + + Download over metered networks + + When switched on, updates will + only be downloaded when your device is charging. + + + Download only when + charging + + Update options + + Updates + + Last checked: %1$s\nLast updated: %2$s + + Never + + + Checking frequency + + + Manual + + Every 3 hours + + Every 6 hours + + Every 12 hours + + Daily + + Weekly + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cd8935fb..72d2b16e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -136,6 +136,7 @@ aboutLibraries = { module = "com.mikepenz:aboutlibraries", version.ref = "aboutL aboutLibraries-compose-material3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutLibraries" } # TODO: Switch to an in-house updater appUpdater = "com.github.AleP04:AppUpdater:2.7.2" +flowPreferences = "com.fredporciuncula:flow-preferences:1.9.1" migration = "io.github.boswelja.migration:migration-core:2.1.4" # Gradle Versions (declared here as a library and not as a plugin