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

Verified Commit 97c3d836 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor(updates): fetch system-apps updates concurrently

System app update checks were sequential — each app's GitLab API call waited for the previous one to complete. With multiple system apps this added seconds of unnecessary latency on the updates screen.

Run all fetches in parallel using coroutineScope + async so total wait time is bounded by the slowest single request rather than the sum of all requests.

Extract per-app logic into fetchSystemAppUpdate() returning null for skipped apps, and replace early-return chains with null-propagation to make the data flow explicit and avoid lambda-return ambiguity.
parent 2cf3614d
Loading
Loading
Loading
Loading
Loading
+57 −24
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ import foundation.e.apps.data.handleNetworkResult
import foundation.e.apps.data.install.pkg.AppLoungePackageManager
import foundation.e.apps.data.system.SystemInfoProvider
import foundation.e.apps.domain.model.install.Status
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -160,17 +162,27 @@ class SystemAppsUpdatesRepository @Inject constructor(
        sdkLevel: Int,
        device: String,
    ): Application? {
        val systemAppProject = systemAppProjectList.find { it.packageName == packageName } ?: return null
        val detailsUrl = getReleaseDetailsUrl(systemAppProject, releaseType) ?: return null

        val systemAppInfo = getSystemAppInfo(packageName, detailsUrl) ?: return null

        return if (isSystemAppBlocked(systemAppInfo, sdkLevel, device)) {
        val systemAppProject = systemAppProjectList.find { it.packageName == packageName }
        val detailsUrl = systemAppProject?.let {
            getReleaseDetailsUrl(it, releaseType)
        }
        val systemAppInfo = detailsUrl?.let {
            getSystemAppInfo(packageName, it)
        }
        val isBlocked = systemAppInfo?.let {
            isSystemAppBlocked(it, sdkLevel, device)
        } == true
        if (isBlocked) {
            Timber.e("Blocked system app: $packageName, details: $systemAppInfo")
            null
        } else {
        }

        val application = if (systemAppInfo != null && !isBlocked) {
            systemAppInfo.toApplication(context)
        } else {
            null
        }

        return application
    }

    private suspend fun getSystemAppInfo(
@@ -225,45 +237,66 @@ class SystemAppsUpdatesRepository @Inject constructor(
        }
    }

    suspend fun getSystemUpdates(): List<Application> {
    suspend fun getSystemUpdates(): List<Application> = coroutineScope {
        val updateList = mutableListOf<Application>()
        val releaseType = getSystemReleaseType()
        val sdkLevel = Build.VERSION.SDK_INT
        val device = getDevice()

        val updatableApps = getUpdatableSystemApps()
        updatableApps.forEach {
            if (!appLoungePackageManager.isInstalled(it)) {
                // Don't install for system apps which are removed (by root or otherwise)
                return@forEach

        val fetchTasks = updatableApps.map { packageName ->
            async {
                fetchSystemAppUpdate(packageName, releaseType, sdkLevel, device)
            }
        }

            val result = handleNetworkResult {
        fetchTasks.forEach { deferred ->
            deferred.await()?.let { updateList.add(it) }
        }

        return@coroutineScope updateList
    }

    private suspend fun fetchSystemAppUpdate(
        packageName: String,
        releaseType: OsReleaseType,
        sdkLevel: Int,
        device: String,
    ): Application? {
        val installed = appLoungePackageManager.isInstalled(packageName)

        val result = if (installed) {
            handleNetworkResult {
                getApplication(
                    it,
                    packageName,
                    releaseType,
                    sdkLevel,
                    device,
                )
            }
        } else {
            null
        }

        if (result != null) {
            if (!result.isSuccess()) {
                Timber.e("Failed to get system app info for $it - ${result.message}")
                return@forEach
                Timber.e("Failed to get system app info for $packageName - ${result.message}")
            }
        }

            val app: Application = result.data ?: return@forEach
            val appStatus = appLoungePackageManager.getPackageStatus(it, app.latest_version_code)
            if (appStatus != Status.UPDATABLE) return@forEach

        val app: Application? = if (result?.isSuccess() == true) result.data else null
        val appStatus = app?.let { appLoungePackageManager.getPackageStatus(packageName, it.latest_version_code) }
        val updateApp = if (app != null && appStatus == Status.UPDATABLE) {
            app.run {
                applicationDataManager.updateStatus(this)
                source = Source.SYSTEM_APP
                updateList.add(this)
            }
            app
        } else {
            null
        }

        return updateList
        return updateApp
    }
}