mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
* #3493 App freezes for 15 seconds when you press Next in UploadMediaDetailsFragment - add apropriate schedulers and convert justs to fromCallable * #3493 App freezes for 15 seconds when you press Next in UploadMediaDetailsFragment - remove test for removed functionality * #3493 App freezes for 15 seconds when you press Next in UploadMediaDetailsFragment - replace kotlin with java
This commit is contained in:
parent
ed5f8efa6b
commit
4bd7a5b1e2
12 changed files with 119 additions and 136 deletions
|
|
@ -179,11 +179,10 @@ public class UploadRemoteDataSource {
|
|||
* ask the UplaodModel for the image quality of the UploadItem
|
||||
*
|
||||
* @param uploadItem
|
||||
* @param shouldValidateTitle
|
||||
* @return
|
||||
*/
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem, boolean shouldValidateTitle) {
|
||||
return uploadModel.getImageQuality(uploadItem, shouldValidateTitle);
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem) {
|
||||
return uploadModel.getImageQuality(uploadItem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -187,11 +187,10 @@ public class UploadRepository {
|
|||
* query the RemoteDataSource for image quality
|
||||
*
|
||||
* @param uploadItem
|
||||
* @param shouldValidateTitle
|
||||
* @return
|
||||
*/
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem, boolean shouldValidateTitle) {
|
||||
return remoteDataSource.getImageQuality(uploadItem, shouldValidateTitle);
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem) {
|
||||
return remoteDataSource.getImageQuality(uploadItem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,23 +1,21 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.EMPTY_TITLE;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.FILE_NAME_EXISTS;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import fr.free.nrw.commons.utils.ImageUtilsWrapper;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.EMPTY_TITLE;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.FILE_NAME_EXISTS;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
/**
|
||||
* Methods for pre-processing images to be uploaded
|
||||
*/
|
||||
|
|
@ -41,38 +39,33 @@ public class ImageProcessingService {
|
|||
this.mediaClient = mediaClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check image quality before upload
|
||||
* - checks duplicate image
|
||||
* - checks dark image
|
||||
* - checks geolocation for image
|
||||
* - check for valid title
|
||||
*/
|
||||
Single<Integer> validateImage(UploadModel.UploadItem uploadItem, boolean checkTitle) {
|
||||
int currentImageQuality = uploadItem.getImageQuality();
|
||||
Timber.d("Current image quality is %d", currentImageQuality);
|
||||
if (currentImageQuality == ImageUtils.IMAGE_KEEP) {
|
||||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
Timber.d("Checking the validity of image");
|
||||
String filePath = uploadItem.getMediaUri().getPath();
|
||||
Single<Integer> duplicateImage = checkDuplicateImage(filePath);
|
||||
Single<Integer> wrongGeoLocation = checkImageGeoLocation(uploadItem.getPlace(), filePath);
|
||||
Single<Integer> darkImage = checkDarkImage(filePath);
|
||||
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
|
||||
Single<Integer> checkFBMD = checkFBMD(filePath);
|
||||
Single<Integer> checkEXIF = checkEXIF(filePath);
|
||||
|
||||
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
|
||||
(duplicate, wrongGeo, dark, title) -> {
|
||||
Timber.d("Result for duplicate: %d, geo: %d, dark: %d, title: %d", duplicate, wrongGeo, dark, title);
|
||||
return duplicate | wrongGeo | dark | title;
|
||||
});
|
||||
return Single.zip(zipResult, checkFBMD , checkEXIF , (zip , fbmd , exif)->{
|
||||
Timber.d("zip:" + zip + "fbmd:" + fbmd + "exif:" + exif);
|
||||
return zip | fbmd | exif;
|
||||
});
|
||||
/**
|
||||
* Check image quality before upload - checks duplicate image - checks dark image - checks
|
||||
* geolocation for image - check for valid title
|
||||
*/
|
||||
Single<Integer> validateImage(UploadModel.UploadItem uploadItem) {
|
||||
int currentImageQuality = uploadItem.getImageQuality();
|
||||
Timber.d("Current image quality is %d", currentImageQuality);
|
||||
if (currentImageQuality == ImageUtils.IMAGE_KEEP) {
|
||||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
Timber.d("Checking the validity of image");
|
||||
String filePath = uploadItem.getMediaUri().getPath();
|
||||
|
||||
return Single.zip(
|
||||
checkDuplicateImage(filePath),
|
||||
checkImageGeoLocation(uploadItem.getPlace(), filePath),
|
||||
checkDarkImage(filePath),
|
||||
validateItemTitle(uploadItem),
|
||||
checkFBMD(filePath),
|
||||
checkEXIF(filePath),
|
||||
(duplicateImage, wrongGeoLocation, darkImage, itemTitle, fbmd, exif) -> {
|
||||
Timber.d("duplicate: %d, geo: %d, dark: %d, title: %d" + "fbmd:" + fbmd + "exif:" + exif,
|
||||
duplicateImage, wrongGeoLocation, darkImage, itemTitle);
|
||||
return duplicateImage | wrongGeoLocation | darkImage | itemTitle | fbmd | exif;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to discourage users from uploading images to Commons that were taken from Facebook.
|
||||
|
|
@ -113,7 +106,8 @@ public class ImageProcessingService {
|
|||
.map(doesFileExist -> {
|
||||
Timber.d("Result for valid title is %s", doesFileExist);
|
||||
return doesFileExist ? FILE_NAME_EXISTS : IMAGE_OK;
|
||||
});
|
||||
})
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,14 +118,14 @@ public class ImageProcessingService {
|
|||
*/
|
||||
private Single<Integer> checkDuplicateImage(String filePath) {
|
||||
Timber.d("Checking for duplicate image %s", filePath);
|
||||
return Single.fromCallable(() ->
|
||||
fileUtilsWrapper.getFileInputStream(filePath))
|
||||
return Single.fromCallable(() -> fileUtilsWrapper.getFileInputStream(filePath))
|
||||
.map(fileUtilsWrapper::getSHA1)
|
||||
.flatMap(mediaClient::checkFileExistsUsingSha)
|
||||
.map(b -> {
|
||||
Timber.d("Result for duplicate image %s", b);
|
||||
return b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK;
|
||||
});
|
||||
})
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,7 +158,8 @@ public class ImageProcessingService {
|
|||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
return imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, place.getLocation());
|
||||
});
|
||||
})
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import io.reactivex.Single;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* We want to discourage users from uploading images to Commons that were taken from Facebook.
|
||||
* This attempts to detect whether an image was downloaded from Facebook by heuristically
|
||||
* searching for metadata that is specific to images that come from Facebook.
|
||||
* We want to discourage users from uploading images to Commons that were taken from Facebook. This
|
||||
* attempts to detect whether an image was downloaded from Facebook by heuristically searching for
|
||||
* metadata that is specific to images that come from Facebook.
|
||||
*/
|
||||
@Singleton
|
||||
public class ReadFBMD {
|
||||
|
||||
@Inject
|
||||
public ReadFBMD() {
|
||||
}
|
||||
@Inject
|
||||
public ReadFBMD() {
|
||||
}
|
||||
|
||||
public Single<Integer> processMetadata(String path) {
|
||||
try {
|
||||
int psBlockOffset;
|
||||
int fbmdOffset;
|
||||
public Single<Integer> processMetadata(String path) {
|
||||
return Single.fromCallable(() -> {
|
||||
try {
|
||||
int psBlockOffset;
|
||||
int fbmdOffset;
|
||||
|
||||
try (FileInputStream fs = new FileInputStream(path)) {
|
||||
byte[] bytes = new byte[4096];
|
||||
fs.read(bytes);
|
||||
fs.close();
|
||||
String fileStr = new String(bytes);
|
||||
psBlockOffset = fileStr.indexOf("8BIM");
|
||||
fbmdOffset = fileStr.indexOf("FBMD");
|
||||
}
|
||||
|
||||
if (psBlockOffset > 0 && fbmdOffset > 0
|
||||
&& fbmdOffset > psBlockOffset && fbmdOffset - psBlockOffset < 0x80) {
|
||||
return Single.just(ImageUtils.FILE_FBMD);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
try (FileInputStream fs = new FileInputStream(path)) {
|
||||
byte[] bytes = new byte[4096];
|
||||
fs.read(bytes);
|
||||
fs.close();
|
||||
String fileStr = new String(bytes);
|
||||
psBlockOffset = fileStr.indexOf("8BIM");
|
||||
fbmdOffset = fileStr.indexOf("FBMD");
|
||||
}
|
||||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
|
||||
if (psBlockOffset > 0 && fbmdOffset > 0
|
||||
&& fbmdOffset > psBlockOffset && fbmdOffset - psBlockOffset < 0x80) {
|
||||
return ImageUtils.FILE_FBMD;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ImageUtils.IMAGE_OK;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ public class UploadModel {
|
|||
return Observable.just(getUploadItem(uploadableFile, place, source, similarImageInterface));
|
||||
}
|
||||
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem, boolean checkTitle) {
|
||||
return imageProcessingService.validateImage(uploadItem, checkTitle);
|
||||
public Single<Integer> getImageQuality(UploadItem uploadItem) {
|
||||
return imageProcessingService.validateImage(uploadItem);
|
||||
}
|
||||
|
||||
private UploadItem getUploadItem(UploadableFile uploadableFile,
|
||||
|
|
@ -378,4 +378,4 @@ public class UploadModel {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
@OnClick(R.id.btn_next)
|
||||
public void onNextButtonClicked() {
|
||||
uploadItem.setDescriptions(descriptionsAdapter.getDescriptions());
|
||||
presenter.verifyImageQuality(uploadItem, true);
|
||||
presenter.verifyImageQuality(uploadItem);
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_previous)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public interface UploadMediaDetailsContract {
|
|||
void receiveImage(UploadableFile uploadableFile, @Contribution.FileSource String source,
|
||||
Place place);
|
||||
|
||||
void verifyImageQuality(UploadItem uploadItem, boolean validateTitle);
|
||||
void verifyImageQuality(UploadItem uploadItem);
|
||||
|
||||
void setUploadItem(int index, UploadItem uploadItem);
|
||||
|
||||
|
|
|
|||
|
|
@ -111,14 +111,14 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
* asks the repository to verify image quality
|
||||
*
|
||||
* @param uploadItem
|
||||
* @param validateTitle
|
||||
*/
|
||||
@Override
|
||||
public void verifyImageQuality(UploadItem uploadItem, boolean validateTitle) {
|
||||
public void verifyImageQuality(UploadItem uploadItem) {
|
||||
view.showProgress(true);
|
||||
Disposable imageQualityDisposable = repository
|
||||
.getImageQuality(uploadItem, true)
|
||||
.subscribeOn(ioScheduler)
|
||||
|
||||
compositeDisposable.add(
|
||||
repository
|
||||
.getImageQuality(uploadItem)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(imageResult -> {
|
||||
view.showProgress(false);
|
||||
|
|
@ -129,9 +129,8 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
view.showMessage("" + throwable.getLocalizedMessage(),
|
||||
R.color.color_error);
|
||||
Timber.e(throwable, "Error occurred while handling image");
|
||||
});
|
||||
|
||||
compositeDisposable.add(imageQualityDisposable);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,31 +1,30 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ImageUtilsWrapper {
|
||||
|
||||
@Inject
|
||||
public ImageUtilsWrapper() {
|
||||
@Inject
|
||||
public ImageUtilsWrapper() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Single<Integer> checkIfImageIsTooDark(String bitmapPath) {
|
||||
return Single.just(ImageUtils.checkIfImageIsTooDark(bitmapPath))
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(Schedulers.computation());
|
||||
}
|
||||
public Single<Integer> checkIfImageIsTooDark(String bitmapPath) {
|
||||
return Single.fromCallable(() -> ImageUtils.checkIfImageIsTooDark(bitmapPath))
|
||||
.subscribeOn(Schedulers.computation());
|
||||
}
|
||||
|
||||
public Single<Integer> checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) {
|
||||
boolean isImageGeoLocationDifferent = ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng);
|
||||
return Single.just(isImageGeoLocationDifferent)
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(Schedulers.computation())
|
||||
.map(isDifferent -> isDifferent ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT : ImageUtils.IMAGE_OK);
|
||||
}
|
||||
public Single<Integer> checkImageGeolocationIsDifferent(String geolocationOfFileString,
|
||||
LatLng latLng) {
|
||||
return Single.fromCallable(
|
||||
() -> ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng))
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.map(isDifferent -> isDifferent ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT
|
||||
: ImageUtils.IMAGE_OK);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class u {
|
|||
@Test
|
||||
fun validateImageForKeepImage() {
|
||||
`when`(uploadItem.imageQuality).thenReturn(ImageUtils.IMAGE_KEEP)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.IMAGE_OK, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
|
|
@ -96,13 +96,13 @@ class u {
|
|||
fun validateImageForDuplicateImage() {
|
||||
`when`(mediaClient!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(true))
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.IMAGE_DUPLICATE, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateImageForOkImage() {
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.IMAGE_OK, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ class u {
|
|||
fun validateImageForDarkImage() {
|
||||
`when`(imageUtilsWrapper?.checkIfImageIsTooDark(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(ImageUtils.IMAGE_DARK))
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.IMAGE_DARK, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
|
|
@ -118,23 +118,15 @@ class u {
|
|||
fun validateImageForWrongGeoLocation() {
|
||||
`when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(ArgumentMatchers.anyString(), any(LatLng::class.java)))
|
||||
.thenReturn(Single.just(ImageUtils.IMAGE_GEOLOCATION_DIFFERENT))
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.IMAGE_GEOLOCATION_DIFFERENT, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateImageForFileNameExistsWithCheckTitleOff() {
|
||||
`when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(true))
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
|
||||
assertEquals(ImageUtils.IMAGE_OK, validateImage.blockingGet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateImageForFileNameExistsWithCheckTitleOn() {
|
||||
`when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(true))
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem, true)
|
||||
val validateImage = imageProcessingService!!.validateImage(uploadItem)
|
||||
assertEquals(ImageUtils.FILE_NAME_EXISTS, validateImage.blockingGet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ class UploadMediaPresenterTest {
|
|||
*/
|
||||
@Test
|
||||
fun verifyImageQualityTest() {
|
||||
Mockito.`when`(repository?.getImageQuality(ArgumentMatchers.any(UploadModel.UploadItem::class.java), ArgumentMatchers.any(Boolean::class.java))).thenReturn(testSingleImageResult)
|
||||
Mockito.`when`(repository?.getImageQuality(ArgumentMatchers.any(UploadModel.UploadItem::class.java))).thenReturn(testSingleImageResult)
|
||||
Mockito.`when`(uploadItem?.imageQuality).thenReturn(ArgumentMatchers.anyInt())
|
||||
uploadMediaPresenter?.verifyImageQuality(uploadItem, true)
|
||||
uploadMediaPresenter?.verifyImageQuality(uploadItem)
|
||||
verify(view)?.showProgress(true)
|
||||
testScheduler?.triggerActions()
|
||||
verify(view)?.showProgress(false)
|
||||
|
|
@ -185,4 +185,4 @@ class UploadMediaPresenterTest {
|
|||
verify(repository)?.updateUploadItem(0,uploadItem)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class UploadModelTest {
|
|||
.thenReturn(mock(FileInputStream::class.java))
|
||||
`when`(fileUtilsWrapper!!.getGeolocationOfFile(anyString()))
|
||||
.thenReturn("")
|
||||
`when`(imageProcessingService!!.validateImage(any(UploadModel.UploadItem::class.java), anyBoolean()))
|
||||
`when`(imageProcessingService!!.validateImage(any(UploadModel.UploadItem::class.java)))
|
||||
.thenReturn(Single.just(IMAGE_OK))
|
||||
|
||||
}
|
||||
|
|
@ -129,4 +129,4 @@ class UploadModelTest {
|
|||
@Test
|
||||
fun buildContributions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue