Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database bug fix #5902

Merged
merged 3 commits into from
Oct 27, 2024
Merged

Database bug fix #5902

merged 3 commits into from
Oct 27, 2024

Conversation

rohit9625
Copy link
Contributor

Description (required)

Fixes #5898

What changes did you make and why?
I added suspend keywords to UploadStatusDao to make the functions thread-safe. Hence, calling these functions from any thread won't block the thread until execution is complete. I also replaced the use of MainScope() with the CoroutineScope for DB operations.

Tests performed (required)

Tested prodDebug on Samsung A14 with API level 34.

Screenshots (for UI changes only)
None

@nicolas-raoul
Copy link
Member

With this branch I always get this crash when going to Custom Picker a second time after using it once to upload 5 pictures:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Expected: main Calling: DefaultDispatcher-worker-6
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:11128)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:2523)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3146)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3146)
at android.view.View.requestLayout(View.java:28098)
at androidx.recyclerview.widget.RecyclerView.requestLayout(RecyclerView.java:4418)
at android.view.View.requestLayout(View.java:28098)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3146)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.requestLayout(View.java:28098)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3146)
at android.view.View.requestLayout(View.java:28098)
at android.view.View.setFlags(View.java:18393)
at android.view.View.setVisibility(View.java:13558)
at androidx.constraintlayout.widget.Group.setVisibility(Group.java:68)
at fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter$ImageViewHolder.itemUploaded(ImageAdapter.kt:521)
at fr.free.nrw.commons.customselector.ui.selector.ImageLoader$queryAndSetView$1.invokeSuspend(ImageLoader.kt:215)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@3cf687c, Dispatchers.IO]

Any idea what might be happening?

@nicolas-raoul
Copy link
Member

nicolas-raoul commented Oct 27, 2024

Also, still getting these, though less often:

Screenshot_20241027-222804.png

Copy link
Member

@nicolas-raoul nicolas-raoul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still seeing the above-mentioned issues but that branch is clearly an improvement.

@nicolas-raoul nicolas-raoul merged commit cdc4f89 into commons-app:main Oct 27, 2024
1 check passed
@rohit9625
Copy link
Contributor Author

rohit9625 commented Oct 27, 2024

Why did you merge this @nicolas-raoul? This issue needs to be solved in this PR. Right?
Should I create a separate issue for that?

@rohit9625
Copy link
Contributor Author

In this block of code, the UI is also being updated which should be handled on the Main Thread. I'm sorry, I didn't notice this when replacing the Main Thread of IO Thread.

scope.launch {
                var result: Result = Result.NOTFOUND

                if (mapHolderImage[holder] != image) {
                    return@launch
                }

                val imageSHA1: String =
                    when (mapImageSHA1[image.uri] != null) {
                        true -> mapImageSHA1[image.uri]!!
                        else ->
                            CustomSelectorUtils.getImageSHA1(
                                image.uri,
                                ioDispatcher,
                                fileUtilsWrapper,
                                context.contentResolver,
                            )
                    }
                mapImageSHA1[image.uri] = imageSHA1

                if (imageSHA1.isEmpty()) {
                    return@launch
                }
                val uploadedStatus = getFromUploaded(imageSHA1)

                val sha1 =
                    uploadedStatus?.let {
                        result = getResultFromUploadedStatus(uploadedStatus)
                        uploadedStatus.modifiedImageSHA1
                    } ?: run {
                        if (mapHolderImage[holder] == image) {
                            getSHA1(image, defaultDispatcher)
                        } else {
                            ""
                        }
                    }

                if (mapHolderImage[holder] != image) {
                    return@launch
                }

                val existsInNotForUploadTable = notForUploadStatusDao.find(imageSHA1)

                if (result in arrayOf(Result.NOTFOUND, Result.INVALID) && sha1.isNotEmpty()) {
                    when {
                        mapResult[imageSHA1] == null -> {
                            // Query original image.
                            result =
                                checkWhetherFileExistsOnCommonsUsingSHA1(
                                    imageSHA1,
                                    ioDispatcher,
                                    mediaClient,
                                )
                            when (result) {
                                is Result.TRUE -> {
                                    mapResult[imageSHA1] = Result.TRUE
                                }
                                is Result.ERROR -> {
                                    mapResult[imageSHA1] = Result.ERROR
                                }
                                is Result.FALSE -> {
                                    mapResult[imageSHA1] = Result.FALSE
                                }
                                is Result.INVALID -> {
                                    mapResult[imageSHA1] = Result.INVALID
                                }
                                is Result.NOTFOUND -> {
                                    mapResult[imageSHA1] = Result.NOTFOUND
                                }
                            }
                        }
                        else -> {
                            result = mapResult[imageSHA1]!!
                        }
                    }
                    if (result is Result.TRUE) {
                        // Original image found.
                        insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false)
                    } else {
                        when {
                            mapResult[sha1] == null -> {
                                // Original image not found, query modified image.
                                result =
                                    checkWhetherFileExistsOnCommonsUsingSHA1(
                                        sha1,
                                        ioDispatcher,
                                        mediaClient,
                                    )
                                when (result) {
                                    is Result.TRUE -> {
                                        mapResult[sha1] = Result.TRUE
                                    }
                                    is Result.ERROR -> {
                                        mapResult[sha1] = Result.ERROR
                                    }
                                    is Result.FALSE -> {
                                        mapResult[sha1] = Result.FALSE
                                    }
                                    is Result.INVALID -> {
                                        mapResult[sha1] = Result.INVALID
                                    }
                                    is Result.NOTFOUND -> {
                                        mapResult[sha1] = Result.NOTFOUND
                                    }
                                }
                            }
                            else -> {
                                result = mapResult[sha1]!!
                            }
                        }
                        if (result != Result.ERROR) {
                            insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE)
                        }
                    }
                }

                val sharedPreferences: SharedPreferences =
                    context
                        .getSharedPreferences(ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, 0)
                val showAlreadyActionedImages =
                    sharedPreferences.getBoolean(
                        ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY,
                        true,
                    )

                if (mapHolderImage[holder] == image) {
                    if ((result is Result.TRUE) && showAlreadyActionedImages) {
                        holder.itemUploaded()
                    } else {
                        holder.itemNotUploaded()
                    }

                    if ((existsInNotForUploadTable > 0) && showAlreadyActionedImages) {
                        holder.itemNotForUpload()
                    } else {
                        holder.itemForUpload()
                    }
                }

                if (uploadedContributionsList.isNotEmpty()) {
                    for (contribution in uploadedContributionsList) {
                        if (contribution.contentUri == image.uri && showAlreadyActionedImages) {
                            holder.itemUploading()
                            break
                        } else {
                            holder.itemNotUploading()
                        }
                    }
                }
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants