mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-02-14 17:12:12 -05:00
Implement "Update all" apps button
This commit is contained in:
@@ -38,10 +38,20 @@ data class InstallNotificationState(
|
||||
*/
|
||||
val isInstallingSomeApp: Boolean = apps.any { it.category == AppStateCategory.INSTALLING }
|
||||
|
||||
/**
|
||||
* Returns true if *all* apps being installed are updates to existing apps.
|
||||
*/
|
||||
private val isUpdatingApps: Boolean = apps.all { it.currentVersionName != null }
|
||||
|
||||
fun getTitle(context: Context): String {
|
||||
val titleRes = if (isUpdatingApps) {
|
||||
R.plurals.notification_updating_title
|
||||
} else {
|
||||
R.plurals.notification_installing_title
|
||||
}
|
||||
val numActiveApps: Int = apps.count { it.category != AppStateCategory.INSTALLED }
|
||||
val installTitle = context.resources.getQuantityString(
|
||||
R.plurals.notification_installing_title,
|
||||
titleRes,
|
||||
numActiveApps,
|
||||
numActiveApps,
|
||||
)
|
||||
|
||||
@@ -113,6 +113,7 @@ fun Main(onListeningForIntent: () -> Unit = {}) {
|
||||
myAppsViewModel.myAppsModel.collectAsStateWithLifecycle().value
|
||||
|
||||
override fun refresh() = myAppsViewModel.refresh()
|
||||
override fun updateAll() = myAppsViewModel.updateAll()
|
||||
override fun changeSortOrder(sort: AppListSortOrder) =
|
||||
myAppsViewModel.changeSortOrder(sort)
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ data class InstallingAppItem(
|
||||
}
|
||||
|
||||
data class AppUpdateItem(
|
||||
val repoId: Long,
|
||||
override val packageName: String,
|
||||
override val name: String,
|
||||
val installedVersionName: String,
|
||||
|
||||
@@ -215,7 +215,7 @@ fun MyApps(
|
||||
.weight(1f),
|
||||
)
|
||||
Button(
|
||||
onClick = {},
|
||||
onClick = myAppsInfo::updateAll,
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
) {
|
||||
Text(stringResource(R.string.update_all))
|
||||
@@ -356,6 +356,7 @@ fun MyAppsPreview() {
|
||||
)
|
||||
)
|
||||
val app1 = AppUpdateItem(
|
||||
repoId = 1,
|
||||
packageName = "B1",
|
||||
name = "App Update 123",
|
||||
installedVersionName = "1.0.1",
|
||||
@@ -363,6 +364,7 @@ fun MyAppsPreview() {
|
||||
whatsNew = "This is new, all is new, nothing old.",
|
||||
)
|
||||
val app2 = AppUpdateItem(
|
||||
repoId = 2,
|
||||
packageName = "B2",
|
||||
name = Names.randomName,
|
||||
installedVersionName = "3.0.1",
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.fdroid.install.InstallState
|
||||
interface MyAppsInfo {
|
||||
val model: MyAppsModel
|
||||
fun refresh()
|
||||
fun updateAll()
|
||||
fun changeSortOrder(sort: AppListSortOrder)
|
||||
fun search(query: String)
|
||||
fun confirmAppInstall(packageName: String, state: InstallState.UserConfirmationNeeded)
|
||||
|
||||
@@ -81,6 +81,10 @@ class MyAppsViewModel @Inject constructor(
|
||||
installedAppsLiveData.removeObserver(installedAppsObserver)
|
||||
}
|
||||
|
||||
fun updateAll() {
|
||||
updatesManager.updateAll()
|
||||
}
|
||||
|
||||
fun search(query: String) {
|
||||
searchQuery.value = query
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ fun UpdatableAppRow(
|
||||
@Composable
|
||||
fun UpdatableAppRowPreview() {
|
||||
val app1 = AppUpdateItem(
|
||||
repoId = 1,
|
||||
packageName = "A",
|
||||
name = "App Update 123",
|
||||
installedVersionName = "1.0.1",
|
||||
@@ -110,6 +111,7 @@ fun UpdatableAppRowPreview() {
|
||||
whatsNew = "This is new, all is new, nothing old.",
|
||||
)
|
||||
val app2 = AppUpdateItem(
|
||||
repoId = 2,
|
||||
packageName = "B",
|
||||
name = "App Update 456",
|
||||
installedVersionName = "1.0.1",
|
||||
|
||||
@@ -210,6 +210,7 @@ fun getAppListInfo(model: AppListModel) = object : AppListInfo {
|
||||
fun getMyAppsInfo(model: MyAppsModel): MyAppsInfo = object : MyAppsInfo {
|
||||
override val model = model
|
||||
override fun refresh() {}
|
||||
override fun updateAll() {}
|
||||
override fun changeSortOrder(sort: AppListSortOrder) {}
|
||||
override fun search(query: String) {}
|
||||
override fun confirmAppInstall(
|
||||
|
||||
@@ -5,20 +5,28 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.database.DbUpdateChecker
|
||||
import org.fdroid.database.FDroidDatabase
|
||||
import org.fdroid.download.getDownloadRequest
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.ui.apps.AppUpdateItem
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.min
|
||||
|
||||
@Singleton
|
||||
class UpdatesManager @Inject constructor(
|
||||
private val db: FDroidDatabase,
|
||||
private val dbUpdateChecker: DbUpdateChecker,
|
||||
@IoDispatcher private val coroutineScope: CoroutineScope,
|
||||
private val repoManager: RepoManager,
|
||||
private val appInstallManager: AppInstallManager,
|
||||
@param:IoDispatcher private val coroutineScope: CoroutineScope,
|
||||
) {
|
||||
private val log = KotlinLogging.logger { }
|
||||
|
||||
@@ -38,6 +46,7 @@ class UpdatesManager @Inject constructor(
|
||||
log.info { "Checking for updates..." }
|
||||
dbUpdateChecker.getUpdatableApps(onlyFromPreferredRepo = true).map { update ->
|
||||
AppUpdateItem(
|
||||
repoId = update.repoId,
|
||||
packageName = update.packageName,
|
||||
name = update.name ?: "Unknown app",
|
||||
installedVersionName = update.installedVersionName,
|
||||
@@ -55,4 +64,30 @@ class UpdatesManager @Inject constructor(
|
||||
_updates.value = updates
|
||||
_numUpdates.value = updates.size
|
||||
}
|
||||
|
||||
fun updateAll() {
|
||||
val concurrencyLimit = min(Runtime.getRuntime().availableProcessors(), 8)
|
||||
val semaphore = Semaphore(concurrencyLimit)
|
||||
updates.value?.forEach { update ->
|
||||
// launch a new co-routine for each app to update
|
||||
coroutineScope.launch {
|
||||
// suspend here until we get a permit from the semaphore (there's free workers)
|
||||
semaphore.withPermit {
|
||||
updateApp(update)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateApp(update: AppUpdateItem) {
|
||||
val app = db.getAppDao().getApp(update.repoId, update.packageName) ?: return
|
||||
appInstallManager.install(
|
||||
appMetadata = app.metadata,
|
||||
// we know this is true, because we set this above in loadUpdates()
|
||||
version = update.update as AppVersion,
|
||||
currentVersionName = update.installedVersionName,
|
||||
repo = repoManager.getRepository(update.repoId) ?: return,
|
||||
iconDownloadRequest = update.iconDownloadRequest
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user