mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +01:00 
			
		
		
		
	* Fix update date template based on date source * Add javadocs
This commit is contained in:
		
							parent
							
								
									083a7a80f0
								
							
						
					
					
						commit
						546322ac35
					
				
					 4 changed files with 151 additions and 19 deletions
				
			
		|  | @ -3,25 +3,33 @@ package fr.free.nrw.commons.contributions; | |||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import android.os.Parcel; | ||||
| import android.support.annotation.IntDef; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.StringDef; | ||||
| 
 | ||||
| import java.lang.annotation.Retention; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.Calendar; | ||||
| import java.util.Date; | ||||
| 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.Media; | ||||
| import fr.free.nrw.commons.filepicker.UploadableFile; | ||||
| import fr.free.nrw.commons.settings.Prefs; | ||||
| import fr.free.nrw.commons.utils.ConfigUtils; | ||||
| import fr.free.nrw.commons.utils.StringUtils; | ||||
| 
 | ||||
| import static java.lang.annotation.RetentionPolicy.SOURCE; | ||||
| 
 | ||||
| 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>() { | ||||
|         @Override | ||||
|         public Contribution createFromParcel(Parcel parcel) { | ||||
|  | @ -57,6 +65,7 @@ public class  Contribution extends Media { | |||
|     private boolean isMultiple; | ||||
|     private String wikiDataEntityId; | ||||
|     private Uri contentProviderUri; | ||||
|     private String dateCreatedSource; | ||||
| 
 | ||||
|     public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated, | ||||
|                         int state, long dataLength, Date dateUploaded, long transferred, | ||||
|  | @ -71,6 +80,7 @@ public class  Contribution extends Media { | |||
|         this.width = width; | ||||
|         this.height = height; | ||||
|         this.license = license; | ||||
|         this.dateCreatedSource = ""; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|         this.decimalCoords = decimalCoords; | ||||
|         this.editSummary = editSummary; | ||||
|         this.dateCreatedSource = ""; | ||||
|     } | ||||
| 
 | ||||
|     public Contribution(Parcel in) { | ||||
|  | @ -99,7 +110,13 @@ public class  Contribution extends Media { | |||
|         parcel.writeInt(isMultiple ? 1 : 0); | ||||
|     } | ||||
| 
 | ||||
|     public String getDateCreatedSource() { | ||||
|         return dateCreatedSource; | ||||
|     } | ||||
| 
 | ||||
|     public void setDateCreatedSource(String dateCreatedSource) { | ||||
|         this.dateCreatedSource = dateCreatedSource; | ||||
|     } | ||||
| 
 | ||||
|     public boolean getMultiple() { | ||||
|         return isMultiple; | ||||
|  | @ -143,20 +160,19 @@ public class  Contribution extends Media { | |||
| 
 | ||||
|     public String getPageContents(Context applicationContext) { | ||||
|         StringBuilder buffer = new StringBuilder(); | ||||
|         SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH); | ||||
| 
 | ||||
|         buffer | ||||
|                 .append("== {{int:filedesc}} ==\n") | ||||
|                 .append("{{Information\n") | ||||
|                 .append("|description=").append(getDescription()).append("\n") | ||||
|                 .append("|source=").append("{{own}}\n") | ||||
|                 .append("|author=[[User:").append(creator).append("|").append(creator).append("]]\n"); | ||||
|         if (dateCreated != null) { | ||||
|             buffer | ||||
|                     .append("|date={{According to EXIF data|").append(isoFormat.format(dateCreated)).append("}}\n"); | ||||
| 
 | ||||
|         String templatizedCreatedDate = getTemplatizedCreatedDate(); | ||||
|         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 | ||||
|         if (decimalCoords != null) { | ||||
|  | @ -178,6 +194,28 @@ public class  Contribution extends Media { | |||
|         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 | ||||
|     public void setFilename(String filename) { | ||||
|         this.filename = filename; | ||||
|  |  | |||
|  | @ -6,7 +6,16 @@ import android.net.Uri; | |||
| import android.os.Parcel; | ||||
| 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.IOException; | ||||
| import java.util.Date; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| 
 | ||||
| import fr.free.nrw.commons.upload.FileUtils; | ||||
| 
 | ||||
|  | @ -62,16 +71,32 @@ public class UploadableFile implements Parcelable { | |||
|         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 | ||||
|      * | ||||
|      * @return | ||||
|      */ | ||||
|     public long getFileCreatedDate(Context context) { | ||||
|     private DateTimeWithSource getFileCreatedDateFromCP(Context context) { | ||||
|         try { | ||||
|             Cursor cursor = context.getContentResolver().query(contentUri, null, null, null, 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 | ||||
|             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 (lastModifiedColumnIndex == -1) { | ||||
|                 return -1l; | ||||
|                 return null; | ||||
|             } | ||||
|             cursor.moveToFirst(); | ||||
|             return cursor.getLong(lastModifiedColumnIndex); | ||||
|             return new DateTimeWithSource(cursor.getLong(lastModifiedColumnIndex), DateTimeWithSource.CP_SOURCE); | ||||
|         } 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 | ||||
|     public void writeToParcel(Parcel parcel, int i) { | ||||
|         parcel.writeParcelable(contentUri, 0); | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ public class UploadModel { | |||
|             "", | ||||
|             GPSExtractor.DUMMY, | ||||
|             null, | ||||
|             -1L) { | ||||
|             -1L, "") { | ||||
|     }; | ||||
|     private final BasicKvStore basicKvStore; | ||||
|     private final List<String> licenses; | ||||
|  | @ -99,10 +99,16 @@ public class UploadModel { | |||
|                                      String source, | ||||
|                                      SimilarImageInterface similarImageInterface) { | ||||
|         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); | ||||
|         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) { | ||||
|  | @ -284,11 +290,13 @@ public class UploadModel { | |||
|             contribution.setTag("mimeType", item.mimeType); | ||||
|             contribution.setSource(item.source); | ||||
|             contribution.setContentProviderUri(item.mediaUri); | ||||
| 
 | ||||
|             Timber.d("Created timestamp while building contribution is %s, %s", | ||||
|                     item.getCreatedTimestamp(), | ||||
|                     new Date(item.getCreatedTimestamp())); | ||||
|             if (item.createdTimestamp != -1L) { | ||||
|                 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 | ||||
|             } | ||||
|             return contribution; | ||||
|  | @ -332,10 +340,15 @@ public class UploadModel { | |||
|         private boolean visited; | ||||
|         private boolean error; | ||||
|         private long createdTimestamp; | ||||
|         private String createdTimestampSource; | ||||
|         private BehaviorSubject<Integer> imageQuality; | ||||
| 
 | ||||
|         @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(); | ||||
|             descriptions = new ArrayList<>(); | ||||
|             descriptions.add(new Description()); | ||||
|  | @ -348,6 +361,10 @@ public class UploadModel { | |||
|             imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT); | ||||
|         } | ||||
| 
 | ||||
|         public String getCreatedTimestampSource() { | ||||
|             return createdTimestampSource; | ||||
|         } | ||||
| 
 | ||||
|         public String getMimeType() { | ||||
|             return mimeType; | ||||
|         } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vivek Maskara
						Vivek Maskara