mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-02-15 01:23:56 -05:00
Add preference for checking for app updates
and show time of next update check
This commit is contained in:
@@ -95,7 +95,7 @@ class App : Application(), Configuration.Provider, SingletonImageLoader.Factory
|
||||
// bail out here if we are the ACRA process to not initialize anything in crash process
|
||||
if (ACRA.isACRASenderServiceProcess()) return
|
||||
|
||||
RepoUpdateWorker.scheduleOrCancel(applicationContext)
|
||||
RepoUpdateWorker.scheduleOrCancel(applicationContext, settingsManager.repoUpdates)
|
||||
AppUpdateWorker.scheduleOrCancel(applicationContext, settingsManager.autoUpdateApps)
|
||||
}
|
||||
|
||||
|
||||
@@ -63,28 +63,33 @@ class RepoUpdateWorker @AssistedInject constructor(
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun scheduleOrCancel(context: Context) {
|
||||
fun scheduleOrCancel(context: Context, enabled: Boolean) {
|
||||
val workManager = WorkManager.getInstance(context)
|
||||
Log.i(TAG, "scheduleOrCancel: enqueueUniquePeriodicWork")
|
||||
val networkType = NetworkType.UNMETERED
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresStorageNotLow(true)
|
||||
.setRequiredNetworkType(networkType)
|
||||
.build()
|
||||
val workRequest = PeriodicWorkRequestBuilder<RepoUpdateWorker>(
|
||||
repeatInterval = 4,
|
||||
repeatIntervalTimeUnit = TimeUnit.HOURS,
|
||||
flexTimeInterval = 15,
|
||||
flexTimeIntervalUnit = MINUTES,
|
||||
)
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
UNIQUE_WORK_NAME_REPO_AUTO_UPDATE,
|
||||
UPDATE,
|
||||
workRequest,
|
||||
)
|
||||
if (enabled) {
|
||||
Log.i(TAG, "scheduleOrCancel: enqueueUniquePeriodicWork")
|
||||
val networkType = NetworkType.UNMETERED
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresStorageNotLow(true)
|
||||
.setRequiredNetworkType(networkType)
|
||||
.build()
|
||||
val workRequest = PeriodicWorkRequestBuilder<RepoUpdateWorker>(
|
||||
repeatInterval = 4,
|
||||
repeatIntervalTimeUnit = TimeUnit.HOURS,
|
||||
flexTimeInterval = 15,
|
||||
flexTimeIntervalUnit = MINUTES,
|
||||
)
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
UNIQUE_WORK_NAME_REPO_AUTO_UPDATE,
|
||||
UPDATE,
|
||||
workRequest,
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "Cancelling job due to settings!")
|
||||
workManager.cancelUniqueWork(UNIQUE_WORK_NAME_REPO_AUTO_UPDATE)
|
||||
}
|
||||
}
|
||||
|
||||
fun getAutoUpdateWorkInfo(context: Context): Flow<WorkInfo?> {
|
||||
|
||||
@@ -10,6 +10,9 @@ object SettingsConstants {
|
||||
const val PREF_KEY_THEME = "theme"
|
||||
const val PREF_DEFAULT_THEME = "followSystem"
|
||||
|
||||
const val PREF_KEY_REPO_UPDATES = "repoUpdates"
|
||||
const val PREF_DEFAULT_REPO_UPDATES = true
|
||||
|
||||
const val PREF_KEY_AUTO_UPDATES = "autoUpdates"
|
||||
const val PREF_DEFAULT_AUTO_UPDATES = true
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import io.ktor.client.engine.ProxyBuilder
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import me.zhanghai.compose.preference.createPreferenceFlow
|
||||
@@ -17,12 +18,14 @@ import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_APP_LIST_SORT_ORDER
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_LAST_UPDATE_CHECK
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_REPO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_SHOW_INCOMPATIBLE
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_THEME
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_APP_LIST_SORT_ORDER
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_LAST_UPDATE_CHECK
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_REPO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_SHOW_INCOMPATIBLE
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_THEME
|
||||
import org.fdroid.settings.SettingsConstants.getAppListSortOrder
|
||||
@@ -46,9 +49,13 @@ class SettingsManager @Inject constructor(
|
||||
*/
|
||||
val prefsFlow by lazy { createPreferenceFlow(prefs) }
|
||||
val theme get() = prefs.getString(PREF_KEY_THEME, PREF_DEFAULT_THEME)!!
|
||||
val themeFlow = prefsFlow.map { it.get<String>(PREF_KEY_THEME) }
|
||||
val themeFlow = prefsFlow.map { it.get<String>(PREF_KEY_THEME) }.distinctUntilChanged()
|
||||
val repoUpdates get() = prefs.getBoolean(PREF_KEY_REPO_UPDATES, PREF_DEFAULT_REPO_UPDATES)
|
||||
val repoUpdatesFlow
|
||||
get() = prefsFlow.map { it.get<Boolean>(PREF_KEY_REPO_UPDATES) }.distinctUntilChanged()
|
||||
val autoUpdateApps get() = prefs.getBoolean(PREF_KEY_AUTO_UPDATES, PREF_DEFAULT_AUTO_UPDATES)
|
||||
val autoUpdateAppsFlow get() = prefsFlow.map { it.get<Boolean>(PREF_KEY_AUTO_UPDATES) }
|
||||
val autoUpdateAppsFlow
|
||||
get() = prefsFlow.map { it.get<Boolean>(PREF_KEY_AUTO_UPDATES) }.distinctUntilChanged()
|
||||
var lastRepoUpdate: Long
|
||||
get() = try {
|
||||
prefs.getInt(PREF_KEY_LAST_UPDATE_CHECK, PREF_DEFAULT_LAST_UPDATE_CHECK)
|
||||
|
||||
@@ -269,8 +269,7 @@ fun Main(onListeningForIntent: () -> Unit = {}) {
|
||||
entry(NavigationKey.Settings) {
|
||||
val viewModel = hiltViewModel<SettingsViewModel>()
|
||||
Settings(
|
||||
prefsFlow = viewModel.prefsFlow,
|
||||
nextAppUpdateFlow = viewModel.nextAppUpdateFlow,
|
||||
model = viewModel.model,
|
||||
onSaveLogcat = {
|
||||
viewModel.onSaveLogcat(it)
|
||||
backStack.removeLastOrNull()
|
||||
|
||||
@@ -18,12 +18,16 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.BrightnessMedium
|
||||
import androidx.compose.material.icons.filled.Notifications
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material.icons.filled.SystemSecurityUpdate
|
||||
import androidx.compose.material.icons.filled.SystemSecurityUpdateWarning
|
||||
import androidx.compose.material.icons.filled.Translate
|
||||
import androidx.compose.material.icons.filled.Update
|
||||
import androidx.compose.material.icons.filled.UpdateDisabled
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -39,10 +43,8 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import me.zhanghai.compose.preference.MapPreferences
|
||||
import me.zhanghai.compose.preference.Preferences
|
||||
import me.zhanghai.compose.preference.ProvidePreferenceLocals
|
||||
import me.zhanghai.compose.preference.listPreference
|
||||
import me.zhanghai.compose.preference.preference
|
||||
@@ -53,19 +55,21 @@ import org.fdroid.R
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_REPO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_THEME
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_REPO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_THEME
|
||||
import org.fdroid.ui.utils.asRelativeTimeString
|
||||
import org.fdroid.utils.getLogName
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.util.concurrent.TimeUnit.HOURS
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun Settings(
|
||||
prefsFlow: MutableStateFlow<Preferences>,
|
||||
nextAppUpdateFlow: Flow<Long>,
|
||||
model: SettingsModel,
|
||||
onSaveLogcat: (Uri?) -> Unit,
|
||||
onBackClicked: () -> Unit,
|
||||
) {
|
||||
@@ -91,7 +95,7 @@ fun Settings(
|
||||
}
|
||||
val context = LocalContext.current
|
||||
val res = LocalResources.current
|
||||
ProvidePreferenceLocals(prefsFlow) {
|
||||
ProvidePreferenceLocals(model.prefsFlow) {
|
||||
val showProxyError = remember { mutableStateOf(false) }
|
||||
val proxyState = rememberPreferenceState(PREF_KEY_PROXY, PREF_DEFAULT_PROXY)
|
||||
LazyColumn(
|
||||
@@ -112,7 +116,6 @@ fun Settings(
|
||||
else -> error("Unknown value: $value")
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
listPreference(
|
||||
key = PREF_KEY_THEME,
|
||||
@@ -170,36 +173,88 @@ fun Settings(
|
||||
key = "pref_category_updates",
|
||||
title = { Text(stringResource(R.string.updates)) },
|
||||
)
|
||||
switchPreference(
|
||||
key = PREF_KEY_REPO_UPDATES,
|
||||
defaultValue = PREF_DEFAULT_REPO_UPDATES,
|
||||
title = {
|
||||
Text(stringResource(R.string.pref_repo_updates_title))
|
||||
},
|
||||
icon = { repoUpdatesEnabled ->
|
||||
if (repoUpdatesEnabled) Icon(
|
||||
imageVector = Icons.Default.SystemSecurityUpdate,
|
||||
contentDescription = null,
|
||||
) else Icon(
|
||||
imageVector = Icons.Default.SystemSecurityUpdateWarning,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
},
|
||||
summary = { repoUpdatesEnabled ->
|
||||
if (repoUpdatesEnabled) {
|
||||
val nextUpdate =
|
||||
model.nextRepoUpdateFlow.collectAsState(Long.MAX_VALUE).value
|
||||
val nextUpdateStr = if (nextUpdate == Long.MAX_VALUE) {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
stringResource(R.string.repositories_last_update_never)
|
||||
)
|
||||
} else if (nextUpdate - currentTimeMillis() <= 0) {
|
||||
stringResource(R.string.auto_update_time_past)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
nextUpdate.asRelativeTimeString()
|
||||
)
|
||||
}
|
||||
val s = stringResource(R.string.pref_repo_updates_summary_enabled) +
|
||||
"\n\n" + nextUpdateStr
|
||||
Text(s)
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(R.string.pref_repo_updates_summary_disabled),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
switchPreference(
|
||||
key = PREF_KEY_AUTO_UPDATES,
|
||||
defaultValue = PREF_DEFAULT_AUTO_UPDATES,
|
||||
title = {
|
||||
Text(stringResource(R.string.update_auto_install))
|
||||
},
|
||||
icon = {
|
||||
icon = { autoUpdatesEnabled ->
|
||||
Icon(
|
||||
imageVector = Icons.Default.Update,
|
||||
imageVector = if (autoUpdatesEnabled) {
|
||||
Icons.Default.Update
|
||||
} else {
|
||||
Icons.Default.UpdateDisabled
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
summary = {
|
||||
val nextUpdate = nextAppUpdateFlow.collectAsState(Long.MAX_VALUE).value
|
||||
val nextUpdateStr = if (nextUpdate == Long.MAX_VALUE) {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
stringResource(R.string.repositories_last_update_never)
|
||||
)
|
||||
} else if (nextUpdate - System.currentTimeMillis() <= 0) {
|
||||
stringResource(R.string.auto_update_time_past)
|
||||
summary = { autoUpdatesEnabled ->
|
||||
val s = if (autoUpdatesEnabled) {
|
||||
val nextUpdate =
|
||||
model.nextAppUpdateFlow.collectAsState(Long.MAX_VALUE).value
|
||||
val nextUpdateStr = if (nextUpdate == Long.MAX_VALUE) {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
stringResource(R.string.repositories_last_update_never)
|
||||
)
|
||||
} else if (nextUpdate - currentTimeMillis() <= 0) {
|
||||
stringResource(R.string.auto_update_time_past)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
nextUpdate.asRelativeTimeString()
|
||||
)
|
||||
}
|
||||
stringResource(R.string.pref_auto_updates_summary_enabled) +
|
||||
"\n\n" + nextUpdateStr
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.auto_update_time,
|
||||
nextUpdate.asRelativeTimeString()
|
||||
)
|
||||
stringResource(R.string.pref_auto_updates_summary_disabled)
|
||||
}
|
||||
val s = stringResource(R.string.pref_auto_updates_summary) +
|
||||
"\n\n" +
|
||||
nextUpdateStr
|
||||
Text(s)
|
||||
},
|
||||
)
|
||||
@@ -230,7 +285,11 @@ fun Settings(
|
||||
@Composable
|
||||
fun SettingsPreview() {
|
||||
FDroidContent {
|
||||
val nextFLow = MutableStateFlow(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(12))
|
||||
Settings(MutableStateFlow(MapPreferences()), nextFLow, {}, { })
|
||||
val model = SettingsModel(
|
||||
prefsFlow = MutableStateFlow(MapPreferences()),
|
||||
nextRepoUpdateFlow = MutableStateFlow(Long.MAX_VALUE),
|
||||
nextAppUpdateFlow = MutableStateFlow(currentTimeMillis() - HOURS.toMillis(12)),
|
||||
)
|
||||
Settings(model, {}, { })
|
||||
}
|
||||
}
|
||||
|
||||
11
next/src/main/kotlin/org/fdroid/ui/settings/SettingsModel.kt
Normal file
11
next/src/main/kotlin/org/fdroid/ui/settings/SettingsModel.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package org.fdroid.ui.settings
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import me.zhanghai.compose.preference.Preferences
|
||||
|
||||
data class SettingsModel(
|
||||
val prefsFlow: MutableStateFlow<Preferences>,
|
||||
val nextRepoUpdateFlow: Flow<Long>,
|
||||
val nextAppUpdateFlow: Flow<Long>,
|
||||
)
|
||||
@@ -16,6 +16,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.R
|
||||
import org.fdroid.repo.RepoUpdateWorker
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.ui.utils.applyNewTheme
|
||||
import org.fdroid.updates.AppUpdateWorker
|
||||
@@ -32,8 +33,12 @@ class SettingsViewModel @Inject constructor(
|
||||
) : AndroidViewModel(app) {
|
||||
|
||||
private val log = KotlinLogging.logger {}
|
||||
val prefsFlow = settingsManager.prefsFlow
|
||||
val nextAppUpdateFlow = updatesManager.nextAppUpdateFlow
|
||||
|
||||
val model = SettingsModel(
|
||||
prefsFlow = settingsManager.prefsFlow,
|
||||
nextRepoUpdateFlow = updatesManager.nextRepoUpdateFlow,
|
||||
nextAppUpdateFlow = updatesManager.nextAppUpdateFlow,
|
||||
)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
@@ -42,6 +47,12 @@ class SettingsViewModel @Inject constructor(
|
||||
if (it != null) applyNewTheme(it)
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
// react to repo-update changes
|
||||
settingsManager.repoUpdatesFlow.drop(1).collect { enable ->
|
||||
if (enable != null) RepoUpdateWorker.scheduleOrCancel(application, enable)
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
// react to auto-update changes
|
||||
settingsManager.autoUpdateAppsFlow.drop(1).collect { enable ->
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.fdroid.database.FDroidDatabase
|
||||
import org.fdroid.download.getImageModel
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.repo.RepoUpdateWorker
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.ui.apps.AppUpdateItem
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
@@ -43,6 +44,14 @@ class UpdatesManager @Inject constructor(
|
||||
private val _numUpdates = MutableStateFlow(0)
|
||||
val numUpdates = _numUpdates.asStateFlow()
|
||||
|
||||
/**
|
||||
* The time in milliseconds of the (earliest!) next repository update run.
|
||||
* This is [Long.MAX_VALUE], if no time is known.
|
||||
*/
|
||||
val nextRepoUpdateFlow = RepoUpdateWorker.getAutoUpdateWorkInfo(context).map { workInfo ->
|
||||
workInfo?.nextScheduleTimeMillis ?: Long.MAX_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
* The time in milliseconds of the (earliest!) next automatic app update run.
|
||||
* This is [Long.MAX_VALUE], if no time is known.
|
||||
|
||||
@@ -102,7 +102,11 @@
|
||||
|
||||
|
||||
<string name="pref_language_summary">Opens system language settings</string>
|
||||
<string name="pref_auto_updates_summary">Download and update apps daily when the device isn\'t being used</string>
|
||||
<string name="pref_auto_updates_summary_enabled">Download and update apps daily when the device isn\'t being used</string>
|
||||
<string name="pref_auto_updates_summary_disabled">Auto-updates disabled • Apps will need to be updated manually</string>
|
||||
<string name="pref_repo_updates_title">Check for updates</string>
|
||||
<string name="pref_repo_updates_summary_enabled">Automatically ask all repositories for app updates</string>
|
||||
<string name="pref_repo_updates_summary_disabled">Do NOT check for updates • Apps will become outdated</string>
|
||||
<string name="pref_category_network">Network</string>
|
||||
<string name="pref_proxy_title">Connect via SOCKS proxy</string>
|
||||
<string name="pref_proxy_disabled">Connect to the internet without proxy</string>
|
||||
|
||||
Reference in New Issue
Block a user