Skip to content

Commit 17a7980

Browse files
dbrantmisaochan
authored andcommitted
Remove dependency on Exif parsing library. (commons-app#2947)
* Remove dependency on Exif parsing library. * Fix test.
1 parent 6842bd8 commit 17a7980

File tree

6 files changed

+56
-119
lines changed

6 files changed

+56
-119
lines changed

app/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ dependencies {
9191
implementation "androidx.cardview:cardview:1.0.0"
9292
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
9393
implementation "androidx.exifinterface:exifinterface:1.0.0"
94-
//metadata extractor
95-
implementation 'com.drewnoakes:metadata-extractor:2.11.0'
94+
95+
//swipe_layout
96+
implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
9697
}
9798

9899
android {

app/src/main/java/fr/free/nrw/commons/filepicker/UploadableFile.java

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package fr.free.nrw.commons.filepicker;
22

3+
import android.annotation.SuppressLint;
34
import android.content.Context;
45
import android.database.Cursor;
56
import android.net.Uri;
67
import android.os.Parcel;
78
import android.os.Parcelable;
89

10+
import androidx.exifinterface.media.ExifInterface;
911
import androidx.annotation.Nullable;
1012

11-
import com.drew.imaging.ImageMetadataReader;
12-
import com.drew.imaging.ImageProcessingException;
13-
import com.drew.metadata.Metadata;
14-
import com.drew.metadata.exif.ExifSubIFDDirectory;
15-
1613
import java.io.File;
1714
import java.io.IOException;
1815
import java.util.Date;
@@ -125,16 +122,13 @@ private DateTimeWithSource getFileCreatedDateFromCP(Context context) {
125122
* @return
126123
*/
127124
private DateTimeWithSource getDateTimeFromExif() {
128-
Metadata metadata;
129125
try {
130-
metadata = ImageMetadataReader.readMetadata(file);
131-
ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
132-
if (directory!=null && directory.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) {
133-
Date date = directory.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL);
126+
ExifInterface exif = new ExifInterface(file.getAbsolutePath());
127+
@SuppressLint("RestrictedApi") long dateTime = exif.getDateTime();
128+
if (dateTime != -1) {
129+
Date date = new Date(dateTime);
134130
return new DateTimeWithSource(date, DateTimeWithSource.EXIF_SOURCE);
135131
}
136-
} catch (ImageProcessingException e) {
137-
e.printStackTrace();
138132
} catch (IOException e) {
139133
e.printStackTrace();
140134
}
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,36 @@
11
package fr.free.nrw.commons.upload;
22

3-
import com.drew.imaging.ImageMetadataReader;
4-
import com.drew.imaging.ImageProcessingException;
5-
import com.drew.metadata.Directory;
6-
import com.drew.metadata.Metadata;
7-
8-
import java.io.File;
9-
import java.io.IOException;
3+
import androidx.exifinterface.media.ExifInterface;
104

115
import javax.inject.Inject;
126
import javax.inject.Singleton;
137

148
import fr.free.nrw.commons.utils.ImageUtils;
159
import io.reactivex.Single;
16-
import timber.log.Timber;
1710

1811
/**
19-
* We try to avoid copyright violations in commons app.
20-
* For doing that we read EXIF data using the library metadata-reader
21-
* If an image doesn't have any EXIF Directoris in it's metadata then the image is an
22-
* internet download image(and not the one taken using phone's camera) */
23-
12+
* We try to minimize uploads from the Commons app that might be copyright violations.
13+
* If an image does not have any Exif metadata, then it was likely downloaded from the internet,
14+
* and is probably not an original work by the user. We detect these kinds of images by looking
15+
* for the presence of some basic Exif metadata.
16+
*/
2417
@Singleton
2518
public class EXIFReader {
2619
@Inject
2720
public EXIFReader() {
28-
//Empty
2921
}
30-
/**
31-
* The method takes in path of the image and reads metadata using the library metadata-extractor
32-
* And the checks for the presence of EXIF Directories in metadata object
33-
* */
3422

3523
public Single<Integer> processMetadata(String path) {
36-
Metadata readMetadata = null;
3724
try {
38-
readMetadata = ImageMetadataReader.readMetadata(new File(path));
39-
} catch (ImageProcessingException e) {
40-
Timber.d(e.toString());
41-
} catch (IOException e) {
42-
Timber.d(e.toString());
43-
}
44-
if (readMetadata != null) {
45-
for (Directory directory : readMetadata.getDirectories()) {
46-
// In case of internet downloaded image these three fields are not present
47-
if (directory.getName().equals("Exif IFD0") //Contains information about the device capturing the photo
48-
|| directory.getName().equals("Exif SubIFD") //contains information like date, time and pixels of the image
49-
|| directory.getName().equals("Exif Thumbnail")) //contains information about image thumbnail like compression and reolution
50-
{
51-
Timber.d(directory.getName() + " Contains metadata");
52-
return Single.just(ImageUtils.IMAGE_OK);
53-
}
25+
ExifInterface exif = new ExifInterface(path);
26+
if (exif.getAttribute(ExifInterface.TAG_MAKE) != null
27+
|| exif.getAttribute(ExifInterface.TAG_DATETIME) != null) {
28+
return Single.just(ImageUtils.IMAGE_OK);
5429
}
30+
} catch (Exception e) {
31+
return Single.just(ImageUtils.FILE_NO_EXIF);
5532
}
5633
return Single.just(ImageUtils.FILE_NO_EXIF);
5734
}
58-
5935
}
6036

app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java

+13-30
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package fr.free.nrw.commons.upload;
22

33
import android.content.Context;
4-
import android.net.Uri;
54

65
import org.apache.commons.lang3.StringUtils;
76

8-
import java.io.IOException;
9-
107
import javax.inject.Inject;
118
import javax.inject.Singleton;
129

@@ -23,10 +20,7 @@
2320

2421
/**
2522
* Methods for pre-processing images to be uploaded
26-
*//*if (dataInBytes[0] == 70 && dataInBytes[1] == 66 && dataInBytes[2] == 77 && dataInBytes[3] == 68) {
27-
Timber.d("Contains FBMD");
28-
return Single.just(ImageUtils.FILE_FBMD);
29-
}*/
23+
*/
3024
@Singleton
3125
public class ImageProcessingService {
3226
private final FileUtilsWrapper fileUtilsWrapper;
@@ -64,12 +58,11 @@ Single<Integer> validateImage(UploadModel.UploadItem uploadItem, boolean checkTi
6458
}
6559
Timber.d("Checking the validity of image");
6660
String filePath = uploadItem.getMediaUri().getPath();
67-
Uri contentUri=uploadItem.getContentUri();
6861
Single<Integer> duplicateImage = checkDuplicateImage(filePath);
6962
Single<Integer> wrongGeoLocation = checkImageGeoLocation(uploadItem.getPlace(), filePath);
7063
Single<Integer> darkImage = checkDarkImage(filePath);
7164
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
72-
Single<Integer> checkFBMD = checkFBMD(context,contentUri);
65+
Single<Integer> checkFBMD = checkFBMD(filePath);
7366
Single<Integer> checkEXIF = checkEXIF(filePath);
7467

7568
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
@@ -84,31 +77,21 @@ Single<Integer> validateImage(UploadModel.UploadItem uploadItem, boolean checkTi
8477
}
8578

8679
/**
87-
* Other than the Image quality we need to check that using this Image doesn't violate's facebook's copyright's.
88-
* Whenever a user tries to upload an image that was downloaded from Facebook then we warn the user with a message to stop the upload
89-
* To know whether the Image is downloaded from facebook:
90-
* -We read the metadata of any Image and check for FBMD
91-
* -Facebook downloaded image's contains metadata of the type IPTC
92-
* - From this IPTC metadata we extract a byte array that contains FBMD as it's initials. If the image was downloaded from facebook
93-
* Thus we successfully protect common's from Facebook's copyright violation
80+
* We want to discourage users from uploading images to Commons that were taken from Facebook.
81+
* This attempts to detect whether an image was downloaded from Facebook by heuristically
82+
* searching for metadata that is specific to images that come from Facebook.
9483
*/
95-
96-
public Single<Integer> checkFBMD(Context context,Uri contentUri) {
97-
try {
98-
return readFBMD.processMetadata(context,contentUri);
99-
} catch (IOException e) {
100-
return Single.just(ImageUtils.FILE_FBMD);
101-
}
84+
private Single<Integer> checkFBMD(String filepath) {
85+
return readFBMD.processMetadata(filepath);
10286
}
10387

10488
/**
105-
* To avoid copyright we check for EXIF data in any image.
106-
* Images that are downloaded from internet generally don't have any EXIF data in them
107-
* while images taken via camera or screenshots in phone have EXIF data with them.
108-
* So we check if the image has no EXIF data then we display a warning to the user
109-
* * */
110-
111-
public Single<Integer> checkEXIF(String filepath){
89+
* We try to minimize uploads from the Commons app that might be copyright violations.
90+
* If an image does not have any Exif metadata, then it was likely downloaded from the internet,
91+
* and is probably not an original work by the user. We detect these kinds of images by looking
92+
* for the presence of some basic Exif metadata.
93+
*/
94+
private Single<Integer> checkEXIF(String filepath) {
11295
return EXIFReader.processMetadata(filepath);
11396
}
11497

app/src/main/java/fr/free/nrw/commons/upload/ReadFBMD.java

+22-29
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
package fr.free.nrw.commons.upload;
22

3-
import android.content.Context;
4-
import android.net.Uri;
5-
6-
import com.drew.imaging.ImageMetadataReader;
7-
import com.drew.imaging.ImageProcessingException;
8-
import com.drew.metadata.Metadata;
9-
import com.drew.metadata.Tag;
10-
import com.drew.metadata.iptc.IptcDirectory;
11-
3+
import java.io.FileInputStream;
124
import java.io.IOException;
135

146
import javax.inject.Inject;
@@ -17,37 +9,38 @@
179
import fr.free.nrw.commons.utils.ImageUtils;
1810
import io.reactivex.Single;
1911

12+
/**
13+
* We want to discourage users from uploading images to Commons that were taken from Facebook.
14+
* This attempts to detect whether an image was downloaded from Facebook by heuristically
15+
* searching for metadata that is specific to images that come from Facebook.
16+
*/
2017
@Singleton
2118
public class ReadFBMD {
2219

2320
@Inject
2421
public ReadFBMD() {
25-
2622
}
2723

28-
public Single<Integer> processMetadata(Context context, Uri contentUri) throws IOException {
29-
Metadata readMetadata = null;
24+
public Single<Integer> processMetadata(String path) {
3025
try {
31-
readMetadata = ImageMetadataReader.readMetadata(context.getContentResolver().openInputStream(contentUri));
32-
} catch (ImageProcessingException e) {
33-
e.printStackTrace();
34-
} catch (IOException e) {
35-
e.printStackTrace();
36-
}
26+
int psBlockOffset;
27+
int fbmdOffset;
28+
29+
try (FileInputStream fs = new FileInputStream(path)) {
30+
byte[] bytes = new byte[4096];
31+
fs.read(bytes);
32+
fs.close();
33+
String fileStr = new String(bytes);
34+
psBlockOffset = fileStr.indexOf("8BIM");
35+
fbmdOffset = fileStr.indexOf("FBMD");
36+
}
3737

38-
IptcDirectory iptcDirectory = readMetadata != null ? readMetadata.getFirstDirectoryOfType(IptcDirectory.class) : null;
39-
if (iptcDirectory == null) {
40-
return Single.just(ImageUtils.IMAGE_OK);
41-
}
42-
/**
43-
* We parse through all the tags in the IPTC directory if the tagname equals "Special Instructions".
44-
* And the description string starts with FBMD.
45-
* Then the source of image is facebook
46-
* */
47-
for (Tag tag : iptcDirectory.getTags()) {
48-
if (tag.getTagName().equals("Special Instructions") && tag.getDescription().substring(0, 4).equals("FBMD")) {
38+
if (psBlockOffset > 0 && fbmdOffset > 0
39+
&& fbmdOffset > psBlockOffset && fbmdOffset - psBlockOffset < 0x80) {
4940
return Single.just(ImageUtils.FILE_FBMD);
5041
}
42+
} catch (IOException e) {
43+
e.printStackTrace();
5144
}
5245
return Single.just(ImageUtils.IMAGE_OK);
5346
}

app/src/main/res/values-en-gb/error.xml

-10
This file was deleted.

0 commit comments

Comments
 (0)