diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index bc76b9dd8..df3cfc035 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -5,6 +5,7 @@ import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED; import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED; import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL; import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME; +import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK; import static fr.free.nrw.commons.utils.LengthUtils.computeBearing; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; @@ -831,10 +832,18 @@ public class ContributionsFragment * @param contribution */ public void restartUpload(Contribution contribution) { - contribution.setState(Contribution.STATE_QUEUED); contribution.setDateUploadStarted(Calendar.getInstance().getTime()); - contributionsPresenter.saveContribution(contribution); - Timber.d("Restarting for %s", contribution.toString()); + if (contribution.getState() == Contribution.STATE_FAILED) { + if (contribution.getErrorInfo() == null){ + contribution.setChunkInfo(null); + contribution.setTransferred(0); + } + contributionsPresenter.checkDuplicateImageAndRestartContribution(contribution); + } else { + contribution.setState(Contribution.STATE_QUEUED); + contributionsPresenter.saveContribution(contribution); + Timber.d("Restarting for %s", contribution.toString()); + } } /** @@ -856,10 +865,6 @@ public class ContributionsFragment contribution.setRetries(retries + 1); Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), retries + 1); - if (contribution.getErrorInfo() == null){ - contribution.setChunkInfo(null); - contribution.setTransferred(0); - } restartUpload(contribution); } else { // TODO: Show the exact reason for failure diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java index 8d08105f4..f7e008353 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java @@ -1,21 +1,26 @@ package fr.free.nrw.commons.contributions; +import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK; + import androidx.work.ExistingWorkPolicy; import fr.free.nrw.commons.MediaDataExtractor; import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener; import fr.free.nrw.commons.di.CommonsApplicationModule; +import fr.free.nrw.commons.repository.UploadRepository; import fr.free.nrw.commons.upload.worker.WorkRequestHelper; import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; import javax.inject.Inject; import javax.inject.Named; +import timber.log.Timber; /** * The presenter class for Contributions */ public class ContributionsPresenter implements UserActionListener { - private final ContributionsRepository repository; + private final ContributionsRepository contributionsRepository; + private final UploadRepository uploadRepository; private final Scheduler ioThreadScheduler; private CompositeDisposable compositeDisposable; private ContributionsContract.View view; @@ -25,8 +30,10 @@ public class ContributionsPresenter implements UserActionListener { @Inject ContributionsPresenter(ContributionsRepository repository, + UploadRepository uploadRepository, @Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) { - this.repository = repository; + this.contributionsRepository = repository; + this.uploadRepository = uploadRepository; this.ioThreadScheduler=ioThreadScheduler; } @@ -44,7 +51,25 @@ public class ContributionsPresenter implements UserActionListener { @Override public Contribution getContributionsWithTitle(String title) { - return repository.getContributionWithFileName(title); + return contributionsRepository.getContributionWithFileName(title); + } + + public void checkDuplicateImageAndRestartContribution(Contribution contribution){ + compositeDisposable.add(uploadRepository + .checkDuplicateImage(contribution.getLocalUriPath().getPath()) + .subscribeOn(ioThreadScheduler) + .subscribe(imageCheckResult -> { + if (imageCheckResult == IMAGE_OK) { + contribution.setState(Contribution.STATE_QUEUED); + saveContribution(contribution); + }else { + Timber.e("Contribution already exists"); + compositeDisposable.add(contributionsRepository + .deleteContributionFromDB(contribution) + .subscribeOn(ioThreadScheduler) + .subscribe()); + } + })); } /** @@ -54,7 +79,7 @@ public class ContributionsPresenter implements UserActionListener { * @param contribution */ public void saveContribution(Contribution contribution) { - compositeDisposable.add(repository + compositeDisposable.add(contributionsRepository .save(contribution) .subscribeOn(ioThreadScheduler) .subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest( diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java index 2374ebfc3..d38a56119 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java @@ -203,6 +203,16 @@ public class UploadRepository { return uploadModel.getImageQuality(uploadItem, location); } + /** + * query the RemoteDataSource for image duplicity check + * + * @param filePath file to be checked + * @return IMAGE_DUPLICATE or IMAGE_OK + */ + public Single checkDuplicateImage(String filePath) { + return uploadModel.checkDuplicateImage(filePath); + } + /** * query the RemoteDataSource for caption quality * diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java b/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java index 45c4b87d9..8065fde56 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java @@ -140,7 +140,7 @@ public class ImageProcessingService { * @param filePath file to be checked * @return IMAGE_DUPLICATE or IMAGE_OK */ - private Single checkDuplicateImage(String filePath) { + Single checkDuplicateImage(String filePath) { Timber.d("Checking for duplicate image %s", filePath); return Single.fromCallable(() -> fileUtilsWrapper.getFileInputStream(filePath)) .map(fileUtilsWrapper::getSHA1) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java index e27e9c68f..4f508208a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java @@ -1,6 +1,8 @@ package fr.free.nrw.commons.upload; +import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK; + import android.content.Context; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; @@ -14,6 +16,7 @@ import fr.free.nrw.commons.contributions.ContributionBoundaryCallback; import fr.free.nrw.commons.contributions.ContributionsRemoteDataSource; import fr.free.nrw.commons.contributions.ContributionsRepository; import fr.free.nrw.commons.di.CommonsApplicationModule; +import fr.free.nrw.commons.repository.UploadRepository; import fr.free.nrw.commons.upload.PendingUploadsContract.UserActionListener; import fr.free.nrw.commons.upload.PendingUploadsContract.View; import fr.free.nrw.commons.upload.worker.WorkRequestHelper; @@ -33,7 +36,8 @@ import timber.log.Timber; public class PendingUploadsPresenter implements UserActionListener { private final ContributionBoundaryCallback contributionBoundaryCallback; - private final ContributionsRepository repository; + private final ContributionsRepository contributionsRepository; + private final UploadRepository uploadRepository; private final Scheduler ioThreadScheduler; private final CompositeDisposable compositeDisposable; @@ -46,10 +50,12 @@ public class PendingUploadsPresenter implements UserActionListener { PendingUploadsPresenter( final ContributionBoundaryCallback contributionBoundaryCallback, final ContributionsRemoteDataSource contributionsRemoteDataSource, - final ContributionsRepository repository, + final ContributionsRepository contributionsRepository, + final UploadRepository uploadRepository, @Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) { this.contributionBoundaryCallback = contributionBoundaryCallback; - this.repository = repository; + this.contributionsRepository = contributionsRepository; + this.uploadRepository = uploadRepository; this.ioThreadScheduler = ioThreadScheduler; this.contributionsRemoteDataSource = contributionsRemoteDataSource; compositeDisposable = new CompositeDisposable(); @@ -68,7 +74,7 @@ public class PendingUploadsPresenter implements UserActionListener { .setPageSize(10).build(); Factory factory; - factory = repository.fetchContributionsWithStatesSortedByDateUploadStarted( + factory = contributionsRepository.fetchContributionsWithStatesSortedByDateUploadStarted( Arrays.asList(Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS, Contribution.STATE_PAUSED)); LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, @@ -82,7 +88,7 @@ public class PendingUploadsPresenter implements UserActionListener { .setPrefetchDistance(50) .setPageSize(10).build(); Factory factory; - factory = repository.fetchContributionsWithStatesSortedByDateUploadStarted( + factory = contributionsRepository.fetchContributionsWithStatesSortedByDateUploadStarted( Collections.singletonList(Contribution.STATE_FAILED)); LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, pagedListConfig); @@ -103,7 +109,7 @@ public class PendingUploadsPresenter implements UserActionListener { @Override public void deleteUpload(final Contribution contribution, Context context) { - compositeDisposable.add(repository + compositeDisposable.add(contributionsRepository .deleteContributionFromDB(contribution) .subscribeOn(ioThreadScheduler) .subscribe()); @@ -111,14 +117,14 @@ public class PendingUploadsPresenter implements UserActionListener { public void pauseUploads(List states, int newState) { CommonsApplication.isPaused = true ; - compositeDisposable.add(repository + compositeDisposable.add(contributionsRepository .updateContributionWithStates(states, newState) .subscribeOn(ioThreadScheduler) .subscribe()); } public void deleteUploads(List states) { - compositeDisposable.add(repository + compositeDisposable.add(contributionsRepository .deleteContributionsFromDBWithStates(states) .subscribeOn(ioThreadScheduler) .subscribe()); @@ -132,27 +138,46 @@ public class PendingUploadsPresenter implements UserActionListener { Contribution it = contributionList.get(index); if (it.getState() == Contribution.STATE_FAILED) { it.setDateUploadStarted(Calendar.getInstance().getTime()); - if (it.getErrorInfo() == null){ + if (it.getErrorInfo() == null) { it.setChunkInfo(null); it.setTransferred(0); } - } - it.setState(Contribution.STATE_QUEUED); - compositeDisposable.add(repository - .save(it) - .subscribeOn(ioThreadScheduler) - .doOnComplete(() -> { - restartUploads(contributionList, index + 1, context); - } - ) - .subscribe(() -> - WorkRequestHelper.Companion.makeOneTimeWorkRequest( - context, ExistingWorkPolicy.KEEP), - throwable -> { + compositeDisposable.add(uploadRepository + .checkDuplicateImage(it.getLocalUriPath().getPath()) + .subscribeOn(ioThreadScheduler) + .subscribe(imageCheckResult -> { + if (imageCheckResult == IMAGE_OK) { + it.setState(Contribution.STATE_QUEUED); + compositeDisposable.add(contributionsRepository + .save(it) + .subscribeOn(ioThreadScheduler) + .doOnComplete(() -> { + restartUploads(contributionList, index + 1, context); + }) + .subscribe()); + } else { + Timber.e("Contribution already exists"); + compositeDisposable.add(contributionsRepository + .deleteContributionFromDB(it) + .subscribeOn(ioThreadScheduler).doOnComplete(() -> { + restartUploads(contributionList, index + 1, context); + }) + .subscribe()); + } + }, throwable -> { Timber.e(throwable); restartUploads(contributionList, index + 1, context); - } - )); + })); + } else { + it.setState(Contribution.STATE_QUEUED); + compositeDisposable.add(contributionsRepository + .save(it) + .subscribeOn(ioThreadScheduler) + .doOnComplete(() -> { + restartUploads(contributionList, index + 1, context); + }) + .subscribe()); + } } public void restartUpload(List contributionList, int index, Context context) { @@ -167,18 +192,31 @@ public class PendingUploadsPresenter implements UserActionListener { it.setChunkInfo(null); it.setTransferred(0); } + compositeDisposable.add(uploadRepository + .checkDuplicateImage(it.getLocalUriPath().getPath()) + .subscribeOn(ioThreadScheduler) + .subscribe(imageCheckResult -> { + if (imageCheckResult == IMAGE_OK) { + it.setState(Contribution.STATE_QUEUED); + compositeDisposable.add(contributionsRepository + .save(it) + .subscribeOn(ioThreadScheduler) + .subscribe()); + }else { + Timber.e("Contribution already exists"); + compositeDisposable.add(contributionsRepository + .deleteContributionFromDB(it) + .subscribeOn(ioThreadScheduler) + .subscribe()); + } + })); + } else { + it.setState(Contribution.STATE_QUEUED); + compositeDisposable.add(contributionsRepository + .save(it) + .subscribeOn(ioThreadScheduler) + .subscribe()); } - it.setState(Contribution.STATE_QUEUED); - compositeDisposable.add(repository - .save(it) - .subscribeOn(ioThreadScheduler) - .subscribe(() -> - WorkRequestHelper.Companion.makeOneTimeWorkRequest( - context, ExistingWorkPolicy.KEEP), - throwable -> { - Timber.e(throwable); - } - )); } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index ed1193e73..2611645de 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -103,6 +103,16 @@ public class UploadModel { return imageProcessingService.validateImage(uploadItem, inAppPictureLocation); } + /** + * Calls checkDuplicateImage() of ImageProcessingService to check if image is duplicate + * + * @param filePath file to be checked + * @return IMAGE_DUPLICATE or IMAGE_OK + */ + public Single checkDuplicateImage(String filePath){ + return imageProcessingService.checkDuplicateImage(filePath); + } + /** * Calls validateCaption() of ImageProcessingService to check caption of image *