diff --git a/.travis.yml b/.travis.yml index a8278300b9..2c48cd70ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_script: - emulator -avd test -no-audio -no-window -no-boot-anim & - android-wait-for-emulator script: -#- "./gradlew clean check connectedCheck jacocoTestReport" +- "./gradlew clean check connectedCheck jacocoTestReport" - if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then mkdir -p app/src/prodRelease/play/release-notes/en-US; fi diff --git a/app/build.gradle b/app/build.gradle index 93d43f1a6d..c842bb6dd8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -90,7 +90,8 @@ dependencies { //swipe_layout implementation 'com.daimajia.swipelayout:library:1.2.0@aar' implementation 'com.nineoldandroids:library:2.4.0' - implementation files('libs/icafe-1.1-SNAPSHOT.jar') + //metadata extractor + implementation 'com.drewnoakes:metadata-extractor:2.11.0' } android { diff --git a/app/libs/icafe-1.1-SNAPSHOT.jar b/app/libs/icafe-1.1-SNAPSHOT.jar deleted file mode 100644 index b60a20c9a0..0000000000 Binary files a/app/libs/icafe-1.1-SNAPSHOT.jar and /dev/null differ diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/UploadableFile.java b/app/src/main/java/fr/free/nrw/commons/filepicker/UploadableFile.java index d4e01c836d..21a1607c30 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/UploadableFile.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/UploadableFile.java @@ -50,6 +50,10 @@ public UploadableFile(Parcel in) { file = (File) in.readSerializable(); } + public Uri getContentUri() { + return contentUri; + } + public File getFile() { return file; } 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 c6887cb219..7ad7b150da 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 @@ -1,5 +1,8 @@ package fr.free.nrw.commons.upload; +import android.content.Context; +import android.net.Uri; + import java.io.IOException; import javax.inject.Inject; @@ -20,7 +23,10 @@ /** * Methods for pre-processing images to be uploaded - */ + *//*if (dataInBytes[0] == 70 && dataInBytes[1] == 66 && dataInBytes[2] == 77 && dataInBytes[3] == 68) { + Timber.d("Contains FBMD"); + return Single.just(ImageUtils.FILE_FBMD); + }*/ @Singleton public class ImageProcessingService { private final FileUtilsWrapper fileUtilsWrapper; @@ -56,11 +62,13 @@ Single validateImage(UploadModel.UploadItem uploadItem, boolean checkTi } Timber.d("Checking the validity of image"); String filePath = uploadItem.getMediaUri().getPath(); + Uri contentUri=uploadItem.getContentUri(); + Context context=uploadItem.getContext(); Single duplicateImage = checkDuplicateImage(filePath); Single wrongGeoLocation = checkImageGeoLocation(uploadItem.getPlace(), filePath); Single darkImage = checkDarkImage(filePath); Single itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK); - Single checkFBMD = checkFBMD(filePath); + Single checkFBMD = checkFBMD(context,contentUri); Single zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle, (duplicate, wrongGeo, dark, title) -> { @@ -84,9 +92,9 @@ Single validateImage(UploadModel.UploadItem uploadItem, boolean checkTi * Thus we successfully protect common's from Facebook's copyright violation */ - public Single checkFBMD(String filePath) { + public Single checkFBMD(Context context,Uri contentUri) { try { - return readFBMD.processMetadata(filePath); + return readFBMD.processMetadata(context,contentUri); } catch (IOException e) { return Single.just(ImageUtils.FILE_FBMD); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ReadFBMD.java b/app/src/main/java/fr/free/nrw/commons/upload/ReadFBMD.java index 419cd9dd64..a0c677b3b0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ReadFBMD.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ReadFBMD.java @@ -1,43 +1,54 @@ package fr.free.nrw.commons.upload; -import com.icafe4j.image.meta.Metadata; -import com.icafe4j.image.meta.MetadataType; -import com.icafe4j.image.meta.iptc.IPTC; +import android.content.Context; +import android.net.Uri; + +import com.drew.imaging.ImageMetadataReader; +import com.drew.imaging.ImageProcessingException; +import com.drew.metadata.Metadata; +import com.drew.metadata.Tag; +import com.drew.metadata.iptc.IptcDirectory; import java.io.IOException; -import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import fr.free.nrw.commons.utils.ImageUtils; import io.reactivex.Single; -import timber.log.Timber; @Singleton public class ReadFBMD { @Inject - public ReadFBMD(){ + public ReadFBMD() { } - public Single processMetadata(String path) throws IOException { - Map metadataMap = Metadata.readMetadata(path); - Metadata metadata = metadataMap.get(MetadataType.IPTC); - byte[] dataInBytes = new byte[0]; + + public Single processMetadata(Context context, Uri contentUri) throws IOException { + Metadata readMetadata = null; try { - dataInBytes = ((IPTC) metadata).getDataSets().get("SpecialInstructions").get(0).getData(); - } catch (NullPointerException e) { + readMetadata = ImageMetadataReader.readMetadata(context.getContentResolver().openInputStream(contentUri)); + } catch (ImageProcessingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + IptcDirectory iptcDirectory = readMetadata != null ? readMetadata.getFirstDirectoryOfType(IptcDirectory.class) : null; + if (iptcDirectory == null) { return Single.just(ImageUtils.IMAGE_OK); } /** - * The byte array so obtained is used is tested to contain FBMD data - * Note: Any image downloaded from Facebook contains the ASCII code of the letters 'FBMD' in this bytecode extracted from it's IPTC metadata - * */ - if (dataInBytes[0] == 70 && dataInBytes[1] == 66 && dataInBytes[2] == 77 && dataInBytes[3] == 68) { - Timber.d("Contains FBMD"); + * We parse through all the tags in the IPTC directory if the tagname equals "Special Instructions". + * And the description string starts with FBMD. + * Then the source of image is facebook + * */ + for (Tag tag : iptcDirectory.getTags()) { + if (tag.getTagName().equals("Special Instructions") && tag.getDescription().substring(0, 4).equals("FBMD")) { return Single.just(ImageUtils.FILE_FBMD); } + } return Single.just(ImageUtils.IMAGE_OK); } } 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 f90708c654..42f43f1661 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 @@ -1,8 +1,11 @@ package fr.free.nrw.commons.upload; import android.annotation.SuppressLint; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.net.Uri; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -38,7 +41,7 @@ public class UploadModel { private static UploadItem DUMMY = new UploadItem( - Uri.EMPTY, + Uri.EMPTY, Uri.EMPTY, "", "", GPSExtractor.DUMMY, @@ -54,7 +57,7 @@ public class UploadModel { private boolean bottomCardState = true; private boolean rightCardState = true; private int currentStepIndex = 0; - private Context context; + public static Context context; private Disposable badImageSubscription; private SessionManager sessionManager; @@ -108,7 +111,7 @@ private UploadItem getUploadItem(UploadableFile uploadableFile, } Timber.d("File created date is %d", fileCreatedDate); GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface); - return new UploadItem(Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate, createdTimestampSource); + return new UploadItem(uploadableFile.getContentUri(), Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate, createdTimestampSource); } void onItemsProcessed(Place place, List uploadItems) { @@ -327,6 +330,7 @@ public List getItems() { @SuppressWarnings("WeakerAccess") static class UploadItem { + private final Uri originalContentUri; private final Uri mediaUri; private final String mimeType; private final String source; @@ -344,10 +348,12 @@ static class UploadItem { private BehaviorSubject imageQuality; @SuppressLint("CheckResult") - UploadItem(Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords, + UploadItem(Uri originalContentUri, + Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords, @Nullable Place place, long createdTimestamp, String createdTimestampSource) { + this.originalContentUri = originalContentUri; this.createdTimestampSource = createdTimestampSource; title = new Title(); descriptions = new ArrayList<>(); @@ -428,6 +434,14 @@ public String getFileName() { public Place getPlace() { return place; } + + public Uri getContentUri() { + return originalContentUri; + } + + public Context getContext(){ + return UploadModel.context; + } } -} +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt index 96b2000a65..3045242e21 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt @@ -82,7 +82,7 @@ class u { .thenReturn(false) `when`(mwApi!!.fileExistsWithName(ArgumentMatchers.anyString())) .thenReturn(false) - `when`(readFBMD?.processMetadata(ArgumentMatchers.anyString())) + `when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any())) .thenReturn(Single.just(ImageUtils.IMAGE_OK)) }