Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a2faa78 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

Merge branch '4235-fix-all-updates-not-enqueueing' into 'v4.0'

fix(updates): fix update getting skipped for large number of available updates

See merge request !788
parents 769e51ab 0d5c3233
Loading
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
import com.aurora.gplayapi.data.models.PlayFile as AuroraFile

@Singleton
class AppManagerImpl @Inject constructor(
    @Named("cacheDir") private val cacheDir: String,
@@ -65,7 +66,6 @@ class AppManagerImpl @Inject constructor(
    @ApplicationContext private val context: Context,
    private val fDroidRepository: FDroidRepository
) : AppManager {

    @Inject
    lateinit var contentRatingDao: ContentRatingDao

@@ -88,8 +88,9 @@ class AppManagerImpl @Inject constructor(

    override suspend fun addDownload(appInstall: AppInstall): Boolean {
        val existingFusedDownload = getDownloadById(appInstall)
        val isInstallWorkRunning = isInstallWorkRunning(existingFusedDownload, appInstall)
        val canAddDownload = when {
            isInstallWorkRunning(existingFusedDownload, appInstall) -> false
            isInstallWorkRunning -> false
            // We don't want to add anything if it already exists without INSTALLATION_ISSUE
            existingFusedDownload != null && !isStatusEligibleToInstall(existingFusedDownload) -> false
            else -> true
+2 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import foundation.e.apps.domain.preferences.SessionRepository
import kotlinx.coroutines.CancellationException
import timber.log.Timber
import javax.inject.Inject

class InstallationEnqueuer @Inject constructor(
    private val preEnqueueChecker: PreEnqueueChecker,
    private val appManager: AppManager,
@@ -37,6 +38,7 @@ class InstallationEnqueuer @Inject constructor(
    private val playStoreAuthStore: PlayStoreAuthStore,
    private val appEventDispatcher: AppEventDispatcher,
) {

    suspend fun enqueue(
        appInstall: AppInstall,
        isAnUpdate: Boolean = false,
+2 −8
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ package foundation.e.apps.data.install.core.helper
import foundation.e.apps.data.application.AppManager
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.model.InstallationType
import timber.log.Timber
import javax.inject.Inject

class PreEnqueueChecker @Inject constructor(
@@ -30,22 +29,17 @@ class PreEnqueueChecker @Inject constructor(
    private val ageLimiter: AgeLimiter,
    private val devicePreconditions: DevicePreconditions,
) {

    suspend fun canEnqueue(appInstall: AppInstall, isAnUpdate: Boolean = false): Boolean {
        val hasUpdatedDownloadUrls = appInstall.type == InstallationType.PWA ||
            downloadUrlRefresher.updateDownloadUrls(appInstall, isAnUpdate)

        val isDownloadAdded = hasUpdatedDownloadUrls && addDownload(appInstall)
        val isAgeLimitAllowed = isDownloadAdded && ageLimiter.allow(appInstall)

        return isAgeLimitAllowed && devicePreconditions.canProceed(appInstall)
    }

    private suspend fun addDownload(appInstall: AppInstall): Boolean {
        val isDownloadAdded = appManager.addDownload(appInstall)
        if (!isDownloadAdded) {
            Timber.i("Update adding ABORTED! status")
        }

        return isDownloadAdded
        return appManager.addDownload(appInstall)
    }
}
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 e Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.install.updates

import foundation.e.apps.data.application.data.Application

data class ManualUpdateChainSnapshot(
    val chainId: String,
    val packages: List<Application>,
    val cursor: Int = 0,
)

fun buildManualUpdateChainSnapshot(
    chainId: String,
    applications: List<Application>,
): ManualUpdateChainSnapshot {
    return ManualUpdateChainSnapshot(
        chainId = chainId,
        packages = applications,
    )
}
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2026 e Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.install.updates

import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.gson.Gson
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.first
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

private const val MANUAL_UPDATE_CHAIN_PREFERENCE_DATA_STORE_NAME = "ManualUpdateChains"
val Context.manualUpdateChainDataStore by preferencesDataStore(
    MANUAL_UPDATE_CHAIN_PREFERENCE_DATA_STORE_NAME
)

@Singleton
class ManualUpdateChainStore @Inject constructor(
    @ApplicationContext
    private val context: Context,
    @Named("gsonCustomAdapter")
    private val gson: Gson,
) {
    companion object {
        private val ACTIVE_CHAIN_SNAPSHOT = stringPreferencesKey("active_chain_snapshot")
    }

    suspend fun readSnapshot(chainId: String): ManualUpdateChainSnapshot? {
        return context.manualUpdateChainDataStore.data.first()[ACTIVE_CHAIN_SNAPSHOT]
            ?.let { gson.fromJson(it, ManualUpdateChainSnapshot::class.java) }
            ?.takeIf { it.chainId == chainId }
    }

    suspend fun writeSnapshot(snapshot: ManualUpdateChainSnapshot) {
        context.manualUpdateChainDataStore.edit {
            it[ACTIVE_CHAIN_SNAPSHOT] = gson.toJson(snapshot)
        }
    }

    suspend fun advanceSnapshot(chainId: String, consumedCount: Int): ManualUpdateChainSnapshot? {
        val snapshot = readSnapshot(chainId) ?: return null
        val advancedSnapshot = snapshot.copy(
            cursor = (snapshot.cursor + consumedCount).coerceAtMost(snapshot.packages.size)
        )
        writeSnapshot(advancedSnapshot)
        return advancedSnapshot
    }

    suspend fun clearSnapshot(chainId: String) {
        context.manualUpdateChainDataStore.edit { preferences ->
            val storedSnapshot = preferences[ACTIVE_CHAIN_SNAPSHOT]
                ?.let { gson.fromJson(it, ManualUpdateChainSnapshot::class.java) }
                ?: return@edit
            if (storedSnapshot.chainId == chainId) {
                preferences.remove(ACTIVE_CHAIN_SNAPSHOT)
            }
        }
    }
}
Loading