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

Verified Commit 200674eb authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

fix: fix progress rendering on Home, Search, and Updates screen

When multiple downloads are in flight, show progress in the list by validating progress against all download IDs for an app instead of a single
    downloadProgress.downloadId.

Previously, validation compared the last cursor row’s downloadId to the app’s ID. With parallel or multi-part downloads, most apps failed validation, so percentage text never updated; UI stayed on “Cancel/Installing” until completion.

Replaced single-ID equality check with set intersection: pull the app’s downloadIdMap keys and accept progress when any of those IDs appears in the aggregated progress maps.
parent 5ca87a9b
Loading
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -147,7 +147,6 @@
    <ID>ReturnCount:SystemAppsUpdatesRepository.kt$SystemAppsUpdatesRepository$private suspend fun getApplication( packageName: String, releaseType: OsReleaseType, sdkLevel: Int, device: String, ): Application?</ID>
    <ID>ReturnCount:SystemAppsUpdatesRepository.kt$SystemAppsUpdatesRepository$private suspend fun getReleaseDetailsUrl( systemAppProject: SystemAppProject, releaseType: OsReleaseType, ): String?</ID>
    <ID>ReturnCount:UpdatesManagerImpl.kt$UpdatesManagerImpl$private suspend fun calculateSignatureVersion(latestCleanapkApp: Application): String</ID>
    <ID>SpreadOperator:DownloadProgressLD.kt$DownloadProgressLD$(*downloadingIds.toLongArray())</ID>
    <ID>SpreadOperator:EglExtensionProvider.kt$EglExtensionProvider$(*`as`)</ID>
    <ID>SpreadOperator:NativeDeviceInfoProviderModule.kt$NativeDeviceInfoProviderModule$(*context.assets.locales)</ID>
    <ID>SpreadOperator:NativeDeviceInfoProviderModule.kt$NativeDeviceInfoProviderModule$(*systemSharedLibraryNames)</ID>
+10 −1
Original line number Diff line number Diff line
@@ -29,5 +29,14 @@ enum class Status {
    BLOCKED,
    INSTALLATION_ISSUE,
    AWAITING,
    PURCHASE_NEEDED
    PURCHASE_NEEDED;

    companion object {
        val downloadStatuses = setOf(
            QUEUED,
            AWAITING,
            DOWNLOADING,
            DOWNLOADED
        )
    }
}
+16 −2
Original line number Diff line number Diff line
@@ -143,8 +143,22 @@ class AppManagerWrapper @Inject constructor(
        application: Application,
        downloadProgress: DownloadProgress
    ): Boolean {
        val download = getFusedDownload(downloadProgress.downloadId)
        return download.id == application._id
        val download = getDownloadList().singleOrNull {
            it.id == application._id && it.packageName == application.package_name
        } ?: return false

        /*
         * We cannot rely on a single downloadId because DownloadProgress aggregates
         * multiple ids and downloadId is overwritten while iterating.
         * Validation instead checks whether any of the app's download ids are present
         * in the progress maps, which makes progress computation resilient to
         * concurrent multi-part downloads.
         */
        val appDownloadIds = download.downloadIdMap.keys
        return appDownloadIds.any { id ->
            downloadProgress.totalSizeBytes.containsKey(id) ||
                downloadProgress.bytesDownloadedSoFar.containsKey(id)
        }
    }

    fun handleRatingFormat(rating: Double): String? {
+43 −41
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.app.DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR
import android.app.DownloadManager.COLUMN_ID
import android.app.DownloadManager.COLUMN_STATUS
import android.app.DownloadManager.COLUMN_TOTAL_SIZE_BYTES
import android.database.Cursor
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
@@ -67,45 +68,47 @@ class DownloadProgressLD @Inject constructor(
                try {
                    findDownloadProgress(downloadingIds)
                } catch (e: Exception) {
                    Timber.e("downloading Ids: $downloadingIds ${e.localizedMessage}")
                    Timber.w("downloading Ids: $downloadingIds $e")
                }
                delay(20)
            }
        }
    }

    @Suppress("SpreadOperator") // DownloadManager#Query requires vararg ids; unavoidable spread.
    private fun findDownloadProgress(downloadingIds: MutableList<Long>) {
        val idsToQuery = downloadingIds.toLongArray()
        if (idsToQuery.isEmpty()) {
            Timber.i("No pending download ids to query; skipping progress refresh.")
            return
        }

        val cursor = downloadManager.query(downloadManagerQuery.setFilterById(*idsToQuery))
        if (cursor == null) {
            Timber.w("DownloadManager returned null cursor for ids $downloadingIds; posting cached progress to avoid stalling UI.")
        downloadManager.query(downloadManagerQuery.setFilterById(*idsToQuery))
            ?.use { safeCursor ->
                processCursor(safeCursor, downloadingIds)
            }
            ?: run {
                Timber.w("DownloadManager returned null cursor for ids $downloadingIds")
                postValue(downloadProgress)
            return
            }
    }

        cursor.use { safeCursor ->
            val foundIds = mutableSetOf<Long>()

            if (!safeCursor.moveToFirst()) {
                Timber.w("Download cursor empty for ids $downloadingIds; posting cached progress to stay responsive.")
    private fun processCursor(
        cursor: Cursor,
        downloadingIds: List<Long>
    ) {
        if (!cursor.moveToFirst()) {
            postValue(downloadProgress)
            return
        }

        do {
                val id = safeCursor.getLong(safeCursor.getColumnIndexOrThrow(COLUMN_ID))
                val status = safeCursor.getInt(safeCursor.getColumnIndexOrThrow(COLUMN_STATUS))
            val id = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID))
            val status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS))
            val totalSizeBytes =
                    safeCursor.getLong(safeCursor.getColumnIndexOrThrow(COLUMN_TOTAL_SIZE_BYTES))
                cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_TOTAL_SIZE_BYTES))
            val bytesDownloadedSoFar =
                    safeCursor.getLong(safeCursor.getColumnIndexOrThrow(COLUMN_BYTES_DOWNLOADED_SO_FAR))
                cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_BYTES_DOWNLOADED_SO_FAR))

                foundIds.add(id)
            downloadProgress.downloadId = id

            if (!downloadProgress.totalSizeBytes.containsKey(id) ||
@@ -126,11 +129,10 @@ class DownloadProgressLD @Inject constructor(
            if (downloadingIds.isEmpty()) {
                cancel()
            }
            } while (safeCursor.moveToNext())
        } while (cursor.moveToNext())

        postValue(downloadProgress)
    }
    }

    override fun onInactive() {
        super.onInactive()
+15 −13
Original line number Diff line number Diff line
@@ -303,7 +303,10 @@ class ApplicationListFragment :
        val adapter = recyclerView.adapter as ApplicationListRVAdapter
        viewLifecycleOwner.lifecycleScope.launch {
            adapter.currentList.forEach { fusedApp ->
                if (fusedApp.status == Status.DOWNLOADING) {
                if (fusedApp.status !in Status.downloadStatuses) {
                    return@forEach
                }

                val progress =
                    appProgressViewModel.calculateProgress(fusedApp, downloadProgress)
                if (progress == -1) {
@@ -319,7 +322,6 @@ class ApplicationListFragment :
            }
        }
    }
    }

    override fun onPause() {
        binding.shimmerLayout.stopShimmer()
Loading