mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-04-17 13:30:33 -04:00
Use more generic PackageVersion in AppInstallManager
This commit is contained in:
@@ -31,13 +31,13 @@ import mu.KotlinLogging
|
||||
import org.fdroid.LocaleChooser.getBestLocale
|
||||
import org.fdroid.NotificationManager
|
||||
import org.fdroid.database.AppMetadata
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.download.DownloaderFactory
|
||||
import org.fdroid.download.getUri
|
||||
import org.fdroid.history.HistoryManager
|
||||
import org.fdroid.history.InstallEvent
|
||||
import org.fdroid.history.UninstallEvent
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
|
||||
@Singleton
|
||||
@@ -81,18 +81,17 @@ constructor(
|
||||
else -> null
|
||||
}
|
||||
// track app state for in progress apps
|
||||
val appState =
|
||||
appStateCategory?.let {
|
||||
// all states that get a category above must be InstallStateWithInfo
|
||||
state as InstallStateWithInfo
|
||||
AppState(
|
||||
packageName = packageName,
|
||||
category = it,
|
||||
name = state.name,
|
||||
installVersionName = state.versionName,
|
||||
currentVersionName = state.currentVersionName,
|
||||
)
|
||||
}
|
||||
val appState = appStateCategory?.let {
|
||||
// all states that get a category above must be InstallStateWithInfo
|
||||
state as InstallStateWithInfo
|
||||
AppState(
|
||||
packageName = packageName,
|
||||
category = it,
|
||||
name = state.name,
|
||||
installVersionName = state.versionName,
|
||||
currentVersionName = state.currentVersionName,
|
||||
)
|
||||
}
|
||||
if (appState != null) appStates.add(appState)
|
||||
}
|
||||
return InstallNotificationState(
|
||||
@@ -116,19 +115,20 @@ constructor(
|
||||
*/
|
||||
@UiThread
|
||||
suspend fun install(
|
||||
packageName: String,
|
||||
appMetadata: AppMetadata?,
|
||||
version: AppVersion,
|
||||
version: PackageVersion,
|
||||
currentVersionName: String?,
|
||||
repo: Repository?,
|
||||
iconModel: Any?,
|
||||
canAskPreApprovalNow: Boolean,
|
||||
): InstallState {
|
||||
if (appMetadata == null || repo == null) {
|
||||
log.error { "Can't install app without metadata for ${version.packageName}" }
|
||||
log.error { "Can't install app without metadata for $packageName" }
|
||||
val error =
|
||||
InstallState.Error(
|
||||
msg = "App ${version.packageName} no longer in DB.",
|
||||
name = version.packageName,
|
||||
msg = "App $packageName no longer in DB.",
|
||||
name = packageName,
|
||||
versionName = version.versionName,
|
||||
currentVersionName = currentVersionName,
|
||||
lastUpdated = version.added,
|
||||
@@ -136,27 +136,25 @@ constructor(
|
||||
)
|
||||
// Write the terminal state so any prior Waiting state is cleared and the
|
||||
// service stop logic in onStatesUpdated() has a chance to run.
|
||||
updateAppState(version.packageName, error)
|
||||
updateAppState(packageName, error)
|
||||
return error
|
||||
}
|
||||
val packageName = appMetadata.packageName
|
||||
val currentState = apps.value[packageName]
|
||||
if (currentState?.showProgress == true && currentState !is InstallState.Waiting) {
|
||||
log.warn { "Attempted to install $packageName with install in progress: $currentState" }
|
||||
return currentState
|
||||
}
|
||||
currentCoroutineContext().ensureActive()
|
||||
val job =
|
||||
scope.async {
|
||||
startInstall(
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = currentVersionName,
|
||||
repo = repo,
|
||||
iconModel = iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
)
|
||||
}
|
||||
val job = scope.async {
|
||||
startInstall(
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = currentVersionName,
|
||||
repo = repo,
|
||||
iconModel = iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
)
|
||||
}
|
||||
// keep track of this job, in case we want to cancel it
|
||||
return trackJob(packageName, job)
|
||||
}
|
||||
@@ -212,7 +210,7 @@ constructor(
|
||||
@WorkerThread
|
||||
private suspend fun startInstall(
|
||||
appMetadata: AppMetadata,
|
||||
version: AppVersion,
|
||||
version: PackageVersion,
|
||||
currentVersionName: String?,
|
||||
repo: Repository,
|
||||
iconModel: Any?,
|
||||
@@ -256,7 +254,14 @@ constructor(
|
||||
)
|
||||
}
|
||||
as InstallState.PreApproved
|
||||
downloadAndInstall(newState, version, currentVersionName, repo, iconModel)
|
||||
downloadAndInstall(
|
||||
state = newState,
|
||||
packageName = appMetadata.packageName,
|
||||
version = version,
|
||||
currentVersionName = currentVersionName,
|
||||
repo = repo,
|
||||
iconModel = iconModel,
|
||||
)
|
||||
}
|
||||
is PreApprovalResult.UserConfirmationRequired -> {
|
||||
InstallState.PreApprovalConfirmationNeeded(
|
||||
@@ -295,16 +300,16 @@ constructor(
|
||||
updateAppState(packageName, result)
|
||||
return if (result is InstallState.PreApproved) {
|
||||
// move us off the UiThread, so we can download/install this app now
|
||||
val job =
|
||||
scope.async {
|
||||
downloadAndInstall(
|
||||
state = result,
|
||||
version = installState.version,
|
||||
currentVersionName = installState.currentVersionName,
|
||||
repo = installState.repo,
|
||||
iconModel = installState.iconModel,
|
||||
)
|
||||
}
|
||||
val job = scope.async {
|
||||
downloadAndInstall(
|
||||
state = result,
|
||||
packageName = packageName,
|
||||
version = installState.version,
|
||||
currentVersionName = installState.currentVersionName,
|
||||
repo = installState.repo,
|
||||
iconModel = installState.iconModel,
|
||||
)
|
||||
}
|
||||
// suspend/wait for this job and track it in case we want to cancel it
|
||||
trackJob(packageName, job)
|
||||
} else result
|
||||
@@ -313,7 +318,8 @@ constructor(
|
||||
@WorkerThread
|
||||
private suspend fun downloadAndInstall(
|
||||
state: InstallState.PreApproved,
|
||||
version: AppVersion,
|
||||
packageName: String,
|
||||
version: PackageVersion,
|
||||
currentVersionName: String?,
|
||||
repo: Repository,
|
||||
iconModel: Any?,
|
||||
@@ -328,7 +334,7 @@ constructor(
|
||||
val now = System.currentTimeMillis()
|
||||
downloader.setListener { bytesRead, totalBytes ->
|
||||
coroutineContext.ensureActive()
|
||||
updateAndGetAppState(version.packageName) {
|
||||
updateAndGetAppState(packageName) {
|
||||
InstallState.Downloading(
|
||||
name = it.name,
|
||||
versionName = it.versionName,
|
||||
@@ -359,7 +365,7 @@ constructor(
|
||||
}
|
||||
currentCoroutineContext().ensureActive()
|
||||
val newState =
|
||||
updateAndGetAppState(version.packageName) {
|
||||
updateAndGetAppState(packageName) {
|
||||
InstallState.Installing(
|
||||
name = it.name,
|
||||
versionName = it.versionName,
|
||||
@@ -368,12 +374,12 @@ constructor(
|
||||
iconModel = it.iconModel,
|
||||
)
|
||||
}
|
||||
val result = sessionInstallManager.install(sessionId, version.packageName, newState, file)
|
||||
val result = sessionInstallManager.install(sessionId, packageName, newState, file)
|
||||
log.debug { "Install result: $result" }
|
||||
return if (result is InstallState.PreApproved && result.result is PreApprovalResult.Error) {
|
||||
// if pre-approval failed (e.g. due to app label mismatch),
|
||||
// then try to install again, this time not using the pre-approved session
|
||||
sessionInstallManager.install(null, version.packageName, newState, file)
|
||||
sessionInstallManager.install(null, packageName, newState, file)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.fdroid.install
|
||||
|
||||
import android.app.PendingIntent
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
|
||||
sealed class InstallState(val showProgress: Boolean) {
|
||||
data object Unknown : InstallState(false)
|
||||
@@ -30,7 +30,7 @@ sealed class InstallState(val showProgress: Boolean) {
|
||||
|
||||
data class PreApprovalConfirmationNeeded(
|
||||
private val state: InstallStateWithInfo,
|
||||
val version: AppVersion,
|
||||
val version: PackageVersion,
|
||||
val repo: Repository,
|
||||
override val sessionId: Int,
|
||||
override val creationTimeMillis: Long = System.currentTimeMillis(),
|
||||
|
||||
@@ -34,7 +34,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.LocaleChooser.getBestLocale
|
||||
import org.fdroid.database.AppMetadata
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
import org.fdroid.ui.utils.isAppInForeground
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
|
||||
@@ -93,13 +93,13 @@ constructor(
|
||||
app: AppMetadata,
|
||||
iconGetter: suspend () -> Bitmap?,
|
||||
isUpdate: Boolean,
|
||||
version: AppVersion,
|
||||
version: PackageVersion,
|
||||
canRequestUserConfirmationNow: Boolean,
|
||||
): PreApprovalResult {
|
||||
return if (!context.isAppInForeground()) {
|
||||
log.info { "App not in foreground, pre-approval for ${app.packageName} not supported." }
|
||||
PreApprovalResult.NotSupported
|
||||
} else if (isUpdate && canDoAutoUpdate(version)) {
|
||||
} else if (isUpdate && canDoAutoUpdate(app.packageName, version)) {
|
||||
// should not be needed, so we say not supported
|
||||
log.info { "Can do auto-update pre-approval for ${app.packageName} not needed." }
|
||||
PreApprovalResult.NotSupported
|
||||
@@ -368,17 +368,17 @@ constructor(
|
||||
return params
|
||||
}
|
||||
|
||||
private fun canDoAutoUpdate(version: AppVersion): Boolean {
|
||||
private fun canDoAutoUpdate(packageName: String, version: PackageVersion): Boolean {
|
||||
if (SDK_INT < 31) return false
|
||||
val targetSdkVersion = version.manifest.targetSdkVersion ?: return false
|
||||
val targetSdkVersion = version.packageManifest.targetSdkVersion ?: return false
|
||||
// docs:
|
||||
// https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
|
||||
return if (isAutoUpdateSupported(targetSdkVersion)) {
|
||||
val ourPackageName = context.packageName
|
||||
if (ourPackageName == version.packageName) return true
|
||||
if (ourPackageName == packageName) return true
|
||||
val sourceInfo =
|
||||
try {
|
||||
context.packageManager.getInstallSourceInfo(version.packageName)
|
||||
context.packageManager.getInstallSourceInfo(packageName)
|
||||
} catch (e: Exception) {
|
||||
log.error(e) { "Could not get package info: " }
|
||||
return false
|
||||
|
||||
@@ -83,12 +83,13 @@ constructor(
|
||||
// TODO we could do better error handling, e.g. when metadata or repo are null
|
||||
// or install state is an error, also maybe show a Toast to user on error
|
||||
appInstallManager.install(
|
||||
appMetadata = db.getAppDao().getApp(version.repoId, packageName)?.metadata,
|
||||
version = version,
|
||||
currentVersionName = null,
|
||||
repo = repoManager.getRepository(version.repoId),
|
||||
iconModel = null,
|
||||
canAskPreApprovalNow = true,
|
||||
packageName = packageName,
|
||||
appMetadata = db.getAppDao().getApp(version.repoId, packageName)?.metadata,
|
||||
version = version,
|
||||
currentVersionName = null,
|
||||
repo = repoManager.getRepository(version.repoId),
|
||||
iconModel = null,
|
||||
canAskPreApprovalNow = true,
|
||||
)
|
||||
Result.success()
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ data class AppDetailsItem(
|
||||
*/
|
||||
val installedSigner: String? = null,
|
||||
/** The currently suggested version for installation. */
|
||||
val suggestedVersion: AppVersion? = null,
|
||||
val suggestedVersion: PackageVersion? = null,
|
||||
/**
|
||||
* Similar to [suggestedVersion], but doesn't obey [appPrefs] for ignoring versions. This is
|
||||
* useful for (un-)ignoring this version.
|
||||
@@ -225,8 +225,8 @@ data class AppDetailsItem(
|
||||
val bitcoinUri = app.bitcoin?.let { "bitcoin:$it" }
|
||||
}
|
||||
|
||||
class AppDetailsActions(
|
||||
val installAction: (AppMetadata, AppVersion, Any?) -> Unit,
|
||||
data class AppDetailsActions(
|
||||
val installAction: (AppMetadata, PackageVersion, Any?) -> Unit,
|
||||
val requestUserConfirmation: (InstallState.UserConfirmationNeeded) -> Unit,
|
||||
/**
|
||||
* A workaround for Android 10, 11, 12 and 13 where tapping outside the confirmation dialog
|
||||
|
||||
@@ -31,13 +31,13 @@ import kotlinx.coroutines.withContext
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.UpdateChecker
|
||||
import org.fdroid.database.AppMetadata
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.database.FDroidDatabase
|
||||
import org.fdroid.download.DownloadRequest
|
||||
import org.fdroid.download.NetworkMonitor
|
||||
import org.fdroid.getCacheKey
|
||||
import org.fdroid.index.RELEASE_CHANNEL_BETA
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.install.InstallState
|
||||
import org.fdroid.repo.RepoPreLoader
|
||||
@@ -116,14 +116,15 @@ constructor(
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun install(appMetadata: AppMetadata, version: AppVersion, iconModel: Any?) {
|
||||
fun install(appMetadata: AppMetadata, version: PackageVersion, iconModel: Any?) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
val result =
|
||||
appInstallManager.install(
|
||||
packageName = packageName,
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = packageInfoFlow.value?.packageInfo?.versionName,
|
||||
repo = repoManager.getRepository(version.repoId) ?: return@launch, // TODO
|
||||
currentVersionName = packageInfoFlow.value?.packageInfo?.versionName, // TODO
|
||||
repo = repoManager.getRepository(appMetadata.repoId) ?: return@launch,
|
||||
iconModel = iconModel,
|
||||
canAskPreApprovalNow = true,
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.fdroid.database.Repository
|
||||
import org.fdroid.download.Mirror
|
||||
import org.fdroid.download.NetworkState
|
||||
import org.fdroid.index.IndexFormatVersion
|
||||
import org.fdroid.index.v2.FileV1
|
||||
import org.fdroid.index.v2.PackageManifest
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
import org.fdroid.index.v2.SignerV2
|
||||
@@ -81,6 +82,7 @@ val testVersion1 =
|
||||
object : PackageVersion {
|
||||
override val versionCode: Long = 42
|
||||
override val versionName: String = "42.23.0-alpha1337-33d2252b90"
|
||||
override val file: FileV1 = FileV1("foo/bar", "abcd", 23)
|
||||
override val added: Long = System.currentTimeMillis() - DAYS.toMillis(4)
|
||||
override val size: Long = 1024 * 1024 * 42
|
||||
override val signer: SignerV2 =
|
||||
@@ -100,6 +102,7 @@ val testVersion2 =
|
||||
object : PackageVersion {
|
||||
override val versionCode: Long = 23
|
||||
override val versionName: String = "23.42.0"
|
||||
override val file: FileV1 = FileV1("foo/bar", "abcd", 23)
|
||||
override val added: Long = System.currentTimeMillis() - DAYS.toMillis(4)
|
||||
override val size: Long = 1024 * 1024 * 23
|
||||
override val signer: SignerV2 =
|
||||
@@ -252,6 +255,7 @@ fun getPreviewVersion(versionName: String, size: Long? = null) =
|
||||
object : PackageVersion {
|
||||
override val versionCode: Long = 23
|
||||
override val versionName: String = versionName
|
||||
override val file: FileV1 = FileV1("foo/bar", "abcd", 23)
|
||||
override val added: Long = System.currentTimeMillis() - DAYS.toMillis(3)
|
||||
override val size: Long? = size
|
||||
override val signer: SignerV2? = null
|
||||
|
||||
@@ -114,12 +114,13 @@ constructor(
|
||||
private suspend fun updateApp(update: AppUpdateItem, canAskPreApprovalNow: Boolean) {
|
||||
val app = db.getAppDao().getApp(update.repoId, update.packageName)
|
||||
appInstallManager.install(
|
||||
appMetadata = app?.metadata,
|
||||
version = update.update as AppVersion,
|
||||
currentVersionName = update.installedVersionName,
|
||||
repo = repoManager.getRepository(update.repoId),
|
||||
iconModel = update.iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
packageName = update.packageName,
|
||||
appMetadata = app?.metadata,
|
||||
version = update.update as AppVersion,
|
||||
currentVersionName = update.installedVersionName,
|
||||
repo = repoManager.getRepository(update.repoId),
|
||||
iconModel = update.iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,12 +624,13 @@ internal class AppInstallManagerTest {
|
||||
val installJob =
|
||||
installScope.async {
|
||||
appInstallManager.install(
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = installedVersionName,
|
||||
repo = repo,
|
||||
iconModel = null,
|
||||
canAskPreApprovalNow = false,
|
||||
packageName = packageName,
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = installedVersionName,
|
||||
repo = repo,
|
||||
iconModel = null,
|
||||
canAskPreApprovalNow = false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -648,12 +649,13 @@ internal class AppInstallManagerTest {
|
||||
canAskPreApprovalNow: Boolean = false,
|
||||
): InstallState {
|
||||
return appInstallManager.install(
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = currentVersionName,
|
||||
repo = repo,
|
||||
iconModel = iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
packageName = packageName,
|
||||
appMetadata = appMetadata,
|
||||
version = version,
|
||||
currentVersionName = currentVersionName,
|
||||
repo = repo,
|
||||
iconModel = iconModel,
|
||||
canAskPreApprovalNow = canAskPreApprovalNow,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.fdroid.database.AppMetadata
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.index.v2.PackageVersion
|
||||
import org.fdroid.ui.utils.isAppInForeground
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@@ -73,7 +74,7 @@ internal class SessionInstallManagerTest {
|
||||
name = mapOf("en-US" to "Example App"),
|
||||
isCompatible = true,
|
||||
)
|
||||
private val appVersion: AppVersion = mockk(relaxed = true)
|
||||
private val appVersion: PackageVersion = mockk(relaxed = true)
|
||||
|
||||
private val installingState =
|
||||
InstallState.Installing(
|
||||
@@ -213,14 +214,13 @@ internal class SessionInstallManagerTest {
|
||||
)
|
||||
assertIs<PreApprovalResult.NotSupported>(notForegroundResult)
|
||||
|
||||
// in foreground + update that can auto-update -> NotSupported
|
||||
// in foreground + update that can auto-update, we say NotSupported, because not needed
|
||||
every { context.isAppInForeground() } returns true
|
||||
every { appVersion.packageName } returns packageName
|
||||
every { appVersion.manifest.targetSdkVersion } returns 34
|
||||
every { appVersion.packageManifest.targetSdkVersion } returns 42
|
||||
val sourceInfo: InstallSourceInfo = mockk(relaxed = true)
|
||||
every { sourceInfo.installingPackageName } returns context.packageName
|
||||
if (SDK_INT >= 34) {
|
||||
every { sourceInfo.updateOwnerPackageName } returns null
|
||||
every { sourceInfo.updateOwnerPackageName } returns context.packageName
|
||||
}
|
||||
every { packageManager.getInstallSourceInfo(packageName) } returns sourceInfo
|
||||
|
||||
@@ -237,8 +237,7 @@ internal class SessionInstallManagerTest {
|
||||
// isUpdate = true but not our package, and we are not the update owner -> NotSupported,
|
||||
// because canDoAutoUpdate() returns false when getInstallSourceInfo() throws
|
||||
every { context.isAppInForeground() } returns true
|
||||
every { appVersion.packageName } returns packageName
|
||||
every { appVersion.manifest.targetSdkVersion } returns 34
|
||||
every { appVersion.packageManifest.targetSdkVersion } returns 34
|
||||
every { packageManager.getInstallSourceInfo(packageName) } throws SecurityException("nope")
|
||||
|
||||
val installSourceError =
|
||||
|
||||
@@ -62,23 +62,25 @@ internal class UpdateInstallerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateAll returns early when updates list is empty`() =
|
||||
testScope.runTest {
|
||||
val installer = createUpdateInstaller()
|
||||
fun `updateAll returns early when updates list is empty`() = testScope.runTest {
|
||||
val installer = createUpdateInstaller()
|
||||
|
||||
installer.updateAll(emptyList(), canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
installer.updateAll(emptyList(), canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify(exactly = 0) { appInstallManager.install(any(), any(), any(), any(), any(), any()) }
|
||||
verify(exactly = 0) { appInstallManager.setWaitingState(any(), any(), any(), any(), any()) }
|
||||
coVerify(exactly = 0) {
|
||||
appInstallManager.install(any(), any(), any(), any(), any(), any(), any())
|
||||
}
|
||||
verify(exactly = 0) { appInstallManager.setWaitingState(any(), any(), any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateAll preApproval is true for single app and forced false for multiple`() =
|
||||
testScope.runTest {
|
||||
every { repoManager.getRepository(1L) } returns makeRepository()
|
||||
coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns
|
||||
mockk()
|
||||
coEvery {
|
||||
appInstallManager.install(any(), any(), any(), any(), any(), any(), any())
|
||||
} returns mockk()
|
||||
|
||||
// single app + canAsk=true -> canAskPreApprovalNow=true
|
||||
val ver = makeAppVersion(versionName = "5.0", versionCode = 50)
|
||||
@@ -98,6 +100,7 @@ internal class UpdateInstallerTest {
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
appInstallManager.install(
|
||||
packageName = "com.example.app",
|
||||
appMetadata =
|
||||
match { metadata ->
|
||||
metadata.packageName == "com.example.app" && metadata.repoId == 1L
|
||||
@@ -121,6 +124,7 @@ internal class UpdateInstallerTest {
|
||||
|
||||
coVerify(exactly = 2) {
|
||||
appInstallManager.install(
|
||||
packageName = any(),
|
||||
appMetadata = any(),
|
||||
version = any(),
|
||||
currentVersionName = any(),
|
||||
@@ -132,70 +136,72 @@ internal class UpdateInstallerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateAll updates own app last and sets waiting state`() =
|
||||
testScope.runTest {
|
||||
val otherPkg = "com.example.other"
|
||||
every { repoManager.getRepository(1L) } returns makeRepository()
|
||||
coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns
|
||||
mockk()
|
||||
fun `updateAll updates own app last and sets waiting state`() = testScope.runTest {
|
||||
val otherPkg = "com.example.other"
|
||||
every { repoManager.getRepository(1L) } returns makeRepository()
|
||||
coEvery {
|
||||
appInstallManager.install(any(), any(), any(), any(), any(), any(), any())
|
||||
} returns mockk()
|
||||
|
||||
val ownVersion =
|
||||
makeAppVersion(packageName = OWN_PACKAGE_NAME, versionName = "3.0", added = 9999L)
|
||||
every { appDao.getApp(1L, OWN_PACKAGE_NAME) } returns makeApp(packageName = OWN_PACKAGE_NAME)
|
||||
every { appDao.getApp(1L, otherPkg) } returns makeApp(packageName = otherPkg)
|
||||
every {
|
||||
appInstallManager.setWaitingState(
|
||||
val ownVersion =
|
||||
makeAppVersion(packageName = OWN_PACKAGE_NAME, versionName = "3.0", added = 9999L)
|
||||
every { appDao.getApp(1L, OWN_PACKAGE_NAME) } returns makeApp(packageName = OWN_PACKAGE_NAME)
|
||||
every { appDao.getApp(1L, otherPkg) } returns makeApp(packageName = otherPkg)
|
||||
every {
|
||||
appInstallManager.setWaitingState(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
name = any(),
|
||||
versionName = any(),
|
||||
currentVersionName = any(),
|
||||
lastUpdated = any(),
|
||||
)
|
||||
} just runs
|
||||
|
||||
val updates =
|
||||
listOf(
|
||||
makeAppUpdateItem(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
name = any(),
|
||||
versionName = any(),
|
||||
currentVersionName = any(),
|
||||
lastUpdated = any(),
|
||||
)
|
||||
} just runs
|
||||
installedVersionName = "2.0",
|
||||
update = ownVersion,
|
||||
),
|
||||
makeAppUpdateItem(packageName = otherPkg),
|
||||
)
|
||||
|
||||
val updates =
|
||||
listOf(
|
||||
makeAppUpdateItem(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
installedVersionName = "2.0",
|
||||
update = ownVersion,
|
||||
),
|
||||
makeAppUpdateItem(packageName = otherPkg),
|
||||
)
|
||||
createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
|
||||
createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify(exactly = 1) {
|
||||
appInstallManager.setWaitingState(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
name = any(),
|
||||
versionName = "3.0",
|
||||
currentVersionName = "2.0",
|
||||
lastUpdated = 9999L,
|
||||
)
|
||||
}
|
||||
|
||||
coVerifyOrder {
|
||||
appInstallManager.install(
|
||||
appMetadata = match { metadata -> metadata.packageName == otherPkg },
|
||||
version = any(),
|
||||
currentVersionName = any(),
|
||||
repo = any(),
|
||||
iconModel = any(),
|
||||
canAskPreApprovalNow = any(),
|
||||
)
|
||||
appInstallManager.install(
|
||||
appMetadata = match { metadata -> metadata.packageName == OWN_PACKAGE_NAME },
|
||||
version = any(),
|
||||
currentVersionName = any(),
|
||||
repo = any(),
|
||||
iconModel = any(),
|
||||
canAskPreApprovalNow = any(),
|
||||
)
|
||||
}
|
||||
verify(exactly = 1) {
|
||||
appInstallManager.setWaitingState(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
name = any(),
|
||||
versionName = "3.0",
|
||||
currentVersionName = "2.0",
|
||||
lastUpdated = 9999L,
|
||||
)
|
||||
}
|
||||
|
||||
coVerifyOrder {
|
||||
appInstallManager.install(
|
||||
packageName = otherPkg,
|
||||
appMetadata = match { metadata -> metadata.packageName == otherPkg },
|
||||
version = any(),
|
||||
currentVersionName = any(),
|
||||
repo = any(),
|
||||
iconModel = any(),
|
||||
canAskPreApprovalNow = any(),
|
||||
)
|
||||
appInstallManager.install(
|
||||
packageName = OWN_PACKAGE_NAME,
|
||||
appMetadata = match { metadata -> metadata.packageName == OWN_PACKAGE_NAME },
|
||||
version = any(),
|
||||
currentVersionName = any(),
|
||||
repo = any(),
|
||||
iconModel = any(),
|
||||
canAskPreApprovalNow = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateApp continues with null values if app missing in DB or repo missing`() =
|
||||
testScope.runTest {
|
||||
@@ -208,22 +214,27 @@ internal class UpdateInstallerTest {
|
||||
update = ver,
|
||||
)
|
||||
)
|
||||
coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns
|
||||
mockk()
|
||||
coEvery {
|
||||
appInstallManager.install(any(), any(), any(), any(), any(), any(), any())
|
||||
} returns mockk()
|
||||
|
||||
// repo is null
|
||||
every { repoManager.getRepository(1L) } returns null
|
||||
every { appDao.getApp(1L, "com.example.app") } returns makeApp()
|
||||
createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
coVerify(exactly = 1) { appInstallManager.install(any(), any(), any(), null, any(), any()) }
|
||||
coVerify(exactly = 1) {
|
||||
appInstallManager.install(any(), any(), any(), any(), null, any(), any())
|
||||
}
|
||||
|
||||
// app is null
|
||||
every { repoManager.getRepository(1L) } returns makeRepository()
|
||||
every { appDao.getApp(1L, "com.example.app") } returns null
|
||||
createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false)
|
||||
advanceUntilIdle()
|
||||
coVerify(exactly = 1) { appInstallManager.install(null, any(), any(), any(), any(), any()) }
|
||||
coVerify(exactly = 1) {
|
||||
appInstallManager.install(any(), null, any(), any(), any(), any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeAppUpdateItem(
|
||||
|
||||
Reference in New Issue
Block a user