Skip to content

Commit 3417d71

Browse files
vanshikaaroraujjwalagrawal17
authored andcommitted
Fixes commons-app#97: Added metadata reader for reading exif data (commons-app#2581)
* initial commit * initial commit * reading 3 exif directories * minor changes * fixed unit tests * reviwied requested changes * minor changes * minor changes
1 parent 8cd87ad commit 3417d71

File tree

5 files changed

+89
-9
lines changed

5 files changed

+89
-9
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package fr.free.nrw.commons.upload;
2+
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;
10+
11+
import javax.inject.Inject;
12+
import javax.inject.Singleton;
13+
14+
import fr.free.nrw.commons.utils.ImageUtils;
15+
import io.reactivex.Single;
16+
import timber.log.Timber;
17+
18+
/**
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+
24+
@Singleton
25+
public class EXIFReader {
26+
@Inject
27+
public EXIFReader() {
28+
//Empty
29+
}
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+
* */
34+
35+
public Single<Integer> processMetadata(String path) {
36+
Metadata readMetadata = null;
37+
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+
}
54+
}
55+
}
56+
return Single.just(ImageUtils.FILE_NO_EXIF);
57+
}
58+
59+
}
60+

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,19 @@ public class ImageProcessingService {
3434
private final ImageUtilsWrapper imageUtilsWrapper;
3535
private final MediaWikiApi mwApi;
3636
private final ReadFBMD readFBMD;
37+
private final EXIFReader EXIFReader;
3738

3839
@Inject
3940
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
4041
BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper,
4142
ImageUtilsWrapper imageUtilsWrapper,
42-
MediaWikiApi mwApi, ReadFBMD readFBMD) {
43+
MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader) {
4344
this.fileUtilsWrapper = fileUtilsWrapper;
4445
this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper;
4546
this.imageUtilsWrapper = imageUtilsWrapper;
4647
this.mwApi = mwApi;
4748
this.readFBMD = readFBMD;
49+
this.EXIFReader = EXIFReader;
4850
}
4951

5052
/**
@@ -69,17 +71,16 @@ Single<Integer> validateImage(UploadModel.UploadItem uploadItem, boolean checkTi
6971
Single<Integer> darkImage = checkDarkImage(filePath);
7072
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
7173
Single<Integer> checkFBMD = checkFBMD(context,contentUri);
72-
74+
Single<Integer> checkEXIF = checkEXIF(filePath);
7375
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
7476
(duplicate, wrongGeo, dark, title) -> {
7577
Timber.d("Result for duplicate: %d, geo: %d, dark: %d, title: %d", duplicate, wrongGeo, dark, title);
7678
return duplicate | wrongGeo | dark | title;
7779
});
78-
79-
return Single.zip(zipResult, checkFBMD, (zip, fbmd) -> {
80-
Timber.d("zip:" + zip + "fbmd:" + fbmd);
81-
return zip | fbmd;
82-
});
80+
return Single.zip(zipResult, checkFBMD , checkEXIF , (zip , fbmd , exif)->{
81+
Timber.d("zip:" + zip + "fbmd:" + fbmd + "exif:" + exif);
82+
return zip | fbmd | exif;
83+
});
8384
}
8485

8586
/**
@@ -100,6 +101,17 @@ public Single<Integer> checkFBMD(Context context,Uri contentUri) {
100101
}
101102
}
102103

104+
/**
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){
112+
return EXIFReader.processMetadata(filepath);
113+
}
114+
103115

104116
/**
105117
* Checks item title

app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ public class ImageUtils {
5555
* ie. 10000
5656
*/
5757
public static final int FILE_FBMD = 1 << 4;
58-
58+
/**
59+
* The parameter FILE_NO_EXIF is returned from the class EXIFReader if the uploaded image does not contains EXIF data else returns IMAGE_OK
60+
* ie. 100000
61+
*/
62+
public static final int FILE_NO_EXIF = 1 << 5;
5963
public static final int IMAGE_OK = 0;
6064
public static final int IMAGE_KEEP = -1;
6165
public static final int IMAGE_WAIT = -2;

app/src/main/res/layout/activity_notification.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<androidx.recyclerview.widget.RecyclerView
3333
android:id="@+id/listView"
3434
android:layout_width="match_parent"
35-
android:layout_height="match_parent" />
35+
android:layout_height="match_parent"/>
3636

3737
</RelativeLayout>
3838

app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class u {
3030
internal var mwApi: MediaWikiApi? = null
3131
@Mock
3232
internal var readFBMD: ReadFBMD?=null
33+
@Mock
34+
internal var readEXIF: EXIFReader?=null
3335

3436
@InjectMocks
3537
var imageProcessingService: ImageProcessingService? = null
@@ -84,6 +86,8 @@ class u {
8486
.thenReturn(false)
8587
`when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any()))
8688
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
89+
`when`(readEXIF?.processMetadata(ArgumentMatchers.anyString()))
90+
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
8791
}
8892

8993
@Test

0 commit comments

Comments
 (0)