Only show one app confirmation dialog at a time

This commit is contained in:
Torsten Grote
2025-12-30 10:49:03 -03:00
parent 9b3fbe0098
commit ddafdd6e8b
4 changed files with 22 additions and 11 deletions

View File

@@ -20,6 +20,7 @@ sealed class InstallState(val showProgress: Boolean) {
val version: AppVersion,
val repo: Repository,
override val sessionId: Int,
override val creationTimeMillis: Long = System.currentTimeMillis(),
override val intent: PendingIntent,
) : InstallConfirmationState() {
override val name: String = state.name
@@ -67,6 +68,7 @@ sealed class InstallState(val showProgress: Boolean) {
override val iconDownloadRequest: DownloadRequest?,
override val sessionId: Int,
override val intent: PendingIntent,
override val creationTimeMillis: Long,
val progress: Float,
) : InstallConfirmationState() {
constructor(
@@ -82,6 +84,7 @@ sealed class InstallState(val showProgress: Boolean) {
iconDownloadRequest = state.iconDownloadRequest,
sessionId = sessionId,
intent = intent,
creationTimeMillis = System.currentTimeMillis(),
progress = progress
)
}
@@ -127,5 +130,14 @@ sealed class InstallStateWithInfo(showProgress: Boolean) : InstallState(showProg
sealed class InstallConfirmationState() : InstallStateWithInfo(true) {
abstract val sessionId: Int
/**
* The epoch time in milliseconds when this state was created.
* This is used to get a stable ordering on apps that require user confirmation.
* The reason this is needed is that we can only show a single confirmation dialog at a time.
* If we show more than one, the second one gets silently swallowed by the system
* and we don't receive any feedback, so installation process of several apps gets stuck.
*/
abstract val creationTimeMillis: Long
abstract val intent: PendingIntent
}

View File

@@ -25,7 +25,6 @@ import androidx.compose.material3.TopAppBarDefaults.enterAlwaysScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -59,18 +58,11 @@ fun MyApps(
modifier: Modifier = Modifier,
) {
val myAppsModel = myAppsInfo.model
val appToConfirm by remember(myAppsInfo.model.installingApps) {
derivedStateOf {
myAppsInfo.model.installingApps.find { app ->
app.installState is InstallConfirmationState
}
}
}
// Ask user to confirm appToConfirm whenever it changes and we are in STARTED state.
// In tests, waiting for RESUME didn't work, because the LaunchedEffect ran before.
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(appToConfirm) {
val app = appToConfirm
LaunchedEffect(myAppsModel.appToConfirm) {
val app = myAppsModel.appToConfirm
if (app != null && lifecycleOwner.lifecycle.currentState.isAtLeast(STARTED)) {
val state = app.installState as InstallConfirmationState
myAppsInfo.confirmAppInstall(app.packageName, state)

View File

@@ -14,8 +14,9 @@ interface MyAppsInfo {
}
data class MyAppsModel(
val installingApps: List<InstallingAppItem>,
val appToConfirm: InstallingAppItem? = null,
val appUpdates: List<AppUpdateItem>? = null,
val installingApps: List<InstallingAppItem>,
val appsWithIssue: List<AppWithIssueItem>? = null,
val installedApps: List<InstalledAppItem>? = null,
val sortOrder: AppListSortOrder = AppListSortOrder.NAME,

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import org.fdroid.database.AppListSortOrder
import org.fdroid.download.NetworkState
import org.fdroid.install.InstallConfirmationState
import org.fdroid.install.InstallState
import org.fdroid.install.InstallStateWithInfo
import org.fdroid.ui.utils.normalize
@@ -87,6 +88,11 @@ fun MyAppsPresenter(
}
} ?: run { updateBytes = null }
return MyAppsModel(
appToConfirm = installingApps.filter {
it.installState is InstallConfirmationState
}.minByOrNull {
(it.installState as InstallConfirmationState).creationTimeMillis
},
installingApps = installingApps.sort(sortOrder),
appUpdates = updates?.sort(sortOrder),
appsWithIssue = withIssues?.sort(sortOrder),