mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
* #3529 Captions/depictions are not saved to Commons - update flow to update appropriate data * #3529 Captions/depictions are not saved to Commons - fix invoking of setlabel * #3529 Captions/depictions are not saved to Commons - fix unit tests * #3529 Captions/depictions are not saved to Commons - use constant for @Named * #3529 Captions/depictions are not saved to Commons - remove captions interface * #3529 Captions/depictions are not saved to Commons - delete unused Contribution fields - enforce Single Responsibility by using PageContentsCreator * #3529 Captions/depictions are not saved to Commons - prefix id with M - remove language from url and only add from Field * #3529 Captions/depictions are not saved to Commons - make edits of depictions and captions sequential * #3529 Captions/depictions are not saved to Commons - remove unused model fields * #3529 Captions/depictions are not saved to Commons - weaken type of categories - copy list on Contribution creation * #3529 Captions/depictions are not saved to Commons - mark Media fields private - weaken types - remove partly implemented fields * #3529 Captions/depictions are not saved to Commons - add semi colon * #3529 Captions/depictions are not saved to Commons - fix test
This commit is contained in:
parent
3f6d26c296
commit
628a6056e0
49 changed files with 925 additions and 1271 deletions
|
|
@ -11,9 +11,7 @@ import fr.free.nrw.commons.utils.CommonsDateUtil;
|
||||||
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -30,62 +28,42 @@ public class Media implements Parcelable {
|
||||||
|
|
||||||
// Primary metadata fields
|
// Primary metadata fields
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri localUri;
|
private Uri localUri;
|
||||||
public String thumbUrl;
|
private String thumbUrl;
|
||||||
public String imageUrl;
|
private String imageUrl;
|
||||||
public String filename;
|
private String filename;
|
||||||
public String thumbnailTitle;
|
private String thumbnailTitle;
|
||||||
/**
|
/*
|
||||||
* Captions are a feature part of Structured data. They are meant to store short, multilingual descriptions about files
|
* Captions are a feature part of Structured data. They are meant to store short, multilingual descriptions about files
|
||||||
* This is a replacement of the previously used titles for images (titles were not multilingual)
|
* This is a replacement of the previously used titles for images (titles were not multilingual)
|
||||||
* Also now captions replace the previous convention of using title for filename
|
* Also now captions replace the previous convention of using title for filename
|
||||||
*/
|
*/
|
||||||
private String caption;
|
private String caption;
|
||||||
public String description; // monolingual description on input...
|
private String description; // monolingual description on input...
|
||||||
public String discussion;
|
private String discussion;
|
||||||
long dataLength;
|
private long dataLength;
|
||||||
public Date dateCreated;
|
private Date dateCreated;
|
||||||
@Nullable public Date dateUploaded;
|
@Nullable private Date dateUploaded;
|
||||||
public int width;
|
private String license;
|
||||||
public int height;
|
private String licenseUrl;
|
||||||
public String license;
|
private String creator;
|
||||||
public String licenseUrl;
|
|
||||||
public String creator;
|
|
||||||
/**
|
/**
|
||||||
* Wikibase Identifier associated with media files
|
* Wikibase Identifier associated with media files
|
||||||
*/
|
*/
|
||||||
public String pageId;
|
private String pageId;
|
||||||
public ArrayList<String> categories; // as loaded at runtime?
|
private List<String> categories; // as loaded at runtime?
|
||||||
/**
|
/**
|
||||||
* Depicts is a feature part of Structured data. Multiple Depictions can be added for an image just like categories.
|
* Depicts is a feature part of Structured data. Multiple Depictions can be added for an image just like categories.
|
||||||
* However unlike categories depictions is multi-lingual
|
* However unlike categories depictions is multi-lingual
|
||||||
*/
|
*/
|
||||||
public ArrayList<Map<String, String>> depictionList;
|
private List<Map<String, String>> depictionList= new ArrayList<>();
|
||||||
/**
|
private boolean requestedDeletion;
|
||||||
* The above hashmap is fetched from API and to diplay in Explore
|
@Nullable private LatLng coordinates;
|
||||||
* However this list of depictions is for storing and retrieving depictions from local storage or cache
|
|
||||||
*/
|
|
||||||
public ArrayList<String> depictions;
|
|
||||||
public boolean requestedDeletion;
|
|
||||||
public HashMap<String, String> descriptions; // multilingual descriptions as loaded
|
|
||||||
/**
|
|
||||||
* This hasmap stores the list of multilingual captions, where
|
|
||||||
* key of the HashMap is the language and value is the caption in the corresponding language
|
|
||||||
* Ex: key = "en", value: "<caption in short in English>"
|
|
||||||
* key = "de" , value: "<caption in german>"
|
|
||||||
*/
|
|
||||||
public Map<String, String> captions;
|
|
||||||
public HashMap<String, String> tags = new HashMap<>();
|
|
||||||
@Nullable public LatLng coordinates;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides local constructor
|
* Provides local constructor
|
||||||
*/
|
*/
|
||||||
protected Media() {
|
public Media() {
|
||||||
this.categories = new ArrayList<>();
|
|
||||||
this.depictions = new ArrayList<>();
|
|
||||||
this.descriptions = new HashMap<>();
|
|
||||||
this.captions = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -94,7 +72,6 @@ public class Media implements Parcelable {
|
||||||
* @param filename Media filename
|
* @param filename Media filename
|
||||||
*/
|
*/
|
||||||
public Media(String filename) {
|
public Media(String filename) {
|
||||||
this();
|
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,29 +80,35 @@ public class Media implements Parcelable {
|
||||||
* @param localUri Media URI
|
* @param localUri Media URI
|
||||||
* @param imageUrl Media image URL
|
* @param imageUrl Media image URL
|
||||||
* @param filename Media filename
|
* @param filename Media filename
|
||||||
* @param captions Media captions
|
|
||||||
* @param description Media description
|
* @param description Media description
|
||||||
* @param dataLength Media date length
|
* @param dataLength Media date length
|
||||||
* @param dateCreated Media creation date
|
* @param dateCreated Media creation date
|
||||||
* @param dateUploaded Media date uploaded
|
* @param dateUploaded Media date uploaded
|
||||||
* @param creator Media creator
|
* @param creator Media creator
|
||||||
*/
|
*/
|
||||||
public Media(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description,
|
public Media(Uri localUri, String imageUrl, String filename,
|
||||||
|
String description,
|
||||||
long dataLength, Date dateCreated, Date dateUploaded, String creator) {
|
long dataLength, Date dateCreated, Date dateUploaded, String creator) {
|
||||||
this();
|
|
||||||
this.localUri = localUri;
|
this.localUri = localUri;
|
||||||
this.thumbUrl = imageUrl;
|
this.thumbUrl = imageUrl;
|
||||||
this.imageUrl = imageUrl;
|
this.imageUrl = imageUrl;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.captions = captions;
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.dataLength = dataLength;
|
this.dataLength = dataLength;
|
||||||
this.dateCreated = dateCreated;
|
this.dateCreated = dateCreated;
|
||||||
this.dateUploaded = dateUploaded;
|
this.dateUploaded = dateUploaded;
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
this.categories = new ArrayList<>();
|
}
|
||||||
this.depictions = new ArrayList<>();
|
|
||||||
this.descriptions = new HashMap<>();
|
public Media(Uri localUri, String filename,
|
||||||
|
String description, String creator, List<String> categories) {
|
||||||
|
this(localUri,null, filename,
|
||||||
|
description, -1, null, new Date(), creator);
|
||||||
|
this.categories = categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Media(String title, Date date, String user) {
|
||||||
|
this(null, null, title, "", -1, date, date, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,7 +128,7 @@ public class Media implements Parcelable {
|
||||||
ExtMetadata metadata = imageInfo.getMetadata();
|
ExtMetadata metadata = imageInfo.getMetadata();
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
Media media = new Media(null, imageInfo.getOriginalUrl(),
|
Media media = new Media(null, imageInfo.getOriginalUrl(),
|
||||||
page.title(), new HashMap<>() , "", 0, null, null, null);
|
page.title(), "", 0, null, null, null);
|
||||||
if (!StringUtils.isBlank(imageInfo.getThumbUrl())) {
|
if (!StringUtils.isBlank(imageInfo.getThumbUrl())) {
|
||||||
media.setThumbUrl(imageInfo.getThumbUrl());
|
media.setThumbUrl(imageInfo.getThumbUrl());
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +138,6 @@ public class Media implements Parcelable {
|
||||||
Media media = new Media(null,
|
Media media = new Media(null,
|
||||||
imageInfo.getOriginalUrl(),
|
imageInfo.getOriginalUrl(),
|
||||||
page.title(),
|
page.title(),
|
||||||
new HashMap<>(),
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
safeParseDate(metadata.dateTime()),
|
safeParseDate(metadata.dateTime()),
|
||||||
|
|
@ -174,7 +156,7 @@ public class Media implements Parcelable {
|
||||||
language = "default";
|
language = "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription()));
|
media.setDescription(metadata.imageDescription());
|
||||||
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.getCategories()));
|
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.getCategories()));
|
||||||
String latitude = metadata.getGpsLatitude();
|
String latitude = metadata.getGpsLatitude();
|
||||||
String longitude = metadata.getGpsLongitude();
|
String longitude = metadata.getGpsLongitude();
|
||||||
|
|
@ -212,31 +194,14 @@ public class Media implements Parcelable {
|
||||||
/**
|
/**
|
||||||
*sets pageId for the current media object
|
*sets pageId for the current media object
|
||||||
*/
|
*/
|
||||||
private void setPageId(String pageId) {
|
public void setPageId(String pageId) {
|
||||||
this.pageId = pageId;
|
this.pageId = pageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getThumbUrl() {
|
public String getThumbUrl() {
|
||||||
return thumbUrl;
|
return thumbUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets tag of media
|
|
||||||
* @param key Media key
|
|
||||||
* @return Media tag
|
|
||||||
*/
|
|
||||||
public Object getTag(String key) {
|
|
||||||
return tags.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies( or creates a) tag of media
|
|
||||||
* @param key Media key
|
|
||||||
* @param value Media value
|
|
||||||
*/
|
|
||||||
public void setTag(String key, String value) {
|
|
||||||
tags.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets media display title
|
* Gets media display title
|
||||||
* @return Media title
|
* @return Media title
|
||||||
|
|
@ -340,22 +305,11 @@ public class Media implements Parcelable {
|
||||||
/**
|
/**
|
||||||
* @return depictions associated with the current media
|
* @return depictions associated with the current media
|
||||||
*/
|
*/
|
||||||
public ArrayList<Map<String, String>> getDepiction() {
|
public List<Map<String, String>> getDepiction() {
|
||||||
return depictionList;
|
return depictionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Captions are a feature part of Structured data. They are meant to store short, multilingual descriptions about files
|
|
||||||
* This is a replacement of the previously used titles for images (titles were not multilingual)
|
|
||||||
* Also now captions replace the previous convention of using title for filename
|
|
||||||
*
|
|
||||||
* key of the HashMap is the language and value is the caption in the corresponding language
|
|
||||||
*
|
|
||||||
* returns list of captions stored in hashmap
|
|
||||||
*/
|
|
||||||
public Map<String, String> getCaptions() {
|
|
||||||
return captions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the file description.
|
* Sets the file description.
|
||||||
|
|
@ -423,38 +377,6 @@ public class Media implements Parcelable {
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the width of the media.
|
|
||||||
* @return file width as an int
|
|
||||||
*/
|
|
||||||
public int getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the width of the media.
|
|
||||||
* @param width file width as an int
|
|
||||||
*/
|
|
||||||
public void setWidth(int width) {
|
|
||||||
this.width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the height of the media.
|
|
||||||
* @return file height as an int
|
|
||||||
*/
|
|
||||||
public int getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the height of the media.
|
|
||||||
* @param height file height as an int
|
|
||||||
*/
|
|
||||||
public void setHeight(int height) {
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the license name of the file.
|
* Gets the license name of the file.
|
||||||
* @return license as a String
|
* @return license as a String
|
||||||
|
|
@ -506,15 +428,8 @@ public class Media implements Parcelable {
|
||||||
* @return file categories as an ArrayList of Strings
|
* @return file categories as an ArrayList of Strings
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ArrayList<String> getCategories() {
|
public List<String> getCategories() {
|
||||||
return (ArrayList<String>) categories.clone(); // feels dirty
|
return categories;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array list of depictions associated with the current media
|
|
||||||
*/
|
|
||||||
public ArrayList<String> getDepictions() {
|
|
||||||
return (ArrayList<String>) depictions.clone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -525,43 +440,7 @@ public class Media implements Parcelable {
|
||||||
* @param categories file categories as a list of Strings
|
* @param categories file categories as a list of Strings
|
||||||
*/
|
*/
|
||||||
public void setCategories(List<String> categories) {
|
public void setCategories(List<String> categories) {
|
||||||
this.categories.clear();
|
this.categories = categories;
|
||||||
this.categories.addAll(categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDepictions(List<String> depictions) {
|
|
||||||
this.depictions.clear();
|
|
||||||
this.depictions.addAll(depictions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Modifies (or sets) media descriptions
|
|
||||||
* @param descriptions Media descriptions
|
|
||||||
*/
|
|
||||||
void setDescriptions(Map<String, String> descriptions) {
|
|
||||||
this.descriptions.clear();
|
|
||||||
this.descriptions.putAll(descriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets media description in preferred language
|
|
||||||
* @param preferredLanguage Language preferred
|
|
||||||
* @return Description in preferred language
|
|
||||||
*/
|
|
||||||
public String getDescription(String preferredLanguage) {
|
|
||||||
if (descriptions.containsKey(preferredLanguage)) {
|
|
||||||
// See if the requested language is there.
|
|
||||||
return descriptions.get(preferredLanguage);
|
|
||||||
} else if (descriptions.containsKey("en")) {
|
|
||||||
// Ah, English. Language of the world, until the Chinese crush us.
|
|
||||||
return descriptions.get("en");
|
|
||||||
} else if (descriptions.containsKey("default")) {
|
|
||||||
// No languages marked...
|
|
||||||
return descriptions.get("default");
|
|
||||||
} else {
|
|
||||||
// FIXME: return the first available non-English description?
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable private static Date safeParseDate(String dateStr) {
|
@Nullable private static Date safeParseDate(String dateStr) {
|
||||||
|
|
@ -572,20 +451,19 @@ public class Media implements Parcelable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set requested deletion to true
|
* Set requested deletion to true
|
||||||
|
* @param requestedDeletion
|
||||||
*/
|
*/
|
||||||
public void setRequestedDeletion(){
|
public void setRequestedDeletion(boolean requestedDeletion){
|
||||||
requestedDeletion = true;
|
this.requestedDeletion = requestedDeletion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of requested deletion
|
* Get the value of requested deletion
|
||||||
* @return boolean requestedDeletion
|
* @return boolean requestedDeletion
|
||||||
*/
|
*/
|
||||||
public boolean getRequestedDeletion(){
|
public boolean isRequestedDeletion(){
|
||||||
return requestedDeletion;
|
return requestedDeletion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -610,15 +488,29 @@ public class Media implements Parcelable {
|
||||||
this.caption = caption;
|
this.caption = caption;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCaptions(HashMap<String, String> captions) {
|
/* Sets depictions for the current media obtained fro Wikibase API*/
|
||||||
this.captions = captions;
|
public void setDepictionList(List<Map<String, String>> depictions) {
|
||||||
|
this.depictionList = depictions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setLocalUri(@Nullable final Uri localUri) {
|
||||||
* Sets depictions for the current media obtained fro Wikibase API
|
this.localUri = localUri;
|
||||||
*/
|
}
|
||||||
public void setDepiction(ArrayList<Map<String, String>> depictions) {
|
|
||||||
this.depictionList = depictions;
|
public void setImageUrl(final String imageUrl) {
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateUploaded(@Nullable final Date dateUploaded) {
|
||||||
|
this.dateUploaded = dateUploaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLicenseUrl(final String licenseUrl) {
|
||||||
|
this.licenseUrl = licenseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, String>> getDepictionList() {
|
||||||
|
return depictionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -645,8 +537,6 @@ public class Media implements Parcelable {
|
||||||
dest.writeLong(this.dataLength);
|
dest.writeLong(this.dataLength);
|
||||||
dest.writeLong(this.dateCreated != null ? this.dateCreated.getTime() : -1);
|
dest.writeLong(this.dateCreated != null ? this.dateCreated.getTime() : -1);
|
||||||
dest.writeLong(this.dateUploaded != null ? this.dateUploaded.getTime() : -1);
|
dest.writeLong(this.dateUploaded != null ? this.dateUploaded.getTime() : -1);
|
||||||
dest.writeInt(this.width);
|
|
||||||
dest.writeInt(this.height);
|
|
||||||
dest.writeString(this.license);
|
dest.writeString(this.license);
|
||||||
dest.writeString(this.licenseUrl);
|
dest.writeString(this.licenseUrl);
|
||||||
dest.writeString(this.creator);
|
dest.writeString(this.creator);
|
||||||
|
|
@ -654,8 +544,6 @@ public class Media implements Parcelable {
|
||||||
dest.writeStringList(this.categories);
|
dest.writeStringList(this.categories);
|
||||||
dest.writeList(this.depictionList);
|
dest.writeList(this.depictionList);
|
||||||
dest.writeByte(this.requestedDeletion ? (byte) 1 : (byte) 0);
|
dest.writeByte(this.requestedDeletion ? (byte) 1 : (byte) 0);
|
||||||
dest.writeSerializable(this.descriptions);
|
|
||||||
dest.writeSerializable(this.tags);
|
|
||||||
dest.writeParcelable(this.coordinates, flags);
|
dest.writeParcelable(this.coordinates, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -673,8 +561,6 @@ public class Media implements Parcelable {
|
||||||
this.dateCreated = tmpDateCreated == -1 ? null : new Date(tmpDateCreated);
|
this.dateCreated = tmpDateCreated == -1 ? null : new Date(tmpDateCreated);
|
||||||
long tmpDateUploaded = in.readLong();
|
long tmpDateUploaded = in.readLong();
|
||||||
this.dateUploaded = tmpDateUploaded == -1 ? null : new Date(tmpDateUploaded);
|
this.dateUploaded = tmpDateUploaded == -1 ? null : new Date(tmpDateUploaded);
|
||||||
this.width = in.readInt();
|
|
||||||
this.height = in.readInt();
|
|
||||||
this.license = in.readString();
|
this.license = in.readString();
|
||||||
this.licenseUrl = in.readString();
|
this.licenseUrl = in.readString();
|
||||||
this.creator = in.readString();
|
this.creator = in.readString();
|
||||||
|
|
@ -684,8 +570,6 @@ public class Media implements Parcelable {
|
||||||
this.categories=list;
|
this.categories=list;
|
||||||
in.readList(depictionList,null);
|
in.readList(depictionList,null);
|
||||||
this.requestedDeletion = in.readByte() != 0;
|
this.requestedDeletion = in.readByte() != 0;
|
||||||
this.descriptions = (HashMap<String, String>) in.readSerializable();
|
|
||||||
this.tags = (HashMap<String, String>) in.readSerializable();
|
|
||||||
this.coordinates = in.readParcelable(LatLng.class.getClassLoader());
|
this.coordinates = in.readParcelable(LatLng.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ public class MediaDataExtractor {
|
||||||
final String caption, final JsonObject depiction) {
|
final String caption, final JsonObject depiction) {
|
||||||
media.setDiscussion(discussion);
|
media.setDiscussion(discussion);
|
||||||
media.setCaption(caption);
|
media.setCaption(caption);
|
||||||
media.setDepiction(formatDepictions(depiction));
|
media.setDepictionList(formatDepictions(depiction));
|
||||||
if (deletionStatus) {
|
if (deletionStatus) {
|
||||||
media.setRequestedDeletion();
|
media.setRequestedDeletion(true);
|
||||||
}
|
}
|
||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,291 +1,156 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.StringDef;
|
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
import androidx.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
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.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
||||||
import java.lang.annotation.Retention;
|
import fr.free.nrw.commons.upload.WikidataPlace;
|
||||||
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.wikipedia.dataclient.mwapi.MwQueryLogEvent;
|
||||||
|
|
||||||
@Entity(tableName = "contribution")
|
@Entity(tableName = "contribution")
|
||||||
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 = "{{According to Exif data|%s}}";
|
|
||||||
|
|
||||||
//2009-01-09 → 9 January 2009
|
|
||||||
private static final String TEMPLATE_DATA_OTHER_SOURCE = "%s";
|
|
||||||
|
|
||||||
// No need to be bitwise - they're mutually exclusive
|
// No need to be bitwise - they're mutually exclusive
|
||||||
public static final int STATE_COMPLETED = -1;
|
public static final int STATE_COMPLETED = -1;
|
||||||
public static final int STATE_FAILED = 1;
|
public static final int STATE_FAILED = 1;
|
||||||
public static final int STATE_QUEUED = 2;
|
public static final int STATE_QUEUED = 2;
|
||||||
public static final int STATE_IN_PROGRESS = 3;
|
public static final int STATE_IN_PROGRESS = 3;
|
||||||
|
|
||||||
@Retention(SOURCE)
|
|
||||||
@StringDef({SOURCE_CAMERA, SOURCE_GALLERY, SOURCE_EXTERNAL})
|
|
||||||
public @interface FileSource {}
|
|
||||||
|
|
||||||
public static final String SOURCE_CAMERA = "camera";
|
|
||||||
public static final String SOURCE_GALLERY = "gallery";
|
|
||||||
public static final String SOURCE_EXTERNAL = "external";
|
|
||||||
@PrimaryKey (autoGenerate = true)
|
@PrimaryKey (autoGenerate = true)
|
||||||
@NonNull
|
private long _id;
|
||||||
public long _id;
|
private int state;
|
||||||
public Uri contentUri;
|
private long transferred;
|
||||||
public String source;
|
private String decimalCoords;
|
||||||
public String editSummary;
|
private String dateCreatedSource;
|
||||||
public int state;
|
private WikidataPlace wikidataPlace;
|
||||||
public long transferred;
|
|
||||||
public String decimalCoords;
|
|
||||||
public boolean isMultiple;
|
|
||||||
public String wikiDataEntityId;
|
|
||||||
public String wikiItemName;
|
|
||||||
private String p18Value;
|
|
||||||
public Uri contentProviderUri;
|
|
||||||
public String dateCreatedSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each depiction loaded in depictions activity is associated with a wikidata entity id,
|
* Each depiction loaded in depictions activity is associated with a wikidata entity id,
|
||||||
* this Id is in turn used to upload depictions to wikibase
|
* this Id is in turn used to upload depictions to wikibase
|
||||||
*/
|
*/
|
||||||
public ArrayList<String> depictionsEntityIds;
|
private List<DepictedItem> depictedItems = new ArrayList<>();
|
||||||
|
private String mimeType;
|
||||||
|
/**
|
||||||
|
* This hasmap stores the list of multilingual captions, where
|
||||||
|
* key of the HashMap is the language and value is the caption in the corresponding language
|
||||||
|
* Ex: key = "en", value: "<caption in short in English>"
|
||||||
|
* key = "de" , value: "<caption in german>"
|
||||||
|
*/
|
||||||
|
private Map<String, String> captions = new HashMap<>();
|
||||||
|
|
||||||
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated,
|
public Contribution() {
|
||||||
int state, long dataLength, Date dateUploaded, long transferred,
|
|
||||||
String source, Map<String, String> captions, String description, String creator, boolean isMultiple,
|
|
||||||
int width, int height, String license) {
|
|
||||||
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
|
|
||||||
this.contentUri = contentUri;
|
|
||||||
this.state = state;
|
|
||||||
this.transferred = transferred;
|
|
||||||
this.source = source;
|
|
||||||
this.isMultiple = isMultiple;
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
this.license = license;
|
|
||||||
this.dateCreatedSource = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Contribution(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description, long dataLength,
|
public Contribution(final UploadItem item, final SessionManager sessionManager,
|
||||||
Date dateCreated, Date dateUploaded, String creator, String editSummary, ArrayList<String> depictionsEntityIds, String decimalCoords) {
|
final List<DepictedItem> depictedItems, final List<String> categories) {
|
||||||
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
|
super(item.getMediaUri(),
|
||||||
this.decimalCoords = decimalCoords;
|
item.getFileName(),
|
||||||
this.editSummary = editSummary;
|
UploadMediaDetail.formatList(item.getUploadMediaDetails()),
|
||||||
this.dateCreatedSource = "";
|
sessionManager.getAuthorName(),
|
||||||
this.depictionsEntityIds = depictionsEntityIds;
|
categories);
|
||||||
|
captions = UploadMediaDetail.formatCaptions(item.getUploadMediaDetails());
|
||||||
|
decimalCoords = item.getGpsCoords().getDecimalCoords();
|
||||||
|
dateCreatedSource = "";
|
||||||
|
this.depictedItems = depictedItems;
|
||||||
|
wikidataPlace = WikidataPlace.from(item.getPlace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Contribution(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description, long dataLength,
|
public Contribution(final MwQueryLogEvent queryLogEvent, final String user) {
|
||||||
Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords, int state) {
|
super(queryLogEvent.title(),queryLogEvent.date(), user);
|
||||||
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
|
decimalCoords = "";
|
||||||
this.decimalCoords = decimalCoords;
|
dateCreatedSource = "";
|
||||||
this.editSummary = editSummary;
|
state = STATE_COMPLETED;
|
||||||
this.dateCreatedSource = "";
|
|
||||||
this.state=state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDateCreatedSource(final String dateCreatedSource) {
|
||||||
|
|
||||||
public void setDateCreatedSource(String dateCreatedSource) {
|
|
||||||
this.dateCreatedSource = dateCreatedSource;
|
this.dateCreatedSource = dateCreatedSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getMultiple() {
|
public String getDateCreatedSource() {
|
||||||
return isMultiple;
|
return dateCreatedSource;
|
||||||
}
|
|
||||||
|
|
||||||
public void setMultiple(boolean multiple) {
|
|
||||||
isMultiple = multiple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTransferred() {
|
public long getTransferred() {
|
||||||
return transferred;
|
return transferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransferred(long transferred) {
|
public void setTransferred(final long transferred) {
|
||||||
this.transferred = transferred;
|
this.transferred = transferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEditSummary() {
|
|
||||||
return editSummary != null ? editSummary : CommonsApplication.DEFAULT_EDIT_SUMMARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uri getContentUri() {
|
|
||||||
return contentUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentUri(Uri contentUri) {
|
|
||||||
this.contentUri = contentUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getState() {
|
public int getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setState(int state) {
|
public void setState(final int state) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDateUploaded(Date date) {
|
|
||||||
this.dateUploaded = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets depiction entity ids for the given contribution
|
|
||||||
*/
|
|
||||||
public void setDepictions(ArrayList<String> depictionsEntityIds) {
|
|
||||||
this.depictionsEntityIds = depictionsEntityIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPageContents(Context applicationContext) {
|
|
||||||
StringBuilder buffer = new StringBuilder();
|
|
||||||
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");
|
|
||||||
|
|
||||||
String templatizedCreatedDate = getTemplatizedCreatedDate();
|
|
||||||
if (!StringUtils.isBlank(templatizedCreatedDate)) {
|
|
||||||
buffer.append("|date=").append(templatizedCreatedDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.append("}}").append("\n");
|
|
||||||
|
|
||||||
//Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null
|
|
||||||
if (decimalCoords != null) {
|
|
||||||
buffer.append("{{Location|").append(decimalCoords).append("}}").append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.append("== {{int:license-header}} ==\n")
|
|
||||||
.append(licenseTemplateFor(getLicense())).append("\n\n")
|
|
||||||
.append("{{Uploaded from Mobile|platform=Android|version=")
|
|
||||||
.append(ConfigUtils.getVersionNameWithSha(applicationContext)).append("}}\n");
|
|
||||||
if(categories!=null&&categories.size()!=0) {
|
|
||||||
for (int i = 0; i < categories.size(); i++) {
|
|
||||||
String category = categories.get(i);
|
|
||||||
buffer.append("\n[[Category:").append(category).append("]]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
buffer.append("{{subst:unc}}");
|
|
||||||
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) {
|
|
||||||
java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
||||||
if (UploadableFile.DateTimeWithSource.EXIF_SOURCE.equals(dateCreatedSource)) {
|
|
||||||
return String.format(Locale.ENGLISH, TEMPLATE_DATE_ACC_TO_EXIF, dateFormat.format(dateCreated)) + "\n";
|
|
||||||
} else {
|
|
||||||
return String.format(Locale.ENGLISH, TEMPLATE_DATA_OTHER_SOURCE, dateFormat.format(dateCreated)) + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFilename(String filename) {
|
|
||||||
this.filename = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImageUrl(String imageUrl) {
|
|
||||||
this.imageUrl = imageUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Contribution() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSource(String source) {
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private String licenseTemplateFor(String license) {
|
|
||||||
switch (license) {
|
|
||||||
case Prefs.Licenses.CC_BY_3:
|
|
||||||
return "{{self|cc-by-3.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_4:
|
|
||||||
return "{{self|cc-by-4.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_3:
|
|
||||||
return "{{self|cc-by-sa-3.0}}";
|
|
||||||
case Prefs.Licenses.CC_BY_SA_4:
|
|
||||||
return "{{self|cc-by-sa-4.0}}";
|
|
||||||
case Prefs.Licenses.CC0:
|
|
||||||
return "{{self|cc-zero}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("Unrecognized license value: " + license);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWikiDataEntityId() {
|
|
||||||
return wikiDataEntityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getWikiItemName() {
|
|
||||||
return wikiItemName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the corresponding wikidata entity is known as in case of nearby uploads, it can be set
|
|
||||||
* using the setter method
|
|
||||||
* @param wikiDataEntityId wikiDataEntityId
|
|
||||||
*/
|
|
||||||
public void setWikiDataEntityId(String wikiDataEntityId) {
|
|
||||||
this.wikiDataEntityId = wikiDataEntityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWikiItemName(String wikiItemName) {
|
|
||||||
this.wikiItemName = wikiItemName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getP18Value() {
|
|
||||||
return p18Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the corresponding image property of wiki entity is known as in case of nearby uploads,
|
|
||||||
* it can be set using the setter method
|
|
||||||
* @param p18Value p18 value, image property of the wikidata item
|
|
||||||
*/
|
|
||||||
public void setP18Value(String p18Value) {
|
|
||||||
this.p18Value = p18Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentProviderUri(Uri contentProviderUri) {
|
|
||||||
this.contentProviderUri = contentProviderUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array list of entityids for the depictions
|
* @return array list of entityids for the depictions
|
||||||
*/
|
*/
|
||||||
public ArrayList<String> getDepictionsEntityIds() {
|
public List<DepictedItem> getDepictedItems() {
|
||||||
return depictionsEntityIds;
|
return depictedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWikidataPlace(final WikidataPlace wikidataPlace) {
|
||||||
|
this.wikidataPlace = wikidataPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WikidataPlace getWikidataPlace() {
|
||||||
|
return wikidataPlace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long get_id() {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_id(final long _id) {
|
||||||
|
this._id = _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDecimalCoords() {
|
||||||
|
return decimalCoords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecimalCoords(final String decimalCoords) {
|
||||||
|
this.decimalCoords = decimalCoords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDepictedItems(final List<DepictedItem> depictedItems) {
|
||||||
|
this.depictedItems = depictedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMimeType(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captions are a feature part of Structured data. They are meant to store short, multilingual descriptions about files
|
||||||
|
* This is a replacement of the previously used titles for images (titles were not multilingual)
|
||||||
|
* Also now captions replace the previous convention of using title for filename
|
||||||
|
*
|
||||||
|
* key of the HashMap is the language and value is the caption in the corresponding language
|
||||||
|
*
|
||||||
|
* returns list of captions stored in hashmap
|
||||||
|
*/
|
||||||
|
public Map<String, String> getCaptions() {
|
||||||
|
return captions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCaptions(Map<String, String> captions) {
|
||||||
|
this.captions = captions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -294,48 +159,34 @@ public class Contribution extends Media {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(final Parcel dest, final int flags) {
|
||||||
super.writeToParcel(dest, flags);
|
super.writeToParcel(dest, flags);
|
||||||
dest.writeLong(this._id);
|
dest.writeLong(_id);
|
||||||
dest.writeParcelable(this.contentUri, flags);
|
dest.writeInt(state);
|
||||||
dest.writeString(this.source);
|
dest.writeLong(transferred);
|
||||||
dest.writeString(this.editSummary);
|
dest.writeString(decimalCoords);
|
||||||
dest.writeInt(this.state);
|
dest.writeString(dateCreatedSource);
|
||||||
dest.writeLong(this.transferred);
|
dest.writeSerializable((HashMap) captions);
|
||||||
dest.writeString(this.decimalCoords);
|
|
||||||
dest.writeByte(this.isMultiple ? (byte) 1 : (byte) 0);
|
|
||||||
dest.writeString(this.wikiDataEntityId);
|
|
||||||
dest.writeString(this.wikiItemName);
|
|
||||||
dest.writeString(this.p18Value);
|
|
||||||
dest.writeParcelable(this.contentProviderUri, flags);
|
|
||||||
dest.writeString(this.dateCreatedSource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Contribution(Parcel in) {
|
protected Contribution(final Parcel in) {
|
||||||
super(in);
|
super(in);
|
||||||
this._id = in.readLong();
|
_id = in.readLong();
|
||||||
this.contentUri = in.readParcelable(Uri.class.getClassLoader());
|
state = in.readInt();
|
||||||
this.source = in.readString();
|
transferred = in.readLong();
|
||||||
this.editSummary = in.readString();
|
decimalCoords = in.readString();
|
||||||
this.state = in.readInt();
|
dateCreatedSource = in.readString();
|
||||||
this.transferred = in.readLong();
|
captions = (HashMap<String, String>) in.readSerializable();
|
||||||
this.decimalCoords = in.readString();
|
|
||||||
this.isMultiple = in.readByte() != 0;
|
|
||||||
this.wikiDataEntityId = in.readString();
|
|
||||||
this.wikiItemName = in.readString();
|
|
||||||
this.p18Value = in.readString();
|
|
||||||
this.contentProviderUri = in.readParcelable(Uri.class.getClassLoader());
|
|
||||||
this.dateCreatedSource = in.readString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<Contribution> CREATOR = new Creator<Contribution>() {
|
public static final Creator<Contribution> CREATOR = new Creator<Contribution>() {
|
||||||
@Override
|
@Override
|
||||||
public Contribution createFromParcel(Parcel source) {
|
public Contribution createFromParcel(final Parcel source) {
|
||||||
return new Contribution(source);
|
return new Contribution(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Contribution[] newArray(int size) {
|
public Contribution[] newArray(final int size) {
|
||||||
return new Contribution[size];
|
return new Contribution[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,13 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
|
||||||
|
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.filepicker.DefaultCallback;
|
import fr.free.nrw.commons.filepicker.DefaultCallback;
|
||||||
import fr.free.nrw.commons.filepicker.FilePicker;
|
import fr.free.nrw.commons.filepicker.FilePicker;
|
||||||
|
|
@ -23,12 +17,11 @@ import fr.free.nrw.commons.nearby.Place;
|
||||||
import fr.free.nrw.commons.upload.UploadActivity;
|
import fr.free.nrw.commons.upload.UploadActivity;
|
||||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
|
import java.util.ArrayList;
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
|
import java.util.List;
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
|
import javax.inject.Inject;
|
||||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
|
import javax.inject.Named;
|
||||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE;
|
import javax.inject.Singleton;
|
||||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ContributionController {
|
public class ContributionController {
|
||||||
|
|
@ -109,7 +102,7 @@ public class ContributionController {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles, FilePicker.ImageSource source, int type) {
|
public void onImagesPicked(@NonNull List<UploadableFile> imagesFiles, FilePicker.ImageSource source, int type) {
|
||||||
Intent intent = handleImagesPicked(activity, imagesFiles, getSourceFromImageSource(source));
|
Intent intent = handleImagesPicked(activity, imagesFiles);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -125,11 +118,9 @@ public class ContributionController {
|
||||||
* Attaches place object for nearby uploads
|
* Attaches place object for nearby uploads
|
||||||
*/
|
*/
|
||||||
private Intent handleImagesPicked(Context context,
|
private Intent handleImagesPicked(Context context,
|
||||||
List<UploadableFile> imagesFiles,
|
List<UploadableFile> imagesFiles) {
|
||||||
String source) {
|
|
||||||
Intent shareIntent = new Intent(context, UploadActivity.class);
|
Intent shareIntent = new Intent(context, UploadActivity.class);
|
||||||
shareIntent.setAction(ACTION_INTERNAL_UPLOADS);
|
shareIntent.setAction(ACTION_INTERNAL_UPLOADS);
|
||||||
shareIntent.putExtra(EXTRA_SOURCE, source);
|
|
||||||
shareIntent.putParcelableArrayListExtra(EXTRA_FILES, new ArrayList<>(imagesFiles));
|
shareIntent.putParcelableArrayListExtra(EXTRA_FILES, new ArrayList<>(imagesFiles));
|
||||||
Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class);
|
Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class);
|
||||||
if (place != null) {
|
if (place != null) {
|
||||||
|
|
@ -139,13 +130,4 @@ public class ContributionController {
|
||||||
return shareIntent;
|
return shareIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get image upload source
|
|
||||||
*/
|
|
||||||
private String getSourceFromImageSource(FilePicker.ImageSource source) {
|
|
||||||
if (source.equals(FilePicker.ImageSource.CAMERA_IMAGE)) {
|
|
||||||
return SOURCE_CAMERA;
|
|
||||||
}
|
|
||||||
return SOURCE_GALLERY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,10 @@ import androidx.room.Insert;
|
||||||
import androidx.room.OnConflictStrategy;
|
import androidx.room.OnConflictStrategy;
|
||||||
import androidx.room.Query;
|
import androidx.room.Query;
|
||||||
import androidx.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
|
|
||||||
import androidx.room.Update;
|
import androidx.room.Update;
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public abstract class ContributionDao {
|
public abstract class ContributionDao {
|
||||||
|
|
@ -40,9 +37,6 @@ public abstract class ContributionDao {
|
||||||
@Delete
|
@Delete
|
||||||
public abstract Single<Integer> delete(Contribution contribution);
|
public abstract Single<Integer> delete(Contribution contribution);
|
||||||
|
|
||||||
@Query("SELECT * from contribution WHERE contentProviderUri=:uri")
|
|
||||||
public abstract List<Contribution> getContributionWithUri(String uri);
|
|
||||||
|
|
||||||
@Query("SELECT * from contribution WHERE filename=:fileName")
|
@Query("SELECT * from contribution WHERE filename=:fileName")
|
||||||
public abstract List<Contribution> getContributionWithTitle(String fileName);
|
public abstract List<Contribution> getContributionWithTitle(String fileName);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX;
|
import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX;
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -60,7 +58,7 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
|
||||||
this.contribution = contribution;
|
this.contribution = contribution;
|
||||||
fetchAndDisplayCaption(contribution);
|
fetchAndDisplayCaption(contribution);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
String imageSource = chooseImageSource(contribution.thumbUrl, contribution.getLocalUri());
|
String imageSource = chooseImageSource(contribution.getThumbUrl(), contribution.getLocalUri());
|
||||||
if (!TextUtils.isEmpty(imageSource)) {
|
if (!TextUtils.isEmpty(imageSource)) {
|
||||||
final ImageRequest imageRequest =
|
final ImageRequest imageRequest =
|
||||||
ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
|
ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
|
||||||
|
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
|
||||||
|
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -12,21 +16,12 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
|
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
import fr.free.nrw.commons.MediaDataExtractor;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.HandlerService;
|
import fr.free.nrw.commons.HandlerService;
|
||||||
|
|
@ -43,7 +38,6 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||||
import fr.free.nrw.commons.media.MediaClient;
|
|
||||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider;
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider;
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||||
|
|
@ -61,14 +55,11 @@ import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
|
|
||||||
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
|
|
||||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
|
||||||
|
|
||||||
public class ContributionsFragment
|
public class ContributionsFragment
|
||||||
extends CommonsDaggerSupportFragment
|
extends CommonsDaggerSupportFragment
|
||||||
implements
|
implements
|
||||||
|
|
@ -224,7 +215,7 @@ public class ContributionsFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fetchMediaUriFor(Contribution contribution) {
|
public void fetchMediaUriFor(Contribution contribution) {
|
||||||
Timber.d("Fetching thumbnail for %s", contribution.filename);
|
Timber.d("Fetching thumbnail for %s", contribution.getFilename());
|
||||||
contributionsPresenter.fetchMediaDetails(contribution);
|
contributionsPresenter.fetchMediaDetails(contribution);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public class ContributionsListAdapter extends RecyclerView.Adapter<ContributionV
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
return contributions.get(position)._id;
|
return contributions.get(position).get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,30 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.DataSetObserver;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.Observer;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.MediaDataExtractor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
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.MediaDataExtractor;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
|
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
|
||||||
import fr.free.nrw.commons.db.AppDatabase;
|
import fr.free.nrw.commons.db.AppDatabase;
|
||||||
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
||||||
import fr.free.nrw.commons.mwapi.UserClient;
|
import fr.free.nrw.commons.mwapi.UserClient;
|
||||||
import fr.free.nrw.commons.utils.ExecutorUtils;
|
|
||||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Scheduler;
|
import io.reactivex.Scheduler;
|
||||||
import io.reactivex.SingleObserver;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The presenter class for Contributions
|
* The presenter class for Contributions
|
||||||
*/
|
*/
|
||||||
|
|
@ -106,12 +90,7 @@ public class ContributionsPresenter implements UserActionListener {
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.doOnNext(mwQueryLogEvent -> Timber.d("Received image %s", mwQueryLogEvent.title()))
|
.doOnNext(mwQueryLogEvent -> Timber.d("Received image %s", mwQueryLogEvent.title()))
|
||||||
.filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted()).doOnNext(mwQueryLogEvent -> Timber.d("Image %s passed filters", mwQueryLogEvent.title()))
|
.filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted()).doOnNext(mwQueryLogEvent -> Timber.d("Image %s passed filters", mwQueryLogEvent.title()))
|
||||||
.map(image -> {
|
.map(image -> new Contribution(image, user))
|
||||||
Contribution contribution = new Contribution(null, null, image.title(),
|
|
||||||
new HashMap<>(), "", -1, image.date(), image.date(), user,
|
|
||||||
"", "", STATE_COMPLETED);
|
|
||||||
return contribution;
|
|
||||||
})
|
|
||||||
.toList()
|
.toList()
|
||||||
.subscribe(this::saveContributionsToDB, error -> {
|
.subscribe(this::saveContributionsToDB, error -> {
|
||||||
Timber.e("Failed to fetch contributions: %s", error.getMessage());
|
Timber.e("Failed to fetch contributions: %s", error.getMessage());
|
||||||
|
|
@ -198,11 +177,11 @@ public class ContributionsPresenter implements UserActionListener {
|
||||||
@Override
|
@Override
|
||||||
public void fetchMediaDetails(Contribution contribution) {
|
public void fetchMediaDetails(Contribution contribution) {
|
||||||
compositeDisposable.add(mediaDataExtractor
|
compositeDisposable.add(mediaDataExtractor
|
||||||
.getMediaFromFileName(contribution.filename)
|
.getMediaFromFileName(contribution.getFilename())
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(media -> {
|
.subscribe(media -> {
|
||||||
contribution.thumbUrl=media.thumbUrl;
|
contribution.setThumbUrl(media.getThumbUrl());
|
||||||
updateContribution(contribution);
|
updateContribution(contribution);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ import com.google.gson.reflect.TypeToken;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import java.util.ArrayList;
|
import fr.free.nrw.commons.upload.WikidataPlace;
|
||||||
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,53 +43,74 @@ public class Converters {
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static String listObjectToString(ArrayList<String> objectList) {
|
public static String listObjectToString(List<String> objectList) {
|
||||||
return objectList == null ? null : getGson().toJson(objectList);
|
return writeObjectToString(objectList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static ArrayList<String> stringToArrayListObject(String objectList) {
|
public static List<String> stringToListObject(String objectList) {
|
||||||
return objectList == null ? null : getGson().fromJson(objectList,new TypeToken<ArrayList<String>>(){}.getType());
|
return readObjectWithTypeToken(objectList, new TypeToken<List<String>>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static String mapObjectToString(HashMap<String,String> objectList) {
|
public static String mapObjectToString(Map<String,String> objectList) {
|
||||||
return objectList == null ? null : getGson().toJson(objectList);
|
return writeObjectToString(objectList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static HashMap<String,String> stringToHashMap(String objectList) {
|
public static Map<String,String> stringToMap(String objectList) {
|
||||||
return objectList == null ? null : getGson().fromJson(objectList,new TypeToken<HashMap<String,String>>(){}.getType());
|
return readObjectWithTypeToken(objectList, new TypeToken<Map<String,String>>(){});
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static String latlngObjectToString(LatLng latlng) {
|
public static String latlngObjectToString(LatLng latlng) {
|
||||||
return latlng == null ? null : getGson().toJson(latlng);
|
return writeObjectToString(latlng);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static LatLng stringToLatLng(String objectList) {
|
public static LatLng stringToLatLng(String objectList) {
|
||||||
return objectList == null ? null : getGson().fromJson(objectList,LatLng.class);
|
return readObjectFromString(objectList,LatLng.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static String listOfMapToString(ArrayList<Map<String,String>> listOfMaps) {
|
public static String listOfMapToString(List<Map<String,String>> listOfMaps) {
|
||||||
return listOfMaps == null ? null : getGson().toJson(listOfMaps);
|
return writeObjectToString(listOfMaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static ArrayList<Map<String,String>> stringToListOfMap(String listOfMaps) {
|
public static List<Map<String,String>> stringToListOfMap(String listOfMaps) {
|
||||||
return listOfMaps == null ? null :getGson().fromJson(listOfMaps,new TypeToken<ArrayList<Map<String,String>>>(){}.getType());
|
return readObjectWithTypeToken(listOfMaps, new TypeToken<List<Map<String, String>>>() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static String mapToString(Map<String,String> map) {
|
public static String wikidataPlaceToString(WikidataPlace wikidataPlace) {
|
||||||
return map == null ? null : getGson().toJson(map);
|
return writeObjectToString(wikidataPlace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
public static Map<String,String> stringToMap(String map) {
|
public static WikidataPlace stringToWikidataPlace(String wikidataPlace) {
|
||||||
return map == null ? null :getGson().fromJson(map,new TypeToken<Map<String,String>>(){}.getType());
|
return readObjectFromString(wikidataPlace, WikidataPlace.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
public static String depictionListToString(List<DepictedItem> depictedItems) {
|
||||||
|
return writeObjectToString(depictedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
public static List<DepictedItem> stringToList(String depictedItems) {
|
||||||
|
return readObjectWithTypeToken(depictedItems, new TypeToken<List<DepictedItem>>() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String writeObjectToString(Object object) {
|
||||||
|
return object == null ? null : getGson().toJson(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static<T> T readObjectFromString(String objectAsString, Class<T> clazz) {
|
||||||
|
return objectAsString == null ? null : getGson().fromJson(objectAsString, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T readObjectWithTypeToken(String objectList, TypeToken<T> typeToken) {
|
||||||
|
return objectList == null ? null : getGson().fromJson(objectList, typeToken.getType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,12 @@
|
||||||
package fr.free.nrw.commons.delete;
|
package fr.free.nrw.commons.delete;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
|
@ -29,10 +19,16 @@ import io.reactivex.Single;
|
||||||
import io.reactivex.SingleSource;
|
import io.reactivex.SingleSource;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refactored async task to Rx
|
* Refactored async task to Rx
|
||||||
*/
|
*/
|
||||||
|
|
@ -104,7 +100,7 @@ public class DeleteHelper {
|
||||||
}
|
}
|
||||||
String creatorName = creator.replace(" (page does not exist)", "");
|
String creatorName = creator.replace(" (page does not exist)", "");
|
||||||
|
|
||||||
return pageEditClient.prependEdit(media.filename, fileDeleteString + "\n", summary)
|
return pageEditClient.prependEdit(media.getFilename(), fileDeleteString + "\n", summary)
|
||||||
.flatMap(result -> {
|
.flatMap(result -> {
|
||||||
if (result) {
|
if (result) {
|
||||||
return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary);
|
return pageEditClient.edit("Commons:Deletion_requests/" + media.getFilename(), subpageString + "\n", summary);
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ public class SubDepictionListPresenter implements SubDepictionListContract.UserA
|
||||||
this.queryList.addAll(mediaList);
|
this.queryList.addAll(mediaList);
|
||||||
view.onSuccess(mediaList);
|
view.onSuccess(mediaList);
|
||||||
for (DepictedItem m : mediaList) {
|
for (DepictedItem m : mediaList) {
|
||||||
fetchThumbnailForEntityId(m.getEntityId(), size++);
|
fetchThumbnailForEntityId(m.getId(), size++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,8 @@ public class WikidataItemDetailsActivity extends NavigationBaseActivity implemen
|
||||||
public static void startYourself(Context context, DepictedItem depictedItem) {
|
public static void startYourself(Context context, DepictedItem depictedItem) {
|
||||||
Intent intent = new Intent(context, WikidataItemDetailsActivity.class);
|
Intent intent = new Intent(context, WikidataItemDetailsActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
intent.putExtra("wikidataItemName", depictedItem.getDepictsLabel());
|
intent.putExtra("wikidataItemName", depictedItem.getName());
|
||||||
intent.putExtra("entityId", depictedItem.getEntityId());
|
intent.putExtra("entityId", depictedItem.getId());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import fr.free.nrw.commons.review.ReviewInterface;
|
||||||
import fr.free.nrw.commons.upload.UploadInterface;
|
import fr.free.nrw.commons.upload.UploadInterface;
|
||||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
|
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface;
|
|
||||||
import fr.free.nrw.commons.wikidata.WikidataInterface;
|
import fr.free.nrw.commons.wikidata.WikidataInterface;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
@ -163,12 +162,6 @@ public class NetworkingModule {
|
||||||
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class);
|
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public CaptionInterface provideCaptionInterface(@Named(NAMED_WIKI_DATA_WIKI_SITE) WikiSite wikidataWikiSite) {
|
|
||||||
return ServiceFactory.get(wikidataWikiSite, BuildConfig.WIKIDATA_URL, CaptionInterface.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public DepictsInterface provideDepictsInterface(@Named(NAMED_WIKI_DATA_WIKI_SITE) WikiSite wikidataWikiSite) {
|
public DepictsInterface provideDepictsInterface(@Named(NAMED_WIKI_DATA_WIKI_SITE) WikiSite wikidataWikiSite) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
|
@ -113,7 +112,6 @@ public class DepictsClient {
|
||||||
Media media = new Media(null,
|
Media media = new Media(null,
|
||||||
getUrl(s.getTitle()),
|
getUrl(s.getTitle()),
|
||||||
s.getTitle(),
|
s.getTitle(),
|
||||||
new HashMap<>(),
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
safeParseDate(s.getTimestamp()),
|
safeParseDate(s.getTimestamp()),
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ public class SearchDepictionsFragmentPresenter extends CommonsDaggerSupportFragm
|
||||||
view.onSuccess(mediaList);
|
view.onSuccess(mediaList);
|
||||||
offset=queryList.size();
|
offset=queryList.size();
|
||||||
for (DepictedItem m : mediaList) {
|
for (DepictedItem m : mediaList) {
|
||||||
fetchThumbnailForEntityId(m.getEntityId(), size++);
|
fetchThumbnailForEntityId(m.getId(), size++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ public class SearchDepictionsRenderer extends Renderer<DepictedItem> {
|
||||||
@Override
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
DepictedItem item = getContent();
|
DepictedItem item = getContent();
|
||||||
tvDepictionLabel.setText(item.getDepictsLabel());
|
tvDepictionLabel.setText(item.getName());
|
||||||
tvDepictionDesc.setText(item.getDescription());
|
tvDepictionDesc.setText(item.getDescription());
|
||||||
imageView.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_wikidata_logo_24dp));
|
imageView.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_wikidata_logo_24dp));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package fr.free.nrw.commons.media;
|
package fr.free.nrw.commons.media;
|
||||||
|
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.graphics.drawable.Animatable;
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
|
@ -22,28 +25,17 @@ import android.widget.ScrollView;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
|
||||||
import com.facebook.drawee.interfaces.DraweeController;
|
|
||||||
import com.facebook.drawee.controller.BaseControllerListener;
|
|
||||||
import com.facebook.drawee.controller.ControllerListener;
|
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
|
||||||
import com.facebook.imagepipeline.image.ImageInfo;
|
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.wikipedia.util.DateUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
import androidx.annotation.Nullable;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
import com.facebook.drawee.controller.BaseControllerListener;
|
||||||
|
import com.facebook.drawee.controller.ControllerListener;
|
||||||
|
import com.facebook.drawee.interfaces.DraweeController;
|
||||||
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
|
import com.facebook.imagepipeline.image.ImageInfo;
|
||||||
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.MediaDataExtractor;
|
import fr.free.nrw.commons.MediaDataExtractor;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
|
@ -63,13 +55,15 @@ import io.reactivex.Single;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.wikipedia.util.DateUtil;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.view.View.GONE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
|
|
||||||
public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
|
|
||||||
private boolean editable;
|
private boolean editable;
|
||||||
|
|
@ -555,8 +549,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
item.setOnClickListener(view -> {
|
item.setOnClickListener(view -> {
|
||||||
DepictedItem depictedItem = new DepictedItem(depictionName, "", "", false, entityId);
|
DepictedItem depictedItem = new DepictedItem(depictionName, "", "", false, entityId);
|
||||||
Intent intent = new Intent(getContext(), WikidataItemDetailsActivity.class);
|
Intent intent = new Intent(getContext(), WikidataItemDetailsActivity.class);
|
||||||
intent.putExtra("wikidataItemName", depictedItem.getDepictsLabel());
|
intent.putExtra("wikidataItemName", depictedItem.getName());
|
||||||
intent.putExtra("entityId", depictedItem.getEntityId());
|
intent.putExtra("entityId", depictedItem.getId());
|
||||||
getContext().startActivity(intent);
|
getContext().startActivity(intent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +603,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
|
|
||||||
private String prettyDescription(Media media) {
|
private String prettyDescription(Media media) {
|
||||||
// @todo use UI language when multilingual descs are available
|
// @todo use UI language when multilingual descs are available
|
||||||
String desc = media.getDescription(locale.getLanguage()).trim();
|
String desc = media.getDescription();
|
||||||
if (desc.equals("")) {
|
if (desc.equals("")) {
|
||||||
return getString(R.string.detail_description_empty);
|
return getString(R.string.detail_description_empty);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -655,7 +649,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDeletion(Media media){
|
private void checkDeletion(Media media){
|
||||||
if (media.getRequestedDeletion()){
|
if (media.isRequestedDeletion()){
|
||||||
delete.setVisibility(GONE);
|
delete.setVisibility(GONE);
|
||||||
nominatedForDeletion.setVisibility(VISIBLE);
|
nominatedForDeletion.setVisibility(VISIBLE);
|
||||||
} else if (!isCategoryImage) {
|
} else if (!isCategoryImage) {
|
||||||
|
|
|
||||||
|
|
@ -170,13 +170,12 @@ public class UploadRemoteDataSource {
|
||||||
*
|
*
|
||||||
* @param uploadableFile
|
* @param uploadableFile
|
||||||
* @param place
|
* @param place
|
||||||
* @param source
|
|
||||||
* @param similarImageInterface
|
* @param similarImageInterface
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
||||||
String source, SimilarImageInterface similarImageInterface) {
|
SimilarImageInterface similarImageInterface) {
|
||||||
return uploadModel.preProcessImage(uploadableFile, place, source, similarImageInterface);
|
return uploadModel.preProcessImage(uploadableFile, place, similarImageInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -213,7 +212,7 @@ public class UploadRemoteDataSource {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void onDepictedItemClicked(DepictedItem depictedItem) {
|
public void onDepictedItemClicked(DepictedItem depictedItem) {
|
||||||
depictModel.onDepictItemClicked(depictedItem);
|
uploadModel.onDepictItemClicked(depictedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -222,7 +221,7 @@ public class UploadRemoteDataSource {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public List<DepictedItem> getSelectedDepictions() {
|
public List<DepictedItem> getSelectedDepictions() {
|
||||||
return depictModel.getSelectedDepictions();
|
return uploadModel.getSelectedDepictions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -233,14 +232,6 @@ public class UploadRemoteDataSource {
|
||||||
return depictModel.searchAllEntities(query);
|
return depictModel.searchAllEntities(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedDepictions(List<String> selectedDepictions) {
|
|
||||||
uploadModel.setSelectedDepictions(selectedDepictions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> depictionsEntityIdList() {
|
|
||||||
return depictModel.depictionsEntityIdList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
||||||
uploadModel.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
uploadModel.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,6 @@ public class UploadRepository {
|
||||||
remoteDataSource.setSelectedCategories(categoryStringList);
|
remoteDataSource.setSelectedCategories(categoryStringList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedDepictions(List<String> selectedDepictions) {
|
|
||||||
remoteDataSource.setSelectedDepictions(selectedDepictions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handles the category selection/deselection
|
* handles the category selection/deselection
|
||||||
*
|
*
|
||||||
|
|
@ -180,14 +176,12 @@ public class UploadRepository {
|
||||||
*
|
*
|
||||||
* @param uploadableFile
|
* @param uploadableFile
|
||||||
* @param place
|
* @param place
|
||||||
* @param source
|
|
||||||
* @param similarImageInterface
|
* @param similarImageInterface
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
||||||
String source, SimilarImageInterface similarImageInterface) {
|
SimilarImageInterface similarImageInterface) {
|
||||||
return remoteDataSource
|
return remoteDataSource.preProcessImage(uploadableFile, place, similarImageInterface);
|
||||||
.preProcessImage(uploadableFile, place, source, similarImageInterface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -294,10 +288,6 @@ public class UploadRepository {
|
||||||
return remoteDataSource.searchAllEntities(query);
|
return remoteDataSource.searchAllEntities(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getDepictionsEntityIdList() {
|
|
||||||
return remoteDataSource.depictionsEntityIdList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns nearest place matching the passed latitude and longitude
|
* Returns nearest place matching the passed latitude and longitude
|
||||||
* @param decLatitude
|
* @param decLatitude
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource;
|
||||||
|
import fr.free.nrw.commons.settings.Prefs.Licenses;
|
||||||
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
class PageContentsCreator {
|
||||||
|
|
||||||
|
//{{According to Exif data|2009-01-09}}
|
||||||
|
private static final String TEMPLATE_DATE_ACC_TO_EXIF = "{{According to Exif data|%s}}";
|
||||||
|
|
||||||
|
//2009-01-09 → 9 January 2009
|
||||||
|
private static final String TEMPLATE_DATA_OTHER_SOURCE = "%s";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PageContentsCreator(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createFrom(Contribution contribution) {
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer
|
||||||
|
.append("== {{int:filedesc}} ==\n")
|
||||||
|
.append("{{Information\n")
|
||||||
|
.append("|description=").append(contribution.getDescription()).append("\n")
|
||||||
|
.append("|source=").append("{{own}}\n")
|
||||||
|
.append("|author=[[User:").append(contribution.getCreator()).append("|")
|
||||||
|
.append(contribution.getCreator()).append("]]\n");
|
||||||
|
|
||||||
|
String templatizedCreatedDate = getTemplatizedCreatedDate(
|
||||||
|
contribution.getDateCreated(), contribution.getDateCreatedSource());
|
||||||
|
if (!StringUtils.isBlank(templatizedCreatedDate)) {
|
||||||
|
buffer.append("|date=").append(templatizedCreatedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append("}}").append("\n");
|
||||||
|
|
||||||
|
//Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null
|
||||||
|
final String decimalCoords = contribution.getDecimalCoords();
|
||||||
|
if (decimalCoords != null) {
|
||||||
|
buffer.append("{{Location|").append(decimalCoords).append("}}").append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append("== {{int:license-header}} ==\n")
|
||||||
|
.append(licenseTemplateFor(contribution.getLicense())).append("\n\n")
|
||||||
|
.append("{{Uploaded from Mobile|platform=Android|version=")
|
||||||
|
.append(ConfigUtils.getVersionNameWithSha(context)).append("}}\n");
|
||||||
|
final List<String> categories = contribution.getCategories();
|
||||||
|
if (categories != null && categories.size() != 0) {
|
||||||
|
for (int i = 0; i < categories.size(); i++) {
|
||||||
|
buffer.append("\n[[Category:").append(categories.get(i)).append("]]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer.append("{{subst:unc}}");
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns upload date in either TEMPLATE_DATE_ACC_TO_EXIF or TEMPLATE_DATA_OTHER_SOURCE
|
||||||
|
*
|
||||||
|
* @param dateCreated
|
||||||
|
* @param dateCreatedSource
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getTemplatizedCreatedDate(Date dateCreated, String dateCreatedSource) {
|
||||||
|
if (dateCreated != null) {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
return String.format(Locale.ENGLISH,
|
||||||
|
isExif(dateCreatedSource) ? TEMPLATE_DATE_ACC_TO_EXIF : TEMPLATE_DATA_OTHER_SOURCE,
|
||||||
|
dateFormat.format(dateCreated)
|
||||||
|
) + "\n";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExif(String dateCreatedSource) {
|
||||||
|
return DateTimeWithSource.EXIF_SOURCE.equals(dateCreatedSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String licenseTemplateFor(String license) {
|
||||||
|
switch (license) {
|
||||||
|
case Licenses.CC_BY_3:
|
||||||
|
return "{{self|cc-by-3.0}}";
|
||||||
|
case Licenses.CC_BY_4:
|
||||||
|
return "{{self|cc-by-4.0}}";
|
||||||
|
case Licenses.CC_BY_SA_3:
|
||||||
|
return "{{self|cc-by-sa-3.0}}";
|
||||||
|
case Licenses.CC_BY_SA_4:
|
||||||
|
return "{{self|cc-by-sa-4.0}}";
|
||||||
|
case Licenses.CC0:
|
||||||
|
return "{{self|cc-zero}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Unrecognized license value: " + license);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
package fr.free.nrw.commons.upload;
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS;
|
||||||
|
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
|
||||||
|
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
|
@ -10,7 +14,6 @@ import android.widget.ImageButton;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.cardview.widget.CardView;
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
@ -20,14 +23,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
|
|
@ -36,7 +31,6 @@ import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.category.CategoriesModel;
|
import fr.free.nrw.commons.category.CategoriesModel;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
|
|
@ -48,18 +42,18 @@ import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
||||||
import fr.free.nrw.commons.upload.license.MediaLicenseFragment;
|
import fr.free.nrw.commons.upload.license.MediaLicenseFragment;
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment;
|
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment;
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.UploadMediaDetailFragmentCallback;
|
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.UploadMediaDetailFragmentCallback;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictModel;
|
|
||||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS;
|
|
||||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
|
|
||||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
|
||||||
|
|
||||||
public class UploadActivity extends BaseActivity implements UploadContract.View, UploadBaseFragment.Callback {
|
public class UploadActivity extends BaseActivity implements UploadContract.View, UploadBaseFragment.Callback {
|
||||||
@Inject
|
@Inject
|
||||||
ContributionController contributionController;
|
ContributionController contributionController;
|
||||||
|
|
@ -108,8 +102,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
||||||
private MediaLicenseFragment mediaLicenseFragment;
|
private MediaLicenseFragment mediaLicenseFragment;
|
||||||
private ThumbnailsAdapter thumbnailsAdapter;
|
private ThumbnailsAdapter thumbnailsAdapter;
|
||||||
|
|
||||||
|
|
||||||
private String source;
|
|
||||||
private Place place;
|
private Place place;
|
||||||
private List<UploadableFile> uploadableFiles = Collections.emptyList();
|
private List<UploadableFile> uploadableFiles = Collections.emptyList();
|
||||||
private int currentSelectedPosition = 0;
|
private int currentSelectedPosition = 0;
|
||||||
|
|
@ -325,7 +317,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
||||||
fragments = new ArrayList<>();
|
fragments = new ArrayList<>();
|
||||||
for (UploadableFile uploadableFile : uploadableFiles) {
|
for (UploadableFile uploadableFile : uploadableFiles) {
|
||||||
UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment();
|
UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment();
|
||||||
uploadMediaDetailFragment.setImageTobeUploaded(uploadableFile, source, place);
|
uploadMediaDetailFragment.setImageTobeUploaded(uploadableFile, place);
|
||||||
uploadMediaDetailFragment.setCallback(new UploadMediaDetailFragmentCallback() {
|
uploadMediaDetailFragment.setCallback(new UploadMediaDetailFragmentCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void deletePictureAtIndex(int index) {
|
public void deletePictureAtIndex(int index) {
|
||||||
|
|
@ -387,16 +379,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
||||||
private void receiveInternalSharedItems() {
|
private void receiveInternalSharedItems() {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
Timber.d("Received intent %s with action %s", intent.toString(), intent.getAction());
|
||||||
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
|
||||||
} else {
|
|
||||||
source = Contribution.SOURCE_EXTERNAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Received intent %s with action %s and from source %s",
|
|
||||||
intent.toString(),
|
|
||||||
intent.getAction(),
|
|
||||||
source);
|
|
||||||
|
|
||||||
uploadableFiles = intent.getParcelableArrayListExtra(EXTRA_FILES);
|
uploadableFiles = intent.getParcelableArrayListExtra(EXTRA_FILES);
|
||||||
Timber.i("Received multiple upload %s", uploadableFiles.size());
|
Timber.i("Received multiple upload %s", uploadableFiles.size());
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,36 @@
|
||||||
package fr.free.nrw.commons.upload;
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import org.wikipedia.csrf.CsrfTokenClient;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.upload.UploadService.NotificationUpdateProgressListener;
|
import fr.free.nrw.commons.upload.UploadService.NotificationUpdateProgressListener;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
import java.io.File;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
|
import org.wikipedia.csrf.CsrfTokenClient;
|
||||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class UploadClient {
|
public class UploadClient {
|
||||||
|
|
||||||
private final UploadInterface uploadInterface;
|
private final UploadInterface uploadInterface;
|
||||||
private final CsrfTokenClient csrfTokenClient;
|
private final CsrfTokenClient csrfTokenClient;
|
||||||
|
private final PageContentsCreator pageContentsCreator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UploadClient(UploadInterface uploadInterface, @Named(NAMED_COMMONS_CSRF) CsrfTokenClient csrfTokenClient) {
|
public UploadClient(UploadInterface uploadInterface,
|
||||||
|
@Named(NAMED_COMMONS_CSRF) CsrfTokenClient csrfTokenClient,
|
||||||
|
PageContentsCreator pageContentsCreator) {
|
||||||
this.uploadInterface = uploadInterface;
|
this.uploadInterface = uploadInterface;
|
||||||
this.csrfTokenClient = csrfTokenClient;
|
this.csrfTokenClient = csrfTokenClient;
|
||||||
|
this.pageContentsCreator = pageContentsCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
Observable<UploadResult> uploadFileToStash(Context context, String filename, File file,
|
Observable<UploadResult> uploadFileToStash(Context context, String filename, File file,
|
||||||
|
|
@ -61,8 +62,8 @@ public class UploadClient {
|
||||||
try {
|
try {
|
||||||
return uploadInterface
|
return uploadInterface
|
||||||
.uploadFileFromStash(csrfTokenClient.getTokenBlocking(),
|
.uploadFileFromStash(csrfTokenClient.getTokenBlocking(),
|
||||||
contribution.getPageContents(context),
|
pageContentsCreator.createFrom(contribution),
|
||||||
contribution.getEditSummary(),
|
CommonsApplication.DEFAULT_EDIT_SUMMARY,
|
||||||
uniqueFileName,
|
uniqueFileName,
|
||||||
fileKey).map(uploadResponse -> uploadResponse.getUpload());
|
fileKey).map(uploadResponse -> uploadResponse.getUpload());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,6 @@ public class UploadController {
|
||||||
contribution.setDescription("");
|
contribution.setDescription("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contribution.getCaption() == null) {
|
|
||||||
contribution.setCaption("");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
final String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
||||||
contribution.setLicense(license);
|
contribution.setLicense(license);
|
||||||
|
|
||||||
|
|
@ -152,7 +148,7 @@ public class UploadController {
|
||||||
|
|
||||||
if (mimeType != null) {
|
if (mimeType != null) {
|
||||||
Timber.d("MimeType is: %s", mimeType);
|
Timber.d("MimeType is: %s", mimeType);
|
||||||
contribution.setTag("mimeType", mimeType);
|
contribution.setMimeType(mimeType);
|
||||||
if(mimeType.startsWith("image/") && contribution.getDateCreated() == null){
|
if(mimeType.startsWith("image/") && contribution.getDateCreated() == null){
|
||||||
contribution.setDateCreated(resolveDateTakenOrNow(contentResolver, contribution));
|
contribution.setDateCreated(resolveDateTakenOrNow(contentResolver, contribution));
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +158,7 @@ public class UploadController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveMimeType(final ContentResolver contentResolver, final Contribution contribution) {
|
private String resolveMimeType(final ContentResolver contentResolver, final Contribution contribution) {
|
||||||
final String mimeType = (String) contribution.getTag("mimeType");
|
final String mimeType = contribution.getMimeType();
|
||||||
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
|
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
|
||||||
return contentResolver.getType(contribution.getLocalUri());
|
return contentResolver.getType(contribution.getLocalUri());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,13 @@ public class UploadDepictsRenderer extends Renderer<DepictedItem> {
|
||||||
public void render() {
|
public void render() {
|
||||||
DepictedItem item = getContent();
|
DepictedItem item = getContent();
|
||||||
checkedView.setChecked(item.isSelected());
|
checkedView.setChecked(item.isSelected());
|
||||||
depictsLabel.setText(item.getDepictsLabel());
|
depictsLabel.setText(item.getName());
|
||||||
description.setText(item.getDescription());
|
description.setText(item.getDescription());
|
||||||
if (!TextUtils.isEmpty(item.getImageUrl())) {
|
if (!TextUtils.isEmpty(item.getImageUrl())) {
|
||||||
if (!item.getImageUrl().equals(NO_IMAGE_FOR_DEPICTION))
|
if (!item.getImageUrl().equals(NO_IMAGE_FOR_DEPICTION))
|
||||||
setImageView(Uri.parse(item.getImageUrl()), imageView);
|
setImageView(Uri.parse(item.getImageUrl()), imageView);
|
||||||
}else{
|
}else{
|
||||||
listener.fetchThumbnailUrlForEntity(item.getEntityId(),item.getPosition());
|
listener.fetchThumbnailUrlForEntity(item.getId(),item.getPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ data class UploadMediaDetail constructor(
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun formatCaptions(uploadMediaDetails: List<UploadMediaDetail>) =
|
fun formatCaptions(uploadMediaDetails: List<UploadMediaDetail>) =
|
||||||
uploadMediaDetails.associate { it.languageCode to it.captionText }
|
uploadMediaDetails.associate { it.languageCode to it.captionText }.filter { it.value.isNotBlank() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the list of descriptions into the format Commons requires for uploads.
|
* Formats the list of descriptions into the format Commons requires for uploads.
|
||||||
|
|
@ -56,11 +56,7 @@ data class UploadMediaDetail constructor(
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun formatList(descriptions: List<UploadMediaDetail>) =
|
fun formatList(descriptions: List<UploadMediaDetail>) =
|
||||||
descriptions.joinToString {
|
descriptions.filter { it.descriptionText.isNotEmpty() }
|
||||||
if (it.descriptionText.isNotEmpty())
|
.joinToString { "{{${it.languageCode}|1=${it.descriptionText}}}" }
|
||||||
"{{${it.languageCode}|1=${it.descriptionText}}}"
|
|
||||||
else
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||||
import fr.free.nrw.commons.utils.ImageUtils;
|
import fr.free.nrw.commons.utils.ImageUtils;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
@ -18,9 +18,7 @@ import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.subjects.BehaviorSubject;
|
import io.reactivex.subjects.BehaviorSubject;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -44,8 +42,8 @@ public class UploadModel {
|
||||||
private final SessionManager sessionManager;
|
private final SessionManager sessionManager;
|
||||||
private final FileProcessor fileProcessor;
|
private final FileProcessor fileProcessor;
|
||||||
private final ImageProcessingService imageProcessingService;
|
private final ImageProcessingService imageProcessingService;
|
||||||
private List<String> selectedCategories;
|
private List<String> selectedCategories = new ArrayList<>();
|
||||||
private ArrayList<String> selectedDepictions;
|
private List<DepictedItem> selectedDepictions = new ArrayList<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UploadModel(@Named("licenses") final List<String> licenses,
|
UploadModel(@Named("licenses") final List<String> licenses,
|
||||||
|
|
@ -71,17 +69,13 @@ public class UploadModel {
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
compositeDisposable.clear();
|
compositeDisposable.clear();
|
||||||
fileProcessor.cleanup();
|
fileProcessor.cleanup();
|
||||||
this.items.clear();
|
items.clear();
|
||||||
if (this.selectedCategories != null) {
|
selectedCategories.clear();
|
||||||
this.selectedCategories.clear();
|
selectedDepictions.clear();
|
||||||
}
|
|
||||||
if (this.selectedDepictions != null) {
|
|
||||||
this.selectedDepictions.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedCategories(List<String> selectedCategories) {
|
public void setSelectedCategories(List<String> selectedCategories) {
|
||||||
this.selectedCategories = newListOf(selectedCategories);
|
this.selectedCategories = selectedCategories;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,10 +83,9 @@ public class UploadModel {
|
||||||
*/
|
*/
|
||||||
public Observable<UploadItem> preProcessImage(final UploadableFile uploadableFile,
|
public Observable<UploadItem> preProcessImage(final UploadableFile uploadableFile,
|
||||||
final Place place,
|
final Place place,
|
||||||
final String source,
|
|
||||||
final SimilarImageInterface similarImageInterface) {
|
final SimilarImageInterface similarImageInterface) {
|
||||||
return Observable.just(
|
return Observable.just(
|
||||||
createAndAddUploadItem(uploadableFile, place, source, similarImageInterface));
|
createAndAddUploadItem(uploadableFile, place, similarImageInterface));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<Integer> getImageQuality(final UploadItem uploadItem) {
|
public Single<Integer> getImageQuality(final UploadItem uploadItem) {
|
||||||
|
|
@ -101,7 +94,6 @@ public class UploadModel {
|
||||||
|
|
||||||
private UploadItem createAndAddUploadItem(final UploadableFile uploadableFile,
|
private UploadItem createAndAddUploadItem(final UploadableFile uploadableFile,
|
||||||
final Place place,
|
final Place place,
|
||||||
final String source,
|
|
||||||
final SimilarImageInterface similarImageInterface) {
|
final SimilarImageInterface similarImageInterface) {
|
||||||
final UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
|
final UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
|
||||||
.getFileCreatedDate(context);
|
.getFileCreatedDate(context);
|
||||||
|
|
@ -116,7 +108,7 @@ public class UploadModel {
|
||||||
.processFileCoordinates(similarImageInterface, uploadableFile.getFilePath());
|
.processFileCoordinates(similarImageInterface, uploadableFile.getFilePath());
|
||||||
final UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
|
final UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
|
||||||
Uri.parse(uploadableFile.getFilePath()),
|
Uri.parse(uploadableFile.getFilePath()),
|
||||||
uploadableFile.getMimeType(context), source, imageCoordinates, place, fileCreatedDate,
|
uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate,
|
||||||
createdTimestampSource);
|
createdTimestampSource);
|
||||||
if (place != null) {
|
if (place != null) {
|
||||||
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
|
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
|
||||||
|
|
@ -151,29 +143,8 @@ public class UploadModel {
|
||||||
public Observable<Contribution> buildContributions() {
|
public Observable<Contribution> buildContributions() {
|
||||||
return Observable.fromIterable(items).map(item ->
|
return Observable.fromIterable(items).map(item ->
|
||||||
{
|
{
|
||||||
final Contribution contribution = new Contribution(item.mediaUri, null,
|
final Contribution contribution = new Contribution(
|
||||||
item.getFileName(), item.uploadMediaDetails.size()!=0? UploadMediaDetail.formatCaptions(item.uploadMediaDetails):new HashMap<>(),
|
item, sessionManager, newListOf(selectedDepictions), newListOf(selectedCategories));
|
||||||
UploadMediaDetail.formatList(item.uploadMediaDetails), -1,
|
|
||||||
null, null, sessionManager.getAuthorName(),
|
|
||||||
CommonsApplication.DEFAULT_EDIT_SUMMARY, new ArrayList<>(selectedDepictions), item.gpsCoords.getDecimalCoords());
|
|
||||||
if (item.place != null) {
|
|
||||||
contribution.setWikiDataEntityId(item.place.getWikiDataEntityId());
|
|
||||||
contribution.setWikiItemName(item.place.getName());
|
|
||||||
// If item already has an image, we need to know it. We don't want to override existing image later
|
|
||||||
contribution.setP18Value(item.place.pic);
|
|
||||||
}
|
|
||||||
if (null == selectedCategories) {//Just a fail safe, this should never be null
|
|
||||||
selectedCategories = new ArrayList<>();
|
|
||||||
}
|
|
||||||
if (selectedDepictions == null) {
|
|
||||||
selectedDepictions = new ArrayList<>();
|
|
||||||
}
|
|
||||||
contribution.setCategories(selectedCategories);
|
|
||||||
contribution.setTag("mimeType", item.mimeType);
|
|
||||||
contribution.setSource(item.source);
|
|
||||||
contribution.setContentProviderUri(item.mediaUri);
|
|
||||||
contribution.setDateUploaded(new Date());
|
|
||||||
|
|
||||||
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()));
|
||||||
|
|
@ -208,12 +179,16 @@ public class UploadModel {
|
||||||
uploadItem1.setMediaDetails(uploadItem.uploadMediaDetails);
|
uploadItem1.setMediaDetails(uploadItem.uploadMediaDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedDepictions(final List<String> selectedDepictions) {
|
public void onDepictItemClicked(DepictedItem depictedItem) {
|
||||||
this.selectedDepictions = newListOf(selectedDepictions);
|
if (depictedItem.isSelected()) {
|
||||||
|
selectedDepictions.add(depictedItem);
|
||||||
|
} else {
|
||||||
|
selectedDepictions.remove(depictedItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private <T> ArrayList<T> newListOf(final List<T> items) {
|
private <T> List<T> newListOf(final List<T> items) {
|
||||||
return items != null ? new ArrayList<>(items) : new ArrayList<>();
|
return items != null ? new ArrayList<>(items) : new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,13 +197,16 @@ public class UploadModel {
|
||||||
items.get(uploadItemIndex).setGpsCoords(imageCoordinates);
|
items.get(uploadItemIndex).setGpsCoords(imageCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DepictedItem> getSelectedDepictions() {
|
||||||
|
return selectedDepictions;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public static class UploadItem {
|
public static class UploadItem {
|
||||||
|
|
||||||
private final Uri originalContentUri;
|
private final Uri originalContentUri;
|
||||||
private final Uri mediaUri;
|
private final Uri mediaUri;
|
||||||
private final String mimeType;
|
private final String mimeType;
|
||||||
private final String source;
|
|
||||||
private ImageCoordinates gpsCoords;
|
private ImageCoordinates gpsCoords;
|
||||||
private List<UploadMediaDetail> uploadMediaDetails;
|
private List<UploadMediaDetail> uploadMediaDetails;
|
||||||
private final Place place;
|
private final Place place;
|
||||||
|
|
@ -237,7 +215,8 @@ public class UploadModel {
|
||||||
private final BehaviorSubject<Integer> imageQuality;
|
private final BehaviorSubject<Integer> imageQuality;
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
UploadItem(final Uri originalContentUri,
|
UploadItem(final Uri originalContentUri,
|
||||||
final Uri mediaUri, final String mimeType, final String source, final ImageCoordinates gpsCoords,
|
final Uri mediaUri, final String mimeType,
|
||||||
|
final ImageCoordinates gpsCoords,
|
||||||
final Place place,
|
final Place place,
|
||||||
final long createdTimestamp,
|
final long createdTimestamp,
|
||||||
final String createdTimestampSource) {
|
final String createdTimestampSource) {
|
||||||
|
|
@ -247,7 +226,6 @@ public class UploadModel {
|
||||||
this.place = place;
|
this.place = place;
|
||||||
this.mediaUri = mediaUri;
|
this.mediaUri = mediaUri;
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.source = source;
|
|
||||||
this.gpsCoords = gpsCoords;
|
this.gpsCoords = gpsCoords;
|
||||||
this.createdTimestamp = createdTimestamp;
|
this.createdTimestamp = createdTimestamp;
|
||||||
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
|
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
|
||||||
|
|
@ -257,10 +235,6 @@ public class UploadModel {
|
||||||
return createdTimestampSource;
|
return createdTimestampSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageCoordinates getGpsCoords() {
|
public ImageCoordinates getGpsCoords() {
|
||||||
return gpsCoords;
|
return gpsCoords;
|
||||||
}
|
}
|
||||||
|
|
@ -323,5 +297,8 @@ public class UploadModel {
|
||||||
this.gpsCoords = gpsCoords;
|
this.gpsCoords = gpsCoords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,16 @@ package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
import org.wikipedia.gallery.ImageInfo
|
import org.wikipedia.gallery.ImageInfo
|
||||||
|
|
||||||
class UploadResult(val result: String, val filekey: String, val filename: String, val sessionkey: String, val imageinfo: ImageInfo)
|
private const val RESULT_SUCCESS = "Success"
|
||||||
|
|
||||||
|
data class UploadResult(
|
||||||
|
val result: String,
|
||||||
|
val filekey: String,
|
||||||
|
val filename: String,
|
||||||
|
val sessionkey: String,
|
||||||
|
val imageinfo: ImageInfo
|
||||||
|
) {
|
||||||
|
fun isSuccessful(): Boolean = result == RESULT_SUCCESS
|
||||||
|
|
||||||
|
fun createCanonicalFileName() = "File:$filename"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,8 @@ import android.content.Intent;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.HandlerService;
|
import fr.free.nrw.commons.HandlerService;
|
||||||
|
|
@ -38,6 +25,15 @@ import io.reactivex.Observable;
|
||||||
import io.reactivex.Scheduler;
|
import io.reactivex.Scheduler;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class UploadService extends HandlerService<Contribution> {
|
public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
@ -47,7 +43,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
public static final int ACTION_UPLOAD_FILE = 1;
|
public static final int ACTION_UPLOAD_FILE = 1;
|
||||||
|
|
||||||
public static final String ACTION_START_SERVICE = EXTRA_PREFIX + ".upload";
|
public static final String ACTION_START_SERVICE = EXTRA_PREFIX + ".upload";
|
||||||
public static final String EXTRA_SOURCE = EXTRA_PREFIX + ".source";
|
|
||||||
public static final String EXTRA_FILES = EXTRA_PREFIX + ".files";
|
public static final String EXTRA_FILES = EXTRA_PREFIX + ".files";
|
||||||
@Inject WikidataEditService wikidataEditService;
|
@Inject WikidataEditService wikidataEditService;
|
||||||
@Inject SessionManager sessionManager;
|
@Inject SessionManager sessionManager;
|
||||||
|
|
@ -151,7 +146,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queue(int what, Contribution contribution) {
|
public void queue(int what, Contribution contribution) {
|
||||||
Timber.d("Upload service queue has contribution with wiki data entity id as %s", contribution.getWikiDataEntityId());
|
|
||||||
switch (what) {
|
switch (what) {
|
||||||
case ACTION_UPLOAD_FILE:
|
case ACTION_UPLOAD_FILE:
|
||||||
|
|
||||||
|
|
@ -168,7 +162,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
.subscribeOn(ioThreadScheduler)
|
.subscribeOn(ioThreadScheduler)
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.subscribe(aLong->{
|
.subscribe(aLong->{
|
||||||
contribution._id = aLong;
|
contribution.set_id(aLong);
|
||||||
UploadService.super.queue(what, contribution);
|
UploadService.super.queue(what, contribution);
|
||||||
}, Throwable::printStackTrace));
|
}, Throwable::printStackTrace));
|
||||||
break;
|
break;
|
||||||
|
|
@ -251,12 +245,7 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
Timber.d("Stash upload response 1 is %s", uploadStash.toString());
|
Timber.d("Stash upload response 1 is %s", uploadStash.toString());
|
||||||
|
|
||||||
String resultStatus = uploadStash.getResult();
|
if (uploadStash.isSuccessful()) {
|
||||||
if (!resultStatus.equals("Success")) {
|
|
||||||
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
|
||||||
showFailedNotification(contribution);
|
|
||||||
return Observable.never();
|
|
||||||
} else {
|
|
||||||
Timber.d("making sure of uniqueness of name: %s", filename);
|
Timber.d("making sure of uniqueness of name: %s", filename);
|
||||||
String uniqueFilename = findUniqueFilename(filename);
|
String uniqueFilename = findUniqueFilename(filename);
|
||||||
unfinishedUploads.add(uniqueFilename);
|
unfinishedUploads.add(uniqueFilename);
|
||||||
|
|
@ -265,6 +254,10 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
contribution,
|
contribution,
|
||||||
uniqueFilename,
|
uniqueFilename,
|
||||||
uploadStash.getFilekey());
|
uploadStash.getFilekey());
|
||||||
|
} else {
|
||||||
|
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
||||||
|
showFailedNotification(contribution);
|
||||||
|
return Observable.never();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
|
@ -282,32 +275,27 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
|
|
||||||
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
|
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
|
||||||
|
|
||||||
String resultStatus = uploadResult.getResult();
|
if (uploadResult.isSuccessful()) {
|
||||||
if (!resultStatus.equals("Success")) {
|
onSuccessfulUpload(contribution, uploadResult);
|
||||||
|
} else {
|
||||||
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
||||||
showFailedNotification(contribution);
|
showFailedNotification(contribution);
|
||||||
} else {
|
|
||||||
String canonicalFilename = "File:" + uploadResult.getFilename();
|
|
||||||
final String wikiDataEntityId = contribution.getWikiDataEntityId();
|
|
||||||
Timber.d("Contribution upload success. Initiating Wikidata edit for entity id %s",
|
|
||||||
wikiDataEntityId);
|
|
||||||
// to perform upload of depictions we pass on depiction entityId of the selected depictions to the wikidataEditService
|
|
||||||
final String p18Value = contribution.getP18Value();
|
|
||||||
final String wikiItemName = contribution.getWikiItemName();
|
|
||||||
if (contribution.getDepictionsEntityIds() != null) {
|
|
||||||
for (String depictionEntityId : contribution.getDepictionsEntityIds()) {
|
|
||||||
wikidataEditService.createClaimWithLogging(depictionEntityId,
|
|
||||||
wikiItemName, canonicalFilename, p18Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.d("Contribution upload success. Initiating Wikidata edit for"
|
|
||||||
+ " entity id %s if necessary (if P18 is null). P18 value is %s",
|
|
||||||
wikiDataEntityId, p18Value);
|
|
||||||
wikidataEditService.createClaimWithLogging(
|
|
||||||
wikiDataEntityId, wikiItemName, canonicalFilename,p18Value);
|
|
||||||
|
|
||||||
wikidataEditService.createLabelforWikidataEntity(canonicalFilename, contribution.getCaptions());
|
private void onSuccessfulUpload(Contribution contribution, UploadResult uploadResult)
|
||||||
contribution.setFilename(canonicalFilename);
|
throws ParseException {
|
||||||
|
compositeDisposable
|
||||||
|
.add(wikidataEditService.addDepictionsAndCaptions(uploadResult, contribution));
|
||||||
|
WikidataPlace wikidataPlace = contribution.getWikidataPlace();
|
||||||
|
if (wikidataPlace != null && wikidataPlace.getImageValue() == null) {
|
||||||
|
wikidataEditService.createImageClaim(wikidataPlace, uploadResult);
|
||||||
|
}
|
||||||
|
saveCompletedContribution(contribution, uploadResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveCompletedContribution(Contribution contribution, UploadResult uploadResult) throws ParseException {
|
||||||
|
contribution.setFilename(uploadResult.createCanonicalFileName());
|
||||||
contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl());
|
contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl());
|
||||||
contribution.setState(Contribution.STATE_COMPLETED);
|
contribution.setState(Contribution.STATE_COMPLETED);
|
||||||
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatTimestamp()
|
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatTimestamp()
|
||||||
|
|
@ -318,7 +306,6 @@ public class UploadService extends HandlerService<Contribution> {
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.subscribe());
|
.subscribe());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("StringFormatInvalid")
|
@SuppressLint("StringFormatInvalid")
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package fr.free.nrw.commons.upload;
|
package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import static org.wikipedia.dataclient.Service.MW_API_PREFIX;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import io.reactivex.Observable;
|
||||||
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
||||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import retrofit2.http.Field;
|
import retrofit2.http.Field;
|
||||||
import retrofit2.http.FormUrlEncoded;
|
import retrofit2.http.FormUrlEncoded;
|
||||||
import retrofit2.http.GET;
|
import retrofit2.http.GET;
|
||||||
|
|
@ -13,8 +13,6 @@ import retrofit2.http.Headers;
|
||||||
import retrofit2.http.POST;
|
import retrofit2.http.POST;
|
||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
import static org.wikipedia.dataclient.Service.MW_API_PREFIX;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrofit calls for managing responses network calls of entity ids required for uploading depictions
|
* Retrofit calls for managing responses network calls of entity ids required for uploading depictions
|
||||||
*/
|
*/
|
||||||
|
|
@ -31,4 +29,18 @@ public interface WikiBaseInterface {
|
||||||
@GET(MW_API_PREFIX + "action=query&prop=info")
|
@GET(MW_API_PREFIX + "action=query&prop=info")
|
||||||
Observable<MwQueryResponse> getFileEntityId(@Query("titles") String fileName);
|
Observable<MwQueryResponse> getFileEntityId(@Query("titles") String fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload Captions for the image when upload is successful
|
||||||
|
*
|
||||||
|
* @param fileEntityId enityId for the uploaded file
|
||||||
|
* @param editToken editToken for the file
|
||||||
|
* @param captionValue value of the caption to be uploaded
|
||||||
|
*/
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST(MW_API_PREFIX + "action=wbsetlabel")
|
||||||
|
Observable<MwPostResponse> addLabelstoWikidata(@Field("id") String fileEntityId,
|
||||||
|
@Field("token") String editToken,
|
||||||
|
@Field("language") String language,
|
||||||
|
@Field("value") String captionValue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
interface WikidataItem {
|
||||||
|
val id:String
|
||||||
|
val name:String
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import fr.free.nrw.commons.nearby.Place
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
internal data class WikidataPlace(override val id: String, override val name: String, val imageValue: String?) :
|
||||||
|
WikidataItem,Parcelable {
|
||||||
|
constructor(place: Place) : this(
|
||||||
|
place.wikiDataEntityId!!,
|
||||||
|
place.name,
|
||||||
|
place.pic.takeIf { it.isNotBlank() })
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun from(place: Place?): WikidataPlace? {
|
||||||
|
return place?.let { WikidataPlace(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -126,7 +126,6 @@ public class DepictsPresenter implements DepictsContract.UserActionListener {
|
||||||
public void verifyDepictions() {
|
public void verifyDepictions() {
|
||||||
List<DepictedItem> selectedDepictions = repository.getSelectedDepictions();
|
List<DepictedItem> selectedDepictions = repository.getSelectedDepictions();
|
||||||
if (selectedDepictions != null && !selectedDepictions.isEmpty()) {
|
if (selectedDepictions != null && !selectedDepictions.isEmpty()) {
|
||||||
repository.setSelectedDepictions(repository.getDepictionsEntityIdList());
|
|
||||||
view.goToNextScreen();
|
view.goToNextScreen();
|
||||||
} else {
|
} else {
|
||||||
view.noDepictionSelected();
|
view.noDepictionSelected();
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload.mediaDetails;
|
|
||||||
|
|
||||||
|
|
||||||
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import retrofit2.http.Field;
|
|
||||||
import retrofit2.http.FormUrlEncoded;
|
|
||||||
import retrofit2.http.POST;
|
|
||||||
|
|
||||||
import static org.wikipedia.dataclient.Service.MW_API_PREFIX;
|
|
||||||
|
|
||||||
public interface CaptionInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload Captions for the image when upload is successful
|
|
||||||
*
|
|
||||||
* @param FileEntityId enityId for the uploaded file
|
|
||||||
* @param editToken editToken for the file
|
|
||||||
* @param captionValue value of the caption to be uploaded
|
|
||||||
* @param caption additional data associated with caption
|
|
||||||
*/
|
|
||||||
@FormUrlEncoded
|
|
||||||
@POST(MW_API_PREFIX + "action=wbsetlabel&language=en")
|
|
||||||
Observable<MwPostResponse> addLabelstoWikidata(@Field("id") String FileEntityId,
|
|
||||||
@Field("token") String editToken,
|
|
||||||
@Field("value") String captionValue,
|
|
||||||
@Field("data") Map<String, String> caption);
|
|
||||||
}
|
|
||||||
|
|
@ -81,7 +81,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||||
JsonKvStore defaultKvStore;
|
JsonKvStore defaultKvStore;
|
||||||
|
|
||||||
private UploadableFile uploadableFile;
|
private UploadableFile uploadableFile;
|
||||||
private String source;
|
|
||||||
private Place place;
|
private Place place;
|
||||||
|
|
||||||
private boolean isExpanded = true;
|
private boolean isExpanded = true;
|
||||||
|
|
@ -97,9 +96,8 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImageTobeUploaded(UploadableFile uploadableFile, String source, Place place) {
|
public void setImageTobeUploaded(UploadableFile uploadableFile, Place place) {
|
||||||
this.uploadableFile = uploadableFile;
|
this.uploadableFile = uploadableFile;
|
||||||
this.source = source;
|
|
||||||
this.place = place;
|
this.place = place;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +120,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||||
callback.getTotalNumberOfSteps()));
|
callback.getTotalNumberOfSteps()));
|
||||||
initRecyclerView();
|
initRecyclerView();
|
||||||
initPresenter();
|
initPresenter();
|
||||||
presenter.receiveImage(uploadableFile, source, place);
|
presenter.receiveImage(uploadableFile, place);
|
||||||
|
|
||||||
if (callback.getIndexInViewFlipper(this) == 0) {
|
if (callback.getIndexInViewFlipper(this) == 0) {
|
||||||
btnPrevious.setEnabled(false);
|
btnPrevious.setEnabled(false);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
package fr.free.nrw.commons.upload.mediaDetails;
|
package fr.free.nrw.commons.upload.mediaDetails;
|
||||||
|
|
||||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import fr.free.nrw.commons.BasePresenter;
|
import fr.free.nrw.commons.BasePresenter;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
|
||||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||||
import fr.free.nrw.commons.upload.SimilarImageInterface;
|
import fr.free.nrw.commons.upload.SimilarImageInterface;
|
||||||
|
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||||
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The contract with with UploadMediaDetails and its presenter would talk to each other
|
* The contract with with UploadMediaDetails and its presenter would talk to each other
|
||||||
|
|
@ -41,8 +39,7 @@ public interface UploadMediaDetailsContract {
|
||||||
|
|
||||||
interface UserActionListener extends BasePresenter<View> {
|
interface UserActionListener extends BasePresenter<View> {
|
||||||
|
|
||||||
void receiveImage(UploadableFile uploadableFile, @Contribution.FileSource String source,
|
void receiveImage(UploadableFile uploadableFile, Place place);
|
||||||
Place place);
|
|
||||||
|
|
||||||
void verifyImageQuality(UploadItem uploadItem);
|
void verifyImageQuality(UploadItem uploadItem);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,14 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives the corresponding uploadable file, processes it and return the view with and uplaod item
|
* Receives the corresponding uploadable file, processes it and return the view with and uplaod item
|
||||||
*
|
|
||||||
* @param uploadableFile
|
* @param uploadableFile
|
||||||
* @param source
|
|
||||||
* @param place
|
* @param place
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receiveImage(UploadableFile uploadableFile, String source, Place place) {
|
public void receiveImage(UploadableFile uploadableFile, Place place) {
|
||||||
view.showProgress(true);
|
view.showProgress(true);
|
||||||
Disposable uploadItemDisposable = repository
|
Disposable uploadItemDisposable = repository
|
||||||
.preProcessImage(uploadableFile, place, source, this)
|
.preProcessImage(uploadableFile, place, this)
|
||||||
.subscribeOn(ioScheduler)
|
.subscribeOn(ioScheduler)
|
||||||
.observeOn(mainThreadScheduler)
|
.observeOn(mainThreadScheduler)
|
||||||
.subscribe(uploadItem ->
|
.subscribe(uploadItem ->
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package fr.free.nrw.commons.upload.structure.depictions
|
||||||
|
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -14,24 +14,6 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
|
||||||
private const val SEARCH_DEPICTS_LIMIT = 25
|
private const val SEARCH_DEPICTS_LIMIT = 25
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedDepictions = mutableListOf<DepictedItem>()
|
|
||||||
|
|
||||||
fun onDepictItemClicked(depictedItem: DepictedItem) {
|
|
||||||
if (depictedItem.isSelected) {
|
|
||||||
selectDepictItem(depictedItem)
|
|
||||||
} else {
|
|
||||||
unselectDepiction(depictedItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unselectDepiction(depictedItem: DepictedItem) {
|
|
||||||
selectedDepictions.remove(depictedItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun selectDepictItem(depictedItem: DepictedItem) {
|
|
||||||
selectedDepictions.add(depictedItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for depictions
|
* Search for depictions
|
||||||
*/
|
*/
|
||||||
|
|
@ -44,5 +26,4 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
|
||||||
.map(::DepictedItem)
|
.map(::DepictedItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun depictionsEntityIdList() = selectedDepictions.map { it.entityId }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
package fr.free.nrw.commons.upload.structure.depictions
|
package fr.free.nrw.commons.upload.structure.depictions
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.upload.WikidataItem
|
||||||
import fr.free.nrw.commons.wikidata.model.DepictSearchItem
|
import fr.free.nrw.commons.wikidata.model.DepictSearchItem
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model class for Depicted Item in Upload and Explore
|
* Model class for Depicted Item in Upload and Explore
|
||||||
*/
|
*/
|
||||||
data class DepictedItem constructor(
|
data class DepictedItem constructor(
|
||||||
val depictsLabel: String?,
|
override val name: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
var imageUrl: String,
|
var imageUrl: String,
|
||||||
var isSelected: Boolean,
|
var isSelected: Boolean,
|
||||||
val entityId: String
|
override val id: String
|
||||||
) {
|
):WikidataItem {
|
||||||
constructor(depictSearchItem: DepictSearchItem) : this(
|
constructor(depictSearchItem: DepictSearchItem) : this(
|
||||||
depictSearchItem.label,
|
depictSearchItem.label,
|
||||||
depictSearchItem.description,
|
depictSearchItem.description,
|
||||||
|
|
@ -24,12 +25,12 @@ data class DepictedItem constructor(
|
||||||
|
|
||||||
override fun equals(o: Any?) = when {
|
override fun equals(o: Any?) = when {
|
||||||
this === o -> true
|
this === o -> true
|
||||||
o is DepictedItem -> depictsLabel == o.depictsLabel
|
o is DepictedItem -> name == o.name
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return depictsLabel?.hashCode() ?: 0
|
return name?.hashCode() ?: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public class DepictionRenderer extends Renderer<DepictedItem> {
|
||||||
public void render() {
|
public void render() {
|
||||||
DepictedItem item = getContent();
|
DepictedItem item = getContent();
|
||||||
checkedView.setChecked(item.isSelected());
|
checkedView.setChecked(item.isSelected());
|
||||||
depictsLabel.setText(item.getDepictsLabel());
|
depictsLabel.setText(item.getName());
|
||||||
description.setText(item.getDescription());
|
description.setText(item.getDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
package fr.free.nrw.commons.wikidata;
|
package fr.free.nrw.commons.wikidata;
|
||||||
|
|
||||||
import org.wikipedia.csrf.CsrfTokenClient;
|
import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX;
|
||||||
|
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.upload.UploadResult;
|
||||||
|
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||||
|
import io.reactivex.Observable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import org.wikipedia.csrf.CsrfTokenClient;
|
||||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import timber.log.Timber;
|
||||||
import io.reactivex.Observable;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wikibase Client for calling WikiBase APIs
|
* Wikibase Client for calling WikiBase APIs
|
||||||
|
|
@ -29,16 +30,32 @@ public class WikiBaseClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<Boolean> postEditEntity(String fileEntityId, String data) {
|
public Observable<Boolean> postEditEntity(String fileEntityId, String data) {
|
||||||
try {
|
return csrfToken()
|
||||||
return wikiBaseInterface.postEditEntity(fileEntityId, csrfTokenClient.getTokenBlocking(), data)
|
.switchMap(editToken -> wikiBaseInterface.postEditEntity(fileEntityId, editToken, data)
|
||||||
.map(response -> (response.getSuccessVal() == 1));
|
.map(response -> (response.getSuccessVal() == 1)));
|
||||||
} catch (Throwable throwable) {
|
|
||||||
return Observable.just(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<Long> getFileEntityId(String fileName) {
|
public Observable<Long> getFileEntityId(UploadResult uploadResult) {
|
||||||
return wikiBaseInterface.getFileEntityId(fileName)
|
return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName())
|
||||||
.map(response -> (long) (response.query().pages().get(0).pageId()));
|
.map(response -> (long) (response.query().pages().get(0).pageId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<MwPostResponse> addLabelstoWikidata(long fileEntityId,
|
||||||
|
String languageCode, String captionValue) {
|
||||||
|
return csrfToken()
|
||||||
|
.switchMap(editToken -> wikiBaseInterface
|
||||||
|
.addLabelstoWikidata(PAGE_ID_PREFIX + fileEntityId, editToken, languageCode, captionValue));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<String> csrfToken() {
|
||||||
|
return Observable.fromCallable(() -> {
|
||||||
|
try {
|
||||||
|
return csrfTokenClient.getTokenBlocking();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
Timber.e(throwable);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.wikidata;
|
package fr.free.nrw.commons.wikidata;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.upload.WikidataItem;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -24,15 +25,16 @@ public class WikidataClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create wikidata claim to add P18 value
|
* Create wikidata claim to add P18 value
|
||||||
* @param entityId wikidata entity ID
|
* @param entity wikidata entity ID
|
||||||
* @param value value of the P18 edit
|
* @param value value of the P18 edit
|
||||||
* @return revisionID of the edit
|
* @return revisionID of the edit
|
||||||
*/
|
*/
|
||||||
Observable<Long> createClaim(String entityId, String value) {
|
Observable<Long> createImageClaim(WikidataItem entity, String value) {
|
||||||
return getCsrfToken()
|
return getCsrfToken()
|
||||||
.flatMap(csrfToken -> wikidataInterface.postCreateClaim(toRequestBody(entityId),
|
.flatMap(csrfToken -> wikidataInterface.postCreateClaim(
|
||||||
|
toRequestBody(entity.getId()),
|
||||||
toRequestBody("value"),
|
toRequestBody("value"),
|
||||||
toRequestBody("P18"),
|
toRequestBody(WikidataProperties.IMAGE.getPropertyName()),
|
||||||
toRequestBody(value),
|
toRequestBody(value),
|
||||||
toRequestBody("en"),
|
toRequestBody("en"),
|
||||||
toRequestBody(csrfToken)))
|
toRequestBody(csrfToken)))
|
||||||
|
|
|
||||||
|
|
@ -4,27 +4,28 @@ import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_I
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface;
|
import fr.free.nrw.commons.upload.UploadResult;
|
||||||
|
import fr.free.nrw.commons.upload.WikidataItem;
|
||||||
|
import fr.free.nrw.commons.upload.WikidataPlace;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.ObservableSource;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import org.wikipedia.csrf.CsrfTokenClient;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -35,129 +36,55 @@ import timber.log.Timber;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class WikidataEditService {
|
public class WikidataEditService {
|
||||||
|
|
||||||
private final static String COMMONS_APP_TAG = "wikimedia-commons-app";
|
private static final String COMMONS_APP_TAG = "wikimedia-commons-app";
|
||||||
private final static String COMMONS_APP_EDIT_REASON = "Add tag for edits made using Android Commons app";
|
private static final String COMMONS_APP_EDIT_REASON = "Add tag for edits made using Android Commons app";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final WikidataEditListener wikidataEditListener;
|
private final WikidataEditListener wikidataEditListener;
|
||||||
private final JsonKvStore directKvStore;
|
private final JsonKvStore directKvStore;
|
||||||
private final CaptionInterface captionInterface;
|
|
||||||
private final WikiBaseClient wikiBaseClient;
|
private final WikiBaseClient wikiBaseClient;
|
||||||
private final WikidataClient wikidataClient;
|
private final WikidataClient wikidataClient;
|
||||||
private final CsrfTokenClient csrfTokenClient;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WikidataEditService(final Context context,
|
public WikidataEditService(final Context context,
|
||||||
final WikidataEditListener wikidataEditListener,
|
final WikidataEditListener wikidataEditListener,
|
||||||
@Named("default_preferences") final JsonKvStore directKvStore,
|
@Named("default_preferences") final JsonKvStore directKvStore,
|
||||||
final WikiBaseClient wikiBaseClient,
|
final WikiBaseClient wikiBaseClient,
|
||||||
final CaptionInterface captionInterface,
|
final WikidataClient wikidataClient) {
|
||||||
final WikidataClient wikidataClient,
|
|
||||||
@Named("commons-csrf") final CsrfTokenClient csrfTokenClient) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.wikidataEditListener = wikidataEditListener;
|
this.wikidataEditListener = wikidataEditListener;
|
||||||
this.directKvStore = directKvStore;
|
this.directKvStore = directKvStore;
|
||||||
this.captionInterface = captionInterface;
|
|
||||||
this.wikiBaseClient = wikiBaseClient;
|
this.wikiBaseClient = wikiBaseClient;
|
||||||
this.wikidataClient = wikidataClient;
|
this.wikidataClient = wikidataClient;
|
||||||
this.csrfTokenClient = csrfTokenClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a P18 claim and log the edit with custom tag
|
|
||||||
*
|
|
||||||
* @param wikidataEntityId a unique id of each Wikidata items
|
|
||||||
* @param fileName name of the file we will upload
|
|
||||||
* @param p18Value pic attribute of Wikidata item
|
|
||||||
*/
|
|
||||||
public void createClaimWithLogging(String wikidataEntityId, String wikiItemName, String fileName, String p18Value) {
|
|
||||||
if (wikidataEntityId == null) {
|
|
||||||
Timber.d("Skipping creation of claim as Wikidata entity ID is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileName == null) {
|
|
||||||
Timber.d("Skipping creation of claim as fileName entity ID is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(directKvStore.getBoolean("Picture_Has_Correct_Location", true))) {
|
|
||||||
Timber.d("Image location and nearby place location mismatched, so Wikidata item won't be edited");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p18Value != null && !p18Value.trim().isEmpty()) {
|
|
||||||
Timber.d("Skipping creation of claim as p18Value is not empty, we won't override existing image");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editWikidataProperty(wikidataEntityId, wikiItemName, fileName);;
|
|
||||||
editWikiBaseDepictsProperty(wikidataEntityId, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edits the wikidata entity by adding the P18 property to it.
|
|
||||||
* Adding the P18 edit requires calling the wikidata API to create a claim against the entity
|
|
||||||
*
|
|
||||||
* @param wikidataEntityId
|
|
||||||
* @param fileName
|
|
||||||
*/
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
private void editWikidataProperty(String wikidataEntityId, String wikiItemName, String fileName) {
|
|
||||||
Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId);
|
|
||||||
Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId);
|
|
||||||
|
|
||||||
final String propertyValue = getFileName(fileName);
|
|
||||||
|
|
||||||
Timber.d("Entity id is %s and property value is %s", wikidataEntityId, propertyValue);
|
|
||||||
wikidataClient.createClaim(wikidataEntityId, propertyValue)
|
|
||||||
.flatMap(revisionId -> {
|
|
||||||
if (revisionId != -1) {
|
|
||||||
return wikidataClient.addEditTag(revisionId, COMMONS_APP_TAG, COMMONS_APP_EDIT_REASON);
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unable to edit wikidata item");
|
|
||||||
})
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(revisionId -> handleClaimResult(wikidataEntityId, wikiItemName, String.valueOf(revisionId)), throwable -> {
|
|
||||||
Timber.e(throwable, "Error occurred while making claim");
|
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edits the wikibase entity by adding DEPICTS property.
|
* Edits the wikibase entity by adding DEPICTS property.
|
||||||
* Adding DEPICTS property requires call to the wikibase API to set tag against the entity.
|
* Adding DEPICTS property requires call to the wikibase API to set tag against the entity.
|
||||||
*
|
|
||||||
* @param wikidataEntityId
|
|
||||||
* @param fileName
|
|
||||||
*/
|
*/
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private void editWikiBaseDepictsProperty(final String wikidataEntityId, final String fileName) {
|
private Observable<Boolean> addDepictsProperty(final String fileEntityId,
|
||||||
wikiBaseClient.getFileEntityId(fileName)
|
final WikidataItem depictedItem) {
|
||||||
.subscribeOn(Schedulers.io())
|
// Wikipedia:Sandbox (Q10)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
final String data = depictionJson(ConfigUtils.isBetaFlavour() ? "Q10" : depictedItem.getId());
|
||||||
.subscribe(fileEntityId -> {
|
|
||||||
if (fileEntityId != null) {
|
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, data)
|
||||||
Timber.d("EntityId for image was received successfully: %s", fileEntityId);
|
.doOnNext(success -> {
|
||||||
addDepictsProperty(wikidataEntityId, fileEntityId.toString());
|
if (success) {
|
||||||
|
Timber.d("DEPICTS property was set successfully for %s", fileEntityId);
|
||||||
} else {
|
} else {
|
||||||
Timber.d("Error acquiring EntityId for image: %s", fileName);
|
Timber.d("Unable to set DEPICTS property for %s", fileEntityId);
|
||||||
}
|
}
|
||||||
}, throwable -> {
|
})
|
||||||
Timber.e(throwable, "Error occurred while getting EntityID to set DEPICTS property");
|
.doOnError( throwable -> {
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
Timber.e(throwable, "Error occurred while setting DEPICTS property");
|
||||||
});
|
ViewUtil.showLongToast(context, throwable.toString());
|
||||||
}
|
})
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
private void addDepictsProperty(String entityId, final String fileEntityId) {
|
|
||||||
if (ConfigUtils.isBetaFlavour()) {
|
|
||||||
entityId = "Q10"; // Wikipedia:Sandbox (Q10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String depictionJson(final String entityId) {
|
||||||
final JsonObject value = new JsonObject();
|
final JsonObject value = new JsonObject();
|
||||||
value.addProperty("entity-type", "item");
|
value.addProperty("entity-type", "item");
|
||||||
value.addProperty("numeric-id", entityId.replace("Q", ""));
|
value.addProperty("numeric-id", entityId.replace("Q", ""));
|
||||||
|
|
@ -169,7 +96,7 @@ public class WikidataEditService {
|
||||||
|
|
||||||
final JsonObject mainSnak = new JsonObject();
|
final JsonObject mainSnak = new JsonObject();
|
||||||
mainSnak.addProperty("snaktype", "value");
|
mainSnak.addProperty("snaktype", "value");
|
||||||
mainSnak.addProperty("property", BuildConfig.DEPICTS_PROPERTY);
|
mainSnak.addProperty("property", WikidataProperties.DEPICTS.getPropertyName());
|
||||||
mainSnak.add("datavalue", dataValue);
|
mainSnak.add("datavalue", dataValue);
|
||||||
|
|
||||||
final JsonObject claim = new JsonObject();
|
final JsonObject claim = new JsonObject();
|
||||||
|
|
@ -183,131 +110,119 @@ public class WikidataEditService {
|
||||||
final JsonObject jsonData = new JsonObject();
|
final JsonObject jsonData = new JsonObject();
|
||||||
jsonData.add("claims", claims);
|
jsonData.add("claims", claims);
|
||||||
|
|
||||||
final String data = jsonData.toString();
|
return jsonData.toString();
|
||||||
|
|
||||||
Observable.defer((Callable<ObservableSource<Boolean>>) () ->
|
|
||||||
wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, data))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(success -> {
|
|
||||||
if (success)
|
|
||||||
Timber.d("DEPICTS property was set successfully for %s", fileEntityId);
|
|
||||||
else
|
|
||||||
Timber.d("Unable to set DEPICTS property for %s", fileEntityId);
|
|
||||||
},
|
|
||||||
throwable -> {
|
|
||||||
Timber.e(throwable, "Error occurred while setting DEPICTS property");
|
|
||||||
ViewUtil.showLongToast(context, throwable.toString());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClaimResult(String wikidataEntityId, String wikiItemName, String revisionId) {
|
|
||||||
if (revisionId != null) {
|
|
||||||
if (wikidataEditListener != null) {
|
|
||||||
wikidataEditListener.onSuccessfulWikidataEdit();
|
|
||||||
}
|
|
||||||
showSuccessToast(wikiItemName);
|
|
||||||
} else {
|
|
||||||
Timber.d("Unable to make wiki data edit for entity %s", wikidataEntityId);
|
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a success toast when the edit is made successfully
|
* Show a success toast when the edit is made successfully
|
||||||
*/
|
*/
|
||||||
private void showSuccessToast(String wikiItemName) {
|
private void showSuccessToast(final String wikiItemName) {
|
||||||
String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
|
final String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
|
||||||
String successMessage = String.format(Locale.getDefault(), successStringTemplate, wikiItemName);
|
final String successMessage = String.format(Locale.getDefault(), successStringTemplate, wikiItemName);
|
||||||
ViewUtil.showLongToast(context, successMessage);
|
ViewUtil.showLongToast(context, successMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats and returns the filename as accepted by the wiki base API
|
|
||||||
* https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim
|
|
||||||
*
|
|
||||||
* @param fileName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String getFileName(String fileName) {
|
|
||||||
fileName = String.format("\"%s\"", fileName.replace("File:", ""));
|
|
||||||
Timber.d("Wikidata property name is %s", fileName);
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adding captions as labels after image is successfully uploaded
|
|
||||||
*/
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
public void createLabelforWikidataEntity(final String fileName,
|
|
||||||
final Map<String, String> captions) {
|
|
||||||
Observable.fromCallable(() -> wikiBaseClient.getFileEntityId(fileName))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(fileEntityId -> {
|
|
||||||
if (fileEntityId != null) {
|
|
||||||
for (final Map.Entry<String, String> entry : captions.entrySet()) {
|
|
||||||
final Map<String, String> caption = new HashMap<>();
|
|
||||||
caption.put(entry.getKey(), entry.getValue());
|
|
||||||
try {
|
|
||||||
wikidataAddLabels(fileEntityId.toString(), caption);
|
|
||||||
} catch (final Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.d("Error acquiring EntityId for image");
|
|
||||||
}
|
|
||||||
}, throwable -> {
|
|
||||||
Timber.e(throwable, "Error occurred while getting EntityID for the file");
|
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds label to Wikidata using the fileEntityId and the edit token, obtained from csrfTokenClient
|
* Adds label to Wikidata using the fileEntityId and the edit token, obtained from csrfTokenClient
|
||||||
*
|
*
|
||||||
* @param fileEntityId
|
* @param fileEntityId
|
||||||
* @param caption
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private void wikidataAddLabels(final String fileEntityId, final Map<String, String> caption) {
|
private Observable<Boolean> addCaption(final long fileEntityId, final String languageCode,
|
||||||
Observable.fromCallable(() -> {
|
final String captionValue) {
|
||||||
try {
|
return wikiBaseClient.addLabelstoWikidata(fileEntityId, languageCode, captionValue)
|
||||||
return csrfTokenClient.getTokenBlocking();
|
.doOnNext(mwPostResponse -> onAddCaptionResponse(fileEntityId, mwPostResponse) )
|
||||||
} catch (Throwable throwable) {
|
.doOnError(throwable -> {
|
||||||
throwable.printStackTrace();
|
Timber.e(throwable, "Error occurred while setting Captions");
|
||||||
return null;
|
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.io())
|
.map(mwPostResponse -> mwPostResponse != null);
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
}
|
||||||
.subscribe(editToken -> {
|
|
||||||
if (editToken != null) {
|
private void onAddCaptionResponse(Long fileEntityId, MwPostResponse response) {
|
||||||
Observable.fromCallable(() -> captionInterface.addLabelstoWikidata(fileEntityId, editToken, caption.get(0), caption))
|
if (response != null) {
|
||||||
.subscribeOn(Schedulers.io())
|
Timber.d("Caption successfully set, revision id = %s", response);
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(revisionId ->
|
|
||||||
{
|
|
||||||
if (revisionId != null) {
|
|
||||||
Timber.d("Caption successfully set, revision id = %s", revisionId);
|
|
||||||
} else {
|
} else {
|
||||||
Timber.d("Error occurred while setting Captions, fileEntityId = %s", fileEntityId);
|
Timber.d("Error occurred while setting Captions, fileEntityId = %s", fileEntityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
|
||||||
throwable -> {
|
|
||||||
Timber.e(throwable, "Error occurred while setting Captions");
|
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
|
||||||
});
|
|
||||||
}else {
|
|
||||||
Timber.d("Error acquiring EntityId for image");
|
|
||||||
}
|
}
|
||||||
}, throwable -> {
|
|
||||||
Timber.e(throwable, "Error occurred while getting EntityID for the File");
|
public void createImageClaim(@Nullable final WikidataPlace wikidataPlace, final UploadResult imageUpload) {
|
||||||
|
if (!(directKvStore.getBoolean("Picture_Has_Correct_Location", true))) {
|
||||||
|
Timber.d("Image location and nearby place location mismatched, so Wikidata item won't be edited");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editWikidataImageProperty(wikidataPlace, imageUpload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
private void editWikidataImageProperty(final WikidataItem wikidataItem, final UploadResult imageUpload) {
|
||||||
|
wikidataClient.createImageClaim(wikidataItem, String.format("\"%s\"", imageUpload.getFilename()))
|
||||||
|
.flatMap(revisionId -> {
|
||||||
|
if (revisionId != -1) {
|
||||||
|
return wikidataClient.addEditTag(revisionId, COMMONS_APP_TAG, COMMONS_APP_EDIT_REASON);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unable to edit wikidata item");
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(revisionId -> handleImageClaimResult(wikidataItem, String.valueOf(revisionId)), throwable -> {
|
||||||
|
Timber.e(throwable, "Error occurred while making claim");
|
||||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleImageClaimResult(final WikidataItem wikidataItem, final String revisionId) {
|
||||||
|
if (revisionId != null) {
|
||||||
|
if (wikidataEditListener != null) {
|
||||||
|
wikidataEditListener.onSuccessfulWikidataEdit();
|
||||||
|
}
|
||||||
|
showSuccessToast(wikidataItem.getName());
|
||||||
|
} else {
|
||||||
|
Timber.d("Unable to make wiki data edit for entity %s", wikidataItem);
|
||||||
|
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Disposable addDepictionsAndCaptions(UploadResult uploadResult, Contribution contribution) {
|
||||||
|
return wikiBaseClient.getFileEntityId(uploadResult)
|
||||||
|
.doOnError(throwable -> {
|
||||||
|
Timber.e(throwable, "Error occurred while getting EntityID to set DEPICTS property");
|
||||||
|
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.switchMap(fileEntityId -> {
|
||||||
|
if (fileEntityId != null) {
|
||||||
|
Timber.d("EntityId for image was received successfully: %s", fileEntityId);
|
||||||
|
return Observable.concat(
|
||||||
|
depictionEdits(contribution, fileEntityId),
|
||||||
|
captionEdits(contribution, fileEntityId)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Timber.d("Error acquiring EntityId for image: %s", uploadResult);
|
||||||
|
return Observable.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).subscribe(
|
||||||
|
success -> Timber.d("edit response: %s", success),
|
||||||
|
throwable -> Timber.e(throwable, "posting edits failed")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<Boolean> captionEdits(Contribution contribution, Long fileEntityId) {
|
||||||
|
return Observable.fromIterable(contribution.getCaptions().entrySet())
|
||||||
|
.concatMap(entry -> addCaption(fileEntityId, entry.getKey(), entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<Boolean> depictionEdits(Contribution contribution, Long fileEntityId) {
|
||||||
|
final ArrayList<WikidataItem> depictedItems = new ArrayList<>(contribution.getDepictedItems());
|
||||||
|
final WikidataPlace wikidataPlace = contribution.getWikidataPlace();
|
||||||
|
if (wikidataPlace != null) {
|
||||||
|
depictedItems.add(wikidataPlace);
|
||||||
|
}
|
||||||
|
return Observable.fromIterable(depictedItems)
|
||||||
|
.concatMap( wikidataItem -> addDepictsProperty(fileEntityId.toString(), wikidataItem));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package fr.free.nrw.commons.wikidata;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
|
|
||||||
|
enum WikidataProperties {
|
||||||
|
IMAGE("P18"),
|
||||||
|
DEPICTS(BuildConfig.DEPICTS_PROPERTY);
|
||||||
|
|
||||||
|
private final String propertyName;
|
||||||
|
|
||||||
|
WikidataProperties(final String propertyName) {
|
||||||
|
this.propertyName = propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPropertyName() {
|
||||||
|
return propertyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,11 +2,26 @@ package fr.free.nrw.commons.wikidata.model
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model class for Depiction item returned from API after calling searchForDepicts
|
* Model class for Depiction item returned from API after calling searchForDepicts
|
||||||
*/
|
"search": [
|
||||||
|
{
|
||||||
|
"repository": "local",
|
||||||
|
"id": "Q23444",
|
||||||
|
"concepturi": "http://www.wikidata.org/entity/Q23444",
|
||||||
|
"title": "Q23444",
|
||||||
|
"pageid": 26835,
|
||||||
|
"url": "//www.wikidata.org/wiki/Q23444",
|
||||||
|
"label": "white",
|
||||||
|
"description": "color",
|
||||||
|
"match": {
|
||||||
|
"type": "label",
|
||||||
|
"language": "en",
|
||||||
|
"text": "white"
|
||||||
|
}
|
||||||
|
}*/
|
||||||
class DepictSearchItem(
|
class DepictSearchItem(
|
||||||
val id: String,
|
val id: String,
|
||||||
val pageid: String,
|
val pageid: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val label: String?,
|
val label: String,
|
||||||
val description: String?
|
val description: String?
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,19 @@ package fr.free.nrw.commons.delete
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.nhaarman.mockitokotlin2.eq
|
import com.nhaarman.mockitokotlin2.eq
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.Media
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.actions.PageEditClient
|
import fr.free.nrw.commons.actions.PageEditClient
|
||||||
import fr.free.nrw.commons.notification.NotificationHelper
|
|
||||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.ArgumentMatchers
|
import org.mockito.ArgumentMatchers
|
||||||
import org.mockito.InjectMocks
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Named
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for delete helper
|
* Tests for delete helper
|
||||||
|
|
@ -25,23 +22,15 @@ import javax.inject.Named
|
||||||
class DeleteHelperTest {
|
class DeleteHelperTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
@field:[Inject Named("commons-page-edit")]
|
internal lateinit var pageEditClient: PageEditClient
|
||||||
internal var pageEditClient: PageEditClient? = null
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
internal var context: Context? = null
|
internal lateinit var context: Context
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
internal var notificationHelper: NotificationHelper? = null
|
internal lateinit var media: Media
|
||||||
|
|
||||||
@Mock
|
lateinit var deleteHelper: DeleteHelper
|
||||||
internal var viewUtil: ViewUtilWrapper? = null
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
internal var media: Media? = null
|
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
var deleteHelper: DeleteHelper? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init mocks for test
|
* Init mocks for test
|
||||||
|
|
@ -49,6 +38,7 @@ class DeleteHelperTest {
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
deleteHelper = DeleteHelper(mock(), pageEditClient, mock(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,23 +46,23 @@ class DeleteHelperTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun makeDeletion() {
|
fun makeDeletion() {
|
||||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
|
|
||||||
`when`(media?.displayTitle).thenReturn("Test file")
|
whenever(media.displayTitle).thenReturn("Test file")
|
||||||
media?.filename="Test file.jpg"
|
|
||||||
|
|
||||||
val creatorName = "Creator"
|
val creatorName = "Creator"
|
||||||
`when`(media?.getCreator()).thenReturn("$creatorName (page does not exist)")
|
whenever(media.creator).thenReturn("$creatorName (page does not exist)")
|
||||||
|
whenever(media.filename).thenReturn("Test file.jpg")
|
||||||
|
|
||||||
val makeDeletion = deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
|
val makeDeletion = deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
|
||||||
assertNotNull(makeDeletion)
|
assertNotNull(makeDeletion)
|
||||||
assertTrue(makeDeletion!!)
|
assertTrue(makeDeletion!!)
|
||||||
verify(pageEditClient)?.appendEdit(eq("User_Talk:$creatorName"), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
|
verify(pageEditClient).appendEdit(eq("User_Talk:$creatorName"), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,63 +70,63 @@ class DeleteHelperTest {
|
||||||
*/
|
*/
|
||||||
@Test(expected = RuntimeException::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun makeDeletionForPrependEditFailure() {
|
fun makeDeletionForPrependEditFailure() {
|
||||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(false))
|
.thenReturn(Observable.just(false))
|
||||||
`when`(pageEditClient?.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(media?.displayTitle).thenReturn("Test file")
|
whenever(media.displayTitle).thenReturn("Test file")
|
||||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
whenever(media.filename).thenReturn("Test file.jpg")
|
||||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
whenever(media.creator).thenReturn("Creator (page does not exist)")
|
||||||
|
|
||||||
deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
|
deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun makeDeletionForEditFailure() {
|
fun makeDeletionForEditFailure() {
|
||||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(false))
|
.thenReturn(Observable.just(false))
|
||||||
`when`(media?.displayTitle).thenReturn("Test file")
|
whenever(media.displayTitle).thenReturn("Test file")
|
||||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
whenever(media.filename).thenReturn("Test file.jpg")
|
||||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
whenever(media.creator).thenReturn("Creator (page does not exist)")
|
||||||
|
|
||||||
deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
|
deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun makeDeletionForAppendEditFailure() {
|
fun makeDeletionForAppendEditFailure() {
|
||||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(false))
|
.thenReturn(Observable.just(false))
|
||||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(media?.displayTitle).thenReturn("Test file")
|
whenever(media.displayTitle).thenReturn("Test file")
|
||||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
whenever(media.filename).thenReturn("Test file.jpg")
|
||||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
whenever(media.creator).thenReturn("Creator (page does not exist)")
|
||||||
|
|
||||||
deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
|
deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun makeDeletionForEmptyCreatorName() {
|
fun makeDeletionForEmptyCreatorName() {
|
||||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Observable.just(true))
|
.thenReturn(Observable.just(true))
|
||||||
|
|
||||||
`when`(media?.displayTitle).thenReturn("Test file")
|
whenever(media.displayTitle).thenReturn("Test file")
|
||||||
media?.filename="Test file.jpg"
|
media.filename ="Test file.jpg"
|
||||||
|
|
||||||
`when`(media?.getCreator()).thenReturn(null)
|
whenever(media.creator).thenReturn(null)
|
||||||
|
|
||||||
deleteHelper?.makeDeletion(context, media, "Test reason")?.blockingGet()
|
deleteHelper.makeDeletion(context, media, "Test reason")?.blockingGet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,11 +74,10 @@ class UploadMediaPresenterTest {
|
||||||
repository.preProcessImage(
|
repository.preProcessImage(
|
||||||
ArgumentMatchers.any(UploadableFile::class.java),
|
ArgumentMatchers.any(UploadableFile::class.java),
|
||||||
ArgumentMatchers.any(Place::class.java),
|
ArgumentMatchers.any(Place::class.java),
|
||||||
ArgumentMatchers.anyString(),
|
|
||||||
ArgumentMatchers.any(UploadMediaPresenter::class.java)
|
ArgumentMatchers.any(UploadMediaPresenter::class.java)
|
||||||
)
|
)
|
||||||
).thenReturn(testObservableUploadItem)
|
).thenReturn(testObservableUploadItem)
|
||||||
uploadMediaPresenter.receiveImage(uploadableFile, ArgumentMatchers.anyString(), place)
|
uploadMediaPresenter.receiveImage(uploadableFile, place)
|
||||||
verify(view).showProgress(true)
|
verify(view).showProgress(true)
|
||||||
testScheduler.triggerActions()
|
testScheduler.triggerActions()
|
||||||
verify(view).onImageProcessed(
|
verify(view).onImageProcessed(
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package fr.free.nrw.commons.wikidata
|
package fr.free.nrw.commons.wikidata
|
||||||
|
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
|
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
|
||||||
import fr.free.nrw.commons.wikidata.model.WbCreateClaimResponse
|
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import okhttp3.RequestBody
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.ArgumentMatchers.*
|
import org.mockito.ArgumentMatchers.any
|
||||||
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.InjectMocks
|
import org.mockito.InjectMocks
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
|
|
@ -37,14 +37,18 @@ class WikidataClientTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createClaim() {
|
fun createClaim() {
|
||||||
`when`(wikidataInterface!!.postCreateClaim(any(RequestBody::class.java),
|
`when`(
|
||||||
any(RequestBody::class.java),
|
wikidataInterface!!.postCreateClaim(
|
||||||
any(RequestBody::class.java),
|
any(),
|
||||||
any(RequestBody::class.java),
|
any(),
|
||||||
any(RequestBody::class.java),
|
any(),
|
||||||
any(RequestBody::class.java)))
|
any(),
|
||||||
.thenReturn(Observable.just(mock(WbCreateClaimResponse::class.java)))
|
any(),
|
||||||
wikidataClient!!.createClaim("Q1", "test.jpg")
|
any()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.thenReturn(Observable.just(mock()))
|
||||||
|
wikidataClient!!.createImageClaim(mock(), "test.jpg")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
package fr.free.nrw.commons.wikidata
|
package fr.free.nrw.commons.wikidata
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||||
|
import fr.free.nrw.commons.upload.UploadResult
|
||||||
|
import fr.free.nrw.commons.upload.WikidataPlace
|
||||||
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
|
import fr.free.nrw.commons.wikidata.model.AddEditTagResponse
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
|
@ -37,42 +40,27 @@ class WikidataEditServiceTest {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noClaimsWhenEntityIdIsNull() {
|
|
||||||
wikidataEditService.createClaimWithLogging(null, null,"Test.jpg","")
|
|
||||||
verifyZeroInteractions(wikidataClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noClaimsWhenFileNameIsNull() {
|
|
||||||
wikidataEditService.createClaimWithLogging("Q1", "Test", null,"")
|
|
||||||
verifyZeroInteractions(wikidataClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun noClaimsWhenP18IsNotEmpty() {
|
|
||||||
wikidataEditService.createClaimWithLogging("Q1", "Test","Test.jpg","Previous.jpg")
|
|
||||||
verifyZeroInteractions(wikidataClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun noClaimsWhenLocationIsNotCorrect() {
|
fun noClaimsWhenLocationIsNotCorrect() {
|
||||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||||
.thenReturn(false)
|
.thenReturn(false)
|
||||||
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
|
wikidataEditService.createImageClaim(mock(), mock())
|
||||||
verifyZeroInteractions(wikidataClient)
|
verifyZeroInteractions(wikidataClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createClaimWithLogging() {
|
fun createImageClaim() {
|
||||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||||
.thenReturn(true)
|
.thenReturn(true)
|
||||||
whenever(wikidataClient.createClaim(anyString(), anyString()))
|
whenever(wikidataClient.createImageClaim(any(), any()))
|
||||||
.thenReturn(Observable.just(1L))
|
.thenReturn(Observable.just(1L))
|
||||||
whenever(wikidataClient.addEditTag(anyLong(), anyString(), anyString()))
|
whenever(wikidataClient.addEditTag(anyLong(), anyString(), anyString()))
|
||||||
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java)))
|
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java)))
|
||||||
whenever(wikibaseClient.getFileEntityId(any())).thenReturn(Observable.just(1L))
|
whenever(wikibaseClient.getFileEntityId(any())).thenReturn(Observable.just(1L))
|
||||||
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
|
val wikidataPlace:WikidataPlace = mock()
|
||||||
verify(wikidataClient, times(1)).createClaim(anyString(), anyString())
|
val uploadResult = mock<UploadResult>()
|
||||||
|
whenever(uploadResult.filename).thenReturn("file")
|
||||||
|
wikidataEditService.createImageClaim(wikidataPlace, uploadResult)
|
||||||
|
verify(wikidataClient, times(1)).createImageClaim(wikidataPlace, """"file"""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue