mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
* initial commit * initial commit * reading 3 exif directories * minor changes * fixed unit tests * reviwied requested changes * minor changes * minor changes
This commit is contained in:
parent
8cd87ad148
commit
3417d713d2
5 changed files with 89 additions and 9 deletions
60
app/src/main/java/fr/free/nrw/commons/upload/EXIFReader.java
Normal file
60
app/src/main/java/fr/free/nrw/commons/upload/EXIFReader.java
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
|
import com.drew.imaging.ImageMetadataReader;
|
||||||
|
import com.drew.imaging.ImageProcessingException;
|
||||||
|
import com.drew.metadata.Directory;
|
||||||
|
import com.drew.metadata.Metadata;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.utils.ImageUtils;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We try to avoid copyright violations in commons app.
|
||||||
|
* For doing that we read EXIF data using the library metadata-reader
|
||||||
|
* If an image doesn't have any EXIF Directoris in it's metadata then the image is an
|
||||||
|
* internet download image(and not the one taken using phone's camera) */
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class EXIFReader {
|
||||||
|
@Inject
|
||||||
|
public EXIFReader() {
|
||||||
|
//Empty
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The method takes in path of the image and reads metadata using the library metadata-extractor
|
||||||
|
* And the checks for the presence of EXIF Directories in metadata object
|
||||||
|
* */
|
||||||
|
|
||||||
|
public Single<Integer> processMetadata(String path) {
|
||||||
|
Metadata readMetadata = null;
|
||||||
|
try {
|
||||||
|
readMetadata = ImageMetadataReader.readMetadata(new File(path));
|
||||||
|
} catch (ImageProcessingException e) {
|
||||||
|
Timber.d(e.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Timber.d(e.toString());
|
||||||
|
}
|
||||||
|
if (readMetadata != null) {
|
||||||
|
for (Directory directory : readMetadata.getDirectories()) {
|
||||||
|
// In case of internet downloaded image these three fields are not present
|
||||||
|
if (directory.getName().equals("Exif IFD0") //Contains information about the device capturing the photo
|
||||||
|
|| directory.getName().equals("Exif SubIFD") //contains information like date, time and pixels of the image
|
||||||
|
|| directory.getName().equals("Exif Thumbnail")) //contains information about image thumbnail like compression and reolution
|
||||||
|
{
|
||||||
|
Timber.d(directory.getName() + " Contains metadata");
|
||||||
|
return Single.just(ImageUtils.IMAGE_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Single.just(ImageUtils.FILE_NO_EXIF);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -34,17 +34,19 @@ public class ImageProcessingService {
|
||||||
private final ImageUtilsWrapper imageUtilsWrapper;
|
private final ImageUtilsWrapper imageUtilsWrapper;
|
||||||
private final MediaWikiApi mwApi;
|
private final MediaWikiApi mwApi;
|
||||||
private final ReadFBMD readFBMD;
|
private final ReadFBMD readFBMD;
|
||||||
|
private final EXIFReader EXIFReader;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
|
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
|
||||||
BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper,
|
BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper,
|
||||||
ImageUtilsWrapper imageUtilsWrapper,
|
ImageUtilsWrapper imageUtilsWrapper,
|
||||||
MediaWikiApi mwApi, ReadFBMD readFBMD) {
|
MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader) {
|
||||||
this.fileUtilsWrapper = fileUtilsWrapper;
|
this.fileUtilsWrapper = fileUtilsWrapper;
|
||||||
this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper;
|
this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper;
|
||||||
this.imageUtilsWrapper = imageUtilsWrapper;
|
this.imageUtilsWrapper = imageUtilsWrapper;
|
||||||
this.mwApi = mwApi;
|
this.mwApi = mwApi;
|
||||||
this.readFBMD = readFBMD;
|
this.readFBMD = readFBMD;
|
||||||
|
this.EXIFReader = EXIFReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,17 +71,16 @@ public class ImageProcessingService {
|
||||||
Single<Integer> darkImage = checkDarkImage(filePath);
|
Single<Integer> darkImage = checkDarkImage(filePath);
|
||||||
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
|
Single<Integer> itemTitle = checkTitle ? validateItemTitle(uploadItem) : Single.just(ImageUtils.IMAGE_OK);
|
||||||
Single<Integer> checkFBMD = checkFBMD(context,contentUri);
|
Single<Integer> checkFBMD = checkFBMD(context,contentUri);
|
||||||
|
Single<Integer> checkEXIF = checkEXIF(filePath);
|
||||||
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
|
Single<Integer> zipResult = Single.zip(duplicateImage, wrongGeoLocation, darkImage, itemTitle,
|
||||||
(duplicate, wrongGeo, dark, title) -> {
|
(duplicate, wrongGeo, dark, title) -> {
|
||||||
Timber.d("Result for duplicate: %d, geo: %d, dark: %d, title: %d", 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 duplicate | wrongGeo | dark | title;
|
||||||
});
|
});
|
||||||
|
return Single.zip(zipResult, checkFBMD , checkEXIF , (zip , fbmd , exif)->{
|
||||||
return Single.zip(zipResult, checkFBMD, (zip, fbmd) -> {
|
Timber.d("zip:" + zip + "fbmd:" + fbmd + "exif:" + exif);
|
||||||
Timber.d("zip:" + zip + "fbmd:" + fbmd);
|
return zip | fbmd | exif;
|
||||||
return zip | fbmd;
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -100,6 +101,17 @@ public class ImageProcessingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To avoid copyright we check for EXIF data in any image.
|
||||||
|
* Images that are downloaded from internet generally don't have any EXIF data in them
|
||||||
|
* while images taken via camera or screenshots in phone have EXIF data with them.
|
||||||
|
* So we check if the image has no EXIF data then we display a warning to the user
|
||||||
|
* * */
|
||||||
|
|
||||||
|
public Single<Integer> checkEXIF(String filepath){
|
||||||
|
return EXIFReader.processMetadata(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks item title
|
* Checks item title
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,11 @@ public class ImageUtils {
|
||||||
* ie. 10000
|
* ie. 10000
|
||||||
*/
|
*/
|
||||||
public static final int FILE_FBMD = 1 << 4;
|
public static final int FILE_FBMD = 1 << 4;
|
||||||
|
/**
|
||||||
|
* The parameter FILE_NO_EXIF is returned from the class EXIFReader if the uploaded image does not contains EXIF data else returns IMAGE_OK
|
||||||
|
* ie. 100000
|
||||||
|
*/
|
||||||
|
public static final int FILE_NO_EXIF = 1 << 5;
|
||||||
public static final int IMAGE_OK = 0;
|
public static final int IMAGE_OK = 0;
|
||||||
public static final int IMAGE_KEEP = -1;
|
public static final int IMAGE_KEEP = -1;
|
||||||
public static final int IMAGE_WAIT = -2;
|
public static final int IMAGE_WAIT = -2;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/listView"
|
android:id="@+id/listView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ class u {
|
||||||
internal var mwApi: MediaWikiApi? = null
|
internal var mwApi: MediaWikiApi? = null
|
||||||
@Mock
|
@Mock
|
||||||
internal var readFBMD: ReadFBMD?=null
|
internal var readFBMD: ReadFBMD?=null
|
||||||
|
@Mock
|
||||||
|
internal var readEXIF: EXIFReader?=null
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
var imageProcessingService: ImageProcessingService? = null
|
var imageProcessingService: ImageProcessingService? = null
|
||||||
|
|
@ -84,6 +86,8 @@ class u {
|
||||||
.thenReturn(false)
|
.thenReturn(false)
|
||||||
`when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any()))
|
`when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any()))
|
||||||
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
|
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
|
||||||
|
`when`(readEXIF?.processMetadata(ArgumentMatchers.anyString()))
|
||||||
|
.thenReturn(Single.just(ImageUtils.IMAGE_OK))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue