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 java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
|
@ -30,62 +28,42 @@ public class Media implements Parcelable {
|
|||
|
||||
// Primary metadata fields
|
||||
@Nullable
|
||||
public Uri localUri;
|
||||
public String thumbUrl;
|
||||
public String imageUrl;
|
||||
public String filename;
|
||||
public String thumbnailTitle;
|
||||
/**
|
||||
private Uri localUri;
|
||||
private String thumbUrl;
|
||||
private String imageUrl;
|
||||
private String filename;
|
||||
private String thumbnailTitle;
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
private String caption;
|
||||
public String description; // monolingual description on input...
|
||||
public String discussion;
|
||||
long dataLength;
|
||||
public Date dateCreated;
|
||||
@Nullable public Date dateUploaded;
|
||||
public int width;
|
||||
public int height;
|
||||
public String license;
|
||||
public String licenseUrl;
|
||||
public String creator;
|
||||
private String description; // monolingual description on input...
|
||||
private String discussion;
|
||||
private long dataLength;
|
||||
private Date dateCreated;
|
||||
@Nullable private Date dateUploaded;
|
||||
private String license;
|
||||
private String licenseUrl;
|
||||
private String creator;
|
||||
/**
|
||||
* Wikibase Identifier associated with media files
|
||||
*/
|
||||
public String pageId;
|
||||
public ArrayList<String> categories; // as loaded at runtime?
|
||||
private String pageId;
|
||||
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.
|
||||
* However unlike categories depictions is multi-lingual
|
||||
*/
|
||||
public ArrayList<Map<String, String>> depictionList;
|
||||
/**
|
||||
* The above hashmap is fetched from API and to diplay in Explore
|
||||
* 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;
|
||||
private List<Map<String, String>> depictionList= new ArrayList<>();
|
||||
private boolean requestedDeletion;
|
||||
@Nullable private LatLng coordinates;
|
||||
|
||||
/**
|
||||
* Provides local constructor
|
||||
*/
|
||||
protected Media() {
|
||||
this.categories = new ArrayList<>();
|
||||
this.depictions = new ArrayList<>();
|
||||
this.descriptions = new HashMap<>();
|
||||
this.captions = new HashMap<>();
|
||||
public Media() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +72,6 @@ public class Media implements Parcelable {
|
|||
* @param filename Media filename
|
||||
*/
|
||||
public Media(String filename) {
|
||||
this();
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
|
|
@ -103,29 +80,35 @@ public class Media implements Parcelable {
|
|||
* @param localUri Media URI
|
||||
* @param imageUrl Media image URL
|
||||
* @param filename Media filename
|
||||
* @param captions Media captions
|
||||
* @param description Media description
|
||||
* @param dataLength Media date length
|
||||
* @param dateCreated Media creation date
|
||||
* @param dateUploaded Media date uploaded
|
||||
* @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) {
|
||||
this();
|
||||
this.localUri = localUri;
|
||||
this.thumbUrl = imageUrl;
|
||||
this.imageUrl = imageUrl;
|
||||
this.filename = filename;
|
||||
this.captions = captions;
|
||||
this.description = description;
|
||||
this.dataLength = dataLength;
|
||||
this.dateCreated = dateCreated;
|
||||
this.dateUploaded = dateUploaded;
|
||||
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();
|
||||
if (metadata == null) {
|
||||
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())) {
|
||||
media.setThumbUrl(imageInfo.getThumbUrl());
|
||||
}
|
||||
|
|
@ -155,7 +138,6 @@ public class Media implements Parcelable {
|
|||
Media media = new Media(null,
|
||||
imageInfo.getOriginalUrl(),
|
||||
page.title(),
|
||||
new HashMap<>(),
|
||||
"",
|
||||
0,
|
||||
safeParseDate(metadata.dateTime()),
|
||||
|
|
@ -174,7 +156,7 @@ public class Media implements Parcelable {
|
|||
language = "default";
|
||||
}
|
||||
|
||||
media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription()));
|
||||
media.setDescription(metadata.imageDescription());
|
||||
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.getCategories()));
|
||||
String latitude = metadata.getGpsLatitude();
|
||||
String longitude = metadata.getGpsLongitude();
|
||||
|
|
@ -212,31 +194,14 @@ public class Media implements Parcelable {
|
|||
/**
|
||||
*sets pageId for the current media object
|
||||
*/
|
||||
private void setPageId(String pageId) {
|
||||
public void setPageId(String pageId) {
|
||||
this.pageId = pageId;
|
||||
}
|
||||
|
||||
public String getThumbUrl() {
|
||||
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
|
||||
* @return Media title
|
||||
|
|
@ -340,22 +305,11 @@ public class Media implements Parcelable {
|
|||
/**
|
||||
* @return depictions associated with the current media
|
||||
*/
|
||||
public ArrayList<Map<String, String>> getDepiction() {
|
||||
public List<Map<String, String>> getDepiction() {
|
||||
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.
|
||||
|
|
@ -423,38 +377,6 @@ public class Media implements Parcelable {
|
|||
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.
|
||||
* @return license as a String
|
||||
|
|
@ -506,15 +428,8 @@ public class Media implements Parcelable {
|
|||
* @return file categories as an ArrayList of Strings
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrayList<String> getCategories() {
|
||||
return (ArrayList<String>) categories.clone(); // feels dirty
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array list of depictions associated with the current media
|
||||
*/
|
||||
public ArrayList<String> getDepictions() {
|
||||
return (ArrayList<String>) depictions.clone();
|
||||
public List<String> getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -525,43 +440,7 @@ public class Media implements Parcelable {
|
|||
* @param categories file categories as a list of Strings
|
||||
*/
|
||||
public void setCategories(List<String> categories) {
|
||||
this.categories.clear();
|
||||
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 "";
|
||||
}
|
||||
this.categories = categories;
|
||||
}
|
||||
|
||||
@Nullable private static Date safeParseDate(String dateStr) {
|
||||
|
|
@ -572,20 +451,19 @@ public class Media implements Parcelable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set requested deletion to true
|
||||
* @param requestedDeletion
|
||||
*/
|
||||
public void setRequestedDeletion(){
|
||||
requestedDeletion = true;
|
||||
public void setRequestedDeletion(boolean requestedDeletion){
|
||||
this.requestedDeletion = requestedDeletion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of requested deletion
|
||||
* @return boolean requestedDeletion
|
||||
*/
|
||||
public boolean getRequestedDeletion(){
|
||||
public boolean isRequestedDeletion(){
|
||||
return requestedDeletion;
|
||||
}
|
||||
|
||||
|
|
@ -610,15 +488,29 @@ public class Media implements Parcelable {
|
|||
this.caption = caption;
|
||||
}
|
||||
|
||||
public void setCaptions(HashMap<String, String> captions) {
|
||||
this.captions = captions;
|
||||
/* Sets depictions for the current media obtained fro Wikibase API*/
|
||||
public void setDepictionList(List<Map<String, String>> depictions) {
|
||||
this.depictionList = depictions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets depictions for the current media obtained fro Wikibase API
|
||||
*/
|
||||
public void setDepiction(ArrayList<Map<String, String>> depictions) {
|
||||
this.depictionList = depictions;
|
||||
public void setLocalUri(@Nullable final Uri localUri) {
|
||||
this.localUri = localUri;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -645,8 +537,6 @@ public class Media implements Parcelable {
|
|||
dest.writeLong(this.dataLength);
|
||||
dest.writeLong(this.dateCreated != null ? this.dateCreated.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.licenseUrl);
|
||||
dest.writeString(this.creator);
|
||||
|
|
@ -654,8 +544,6 @@ public class Media implements Parcelable {
|
|||
dest.writeStringList(this.categories);
|
||||
dest.writeList(this.depictionList);
|
||||
dest.writeByte(this.requestedDeletion ? (byte) 1 : (byte) 0);
|
||||
dest.writeSerializable(this.descriptions);
|
||||
dest.writeSerializable(this.tags);
|
||||
dest.writeParcelable(this.coordinates, flags);
|
||||
}
|
||||
|
||||
|
|
@ -673,8 +561,6 @@ public class Media implements Parcelable {
|
|||
this.dateCreated = tmpDateCreated == -1 ? null : new Date(tmpDateCreated);
|
||||
long tmpDateUploaded = in.readLong();
|
||||
this.dateUploaded = tmpDateUploaded == -1 ? null : new Date(tmpDateUploaded);
|
||||
this.width = in.readInt();
|
||||
this.height = in.readInt();
|
||||
this.license = in.readString();
|
||||
this.licenseUrl = in.readString();
|
||||
this.creator = in.readString();
|
||||
|
|
@ -684,8 +570,6 @@ public class Media implements Parcelable {
|
|||
this.categories=list;
|
||||
in.readList(depictionList,null);
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ public class MediaDataExtractor {
|
|||
final String caption, final JsonObject depiction) {
|
||||
media.setDiscussion(discussion);
|
||||
media.setCaption(caption);
|
||||
media.setDepiction(formatDepictions(depiction));
|
||||
media.setDepictionList(formatDepictions(depiction));
|
||||
if (deletionStatus) {
|
||||
media.setRequestedDeletion();
|
||||
media.setRequestedDeletion(true);
|
||||
}
|
||||
return media;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,291 +1,156 @@
|
|||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringDef;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import java.lang.annotation.Retention;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
||||
import fr.free.nrw.commons.upload.WikidataPlace;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryLogEvent;
|
||||
|
||||
@Entity(tableName = "contribution")
|
||||
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
|
||||
public static final int STATE_COMPLETED = -1;
|
||||
public static final int STATE_FAILED = 1;
|
||||
public static final int STATE_QUEUED = 2;
|
||||
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)
|
||||
@NonNull
|
||||
public long _id;
|
||||
public Uri contentUri;
|
||||
public String source;
|
||||
public String editSummary;
|
||||
public int state;
|
||||
public long transferred;
|
||||
public String decimalCoords;
|
||||
public boolean isMultiple;
|
||||
public String wikiDataEntityId;
|
||||
public String wikiItemName;
|
||||
private String p18Value;
|
||||
public Uri contentProviderUri;
|
||||
public String dateCreatedSource;
|
||||
|
||||
private long _id;
|
||||
private int state;
|
||||
private long transferred;
|
||||
private String decimalCoords;
|
||||
private String dateCreatedSource;
|
||||
private WikidataPlace wikidataPlace;
|
||||
/**
|
||||
* Each depiction loaded in depictions activity is associated with a wikidata entity id,
|
||||
* 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,
|
||||
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() {
|
||||
}
|
||||
|
||||
public Contribution(Uri localUri, String imageUrl, String filename, Map<String, String> captions, String description, long dataLength,
|
||||
Date dateCreated, Date dateUploaded, String creator, String editSummary, ArrayList<String> depictionsEntityIds, String decimalCoords) {
|
||||
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
|
||||
this.decimalCoords = decimalCoords;
|
||||
this.editSummary = editSummary;
|
||||
this.dateCreatedSource = "";
|
||||
this.depictionsEntityIds = depictionsEntityIds;
|
||||
public Contribution(final UploadItem item, final SessionManager sessionManager,
|
||||
final List<DepictedItem> depictedItems, final List<String> categories) {
|
||||
super(item.getMediaUri(),
|
||||
item.getFileName(),
|
||||
UploadMediaDetail.formatList(item.getUploadMediaDetails()),
|
||||
sessionManager.getAuthorName(),
|
||||
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,
|
||||
Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords, int state) {
|
||||
super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator);
|
||||
this.decimalCoords = decimalCoords;
|
||||
this.editSummary = editSummary;
|
||||
this.dateCreatedSource = "";
|
||||
this.state=state;
|
||||
public Contribution(final MwQueryLogEvent queryLogEvent, final String user) {
|
||||
super(queryLogEvent.title(),queryLogEvent.date(), user);
|
||||
decimalCoords = "";
|
||||
dateCreatedSource = "";
|
||||
state = STATE_COMPLETED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setDateCreatedSource(String dateCreatedSource) {
|
||||
public void setDateCreatedSource(final String dateCreatedSource) {
|
||||
this.dateCreatedSource = dateCreatedSource;
|
||||
}
|
||||
|
||||
public boolean getMultiple() {
|
||||
return isMultiple;
|
||||
}
|
||||
|
||||
public void setMultiple(boolean multiple) {
|
||||
isMultiple = multiple;
|
||||
public String getDateCreatedSource() {
|
||||
return dateCreatedSource;
|
||||
}
|
||||
|
||||
public long getTransferred() {
|
||||
return transferred;
|
||||
}
|
||||
|
||||
public void setTransferred(long transferred) {
|
||||
public void setTransferred(final long 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() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
public void setState(final int 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
|
||||
*/
|
||||
public ArrayList<String> getDepictionsEntityIds() {
|
||||
return depictionsEntityIds;
|
||||
public List<DepictedItem> getDepictedItems() {
|
||||
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
|
||||
|
|
@ -294,48 +159,34 @@ public class Contribution extends Media {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeLong(this._id);
|
||||
dest.writeParcelable(this.contentUri, flags);
|
||||
dest.writeString(this.source);
|
||||
dest.writeString(this.editSummary);
|
||||
dest.writeInt(this.state);
|
||||
dest.writeLong(this.transferred);
|
||||
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);
|
||||
dest.writeLong(_id);
|
||||
dest.writeInt(state);
|
||||
dest.writeLong(transferred);
|
||||
dest.writeString(decimalCoords);
|
||||
dest.writeString(dateCreatedSource);
|
||||
dest.writeSerializable((HashMap) captions);
|
||||
}
|
||||
|
||||
protected Contribution(Parcel in) {
|
||||
protected Contribution(final Parcel in) {
|
||||
super(in);
|
||||
this._id = in.readLong();
|
||||
this.contentUri = in.readParcelable(Uri.class.getClassLoader());
|
||||
this.source = in.readString();
|
||||
this.editSummary = in.readString();
|
||||
this.state = in.readInt();
|
||||
this.transferred = in.readLong();
|
||||
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();
|
||||
_id = in.readLong();
|
||||
state = in.readInt();
|
||||
transferred = in.readLong();
|
||||
decimalCoords = in.readString();
|
||||
dateCreatedSource = in.readString();
|
||||
captions = (HashMap<String, String>) in.readSerializable();
|
||||
}
|
||||
|
||||
public static final Creator<Contribution> CREATOR = new Creator<Contribution>() {
|
||||
@Override
|
||||
public Contribution createFromParcel(Parcel source) {
|
||||
public Contribution createFromParcel(final Parcel source) {
|
||||
return new Contribution(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contribution[] newArray(int size) {
|
||||
public Contribution[] newArray(final int size) {
|
||||
return new Contribution[size];
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,13 @@
|
|||
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.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
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.filepicker.DefaultCallback;
|
||||
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.utils.PermissionUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
|
||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
|
||||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
|
||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
|
||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE;
|
||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class ContributionController {
|
||||
|
|
@ -109,7 +102,7 @@ public class ContributionController {
|
|||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
|
@ -125,11 +118,9 @@ public class ContributionController {
|
|||
* Attaches place object for nearby uploads
|
||||
*/
|
||||
private Intent handleImagesPicked(Context context,
|
||||
List<UploadableFile> imagesFiles,
|
||||
String source) {
|
||||
List<UploadableFile> imagesFiles) {
|
||||
Intent shareIntent = new Intent(context, UploadActivity.class);
|
||||
shareIntent.setAction(ACTION_INTERNAL_UPLOADS);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, source);
|
||||
shareIntent.putParcelableArrayListExtra(EXTRA_FILES, new ArrayList<>(imagesFiles));
|
||||
Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class);
|
||||
if (place != null) {
|
||||
|
|
@ -139,13 +130,4 @@ public class ContributionController {
|
|||
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.Query;
|
||||
import androidx.room.Transaction;
|
||||
|
||||
import androidx.room.Update;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Single;
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public abstract class ContributionDao {
|
||||
|
|
@ -40,9 +37,6 @@ public abstract class ContributionDao {
|
|||
@Delete
|
||||
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")
|
||||
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 android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
|
@ -60,7 +58,7 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
|
|||
this.contribution = contribution;
|
||||
fetchAndDisplayCaption(contribution);
|
||||
this.position = position;
|
||||
String imageSource = chooseImageSource(contribution.thumbUrl, contribution.getLocalUri());
|
||||
String imageSource = chooseImageSource(contribution.getThumbUrl(), contribution.getLocalUri());
|
||||
if (!TextUtils.isEmpty(imageSource)) {
|
||||
final ImageRequest imageRequest =
|
||||
ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
|
@ -12,21 +16,12 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
|
||||
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.ButterKnife;
|
||||
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.LocationServiceManager;
|
||||
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.MediaDetailProvider;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
|
|
@ -61,14 +55,11 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
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 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
|
||||
extends CommonsDaggerSupportFragment
|
||||
implements
|
||||
|
|
@ -224,7 +215,7 @@ public class ContributionsFragment
|
|||
|
||||
@Override
|
||||
public void fetchMediaUriFor(Contribution contribution) {
|
||||
Timber.d("Fetching thumbnail for %s", contribution.filename);
|
||||
Timber.d("Fetching thumbnail for %s", contribution.getFilename());
|
||||
contributionsPresenter.fetchMediaDetails(contribution);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public class ContributionsListAdapter extends RecyclerView.Adapter<ContributionV
|
|||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return contributions.get(position)._id;
|
||||
return contributions.get(position).get_id();
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
|
|
|
|||
|
|
@ -1,46 +1,30 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
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.Nullable;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
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.Media;
|
||||
import fr.free.nrw.commons.MediaDataExtractor;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
|
||||
import fr.free.nrw.commons.db.AppDatabase;
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule;
|
||||
import fr.free.nrw.commons.mwapi.UserClient;
|
||||
import fr.free.nrw.commons.utils.ExecutorUtils;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
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 static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
|
||||
|
||||
/**
|
||||
* The presenter class for Contributions
|
||||
*/
|
||||
|
|
@ -106,12 +90,7 @@ public class ContributionsPresenter implements UserActionListener {
|
|||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext(mwQueryLogEvent -> Timber.d("Received image %s", mwQueryLogEvent.title()))
|
||||
.filter(mwQueryLogEvent -> !mwQueryLogEvent.isDeleted()).doOnNext(mwQueryLogEvent -> Timber.d("Image %s passed filters", mwQueryLogEvent.title()))
|
||||
.map(image -> {
|
||||
Contribution contribution = new Contribution(null, null, image.title(),
|
||||
new HashMap<>(), "", -1, image.date(), image.date(), user,
|
||||
"", "", STATE_COMPLETED);
|
||||
return contribution;
|
||||
})
|
||||
.map(image -> new Contribution(image, user))
|
||||
.toList()
|
||||
.subscribe(this::saveContributionsToDB, error -> {
|
||||
Timber.e("Failed to fetch contributions: %s", error.getMessage());
|
||||
|
|
@ -198,11 +177,11 @@ public class ContributionsPresenter implements UserActionListener {
|
|||
@Override
|
||||
public void fetchMediaDetails(Contribution contribution) {
|
||||
compositeDisposable.add(mediaDataExtractor
|
||||
.getMediaFromFileName(contribution.filename)
|
||||
.getMediaFromFileName(contribution.getFilename())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(media -> {
|
||||
contribution.thumbUrl=media.thumbUrl;
|
||||
contribution.setThumbUrl(media.getThumbUrl());
|
||||
updateContribution(contribution);
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import com.google.gson.reflect.TypeToken;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
|
@ -42,53 +43,74 @@ public class Converters {
|
|||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String listObjectToString(ArrayList<String> objectList) {
|
||||
return objectList == null ? null : getGson().toJson(objectList);
|
||||
public static String listObjectToString(List<String> objectList) {
|
||||
return writeObjectToString(objectList);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static ArrayList<String> stringToArrayListObject(String objectList) {
|
||||
return objectList == null ? null : getGson().fromJson(objectList,new TypeToken<ArrayList<String>>(){}.getType());
|
||||
public static List<String> stringToListObject(String objectList) {
|
||||
return readObjectWithTypeToken(objectList, new TypeToken<List<String>>() {});
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String mapObjectToString(HashMap<String,String> objectList) {
|
||||
return objectList == null ? null : getGson().toJson(objectList);
|
||||
public static String mapObjectToString(Map<String,String> objectList) {
|
||||
return writeObjectToString(objectList);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static HashMap<String,String> stringToHashMap(String objectList) {
|
||||
return objectList == null ? null : getGson().fromJson(objectList,new TypeToken<HashMap<String,String>>(){}.getType());
|
||||
public static Map<String,String> stringToMap(String objectList) {
|
||||
return readObjectWithTypeToken(objectList, new TypeToken<Map<String,String>>(){});
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String latlngObjectToString(LatLng latlng) {
|
||||
return latlng == null ? null : getGson().toJson(latlng);
|
||||
return writeObjectToString(latlng);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static LatLng stringToLatLng(String objectList) {
|
||||
return objectList == null ? null : getGson().fromJson(objectList,LatLng.class);
|
||||
return readObjectFromString(objectList,LatLng.class);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String listOfMapToString(ArrayList<Map<String,String>> listOfMaps) {
|
||||
return listOfMaps == null ? null : getGson().toJson(listOfMaps);
|
||||
public static String listOfMapToString(List<Map<String,String>> listOfMaps) {
|
||||
return writeObjectToString(listOfMaps);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static ArrayList<Map<String,String>> stringToListOfMap(String listOfMaps) {
|
||||
return listOfMaps == null ? null :getGson().fromJson(listOfMaps,new TypeToken<ArrayList<Map<String,String>>>(){}.getType());
|
||||
public static List<Map<String,String>> stringToListOfMap(String listOfMaps) {
|
||||
return readObjectWithTypeToken(listOfMaps, new TypeToken<List<Map<String, String>>>() {});
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String mapToString(Map<String,String> map) {
|
||||
return map == null ? null : getGson().toJson(map);
|
||||
public static String wikidataPlaceToString(WikidataPlace wikidataPlace) {
|
||||
return writeObjectToString(wikidataPlace);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static Map<String,String> stringToMap(String map) {
|
||||
return map == null ? null :getGson().fromJson(map,new TypeToken<Map<String,String>>(){}.getType());
|
||||
public static WikidataPlace stringToWikidataPlace(String wikidataPlace) {
|
||||
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;
|
||||
|
||||
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
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.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -29,10 +19,16 @@ import io.reactivex.Single;
|
|||
import io.reactivex.SingleSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
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 static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
|
||||
|
||||
/**
|
||||
* Refactored async task to Rx
|
||||
*/
|
||||
|
|
@ -104,7 +100,7 @@ public class DeleteHelper {
|
|||
}
|
||||
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 -> {
|
||||
if (result) {
|
||||
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);
|
||||
view.onSuccess(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) {
|
||||
Intent intent = new Intent(context, WikidataItemDetailsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra("wikidataItemName", depictedItem.getDepictsLabel());
|
||||
intent.putExtra("entityId", depictedItem.getEntityId());
|
||||
intent.putExtra("wikidataItemName", depictedItem.getName());
|
||||
intent.putExtra("entityId", depictedItem.getId());
|
||||
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.WikiBaseInterface;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
|
||||
import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface;
|
||||
import fr.free.nrw.commons.wikidata.WikidataInterface;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
|
@ -163,12 +162,6 @@ public class NetworkingModule {
|
|||
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
|
||||
@Singleton
|
||||
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.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
|
@ -113,7 +112,6 @@ public class DepictsClient {
|
|||
Media media = new Media(null,
|
||||
getUrl(s.getTitle()),
|
||||
s.getTitle(),
|
||||
new HashMap<>(),
|
||||
"",
|
||||
0,
|
||||
safeParseDate(s.getTimestamp()),
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ public class SearchDepictionsFragmentPresenter extends CommonsDaggerSupportFragm
|
|||
view.onSuccess(mediaList);
|
||||
offset=queryList.size();
|
||||
for (DepictedItem m : mediaList) {
|
||||
fetchThumbnailForEntityId(m.getEntityId(), size++);
|
||||
fetchThumbnailForEntityId(m.getId(), size++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public class SearchDepictionsRenderer extends Renderer<DepictedItem> {
|
|||
@Override
|
||||
public void render() {
|
||||
DepictedItem item = getContent();
|
||||
tvDepictionLabel.setText(item.getDepictsLabel());
|
||||
tvDepictionLabel.setText(item.getName());
|
||||
tvDepictionDesc.setText(item.getDescription());
|
||||
imageView.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_wikidata_logo_24dp));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
|
|
@ -22,28 +25,17 @@ import android.widget.ScrollView;
|
|||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
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 androidx.annotation.Nullable;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
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.MediaDataExtractor;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -63,13 +55,15 @@ import io.reactivex.Single;
|
|||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
private boolean editable;
|
||||
|
|
@ -555,8 +549,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
item.setOnClickListener(view -> {
|
||||
DepictedItem depictedItem = new DepictedItem(depictionName, "", "", false, entityId);
|
||||
Intent intent = new Intent(getContext(), WikidataItemDetailsActivity.class);
|
||||
intent.putExtra("wikidataItemName", depictedItem.getDepictsLabel());
|
||||
intent.putExtra("entityId", depictedItem.getEntityId());
|
||||
intent.putExtra("wikidataItemName", depictedItem.getName());
|
||||
intent.putExtra("entityId", depictedItem.getId());
|
||||
getContext().startActivity(intent);
|
||||
});
|
||||
}
|
||||
|
|
@ -609,7 +603,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
private String prettyDescription(Media media) {
|
||||
// @todo use UI language when multilingual descs are available
|
||||
String desc = media.getDescription(locale.getLanguage()).trim();
|
||||
String desc = media.getDescription();
|
||||
if (desc.equals("")) {
|
||||
return getString(R.string.detail_description_empty);
|
||||
} else {
|
||||
|
|
@ -655,7 +649,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
private void checkDeletion(Media media){
|
||||
if (media.getRequestedDeletion()){
|
||||
if (media.isRequestedDeletion()){
|
||||
delete.setVisibility(GONE);
|
||||
nominatedForDeletion.setVisibility(VISIBLE);
|
||||
} else if (!isCategoryImage) {
|
||||
|
|
|
|||
|
|
@ -170,13 +170,12 @@ public class UploadRemoteDataSource {
|
|||
*
|
||||
* @param uploadableFile
|
||||
* @param place
|
||||
* @param source
|
||||
* @param similarImageInterface
|
||||
* @return
|
||||
*/
|
||||
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
||||
String source, SimilarImageInterface similarImageInterface) {
|
||||
return uploadModel.preProcessImage(uploadableFile, place, source, similarImageInterface);
|
||||
SimilarImageInterface similarImageInterface) {
|
||||
return uploadModel.preProcessImage(uploadableFile, place, similarImageInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -213,7 +212,7 @@ public class UploadRemoteDataSource {
|
|||
*/
|
||||
|
||||
public void onDepictedItemClicked(DepictedItem depictedItem) {
|
||||
depictModel.onDepictItemClicked(depictedItem);
|
||||
uploadModel.onDepictItemClicked(depictedItem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -222,7 +221,7 @@ public class UploadRemoteDataSource {
|
|||
*/
|
||||
|
||||
public List<DepictedItem> getSelectedDepictions() {
|
||||
return depictModel.getSelectedDepictions();
|
||||
return uploadModel.getSelectedDepictions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,14 +232,6 @@ public class UploadRemoteDataSource {
|
|||
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) {
|
||||
uploadModel.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,10 +115,6 @@ public class UploadRepository {
|
|||
remoteDataSource.setSelectedCategories(categoryStringList);
|
||||
}
|
||||
|
||||
public void setSelectedDepictions(List<String> selectedDepictions) {
|
||||
remoteDataSource.setSelectedDepictions(selectedDepictions);
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the category selection/deselection
|
||||
*
|
||||
|
|
@ -180,14 +176,12 @@ public class UploadRepository {
|
|||
*
|
||||
* @param uploadableFile
|
||||
* @param place
|
||||
* @param source
|
||||
* @param similarImageInterface
|
||||
* @return
|
||||
*/
|
||||
public Observable<UploadItem> preProcessImage(UploadableFile uploadableFile, Place place,
|
||||
String source, SimilarImageInterface similarImageInterface) {
|
||||
return remoteDataSource
|
||||
.preProcessImage(uploadableFile, place, source, similarImageInterface);
|
||||
SimilarImageInterface similarImageInterface) {
|
||||
return remoteDataSource.preProcessImage(uploadableFile, place, similarImageInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -294,10 +288,6 @@ public class UploadRepository {
|
|||
return remoteDataSource.searchAllEntities(query);
|
||||
}
|
||||
|
||||
public List<String> getDepictionsEntityIdList() {
|
||||
return remoteDataSource.depictionsEntityIdList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nearest place matching the passed latitude and longitude
|
||||
* @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;
|
||||
|
||||
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.annotation.SuppressLint;
|
||||
import android.app.ProgressDialog;
|
||||
|
|
@ -10,7 +14,6 @@ import android.widget.ImageButton;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
|
@ -20,14 +23,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
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.ButterKnife;
|
||||
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.SessionManager;
|
||||
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.filepicker.UploadableFile;
|
||||
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.mediaDetails.UploadMediaDetailFragment;
|
||||
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.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
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 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 {
|
||||
@Inject
|
||||
ContributionController contributionController;
|
||||
|
|
@ -108,8 +102,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
private MediaLicenseFragment mediaLicenseFragment;
|
||||
private ThumbnailsAdapter thumbnailsAdapter;
|
||||
|
||||
|
||||
private String source;
|
||||
private Place place;
|
||||
private List<UploadableFile> uploadableFiles = Collections.emptyList();
|
||||
private int currentSelectedPosition = 0;
|
||||
|
|
@ -325,7 +317,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
fragments = new ArrayList<>();
|
||||
for (UploadableFile uploadableFile : uploadableFiles) {
|
||||
UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment();
|
||||
uploadMediaDetailFragment.setImageTobeUploaded(uploadableFile, source, place);
|
||||
uploadMediaDetailFragment.setImageTobeUploaded(uploadableFile, place);
|
||||
uploadMediaDetailFragment.setCallback(new UploadMediaDetailFragmentCallback() {
|
||||
@Override
|
||||
public void deletePictureAtIndex(int index) {
|
||||
|
|
@ -387,16 +379,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
private void receiveInternalSharedItems() {
|
||||
Intent intent = getIntent();
|
||||
|
||||
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
||||
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);
|
||||
Timber.d("Received intent %s with action %s", intent.toString(), intent.getAction());
|
||||
|
||||
uploadableFiles = intent.getParcelableArrayListExtra(EXTRA_FILES);
|
||||
Timber.i("Received multiple upload %s", uploadableFiles.size());
|
||||
|
|
|
|||
|
|
@ -1,35 +1,36 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
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.CommonsApplication;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.upload.UploadService.NotificationUpdateProgressListener;
|
||||
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.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||
import org.wikipedia.csrf.CsrfTokenClient;
|
||||
|
||||
@Singleton
|
||||
public class UploadClient {
|
||||
|
||||
private final UploadInterface uploadInterface;
|
||||
private final CsrfTokenClient csrfTokenClient;
|
||||
private final PageContentsCreator pageContentsCreator;
|
||||
|
||||
@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.csrfTokenClient = csrfTokenClient;
|
||||
this.pageContentsCreator = pageContentsCreator;
|
||||
}
|
||||
|
||||
Observable<UploadResult> uploadFileToStash(Context context, String filename, File file,
|
||||
|
|
@ -61,8 +62,8 @@ public class UploadClient {
|
|||
try {
|
||||
return uploadInterface
|
||||
.uploadFileFromStash(csrfTokenClient.getTokenBlocking(),
|
||||
contribution.getPageContents(context),
|
||||
contribution.getEditSummary(),
|
||||
pageContentsCreator.createFrom(contribution),
|
||||
CommonsApplication.DEFAULT_EDIT_SUMMARY,
|
||||
uniqueFileName,
|
||||
fileKey).map(uploadResponse -> uploadResponse.getUpload());
|
||||
} catch (Throwable throwable) {
|
||||
|
|
|
|||
|
|
@ -115,10 +115,6 @@ public class UploadController {
|
|||
contribution.setDescription("");
|
||||
}
|
||||
|
||||
if (contribution.getCaption() == null) {
|
||||
contribution.setCaption("");
|
||||
}
|
||||
|
||||
final String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
||||
contribution.setLicense(license);
|
||||
|
||||
|
|
@ -152,7 +148,7 @@ public class UploadController {
|
|||
|
||||
if (mimeType != null) {
|
||||
Timber.d("MimeType is: %s", mimeType);
|
||||
contribution.setTag("mimeType", mimeType);
|
||||
contribution.setMimeType(mimeType);
|
||||
if(mimeType.startsWith("image/") && contribution.getDateCreated() == null){
|
||||
contribution.setDateCreated(resolveDateTakenOrNow(contentResolver, contribution));
|
||||
}
|
||||
|
|
@ -162,7 +158,7 @@ public class UploadController {
|
|||
}
|
||||
|
||||
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("*")) {
|
||||
return contentResolver.getType(contribution.getLocalUri());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,13 +89,13 @@ public class UploadDepictsRenderer extends Renderer<DepictedItem> {
|
|||
public void render() {
|
||||
DepictedItem item = getContent();
|
||||
checkedView.setChecked(item.isSelected());
|
||||
depictsLabel.setText(item.getDepictsLabel());
|
||||
depictsLabel.setText(item.getName());
|
||||
description.setText(item.getDescription());
|
||||
if (!TextUtils.isEmpty(item.getImageUrl())) {
|
||||
if (!item.getImageUrl().equals(NO_IMAGE_FOR_DEPICTION))
|
||||
setImageView(Uri.parse(item.getImageUrl()), imageView);
|
||||
}else{
|
||||
listener.fetchThumbnailUrlForEntity(item.getEntityId(),item.getPosition());
|
||||
listener.fetchThumbnailUrlForEntity(item.getId(),item.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ data class UploadMediaDetail constructor(
|
|||
*/
|
||||
@JvmStatic
|
||||
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.
|
||||
|
|
@ -56,11 +56,7 @@ data class UploadMediaDetail constructor(
|
|||
*/
|
||||
@JvmStatic
|
||||
fun formatList(descriptions: List<UploadMediaDetail>) =
|
||||
descriptions.joinToString {
|
||||
if (it.descriptionText.isNotEmpty())
|
||||
"{{${it.languageCode}|1=${it.descriptionText}}}"
|
||||
else
|
||||
""
|
||||
}
|
||||
descriptions.filter { it.descriptionText.isNotEmpty() }
|
||||
.joinToString { "{{${it.languageCode}|1=${it.descriptionText}}}" }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import android.annotation.SuppressLint;
|
|||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
|
@ -18,9 +18,7 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||
import io.reactivex.subjects.BehaviorSubject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -44,8 +42,8 @@ public class UploadModel {
|
|||
private final SessionManager sessionManager;
|
||||
private final FileProcessor fileProcessor;
|
||||
private final ImageProcessingService imageProcessingService;
|
||||
private List<String> selectedCategories;
|
||||
private ArrayList<String> selectedDepictions;
|
||||
private List<String> selectedCategories = new ArrayList<>();
|
||||
private List<DepictedItem> selectedDepictions = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
UploadModel(@Named("licenses") final List<String> licenses,
|
||||
|
|
@ -71,17 +69,13 @@ public class UploadModel {
|
|||
public void cleanUp() {
|
||||
compositeDisposable.clear();
|
||||
fileProcessor.cleanup();
|
||||
this.items.clear();
|
||||
if (this.selectedCategories != null) {
|
||||
this.selectedCategories.clear();
|
||||
}
|
||||
if (this.selectedDepictions != null) {
|
||||
this.selectedDepictions.clear();
|
||||
}
|
||||
items.clear();
|
||||
selectedCategories.clear();
|
||||
selectedDepictions.clear();
|
||||
}
|
||||
|
||||
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,
|
||||
final Place place,
|
||||
final String source,
|
||||
final SimilarImageInterface similarImageInterface) {
|
||||
return Observable.just(
|
||||
createAndAddUploadItem(uploadableFile, place, source, similarImageInterface));
|
||||
createAndAddUploadItem(uploadableFile, place, similarImageInterface));
|
||||
}
|
||||
|
||||
public Single<Integer> getImageQuality(final UploadItem uploadItem) {
|
||||
|
|
@ -101,7 +94,6 @@ public class UploadModel {
|
|||
|
||||
private UploadItem createAndAddUploadItem(final UploadableFile uploadableFile,
|
||||
final Place place,
|
||||
final String source,
|
||||
final SimilarImageInterface similarImageInterface) {
|
||||
final UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
|
||||
.getFileCreatedDate(context);
|
||||
|
|
@ -116,7 +108,7 @@ public class UploadModel {
|
|||
.processFileCoordinates(similarImageInterface, uploadableFile.getFilePath());
|
||||
final UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
|
||||
Uri.parse(uploadableFile.getFilePath()),
|
||||
uploadableFile.getMimeType(context), source, imageCoordinates, place, fileCreatedDate,
|
||||
uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate,
|
||||
createdTimestampSource);
|
||||
if (place != null) {
|
||||
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
|
||||
|
|
@ -151,29 +143,8 @@ public class UploadModel {
|
|||
public Observable<Contribution> buildContributions() {
|
||||
return Observable.fromIterable(items).map(item ->
|
||||
{
|
||||
final Contribution contribution = new Contribution(item.mediaUri, null,
|
||||
item.getFileName(), item.uploadMediaDetails.size()!=0? UploadMediaDetail.formatCaptions(item.uploadMediaDetails):new HashMap<>(),
|
||||
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());
|
||||
|
||||
final Contribution contribution = new Contribution(
|
||||
item, sessionManager, newListOf(selectedDepictions), newListOf(selectedCategories));
|
||||
Timber.d("Created timestamp while building contribution is %s, %s",
|
||||
item.getCreatedTimestamp(),
|
||||
new Date(item.getCreatedTimestamp()));
|
||||
|
|
@ -208,12 +179,16 @@ public class UploadModel {
|
|||
uploadItem1.setMediaDetails(uploadItem.uploadMediaDetails);
|
||||
}
|
||||
|
||||
public void setSelectedDepictions(final List<String> selectedDepictions) {
|
||||
this.selectedDepictions = newListOf(selectedDepictions);
|
||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
||||
if (depictedItem.isSelected()) {
|
||||
selectedDepictions.add(depictedItem);
|
||||
} else {
|
||||
selectedDepictions.remove(depictedItem);
|
||||
}
|
||||
}
|
||||
|
||||
@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<>();
|
||||
}
|
||||
|
||||
|
|
@ -222,13 +197,16 @@ public class UploadModel {
|
|||
items.get(uploadItemIndex).setGpsCoords(imageCoordinates);
|
||||
}
|
||||
|
||||
public List<DepictedItem> getSelectedDepictions() {
|
||||
return selectedDepictions;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static class UploadItem {
|
||||
|
||||
private final Uri originalContentUri;
|
||||
private final Uri mediaUri;
|
||||
private final String mimeType;
|
||||
private final String source;
|
||||
private ImageCoordinates gpsCoords;
|
||||
private List<UploadMediaDetail> uploadMediaDetails;
|
||||
private final Place place;
|
||||
|
|
@ -237,7 +215,8 @@ public class UploadModel {
|
|||
private final BehaviorSubject<Integer> imageQuality;
|
||||
@SuppressLint("CheckResult")
|
||||
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 long createdTimestamp,
|
||||
final String createdTimestampSource) {
|
||||
|
|
@ -247,7 +226,6 @@ public class UploadModel {
|
|||
this.place = place;
|
||||
this.mediaUri = mediaUri;
|
||||
this.mimeType = mimeType;
|
||||
this.source = source;
|
||||
this.gpsCoords = gpsCoords;
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
|
||||
|
|
@ -257,10 +235,6 @@ public class UploadModel {
|
|||
return createdTimestampSource;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public ImageCoordinates getGpsCoords() {
|
||||
return gpsCoords;
|
||||
}
|
||||
|
|
@ -323,5 +297,8 @@ public class UploadModel {
|
|||
this.gpsCoords = gpsCoords;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,16 @@ package fr.free.nrw.commons.upload
|
|||
|
||||
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.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
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.CommonsApplication;
|
||||
import fr.free.nrw.commons.HandlerService;
|
||||
|
|
@ -38,6 +25,15 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
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;
|
||||
|
||||
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 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";
|
||||
@Inject WikidataEditService wikidataEditService;
|
||||
@Inject SessionManager sessionManager;
|
||||
|
|
@ -151,7 +146,6 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
|
||||
@Override
|
||||
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) {
|
||||
case ACTION_UPLOAD_FILE:
|
||||
|
||||
|
|
@ -168,7 +162,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
.subscribeOn(ioThreadScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(aLong->{
|
||||
contribution._id = aLong;
|
||||
contribution.set_id(aLong);
|
||||
UploadService.super.queue(what, contribution);
|
||||
}, Throwable::printStackTrace));
|
||||
break;
|
||||
|
|
@ -251,12 +245,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
|
||||
Timber.d("Stash upload response 1 is %s", uploadStash.toString());
|
||||
|
||||
String resultStatus = uploadStash.getResult();
|
||||
if (!resultStatus.equals("Success")) {
|
||||
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
||||
showFailedNotification(contribution);
|
||||
return Observable.never();
|
||||
} else {
|
||||
if (uploadStash.isSuccessful()) {
|
||||
Timber.d("making sure of uniqueness of name: %s", filename);
|
||||
String uniqueFilename = findUniqueFilename(filename);
|
||||
unfinishedUploads.add(uniqueFilename);
|
||||
|
|
@ -265,6 +254,10 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
contribution,
|
||||
uniqueFilename,
|
||||
uploadStash.getFilekey());
|
||||
} else {
|
||||
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
||||
showFailedNotification(contribution);
|
||||
return Observable.never();
|
||||
}
|
||||
})
|
||||
.subscribe(
|
||||
|
|
@ -282,32 +275,27 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
|
||||
notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS);
|
||||
|
||||
String resultStatus = uploadResult.getResult();
|
||||
if (!resultStatus.equals("Success")) {
|
||||
if (uploadResult.isSuccessful()) {
|
||||
onSuccessfulUpload(contribution, uploadResult);
|
||||
} else {
|
||||
Timber.d("Contribution upload failed. Wikidata entity won't be edited");
|
||||
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());
|
||||
contribution.setFilename(canonicalFilename);
|
||||
private void onSuccessfulUpload(Contribution contribution, UploadResult uploadResult)
|
||||
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.setState(Contribution.STATE_COMPLETED);
|
||||
contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatTimestamp()
|
||||
|
|
@ -318,7 +306,6 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
.observeOn(mainThreadScheduler)
|
||||
.subscribe());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
@SuppressWarnings("deprecation")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
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.MwQueryResponse;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
|
|
@ -13,8 +13,6 @@ import retrofit2.http.Headers;
|
|||
import retrofit2.http.POST;
|
||||
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
|
||||
*/
|
||||
|
|
@ -31,4 +29,18 @@ public interface WikiBaseInterface {
|
|||
@GET(MW_API_PREFIX + "action=query&prop=info")
|
||||
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() {
|
||||
List<DepictedItem> selectedDepictions = repository.getSelectedDepictions();
|
||||
if (selectedDepictions != null && !selectedDepictions.isEmpty()) {
|
||||
repository.setSelectedDepictions(repository.getDepictionsEntityIdList());
|
||||
view.goToNextScreen();
|
||||
} else {
|
||||
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;
|
||||
|
||||
private UploadableFile uploadableFile;
|
||||
private String source;
|
||||
private Place place;
|
||||
|
||||
private boolean isExpanded = true;
|
||||
|
|
@ -97,9 +96,8 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public void setImageTobeUploaded(UploadableFile uploadableFile, String source, Place place) {
|
||||
public void setImageTobeUploaded(UploadableFile uploadableFile, Place place) {
|
||||
this.uploadableFile = uploadableFile;
|
||||
this.source = source;
|
||||
this.place = place;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +120,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
callback.getTotalNumberOfSteps()));
|
||||
initRecyclerView();
|
||||
initPresenter();
|
||||
presenter.receiveImage(uploadableFile, source, place);
|
||||
presenter.receiveImage(uploadableFile, place);
|
||||
|
||||
if (callback.getIndexInViewFlipper(this) == 0) {
|
||||
btnPrevious.setEnabled(false);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
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.contributions.Contribution;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
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.UploadMediaDetail;
|
||||
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
|
||||
|
|
@ -41,8 +39,7 @@ public interface UploadMediaDetailsContract {
|
|||
|
||||
interface UserActionListener extends BasePresenter<View> {
|
||||
|
||||
void receiveImage(UploadableFile uploadableFile, @Contribution.FileSource String source,
|
||||
Place place);
|
||||
void receiveImage(UploadableFile uploadableFile, Place place);
|
||||
|
||||
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
|
||||
*
|
||||
* @param uploadableFile
|
||||
* @param source
|
||||
* @param place
|
||||
*/
|
||||
@Override
|
||||
public void receiveImage(UploadableFile uploadableFile, String source, Place place) {
|
||||
public void receiveImage(UploadableFile uploadableFile, Place place) {
|
||||
view.showProgress(true);
|
||||
Disposable uploadItemDisposable = repository
|
||||
.preProcessImage(uploadableFile, place, source, this)
|
||||
.preProcessImage(uploadableFile, place, this)
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(uploadItem ->
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package fr.free.nrw.commons.upload.structure.depictions
|
|||
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
||||
import io.reactivex.Observable
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -14,24 +14,6 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
|
|||
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
|
||||
*/
|
||||
|
|
@ -44,5 +26,4 @@ class DepictModel @Inject constructor(private val depictsInterface: DepictsInter
|
|||
.map(::DepictedItem)
|
||||
}
|
||||
|
||||
fun depictionsEntityIdList() = selectedDepictions.map { it.entityId }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
package fr.free.nrw.commons.upload.structure.depictions
|
||||
|
||||
import fr.free.nrw.commons.upload.WikidataItem
|
||||
import fr.free.nrw.commons.wikidata.model.DepictSearchItem
|
||||
|
||||
/**
|
||||
* Model class for Depicted Item in Upload and Explore
|
||||
*/
|
||||
data class DepictedItem constructor(
|
||||
val depictsLabel: String?,
|
||||
override val name: String,
|
||||
val description: String?,
|
||||
var imageUrl: String,
|
||||
var isSelected: Boolean,
|
||||
val entityId: String
|
||||
) {
|
||||
override val id: String
|
||||
):WikidataItem {
|
||||
constructor(depictSearchItem: DepictSearchItem) : this(
|
||||
depictSearchItem.label,
|
||||
depictSearchItem.description,
|
||||
|
|
@ -24,12 +25,12 @@ data class DepictedItem constructor(
|
|||
|
||||
override fun equals(o: Any?) = when {
|
||||
this === o -> true
|
||||
o is DepictedItem -> depictsLabel == o.depictsLabel
|
||||
o is DepictedItem -> name == o.name
|
||||
else -> false
|
||||
}
|
||||
|
||||
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() {
|
||||
DepictedItem item = getContent();
|
||||
checkedView.setChecked(item.isSelected());
|
||||
depictsLabel.setText(item.getDepictsLabel());
|
||||
depictsLabel.setText(item.getName());
|
||||
description.setText(item.getDescription());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
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.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||
import org.wikipedia.csrf.CsrfTokenClient;
|
||||
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Wikibase Client for calling WikiBase APIs
|
||||
|
|
@ -29,16 +30,32 @@ public class WikiBaseClient {
|
|||
}
|
||||
|
||||
public Observable<Boolean> postEditEntity(String fileEntityId, String data) {
|
||||
try {
|
||||
return wikiBaseInterface.postEditEntity(fileEntityId, csrfTokenClient.getTokenBlocking(), data)
|
||||
.map(response -> (response.getSuccessVal() == 1));
|
||||
} catch (Throwable throwable) {
|
||||
return Observable.just(false);
|
||||
}
|
||||
return csrfToken()
|
||||
.switchMap(editToken -> wikiBaseInterface.postEditEntity(fileEntityId, editToken, data)
|
||||
.map(response -> (response.getSuccessVal() == 1)));
|
||||
}
|
||||
|
||||
public Observable<Long> getFileEntityId(String fileName) {
|
||||
return wikiBaseInterface.getFileEntityId(fileName)
|
||||
public Observable<Long> getFileEntityId(UploadResult uploadResult) {
|
||||
return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName())
|
||||
.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;
|
||||
|
||||
import fr.free.nrw.commons.upload.WikidataItem;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -24,15 +25,16 @@ public class WikidataClient {
|
|||
|
||||
/**
|
||||
* Create wikidata claim to add P18 value
|
||||
* @param entityId wikidata entity ID
|
||||
* @param entity wikidata entity ID
|
||||
* @param value value of the P18 edit
|
||||
* @return revisionID of the edit
|
||||
*/
|
||||
Observable<Long> createClaim(String entityId, String value) {
|
||||
Observable<Long> createImageClaim(WikidataItem entity, String value) {
|
||||
return getCsrfToken()
|
||||
.flatMap(csrfToken -> wikidataInterface.postCreateClaim(toRequestBody(entityId),
|
||||
.flatMap(csrfToken -> wikidataInterface.postCreateClaim(
|
||||
toRequestBody(entity.getId()),
|
||||
toRequestBody("value"),
|
||||
toRequestBody("P18"),
|
||||
toRequestBody(WikidataProperties.IMAGE.getPropertyName()),
|
||||
toRequestBody(value),
|
||||
toRequestBody("en"),
|
||||
toRequestBody(csrfToken)))
|
||||
|
|
|
|||
|
|
@ -4,27 +4,28 @@ import static fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_I
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
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.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.ViewUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.wikipedia.csrf.CsrfTokenClient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.wikipedia.dataclient.mwapi.MwPostResponse;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -35,129 +36,55 @@ import timber.log.Timber;
|
|||
@Singleton
|
||||
public class WikidataEditService {
|
||||
|
||||
private final static 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_TAG = "wikimedia-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 WikidataEditListener wikidataEditListener;
|
||||
private final JsonKvStore directKvStore;
|
||||
private final CaptionInterface captionInterface;
|
||||
private final WikiBaseClient wikiBaseClient;
|
||||
private final WikidataClient wikidataClient;
|
||||
private final CsrfTokenClient csrfTokenClient;
|
||||
|
||||
@Inject
|
||||
public WikidataEditService(final Context context,
|
||||
final WikidataEditListener wikidataEditListener,
|
||||
@Named("default_preferences") final JsonKvStore directKvStore,
|
||||
final WikiBaseClient wikiBaseClient,
|
||||
final CaptionInterface captionInterface,
|
||||
final WikidataClient wikidataClient,
|
||||
@Named("commons-csrf") final CsrfTokenClient csrfTokenClient) {
|
||||
final WikidataClient wikidataClient) {
|
||||
this.context = context;
|
||||
this.wikidataEditListener = wikidataEditListener;
|
||||
this.directKvStore = directKvStore;
|
||||
this.captionInterface = captionInterface;
|
||||
this.wikiBaseClient = wikiBaseClient;
|
||||
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.
|
||||
* Adding DEPICTS property requires call to the wikibase API to set tag against the entity.
|
||||
*
|
||||
* @param wikidataEntityId
|
||||
* @param fileName
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void editWikiBaseDepictsProperty(final String wikidataEntityId, final String fileName) {
|
||||
wikiBaseClient.getFileEntityId(fileName)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(fileEntityId -> {
|
||||
if (fileEntityId != null) {
|
||||
Timber.d("EntityId for image was received successfully: %s", fileEntityId);
|
||||
addDepictsProperty(wikidataEntityId, fileEntityId.toString());
|
||||
private Observable<Boolean> addDepictsProperty(final String fileEntityId,
|
||||
final WikidataItem depictedItem) {
|
||||
// Wikipedia:Sandbox (Q10)
|
||||
final String data = depictionJson(ConfigUtils.isBetaFlavour() ? "Q10" : depictedItem.getId());
|
||||
|
||||
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, data)
|
||||
.doOnNext(success -> {
|
||||
if (success) {
|
||||
Timber.d("DEPICTS property was set successfully for %s", fileEntityId);
|
||||
} 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");
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private void addDepictsProperty(String entityId, final String fileEntityId) {
|
||||
if (ConfigUtils.isBetaFlavour()) {
|
||||
entityId = "Q10"; // Wikipedia:Sandbox (Q10)
|
||||
})
|
||||
.doOnError( throwable -> {
|
||||
Timber.e(throwable, "Error occurred while setting DEPICTS property");
|
||||
ViewUtil.showLongToast(context, throwable.toString());
|
||||
})
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String depictionJson(final String entityId) {
|
||||
final JsonObject value = new JsonObject();
|
||||
value.addProperty("entity-type", "item");
|
||||
value.addProperty("numeric-id", entityId.replace("Q", ""));
|
||||
|
|
@ -169,7 +96,7 @@ public class WikidataEditService {
|
|||
|
||||
final JsonObject mainSnak = new JsonObject();
|
||||
mainSnak.addProperty("snaktype", "value");
|
||||
mainSnak.addProperty("property", BuildConfig.DEPICTS_PROPERTY);
|
||||
mainSnak.addProperty("property", WikidataProperties.DEPICTS.getPropertyName());
|
||||
mainSnak.add("datavalue", dataValue);
|
||||
|
||||
final JsonObject claim = new JsonObject();
|
||||
|
|
@ -183,131 +110,119 @@ public class WikidataEditService {
|
|||
final JsonObject jsonData = new JsonObject();
|
||||
jsonData.add("claims", claims);
|
||||
|
||||
final String data = 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());
|
||||
});
|
||||
return jsonData.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
|
||||
*/
|
||||
private void showSuccessToast(String wikiItemName) {
|
||||
String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
|
||||
String successMessage = String.format(Locale.getDefault(), successStringTemplate, wikiItemName);
|
||||
private void showSuccessToast(final String wikiItemName) {
|
||||
final String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
|
||||
final String successMessage = String.format(Locale.getDefault(), successStringTemplate, wikiItemName);
|
||||
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
|
||||
*
|
||||
* @param fileEntityId
|
||||
* @param caption
|
||||
* @return
|
||||
*/
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private void wikidataAddLabels(final String fileEntityId, final Map<String, String> caption) {
|
||||
Observable.fromCallable(() -> {
|
||||
try {
|
||||
return csrfTokenClient.getTokenBlocking();
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
private Observable<Boolean> addCaption(final long fileEntityId, final String languageCode,
|
||||
final String captionValue) {
|
||||
return wikiBaseClient.addLabelstoWikidata(fileEntityId, languageCode, captionValue)
|
||||
.doOnNext(mwPostResponse -> onAddCaptionResponse(fileEntityId, mwPostResponse) )
|
||||
.doOnError(throwable -> {
|
||||
Timber.e(throwable, "Error occurred while setting Captions");
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(editToken -> {
|
||||
if (editToken != null) {
|
||||
Observable.fromCallable(() -> captionInterface.addLabelstoWikidata(fileEntityId, editToken, caption.get(0), caption))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(revisionId ->
|
||||
{
|
||||
if (revisionId != null) {
|
||||
Timber.d("Caption successfully set, revision id = %s", revisionId);
|
||||
.map(mwPostResponse -> mwPostResponse != null);
|
||||
}
|
||||
|
||||
private void onAddCaptionResponse(Long fileEntityId, MwPostResponse response) {
|
||||
if (response != null) {
|
||||
Timber.d("Caption successfully set, revision id = %s", response);
|
||||
} else {
|
||||
Timber.d("Error occurred while setting Captions, fileEntityId = %s", fileEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
throwable -> {
|
||||
Timber.e(throwable, "Error occurred while setting Captions");
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleImageClaimResult(final WikidataItem wikidataItem, final String revisionId) {
|
||||
if (revisionId != null) {
|
||||
if (wikidataEditListener != null) {
|
||||
wikidataEditListener.onSuccessfulWikidataEdit();
|
||||
}
|
||||
showSuccessToast(wikidataItem.getName());
|
||||
} else {
|
||||
Timber.d("Error acquiring EntityId for image");
|
||||
}
|
||||
}, throwable -> {
|
||||
Timber.e(throwable, "Error occurred while getting EntityID for the File");
|
||||
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
|
||||
*/
|
||||
"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(
|
||||
val id: String,
|
||||
val pageid: String,
|
||||
val url: String,
|
||||
val label: String?,
|
||||
val label: String,
|
||||
val description: String?
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,22 +2,19 @@ package fr.free.nrw.commons.delete
|
|||
|
||||
import android.content.Context
|
||||
import com.nhaarman.mockitokotlin2.eq
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.Media
|
||||
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 org.junit.Assert.*
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.MockitoAnnotations
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
/**
|
||||
* Tests for delete helper
|
||||
|
|
@ -25,23 +22,15 @@ import javax.inject.Named
|
|||
class DeleteHelperTest {
|
||||
|
||||
@Mock
|
||||
@field:[Inject Named("commons-page-edit")]
|
||||
internal var pageEditClient: PageEditClient? = null
|
||||
internal lateinit var pageEditClient: PageEditClient
|
||||
|
||||
@Mock
|
||||
internal var context: Context? = null
|
||||
internal lateinit var context: Context
|
||||
|
||||
@Mock
|
||||
internal var notificationHelper: NotificationHelper? = null
|
||||
internal lateinit var media: Media
|
||||
|
||||
@Mock
|
||||
internal var viewUtil: ViewUtilWrapper? = null
|
||||
|
||||
@Mock
|
||||
internal var media: Media? = null
|
||||
|
||||
@InjectMocks
|
||||
var deleteHelper: DeleteHelper? = null
|
||||
lateinit var deleteHelper: DeleteHelper
|
||||
|
||||
/**
|
||||
* Init mocks for test
|
||||
|
|
@ -49,6 +38,7 @@ class DeleteHelperTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
deleteHelper = DeleteHelper(mock(), pageEditClient, mock(), "")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,23 +46,23 @@ class DeleteHelperTest {
|
|||
*/
|
||||
@Test
|
||||
fun makeDeletion() {
|
||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.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))
|
||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.thenReturn(Observable.just(true))
|
||||
|
||||
`when`(media?.displayTitle).thenReturn("Test file")
|
||||
media?.filename="Test file.jpg"
|
||||
whenever(media.displayTitle).thenReturn("Test file")
|
||||
|
||||
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)
|
||||
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)
|
||||
fun makeDeletionForPrependEditFailure() {
|
||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.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))
|
||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.thenReturn(Observable.just(true))
|
||||
`when`(media?.displayTitle).thenReturn("Test file")
|
||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
||||
whenever(media.displayTitle).thenReturn("Test file")
|
||||
whenever(media.filename).thenReturn("Test file.jpg")
|
||||
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)
|
||||
fun makeDeletionForEditFailure() {
|
||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.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))
|
||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.thenReturn(Observable.just(false))
|
||||
`when`(media?.displayTitle).thenReturn("Test file")
|
||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
||||
whenever(media.displayTitle).thenReturn("Test file")
|
||||
whenever(media.filename).thenReturn("Test file.jpg")
|
||||
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)
|
||||
fun makeDeletionForAppendEditFailure() {
|
||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.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))
|
||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.thenReturn(Observable.just(true))
|
||||
`when`(media?.displayTitle).thenReturn("Test file")
|
||||
`when`(media?.filename).thenReturn("Test file.jpg")
|
||||
`when`(media?.creator).thenReturn("Creator (page does not exist)")
|
||||
whenever(media.displayTitle).thenReturn("Test file")
|
||||
whenever(media.filename).thenReturn("Test file.jpg")
|
||||
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)
|
||||
fun makeDeletionForEmptyCreatorName() {
|
||||
`when`(pageEditClient?.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.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))
|
||||
`when`(pageEditClient?.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString()))
|
||||
.thenReturn(Observable.just(true))
|
||||
|
||||
`when`(media?.displayTitle).thenReturn("Test file")
|
||||
media?.filename="Test file.jpg"
|
||||
whenever(media.displayTitle).thenReturn("Test file")
|
||||
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(
|
||||
ArgumentMatchers.any(UploadableFile::class.java),
|
||||
ArgumentMatchers.any(Place::class.java),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.any(UploadMediaPresenter::class.java)
|
||||
)
|
||||
).thenReturn(testObservableUploadItem)
|
||||
uploadMediaPresenter.receiveImage(uploadableFile, ArgumentMatchers.anyString(), place)
|
||||
uploadMediaPresenter.receiveImage(uploadableFile, place)
|
||||
verify(view).showProgress(true)
|
||||
testScheduler.triggerActions()
|
||||
verify(view).onImageProcessed(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
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.WbCreateClaimResponse
|
||||
import io.reactivex.Observable
|
||||
import okhttp3.RequestBody
|
||||
import org.junit.Before
|
||||
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.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
|
|
@ -37,14 +37,18 @@ class WikidataClientTest {
|
|||
|
||||
@Test
|
||||
fun createClaim() {
|
||||
`when`(wikidataInterface!!.postCreateClaim(any(RequestBody::class.java),
|
||||
any(RequestBody::class.java),
|
||||
any(RequestBody::class.java),
|
||||
any(RequestBody::class.java),
|
||||
any(RequestBody::class.java),
|
||||
any(RequestBody::class.java)))
|
||||
.thenReturn(Observable.just(mock(WbCreateClaimResponse::class.java)))
|
||||
wikidataClient!!.createClaim("Q1", "test.jpg")
|
||||
`when`(
|
||||
wikidataInterface!!.postCreateClaim(
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
any()
|
||||
)
|
||||
)
|
||||
.thenReturn(Observable.just(mock()))
|
||||
wikidataClient!!.createImageClaim(mock(), "test.jpg")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package fr.free.nrw.commons.wikidata
|
||||
|
||||
import android.content.Context
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
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 io.reactivex.Observable
|
||||
import org.junit.Before
|
||||
|
|
@ -37,42 +40,27 @@ class WikidataEditServiceTest {
|
|||
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
|
||||
fun noClaimsWhenLocationIsNotCorrect() {
|
||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||
.thenReturn(false)
|
||||
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
|
||||
wikidataEditService.createImageClaim(mock(), mock())
|
||||
verifyZeroInteractions(wikidataClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createClaimWithLogging() {
|
||||
fun createImageClaim() {
|
||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||
.thenReturn(true)
|
||||
whenever(wikidataClient.createClaim(anyString(), anyString()))
|
||||
whenever(wikidataClient.createImageClaim(any(), any()))
|
||||
.thenReturn(Observable.just(1L))
|
||||
whenever(wikidataClient.addEditTag(anyLong(), anyString(), anyString()))
|
||||
.thenReturn(Observable.just(mock(AddEditTagResponse::class.java)))
|
||||
whenever(wikibaseClient.getFileEntityId(any())).thenReturn(Observable.just(1L))
|
||||
wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "")
|
||||
verify(wikidataClient, times(1)).createClaim(anyString(), anyString())
|
||||
val wikidataPlace:WikidataPlace = mock()
|
||||
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