diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt index d9f2fc55e..c0282c92c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt @@ -50,39 +50,37 @@ abstract class UploadedStatusDao { /** * Asynchronous insert into uploaded status table. */ - fun insertUploaded(uploadedStatus: UploadedStatus) = runBlocking { - async { - uploadedStatus.lastUpdated = Calendar.getInstance().time as Date? - insert(uploadedStatus) - }.await() + suspend fun insertUploaded(uploadedStatus: UploadedStatus) { + uploadedStatus.lastUpdated = Calendar.getInstance().time as Date? + insert(uploadedStatus) } /** * Asynchronous delete from uploaded status table. */ - fun deleteUploaded(uploadedStatus: UploadedStatus) = runBlocking { - async { delete(uploadedStatus) } + suspend fun deleteUploaded(uploadedStatus: UploadedStatus) { + delete(uploadedStatus) } /** * Asynchronous update entry in uploaded status table. */ - fun updateUploaded(uploadedStatus: UploadedStatus) = runBlocking { - async { update(uploadedStatus) } + suspend fun updateUploaded(uploadedStatus: UploadedStatus) { + update(uploadedStatus) } /** * Asynchronous image sha1 query. */ - fun getUploadedFromImageSHA1(imageSHA1: String) = runBlocking { - async { getFromImageSHA1(imageSHA1) }.await() + suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus { + return getFromImageSHA1(imageSHA1) } /** * Asynchronous modified image sha1 query. */ - fun getUploadedFromModifiedImageSHA1(modifiedImageSHA1: String) = runBlocking { - async { getFromModifiedImageSHA1(modifiedImageSHA1) }.await() + suspend fun getUploadedFromModifiedImageSHA1(modifiedImageSHA1: String):UploadedStatus { + return getFromModifiedImageSHA1(modifiedImageSHA1) } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index 9029e03bc..ff41048f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -76,7 +76,7 @@ class ImageAdapter( else { holder.itemUnselected(); } - Glide.with(context).load(image.uri).into(holder.image) + Glide.with(context).load(image.uri).thumbnail(0.3f).into(holder.image) imageLoader.queryAndSetView(holder,image) holder.itemView.setOnClickListener { selectOrRemoveImage(holder, position) @@ -99,8 +99,8 @@ class ImageAdapter( if(holder.isItemUploaded()){ Toast.makeText(context,"Already Uploaded image", Toast.LENGTH_SHORT).show() } else { - selectedImages.add(images[position]) - notifyItemChanged(position, ImageSelectedOrUpdated()) + selectedImages.add(images[position]) + notifyItemChanged(position, ImageSelectedOrUpdated()) } } imageSelectListener.onSelectedImagesChanged(selectedImages) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt index f1583c54f..cbb3fc442 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -148,4 +148,12 @@ class ImageFragment: CommonsDaggerSupportFragment() { return 3 // todo change span count depending on the device orientation and other factos. } + + /** + * OnDestroy Cleanup the imageLoader coroutine. + */ + override fun onDestroy() { + imageLoader?.cleanUP() + super.onDestroy() + } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt index 5680cc775..a617b2d2a 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt @@ -11,10 +11,7 @@ import fr.free.nrw.commons.filepicker.PickedFiles import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import timber.log.Timber import java.io.IOException import java.net.UnknownHostException @@ -57,9 +54,17 @@ class ImageLoader @Inject constructor( /** * Maps to facilitate image query. */ - private var mapImageSHA1: HashMap = HashMap() + private var mapModifiedImageSHA1: HashMap = HashMap() private var mapHolderImage : HashMap = HashMap() private var mapResult: HashMap = HashMap() + private var mapImageSHA1: HashMap = HashMap() + + /** + * Coroutine Dispatchers and Scope. + */ + private var defaultDispatcher = Dispatchers.Default + private var ioDispatcher = Dispatchers.IO + private val scope = MainScope() /** * Query image and setUp the view. @@ -72,42 +77,43 @@ class ImageLoader @Inject constructor( mapHolderImage[holder] = image holder.itemNotUploaded() - CoroutineScope(Dispatchers.Main).launch { + scope.launch { - var result : Result = Result.NOTFOUND - withContext(Dispatchers.Default) { + var result: Result = Result.NOTFOUND + if (mapHolderImage[holder] != image) { + return@launch + } + + val imageSHA1 = getImageSHA1(image.uri) + val uploadedStatus = getFromUploaded(imageSHA1) + + val sha1 = uploadedStatus?.let { + result = getResultFromUploadedStatus(uploadedStatus) + uploadedStatus.modifiedImageSHA1 + } ?: run { if (mapHolderImage[holder] == image) { - val imageSHA1 = getImageSHA1(image.uri) - val uploadedStatus = uploadedStatusDao.getUploadedFromImageSHA1(imageSHA1) + getSHA1(image) + } else { + "" + } + } - val sha1 = uploadedStatus?.let { - result = getResultFromUploadedStatus(uploadedStatus) - uploadedStatus.modifiedImageSHA1 - } ?: run { - if(mapHolderImage[holder] == image) { - getSHA1(image) - } else { - "" - } - } + if (mapHolderImage[holder] != image) { + return@launch + } - if (mapHolderImage[holder] == image && - result in arrayOf(Result.NOTFOUND, Result.INVALID) && - sha1.isNotEmpty()) { - // Query original image. - result = querySHA1(imageSHA1) - if( result is Result.TRUE ) { - // Original image found. - insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false) - } - else { - // Original image not found, query modified image. - result = querySHA1(sha1) - if (result != Result.ERROR) { - insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE) - } - } + if (result in arrayOf(Result.NOTFOUND, Result.INVALID) && sha1.isNotEmpty()) { + // Query original image. + result = querySHA1(imageSHA1) + if (result is Result.TRUE) { + // Original image found. + insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false) + } else { + // Original image not found, query modified image. + result = querySHA1(sha1) + if (result != Result.ERROR) { + insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE) } } } @@ -122,25 +128,27 @@ class ImageLoader @Inject constructor( * * @return Query result. */ - private fun querySHA1(SHA1: String): Result { - mapResult[SHA1]?.let{ - return it - } - var result : Result = Result.FALSE - try { - if (mediaClient.checkFileExistsUsingSha(SHA1).blockingGet()) { - mapResult[SHA1] = Result.TRUE - result = Result.TRUE + + private suspend fun querySHA1(SHA1: String): Result { + return withContext(ioDispatcher) { + mapResult[SHA1]?.let { + return@withContext it } - } catch (e: Exception) { - if (e is UnknownHostException) { - // Handle no network connection. - Timber.e(e, "Network Connection Error") + var result: Result = Result.FALSE + try { + if (mediaClient.checkFileExistsUsingSha(SHA1).blockingGet()) { + mapResult[SHA1] = Result.TRUE + result = Result.TRUE + } + } catch (e: Exception) { + if (e is UnknownHostException) { + // Handle no network connection. + Timber.e(e, "Network Connection Error") + } + result = Result.ERROR + e.printStackTrace() } - result = Result.ERROR - e.printStackTrace() - } finally { - return result + result } } @@ -149,27 +157,48 @@ class ImageLoader @Inject constructor( * * @return sha1 of the image */ - private fun getSHA1(image: Image): String { - mapImageSHA1[image]?.let{ + private suspend fun getSHA1(image: Image): String { + mapModifiedImageSHA1[image]?.let{ return it } val sha1 = generateModifiedSHA1(image); - mapImageSHA1[image] = sha1; + mapModifiedImageSHA1[image] = sha1; return sha1; } + /** + * Get the uploaded status entry from the database. + */ + private suspend fun getFromUploaded(imageSha1:String): UploadedStatus?{ + return uploadedStatusDao.getUploadedFromImageSHA1(imageSha1) + } + /** * Insert into uploaded status table. */ - private fun insertIntoUploaded(imageSha1:String, modifiedImageSha1:String, imageResult:Boolean, modifiedImageResult: Boolean){ - uploadedStatusDao.insertUploaded(UploadedStatus(imageSha1, modifiedImageSha1, imageResult, modifiedImageResult)) + private suspend fun insertIntoUploaded(imageSha1:String, modifiedImageSha1:String, imageResult:Boolean, modifiedImageResult: Boolean){ + uploadedStatusDao.insertUploaded( + UploadedStatus( + imageSha1, + modifiedImageSha1, + imageResult, + modifiedImageResult + ) + ) } /** * Get image sha1 from uri, used to retrieve the original image sha1. */ - private fun getImageSHA1(uri: Uri): String { - return fileUtilsWrapper.getSHA1(context.contentResolver.openInputStream(uri)) + private suspend fun getImageSHA1(uri: Uri): String { + return withContext(ioDispatcher) { + mapImageSHA1[uri]?.let{ + return@withContext it + } + val result = fileUtilsWrapper.getSHA1(context.contentResolver.openInputStream(uri)) + mapImageSHA1[uri] = result + result + } } /** @@ -194,18 +223,28 @@ class ImageLoader @Inject constructor( * * @return modified sha1 */ - private fun generateModifiedSHA1(image: Image) : String { - val uploadableFile = PickedFiles.pickedExistingPicture(context, image.uri) - val exifInterface: ExifInterface? = try { - ExifInterface(uploadableFile.file!!) - } catch (e: IOException) { - Timber.e(e) - null + private suspend fun generateModifiedSHA1(image: Image) : String { + return withContext(defaultDispatcher) { + val uploadableFile = PickedFiles.pickedExistingPicture(context, image.uri) + val exifInterface: ExifInterface? = try { + ExifInterface(uploadableFile.file!!) + } catch (e: IOException) { + Timber.e(e) + null + } + fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact()) + val sha1 = + fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(uploadableFile.filePath)) + uploadableFile.file.delete() + sha1 } - fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact()) - val sha1 = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(uploadableFile.filePath)) - uploadableFile.file.delete() - return sha1 + } + + /** + * CleanUp function. + */ + fun cleanUP() { + scope.cancel() } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 8f9bc9504..d46c5e4ac 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -29,9 +29,11 @@ import fr.free.nrw.commons.upload.UploadClient import fr.free.nrw.commons.upload.UploadResult import fr.free.nrw.commons.wikidata.WikidataEditService import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber import java.io.IOException @@ -438,7 +440,16 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : contribution.contentUri?.let { val imageSha1 = fileUtilsWrapper.getSHA1(appContext.contentResolver.openInputStream(it)) val modifiedSha1 = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(contribution.localUri?.path)) - uploadedStatusDao.insertUploaded(UploadedStatus(imageSha1, modifiedSha1, imageSha1 == modifiedSha1, true)); + MainScope().launch { + uploadedStatusDao.insertUploaded( + UploadedStatus( + imageSha1, + modifiedSha1, + imageSha1 == modifiedSha1, + true + ) + ); + } } }