mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
[Bug fix] Check if duplicate exist using both original and modified file's checksum (#6169)
* check original file's SHA too along with modified one Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> * fix tests Signed-off-by: parneet-guraya <gurayaparneet@gmail.com> --------- Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
This commit is contained in:
parent
30a7f702a1
commit
43dca1dd14
6 changed files with 61 additions and 17 deletions
|
|
@ -62,7 +62,10 @@ public class ContributionsPresenter implements UserActionListener {
|
||||||
*/
|
*/
|
||||||
public void checkDuplicateImageAndRestartContribution(Contribution contribution) {
|
public void checkDuplicateImageAndRestartContribution(Contribution contribution) {
|
||||||
compositeDisposable.add(uploadRepository
|
compositeDisposable.add(uploadRepository
|
||||||
.checkDuplicateImage(contribution.getLocalUriPath().getPath())
|
.checkDuplicateImage(
|
||||||
|
contribution.getContentUri(),
|
||||||
|
contribution.getLocalUri()
|
||||||
|
)
|
||||||
.subscribeOn(ioThreadScheduler)
|
.subscribeOn(ioThreadScheduler)
|
||||||
.subscribe(imageCheckResult -> {
|
.subscribe(imageCheckResult -> {
|
||||||
if (imageCheckResult == IMAGE_OK) {
|
if (imageCheckResult == IMAGE_OK) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.repository
|
package fr.free.nrw.commons.repository
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import fr.free.nrw.commons.Media
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.category.CategoriesModel
|
import fr.free.nrw.commons.category.CategoriesModel
|
||||||
import fr.free.nrw.commons.category.CategoryItem
|
import fr.free.nrw.commons.category.CategoryItem
|
||||||
|
|
@ -203,8 +204,8 @@ class UploadRepository @Inject constructor(
|
||||||
* @param filePath file to be checked
|
* @param filePath file to be checked
|
||||||
* @return IMAGE_DUPLICATE or IMAGE_OK
|
* @return IMAGE_DUPLICATE or IMAGE_OK
|
||||||
*/
|
*/
|
||||||
fun checkDuplicateImage(filePath: String): Single<Int> {
|
fun checkDuplicateImage(originalFilePath: Uri, modifiedFilePath: Uri): Single<Int> {
|
||||||
return uploadModel.checkDuplicateImage(filePath)
|
return uploadModel.checkDuplicateImage(originalFilePath, modifiedFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package fr.free.nrw.commons.upload
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import fr.free.nrw.commons.location.LatLng
|
import fr.free.nrw.commons.location.LatLng
|
||||||
import fr.free.nrw.commons.media.MediaClient
|
import fr.free.nrw.commons.media.MediaClient
|
||||||
import fr.free.nrw.commons.nearby.Place
|
import fr.free.nrw.commons.nearby.Place
|
||||||
|
|
@ -13,7 +15,7 @@ import io.reactivex.Single
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.FileInputStream
|
import java.io.InputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -26,7 +28,8 @@ class ImageProcessingService @Inject constructor(
|
||||||
private val imageUtilsWrapper: ImageUtilsWrapper,
|
private val imageUtilsWrapper: ImageUtilsWrapper,
|
||||||
private val readFBMD: ReadFBMD,
|
private val readFBMD: ReadFBMD,
|
||||||
private val exifReader: EXIFReader,
|
private val exifReader: EXIFReader,
|
||||||
private val mediaClient: MediaClient
|
private val mediaClient: MediaClient,
|
||||||
|
private val appContext: Context
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Check image quality before upload - checks duplicate image - checks dark image - checks
|
* Check image quality before upload - checks duplicate image - checks dark image - checks
|
||||||
|
|
@ -47,7 +50,10 @@ class ImageProcessingService @Inject constructor(
|
||||||
val filePath = uploadItem.mediaUri?.path
|
val filePath = uploadItem.mediaUri?.path
|
||||||
|
|
||||||
return Single.zip(
|
return Single.zip(
|
||||||
checkDuplicateImage(filePath),
|
checkIfFileAlreadyExists(
|
||||||
|
originalFilePath = uploadItem.contentUri!!,
|
||||||
|
modifiedFilePath = uploadItem.mediaUri!!
|
||||||
|
),
|
||||||
checkImageGeoLocation(uploadItem.place, filePath, inAppPictureLocation),
|
checkImageGeoLocation(uploadItem.place, filePath, inAppPictureLocation),
|
||||||
checkDarkImage(filePath!!),
|
checkDarkImage(filePath!!),
|
||||||
checkFBMD(filePath),
|
checkFBMD(filePath),
|
||||||
|
|
@ -114,18 +120,31 @@ class ImageProcessingService @Inject constructor(
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if file already exists using original & modified file's SHA
|
||||||
|
*
|
||||||
|
* @param originalFilePath original file to be checked
|
||||||
|
* @param modifiedFilePath modified (after exif modifications) file to be checked
|
||||||
|
* @return IMAGE_DUPLICATE or IMAGE_OK
|
||||||
|
*/
|
||||||
|
fun checkIfFileAlreadyExists(originalFilePath: Uri, modifiedFilePath: Uri): Single<Int> {
|
||||||
|
return Single.zip(
|
||||||
|
checkDuplicateImage(inputStream = appContext.contentResolver.openInputStream(originalFilePath)!!),
|
||||||
|
checkDuplicateImage(inputStream = fileUtilsWrapper.getFileInputStream(modifiedFilePath.path))
|
||||||
|
) { resultForOriginal, resultForDuplicate ->
|
||||||
|
return@zip if (resultForOriginal == IMAGE_DUPLICATE || resultForDuplicate == IMAGE_DUPLICATE)
|
||||||
|
IMAGE_DUPLICATE else IMAGE_OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for duplicate image
|
* Checks for duplicate image
|
||||||
*
|
*
|
||||||
* @param filePath file to be checked
|
* @param filePath file to be checked
|
||||||
* @return IMAGE_DUPLICATE or IMAGE_OK
|
* @return IMAGE_DUPLICATE or IMAGE_OK
|
||||||
*/
|
*/
|
||||||
fun checkDuplicateImage(filePath: String?): Single<Int> {
|
private fun checkDuplicateImage(inputStream: InputStream): Single<Int> {
|
||||||
Timber.d("Checking for duplicate image %s", filePath)
|
return Single.fromCallable { fileUtilsWrapper.getSHA1(inputStream) }
|
||||||
return Single.fromCallable { fileUtilsWrapper.getFileInputStream(filePath) }
|
|
||||||
.map { stream: FileInputStream? ->
|
|
||||||
fileUtilsWrapper.getSHA1(stream)
|
|
||||||
}
|
|
||||||
.flatMap { fileSha: String? ->
|
.flatMap { fileSha: String? ->
|
||||||
mediaClient.checkFileExistsUsingSha(fileSha)
|
mediaClient.checkFileExistsUsingSha(fileSha)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,10 @@ class PendingUploadsPresenter @Inject internal constructor(
|
||||||
}
|
}
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
uploadRepository
|
uploadRepository
|
||||||
.checkDuplicateImage(contribution.localUriPath!!.path)
|
.checkDuplicateImage(
|
||||||
|
originalFilePath = contribution.contentUri!!,
|
||||||
|
modifiedFilePath = contribution.localUri!!
|
||||||
|
)
|
||||||
.subscribeOn(ioThreadScheduler)
|
.subscribeOn(ioThreadScheduler)
|
||||||
.subscribe({ imageCheckResult: Int ->
|
.subscribe({ imageCheckResult: Int ->
|
||||||
if (imageCheckResult == IMAGE_OK) {
|
if (imageCheckResult == IMAGE_OK) {
|
||||||
|
|
@ -218,7 +221,10 @@ class PendingUploadsPresenter @Inject internal constructor(
|
||||||
}
|
}
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
uploadRepository
|
uploadRepository
|
||||||
.checkDuplicateImage(contribution.localUriPath!!.path)
|
.checkDuplicateImage(
|
||||||
|
originalFilePath = contribution.contentUri!!,
|
||||||
|
modifiedFilePath = contribution.localUri!!
|
||||||
|
)
|
||||||
.subscribeOn(ioThreadScheduler)
|
.subscribeOn(ioThreadScheduler)
|
||||||
.subscribe { imageCheckResult: Int ->
|
.subscribe { imageCheckResult: Int ->
|
||||||
if (imageCheckResult == IMAGE_OK) {
|
if (imageCheckResult == IMAGE_OK) {
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,11 @@ class UploadModel @Inject internal constructor(
|
||||||
* @param filePath file to be checked
|
* @param filePath file to be checked
|
||||||
* @return IMAGE_DUPLICATE or IMAGE_OK
|
* @return IMAGE_DUPLICATE or IMAGE_OK
|
||||||
*/
|
*/
|
||||||
fun checkDuplicateImage(filePath: String?): Single<Int> =
|
fun checkDuplicateImage(originalFilePath: Uri?, modifiedFilePath: Uri?): Single<Int> =
|
||||||
imageProcessingService.checkDuplicateImage(filePath)
|
imageProcessingService.checkIfFileAlreadyExists(
|
||||||
|
originalFilePath = originalFilePath!!,
|
||||||
|
modifiedFilePath = modifiedFilePath!!
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls validateCaption() of ImageProcessingService to check caption of image
|
* Calls validateCaption() of ImageProcessingService to check caption of image
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package fr.free.nrw.commons.upload
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import fr.free.nrw.commons.location.LatLng
|
import fr.free.nrw.commons.location.LatLng
|
||||||
import fr.free.nrw.commons.media.MediaClient
|
import fr.free.nrw.commons.media.MediaClient
|
||||||
|
|
@ -18,6 +20,7 @@ import org.mockito.Mockito.mock
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
class ImageProcessingServiceTest {
|
class ImageProcessingServiceTest {
|
||||||
@Mock
|
@Mock
|
||||||
|
|
@ -38,6 +41,9 @@ class ImageProcessingServiceTest {
|
||||||
@Mock
|
@Mock
|
||||||
internal var location: LatLng? = null
|
internal var location: LatLng? = null
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
internal lateinit var appContext: Context
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
var imageProcessingService: ImageProcessingService? = null
|
var imageProcessingService: ImageProcessingService? = null
|
||||||
|
|
||||||
|
|
@ -49,8 +55,10 @@ class ImageProcessingServiceTest {
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.openMocks(this)
|
MockitoAnnotations.openMocks(this)
|
||||||
val mediaUri = mock(Uri::class.java)
|
val mediaUri = mock(Uri::class.java)
|
||||||
|
val contentUri = mock(Uri::class.java)
|
||||||
val mockPlace = mock(Place::class.java)
|
val mockPlace = mock(Place::class.java)
|
||||||
val mockTitle = mock(List::class.java)
|
val mockTitle = mock(List::class.java)
|
||||||
|
val contentResolver = mock(ContentResolver::class.java)
|
||||||
|
|
||||||
`when`(mockPlace.wikiDataEntityId).thenReturn("Q1")
|
`when`(mockPlace.wikiDataEntityId).thenReturn("Q1")
|
||||||
`when`(mockPlace.getLocation()).thenReturn(mock(LatLng::class.java))
|
`when`(mockPlace.getLocation()).thenReturn(mock(LatLng::class.java))
|
||||||
|
|
@ -59,6 +67,8 @@ class ImageProcessingServiceTest {
|
||||||
`when`(mockTitle.isSet).thenReturn(true)*/
|
`when`(mockTitle.isSet).thenReturn(true)*/
|
||||||
|
|
||||||
`when`(uploadItem.mediaUri).thenReturn(mediaUri)
|
`when`(uploadItem.mediaUri).thenReturn(mediaUri)
|
||||||
|
`when`(uploadItem.contentUri).thenReturn(contentUri)
|
||||||
|
`when`(appContext.contentResolver).thenReturn(contentResolver)
|
||||||
`when`(uploadItem.imageQuality).thenReturn(ImageUtils.IMAGE_WAIT)
|
`when`(uploadItem.imageQuality).thenReturn(ImageUtils.IMAGE_WAIT)
|
||||||
|
|
||||||
`when`(uploadItem.uploadMediaDetails).thenReturn(mockTitle as MutableList<UploadMediaDetail>?)
|
`when`(uploadItem.uploadMediaDetails).thenReturn(mockTitle as MutableList<UploadMediaDetail>?)
|
||||||
|
|
@ -68,7 +78,9 @@ class ImageProcessingServiceTest {
|
||||||
|
|
||||||
`when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString()))
|
`when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString()))
|
||||||
.thenReturn(mock(FileInputStream::class.java))
|
.thenReturn(mock(FileInputStream::class.java))
|
||||||
`when`(fileUtilsWrapper!!.getSHA1(any(FileInputStream::class.java)))
|
`when`(appContext.contentResolver.openInputStream(ArgumentMatchers.any()))
|
||||||
|
.thenReturn(mock(InputStream::class.java))
|
||||||
|
`when`(fileUtilsWrapper!!.getSHA1(any(InputStream::class.java)))
|
||||||
.thenReturn("fileSha")
|
.thenReturn("fileSha")
|
||||||
|
|
||||||
`when`(fileUtilsWrapper!!.getGeolocationOfFile(ArgumentMatchers.anyString(), any(LatLng::class.java)))
|
`when`(fileUtilsWrapper!!.getGeolocationOfFile(ArgumentMatchers.anyString(), any(LatLng::class.java)))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue