Fix update date template based on date source (#2483)

* Fix update date template based on date source

* Add javadocs
This commit is contained in:
Vivek Maskara 2019-02-20 15:54:52 +05:30 committed by Josephine Lim
parent b698d14172
commit 3ba6c5990e
4 changed files with 151 additions and 19 deletions

View file

@ -27,7 +27,8 @@ dependencies {
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
implementation 'com.facebook.fresco:fresco:1.10.0' implementation 'com.facebook.fresco:fresco:1.10.0'
implementation 'com.drewnoakes:metadata-extractor:2.11.0'
// UI // UI
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'com.github.chrisbanes:PhotoView:2.0.0' implementation 'com.github.chrisbanes:PhotoView:2.0.0'

View file

@ -3,25 +3,33 @@ package fr.free.nrw.commons.contributions;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.StringDef; import android.support.annotation.StringDef;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.filepicker.UploadableFile;
import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.StringUtils;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
public class Contribution extends Media { public class Contribution extends Media {
//{{According to EXIF data|2009-01-09}}
private static final String TEMPLATE_DATE_ACC_TO_EXIF = "|date={{According to EXIF data|%s}}";
//{{date|2009|1|9}} 9 January 2009
private static final String TEMPLATE_DATA_OTHER_SOURCE = "{{date|%d|%d|%d}}";
public static Creator<Contribution> CREATOR = new Creator<Contribution>() { public static Creator<Contribution> CREATOR = new Creator<Contribution>() {
@Override @Override
public Contribution createFromParcel(Parcel parcel) { public Contribution createFromParcel(Parcel parcel) {
@ -57,6 +65,7 @@ public class Contribution extends Media {
private boolean isMultiple; private boolean isMultiple;
private String wikiDataEntityId; private String wikiDataEntityId;
private Uri contentProviderUri; private Uri contentProviderUri;
private String dateCreatedSource;
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated, public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated,
int state, long dataLength, Date dateUploaded, long transferred, int state, long dataLength, Date dateUploaded, long transferred,
@ -71,6 +80,7 @@ public class Contribution extends Media {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.license = license; this.license = license;
this.dateCreatedSource = "";
} }
public Contribution(Uri localUri, String imageUrl, String filename, String description, long dataLength, public Contribution(Uri localUri, String imageUrl, String filename, String description, long dataLength,
@ -78,6 +88,7 @@ public class Contribution extends Media {
super(localUri, imageUrl, filename, description, dataLength, dateCreated, dateUploaded, creator); super(localUri, imageUrl, filename, description, dataLength, dateCreated, dateUploaded, creator);
this.decimalCoords = decimalCoords; this.decimalCoords = decimalCoords;
this.editSummary = editSummary; this.editSummary = editSummary;
this.dateCreatedSource = "";
} }
public Contribution(Parcel in) { public Contribution(Parcel in) {
@ -99,7 +110,13 @@ public class Contribution extends Media {
parcel.writeInt(isMultiple ? 1 : 0); parcel.writeInt(isMultiple ? 1 : 0);
} }
public String getDateCreatedSource() {
return dateCreatedSource;
}
public void setDateCreatedSource(String dateCreatedSource) {
this.dateCreatedSource = dateCreatedSource;
}
public boolean getMultiple() { public boolean getMultiple() {
return isMultiple; return isMultiple;
@ -143,20 +160,19 @@ public class Contribution extends Media {
public String getPageContents(Context applicationContext) { public String getPageContents(Context applicationContext) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
buffer buffer
.append("== {{int:filedesc}} ==\n") .append("== {{int:filedesc}} ==\n")
.append("{{Information\n") .append("{{Information\n")
.append("|description=").append(getDescription()).append("\n") .append("|description=").append(getDescription()).append("\n")
.append("|source=").append("{{own}}\n") .append("|source=").append("{{own}}\n")
.append("|author=[[User:").append(creator).append("|").append(creator).append("]]\n"); .append("|author=[[User:").append(creator).append("|").append(creator).append("]]\n");
if (dateCreated != null) {
buffer String templatizedCreatedDate = getTemplatizedCreatedDate();
.append("|date={{According to EXIF data|").append(isoFormat.format(dateCreated)).append("}}\n"); if (!StringUtils.isNullOrWhiteSpace(templatizedCreatedDate)) {
buffer.append("|date=").append(templatizedCreatedDate);
} }
buffer
.append("}}").append("\n"); buffer.append("}}").append("\n");
//Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null //Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null
if (decimalCoords != null) { if (decimalCoords != null) {
@ -178,6 +194,28 @@ public class Contribution extends Media {
return buffer.toString(); return buffer.toString();
} }
/**
* Returns upload date in either TEMPLATE_DATE_ACC_TO_EXIF or TEMPLATE_DATA_OTHER_SOURCE
* @return
*/
private String getTemplatizedCreatedDate() {
if (dateCreated != null) {
if (UploadableFile.DateTimeWithSource.EXIF_SOURCE.equals(dateCreatedSource)) {
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
return String.format(Locale.ENGLISH, TEMPLATE_DATE_ACC_TO_EXIF, isoFormat.format(dateCreated)) + "\n";
} else {
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateCreated);
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
return String.format(Locale.ENGLISH, TEMPLATE_DATA_OTHER_SOURCE,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)) + "\n";
}
}
return "";
}
@Override @Override
public void setFilename(String filename) { public void setFilename(String filename) {
this.filename = filename; this.filename = filename;

View file

@ -6,7 +6,16 @@ import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Date;
import javax.annotation.Nullable;
import fr.free.nrw.commons.upload.FileUtils; import fr.free.nrw.commons.upload.FileUtils;
@ -62,16 +71,32 @@ public class UploadableFile implements Parcelable {
return 0; return 0;
} }
/**
* First try to get the file creation date from EXIF else fall back to CP
* @param context
* @return
*/
@Nullable
public DateTimeWithSource getFileCreatedDate(Context context) {
DateTimeWithSource dateTimeFromExif = getDateTimeFromExif();
if (dateTimeFromExif == null) {
return getFileCreatedDateFromCP(context);
} else {
return dateTimeFromExif;
}
}
/** /**
* Get filePath creation date from uri from all possible content providers * Get filePath creation date from uri from all possible content providers
* *
* @return * @return
*/ */
public long getFileCreatedDate(Context context) { private DateTimeWithSource getFileCreatedDateFromCP(Context context) {
try { try {
Cursor cursor = context.getContentResolver().query(contentUri, null, null, null, null); Cursor cursor = context.getContentResolver().query(contentUri, null, null, null, null);
if (cursor == null) { if (cursor == null) {
return -1;//Could not fetch last_modified return null;//Could not fetch last_modified
} }
//Content provider contracts for opening gallery from the app and that by sharing from gallery from outside are different and we need to handle both the cases //Content provider contracts for opening gallery from the app and that by sharing from gallery from outside are different and we need to handle both the cases
int lastModifiedColumnIndex = cursor.getColumnIndex("last_modified");//If gallery is opened from in app int lastModifiedColumnIndex = cursor.getColumnIndex("last_modified");//If gallery is opened from in app
@ -80,18 +105,69 @@ public class UploadableFile implements Parcelable {
} }
//If both the content providers do not give the data, lets leave it to Jesus //If both the content providers do not give the data, lets leave it to Jesus
if (lastModifiedColumnIndex == -1) { if (lastModifiedColumnIndex == -1) {
return -1l; return null;
} }
cursor.moveToFirst(); cursor.moveToFirst();
return cursor.getLong(lastModifiedColumnIndex); return new DateTimeWithSource(cursor.getLong(lastModifiedColumnIndex), DateTimeWithSource.CP_SOURCE);
} catch (Exception e) { } catch (Exception e) {
return -1;////Could not fetch last_modified return null;////Could not fetch last_modified
} }
} }
/**
* Get filePath creation date from uri from EXIF
*
* @return
*/
private DateTimeWithSource getDateTimeFromExif() {
Metadata metadata;
try {
metadata = ImageMetadataReader.readMetadata(file);
ExifSubIFDDirectory directory = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
if (directory!=null && directory.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)) {
Date date = directory.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL);
return new DateTimeWithSource(date, DateTimeWithSource.EXIF_SOURCE);
}
} catch (ImageProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override @Override
public void writeToParcel(Parcel parcel, int i) { public void writeToParcel(Parcel parcel, int i) {
parcel.writeParcelable(contentUri, 0); parcel.writeParcelable(contentUri, 0);
parcel.writeSerializable(file); parcel.writeSerializable(file);
} }
/**
* This class contains the epochDate along with the source from which it was extracted
*/
public class DateTimeWithSource {
public static final String CP_SOURCE = "contentProvider";
public static final String EXIF_SOURCE = "exif";
private final long epochDate;
private final String source;
public DateTimeWithSource(long epochDate, String source) {
this.epochDate = epochDate;
this.source = source;
}
public DateTimeWithSource(Date date, String source) {
this.epochDate = date.getTime();
this.source = source;
}
public long getEpochDate() {
return epochDate;
}
public String getSource() {
return source;
}
}
} }

View file

@ -43,7 +43,7 @@ public class UploadModel {
"", "",
GPSExtractor.DUMMY, GPSExtractor.DUMMY,
null, null,
-1L) { -1L, "") {
}; };
private final BasicKvStore basicKvStore; private final BasicKvStore basicKvStore;
private final List<String> licenses; private final List<String> licenses;
@ -99,10 +99,16 @@ public class UploadModel {
String source, String source,
SimilarImageInterface similarImageInterface) { SimilarImageInterface similarImageInterface) {
fileProcessor.initFileDetails(Objects.requireNonNull(uploadableFile.getFilePath()), context.getContentResolver()); fileProcessor.initFileDetails(Objects.requireNonNull(uploadableFile.getFilePath()), context.getContentResolver());
long fileCreatedDate = uploadableFile.getFileCreatedDate(context); UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile.getFileCreatedDate(context);
long fileCreatedDate = -1;
String createdTimestampSource = "";
if (dateTimeWithSource != null) {
fileCreatedDate = dateTimeWithSource.getEpochDate();
createdTimestampSource = dateTimeWithSource.getSource();
}
Timber.d("File created date is %d", fileCreatedDate); Timber.d("File created date is %d", fileCreatedDate);
GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface); GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface);
return new UploadItem(Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate); return new UploadItem(Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate, createdTimestampSource);
} }
void onItemsProcessed(Place place, List<UploadItem> uploadItems) { void onItemsProcessed(Place place, List<UploadItem> uploadItems) {
@ -284,11 +290,13 @@ public class UploadModel {
contribution.setTag("mimeType", item.mimeType); contribution.setTag("mimeType", item.mimeType);
contribution.setSource(item.source); contribution.setSource(item.source);
contribution.setContentProviderUri(item.mediaUri); contribution.setContentProviderUri(item.mediaUri);
Timber.d("Created timestamp while building contribution is %s, %s", Timber.d("Created timestamp while building contribution is %s, %s",
item.getCreatedTimestamp(), item.getCreatedTimestamp(),
new Date(item.getCreatedTimestamp())); new Date(item.getCreatedTimestamp()));
if (item.createdTimestamp != -1L) { if (item.createdTimestamp != -1L) {
contribution.setDateCreated(new Date(item.getCreatedTimestamp())); contribution.setDateCreated(new Date(item.getCreatedTimestamp()));
contribution.setDateCreatedSource(item.getCreatedTimestampSource());
//Set the date only if you have it, else the upload service is gonna try it the other way //Set the date only if you have it, else the upload service is gonna try it the other way
} }
return contribution; return contribution;
@ -332,10 +340,15 @@ public class UploadModel {
private boolean visited; private boolean visited;
private boolean error; private boolean error;
private long createdTimestamp; private long createdTimestamp;
private String createdTimestampSource;
private BehaviorSubject<Integer> imageQuality; private BehaviorSubject<Integer> imageQuality;
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
UploadItem(Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords, @Nullable Place place, long createdTimestamp) { UploadItem(Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords,
@Nullable Place place,
long createdTimestamp,
String createdTimestampSource) {
this.createdTimestampSource = createdTimestampSource;
title = new Title(); title = new Title();
descriptions = new ArrayList<>(); descriptions = new ArrayList<>();
descriptions.add(new Description()); descriptions.add(new Description());
@ -348,6 +361,10 @@ public class UploadModel {
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT); imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
} }
public String getCreatedTimestampSource() {
return createdTimestampSource;
}
public String getMimeType() { public String getMimeType() {
return mimeType; return mimeType;
} }