From 23b8c2e6594131a3e59ad44ba021ad29061b0655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A1n=20Mac=20Gillicuddy?= Date: Wed, 25 Mar 2020 10:42:29 +0000 Subject: [PATCH] #3529 Captions/depictions are not saved to Commons (#3574) * #3529 Captions/depictions are not saved to Commons - make copy of list of depictionEntityIds - uncomment editBaseDepictsProperty - refactor upload related classes * #3529 Captions/depictions are not saved to Commons - fix wrong ArrayList usage * #3529 Captions/depictions are not saved to Commons - fix test --- .../main/java/fr/free/nrw/commons/Media.java | 27 ++- .../commons/contributions/Contribution.java | 26 ++- .../free/nrw/commons/di/NetworkingModule.java | 31 ++-- .../nrw/commons/upload/UploadController.java | 163 ++++++++---------- .../nrw/commons/upload/UploadMediaDetail.java | 117 ------------- .../nrw/commons/upload/UploadMediaDetail.kt | 66 +++++++ .../upload/UploadMediaDetailAdapter.java | 41 +---- .../free/nrw/commons/upload/UploadModel.java | 139 +++++++-------- .../nrw/commons/upload/UploadService.java | 101 ++++++----- .../UploadMediaDetailFragment.java | 24 +-- .../commons/wikidata/WikidataEditService.java | 67 +++---- .../wikidata/WikidataEditServiceTest.kt | 62 +++---- 12 files changed, 360 insertions(+), 504 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.java create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java index 773e9d615..a989d782e 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -3,18 +3,12 @@ package fr.free.nrw.commons; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.room.Entity; -import androidx.room.PrimaryKey; - -import org.apache.commons.lang3.StringUtils; -import org.wikipedia.dataclient.mwapi.MwQueryPage; -import org.wikipedia.gallery.ExtMetadata; -import org.wikipedia.gallery.ImageInfo; -import org.wikipedia.page.PageTitle; - +import fr.free.nrw.commons.location.LatLng; +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; @@ -23,10 +17,11 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; - -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.utils.CommonsDateUtil; -import fr.free.nrw.commons.utils.MediaDataExtractorUtil; +import org.apache.commons.lang3.StringUtils; +import org.wikipedia.dataclient.mwapi.MwQueryPage; +import org.wikipedia.gallery.ExtMetadata; +import org.wikipedia.gallery.ImageInfo; +import org.wikipedia.page.PageTitle; @Entity public class Media implements Parcelable { @@ -90,7 +85,7 @@ public class Media implements Parcelable { * Ex: key = "en", value: "" * key = "de" , value: "" */ - public HashMap captions; + public Map captions; public HashMap tags = new HashMap<>(); @Nullable public LatLng coordinates; @@ -126,7 +121,7 @@ public class Media implements Parcelable { * @param dateUploaded Media date uploaded * @param creator Media creator */ - public Media(Uri localUri, String imageUrl, String filename, HashMap captions, String description, + public Media(Uri localUri, String imageUrl, String filename, Map captions, String description, long dataLength, Date dateCreated, Date dateUploaded, String creator) { this(); this.localUri = localUri; @@ -395,7 +390,7 @@ public class Media implements Parcelable { * * returns list of captions stored in hashmap */ - public HashMap getCaptions() { + public Map getCaptions() { return captions; } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java index 3b720344b..d9fb42856 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java @@ -1,29 +1,25 @@ 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 org.apache.commons.lang3.StringUtils; - -import java.lang.annotation.Retention; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; - 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 static java.lang.annotation.RetentionPolicy.SOURCE; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; @Entity(tableName = "contribution") public class Contribution extends Media { @@ -83,7 +79,7 @@ public class Contribution extends Media { public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date dateCreated, int state, long dataLength, Date dateUploaded, long transferred, - String source, HashMap captions, String description, String creator, boolean isMultiple, + String source, Map 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; @@ -97,7 +93,7 @@ public class Contribution extends Media { this.dateCreatedSource = ""; } - public Contribution(Uri localUri, String imageUrl, String filename, HashMap captions, String description, long dataLength, + public Contribution(Uri localUri, String imageUrl, String filename, Map captions, String description, long dataLength, Date dateCreated, Date dateUploaded, String creator, String editSummary, ArrayList depictionsEntityIds, String decimalCoords) { super(localUri, imageUrl, filename, captions, description, dataLength, dateCreated, dateUploaded, creator); this.decimalCoords = decimalCoords; @@ -106,7 +102,7 @@ public class Contribution extends Media { this.depictionsEntityIds = depictionsEntityIds; } - public Contribution(Uri localUri, String imageUrl, String filename, HashMap captions, String description, long dataLength, + public Contribution(Uri localUri, String imageUrl, String filename, Map 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; diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java index f85c69c36..af9111dc5 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -1,24 +1,8 @@ package fr.free.nrw.commons.di; import android.content.Context; - import androidx.annotation.NonNull; - import com.google.gson.Gson; - -import org.wikipedia.csrf.CsrfTokenClient; -import org.wikipedia.dataclient.Service; -import org.wikipedia.dataclient.ServiceFactory; -import org.wikipedia.dataclient.WikiSite; -import org.wikipedia.json.GsonUtil; -import org.wikipedia.login.LoginClient; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -import javax.inject.Named; -import javax.inject.Singleton; - import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.BuildConfig; @@ -32,14 +16,25 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.UserInterface; import fr.free.nrw.commons.review.ReviewInterface; import fr.free.nrw.commons.upload.UploadInterface; -import fr.free.nrw.commons.wikidata.WikidataInterface; 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; +import javax.inject.Named; +import javax.inject.Singleton; import okhttp3.Cache; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import org.wikipedia.csrf.CsrfTokenClient; +import org.wikipedia.dataclient.Service; +import org.wikipedia.dataclient.ServiceFactory; +import org.wikipedia.dataclient.WikiSite; +import org.wikipedia.json.GsonUtil; +import org.wikipedia.login.LoginClient; import timber.log.Timber; @Module @@ -76,7 +71,7 @@ public class NetworkingModule { HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(message -> { Timber.tag("OkHttp").v(message); }); - httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + httpLoggingInterceptor.level(BuildConfig.DEBUG ? Level.BODY: HttpLoggingInterceptor.Level.BASIC); return httpLoggingInterceptor; } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java index cb72c80a9..1a5f5ae81 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java @@ -13,17 +13,8 @@ import android.net.Uri; import android.os.IBinder; import android.provider.MediaStore; import android.text.TextUtils; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; - -import javax.inject.Inject; -import javax.inject.Singleton; - import fr.free.nrw.commons.HandlerService; +import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.Contribution; @@ -34,23 +25,26 @@ import io.reactivex.Single; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import javax.inject.Inject; +import javax.inject.Singleton; import timber.log.Timber; @Singleton public class UploadController { private UploadService uploadService; - private SessionManager sessionManager; - private Context context; - private JsonKvStore store; - - public interface ContributionUploadProgress { - void onUploadStarted(Contribution contribution); - } + private final SessionManager sessionManager; + private final Context context; + private final JsonKvStore store; @Inject - public UploadController(SessionManager sessionManager, - Context context, - JsonKvStore store) { + public UploadController(final SessionManager sessionManager, + final Context context, + final JsonKvStore store) { this.sessionManager = sessionManager; this.context = context; this.store = store; @@ -59,13 +53,13 @@ public class UploadController { private boolean isUploadServiceConnected; public ServiceConnection uploadServiceConnection = new ServiceConnection() { @Override - public void onServiceConnected(ComponentName componentName, IBinder binder) { + public void onServiceConnected(final ComponentName componentName, final IBinder binder) { uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder).getService(); isUploadServiceConnected = true; } @Override - public void onServiceDisconnected(ComponentName componentName) { + public void onServiceDisconnected(final ComponentName componentName) { // this should never happen isUploadServiceConnected = false; Timber.e(new RuntimeException("UploadService died but the rest of the process did not!")); @@ -76,7 +70,7 @@ public class UploadController { * Prepares the upload service. */ public void prepareService() { - Intent uploadServiceIntent = new Intent(context, UploadService.class); + final Intent uploadServiceIntent = new Intent(context, UploadService.class); uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE); context.startService(uploadServiceIntent); context.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE); @@ -96,28 +90,18 @@ public class UploadController { * * @param contribution the contribution object */ - public void startUpload(Contribution contribution) { - startUpload(contribution, c -> {}); - } - - /** - * Starts a new upload task. - * - * @param contribution the contribution object - * @param onComplete the progress tracker - */ @SuppressLint("StaticFieldLeak") - private void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) { + public void startUpload(final Contribution contribution) { //Set creator, desc, and license // If author name is enabled and set, use it if (store.getBoolean("useAuthorName", false)) { - String authorName = store.getString("authorName", ""); + final String authorName = store.getString("authorName", ""); contribution.setCreator(authorName); } if (TextUtils.isEmpty(contribution.getCreator())) { - Account currentAccount = sessionManager.getCurrentAccount(); + final Account currentAccount = sessionManager.getCurrentAccount(); if (currentAccount == null) { Timber.d("Current account is null"); ViewUtil.showLongToast(context, context.getString(R.string.user_not_logged_in)); @@ -135,23 +119,23 @@ public class UploadController { contribution.setCaption(""); } - String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); + final String license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); contribution.setLicense(license); - uploadTask(contribution, onComplete); + uploadTask(contribution); } /** * Initiates the upload task * @param contribution - * @param onComplete * @return */ - private Disposable uploadTask(Contribution contribution, ContributionUploadProgress onComplete) { - return Single.fromCallable(() -> makeUpload(contribution)) + private Disposable uploadTask(final Contribution contribution) { + return Single.just(contribution) + .map(this::buildUpload) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(finalContribution -> onUploadCompleted(finalContribution, onComplete)); + .subscribe(this::upload); } /** @@ -159,71 +143,76 @@ public class UploadController { * @param contribution * @return */ - private Contribution makeUpload(Contribution contribution) { - long length; - ContentResolver contentResolver = context.getContentResolver(); + private Contribution buildUpload(final Contribution contribution) { + final ContentResolver contentResolver = context.getContentResolver(); + + contribution.setDataLength(resolveDataLength(contentResolver, contribution)); + + final String mimeType = resolveMimeType(contentResolver, contribution); + + if (mimeType != null) { + Timber.d("MimeType is: %s", mimeType); + contribution.setTag("mimeType", mimeType); + if(mimeType.startsWith("image/") && contribution.getDateCreated() == null){ + contribution.setDateCreated(resolveDateTakenOrNow(contentResolver, contribution)); + } + } + + return contribution; + } + + private String resolveMimeType(final ContentResolver contentResolver, final Contribution contribution) { + final String mimeType = (String) contribution.getTag("mimeType"); + if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) { + return contentResolver.getType(contribution.getLocalUri()); + } + return mimeType; + } + + private long resolveDataLength(final ContentResolver contentResolver, final Media contribution) { try { if (contribution.getDataLength() <= 0) { Timber.d("UploadController/doInBackground, contribution.getLocalUri():%s", contribution.getLocalUri()); - AssetFileDescriptor assetFileDescriptor = contentResolver - .openAssetFileDescriptor(Uri.fromFile(new File(contribution.getLocalUri().getPath())), "r"); + final AssetFileDescriptor assetFileDescriptor = contentResolver + .openAssetFileDescriptor(Uri.fromFile(new File(contribution.getLocalUri().getPath())), "r"); if (assetFileDescriptor != null) { - length = assetFileDescriptor.getLength(); - if (length == -1) { - // Let us find out the long way! - length = countBytes(contentResolver - .openInputStream(contribution.getLocalUri())); - } - contribution.setDataLength(length); + final long length = assetFileDescriptor.getLength(); + return length != -1 ? length + : countBytes(contentResolver.openInputStream(contribution.getLocalUri())); } } - } catch (IOException | NullPointerException | SecurityException e) { + } catch (final IOException | NullPointerException | SecurityException e) { Timber.e(e, "Exception occurred while uploading image"); } + return contribution.getDataLength(); + } - String mimeType = (String) contribution.getTag("mimeType"); - boolean imagePrefix = false; - - if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) { - mimeType = contentResolver.getType(contribution.getLocalUri()); - } - - if (mimeType != null) { - contribution.setTag("mimeType", mimeType); - imagePrefix = mimeType.startsWith("image/"); - Timber.d("MimeType is: %s", mimeType); - } - - if (imagePrefix && contribution.getDateCreated() == null) { - Timber.d("local uri %s", contribution.getLocalUri()); - Cursor cursor = contentResolver.query(contribution.getLocalUri(), - new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null); + private Date resolveDateTakenOrNow(final ContentResolver contentResolver, final Media contribution) { + Timber.d("local uri %s", contribution.getLocalUri()); + try(final Cursor cursor = dateTakenCursor(contentResolver, contribution)) { if (cursor != null && cursor.getCount() != 0 && cursor.getColumnCount() != 0) { cursor.moveToFirst(); - Date dateCreated = new Date(cursor.getLong(0)); - Date epochStart = new Date(0); - if (dateCreated.equals(epochStart) || dateCreated.before(epochStart)) { - // If date is incorrect (1st second of unix time) then set it to the current date - dateCreated = new Date(); + final Date dateCreated = new Date(cursor.getLong(0)); + if (dateCreated.after(new Date(0))) { + return dateCreated; } - contribution.setDateCreated(dateCreated); - cursor.close(); - } else { - contribution.setDateCreated(new Date()); } + return new Date(); } - return contribution; + } + + private Cursor dateTakenCursor(final ContentResolver contentResolver, final Media contribution) { + return contentResolver.query(contribution.getLocalUri(), + new String[]{MediaStore.Images.ImageColumns.DATE_TAKEN}, null, null, null); } /** * When the contribution object is completely formed, the item is queued to the upload service * @param contribution - * @param onComplete */ - private void onUploadCompleted(Contribution contribution, ContributionUploadProgress onComplete) { + private void upload(final Contribution contribution) { //Starts the upload. If commented out, user can proceed to next Fragment but upload doesn't happen uploadService.queue(UploadService.ACTION_UPLOAD_FILE, contribution); - onComplete.onUploadStarted(contribution); } @@ -234,9 +223,9 @@ public class UploadController { * @return the number of bytes in {@code stream} * @throws IOException if an I/O error occurs */ - private long countBytes(InputStream stream) throws IOException { + private long countBytes(final InputStream stream) throws IOException { long count = 0; - BufferedInputStream bis = new BufferedInputStream(stream); + final BufferedInputStream bis = new BufferedInputStream(stream); while (bis.read() != -1) { count++; } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.java deleted file mode 100644 index e54720b2a..000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.java +++ /dev/null @@ -1,117 +0,0 @@ -package fr.free.nrw.commons.upload; - -import android.util.Log; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import timber.log.Timber; - -/** - * Holds a description of an item being uploaded by {@link UploadActivity} - */ -public class UploadMediaDetail { - - private String languageCode; - private String descriptionText; - public String captionText; - private int selectedLanguageIndex = -1; - private boolean isManuallyAdded=false; - - /** - * Formatting captions to the Wikibase format for sending labels - * @param uploadMediaDetails list of media Details - */ - - public static HashMap formatCaptions(List uploadMediaDetails) { - HashMap caption = new HashMap<>(); - for (UploadMediaDetail uploadMediaDetail : uploadMediaDetails) { - caption.put(uploadMediaDetail.getLanguageCode(),uploadMediaDetail.getCaptionText()); - } - return caption; - } - - public String getCaptionText() { - return captionText; - } - - public void setCaptionText(String captionText) { - this.captionText = captionText; - } - - /** - * @return The language code ie. "en" or "fr" - */ - String getLanguageCode() { - return languageCode; - } - - /** - * @param languageCode The language code ie. "en" or "fr" - */ - public void setLanguageCode(String languageCode) { - this.languageCode = languageCode; - } - - String getDescriptionText() { - return descriptionText; - } - - public void setDescriptionText(String descriptionText) { - this.descriptionText = descriptionText; - } - - /** - * @return the index of the language selected in a spinner with {@link SpinnerLanguagesAdapter} - */ - int getSelectedLanguageIndex() { - return selectedLanguageIndex; - } - - /** - * @param selectedLanguageIndex the index of the language selected in a spinner with {@link SpinnerLanguagesAdapter} - */ - void setSelectedLanguageIndex(int selectedLanguageIndex) { - this.selectedLanguageIndex = selectedLanguageIndex; - } - - /** - * returns if the description was added manually (by the user, or we have added it programaticallly) - * @return - */ - public boolean isManuallyAdded() { - return isManuallyAdded; - } - - /** - * sets to true if the description was manually added by the user - * @param manuallyAdded - */ - public void setManuallyAdded(boolean manuallyAdded) { - isManuallyAdded = manuallyAdded; - } - - /** - * Formats the list of descriptions into the format Commons requires for uploads. - * - * @param descriptions the list of descriptions, description is ignored if text is null. - * @return a string with the pattern of {{en|1=descriptionText}} - */ - static String formatList(List descriptions) { - StringBuilder descListString = new StringBuilder(); - for (UploadMediaDetail description : descriptions) { - if (!description.isEmpty()) { - String individualDescription = String.format("{{%s|1=%s}}", description.getLanguageCode(), - description.getDescriptionText()); - descListString.append(individualDescription); - } - } - return descListString.toString(); - } - - public boolean isEmpty() { - return descriptionText == null || descriptionText.isEmpty(); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt new file mode 100644 index 000000000..08ad2b841 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt @@ -0,0 +1,66 @@ +package fr.free.nrw.commons.upload + +import fr.free.nrw.commons.nearby.Place +import java.util.* + +/** + * Holds a description of an item being uploaded by [UploadActivity] + */ +data class UploadMediaDetail constructor( + /** + * @return The language code ie. "en" or "fr" + */ + /** + * @param languageCode The language code ie. "en" or "fr" + */ + var languageCode: String? = null, + var descriptionText: String = "", + var captionText: String = "" +) { + constructor(place: Place) : this( + Locale.getDefault().language, + place.longDescription, + place.name + ) + /** + * @return the index of the language selected in a spinner with [SpinnerLanguagesAdapter] + */ + /** + * @param selectedLanguageIndex the index of the language selected in a spinner with [SpinnerLanguagesAdapter] + */ + var selectedLanguageIndex: Int = -1 + /** + * returns if the description was added manually (by the user, or we have added it programaticallly) + * @return + */ + /** + * sets to true if the description was manually added by the user + * @param manuallyAdded + */ + var isManuallyAdded: Boolean = false + + companion object { + /** + * Formatting captions to the Wikibase format for sending labels + * @param uploadMediaDetails list of media Details + */ + @JvmStatic + fun formatCaptions(uploadMediaDetails: List) = + uploadMediaDetails.associate { it.languageCode to it.captionText } + + /** + * Formats the list of descriptions into the format Commons requires for uploads. + * + * @param descriptions the list of descriptions, description is ignored if text is null. + * @return a string with the pattern of {{en|1=descriptionText}} + */ + @JvmStatic + fun formatList(descriptions: List) = + descriptions.joinToString { + if (it.descriptionText.isNotEmpty()) + "{{${it.languageCode}|1=${it.descriptionText}}}" + else + "" + } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java index 26461aa4c..e1324cbd3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetailAdapter.java @@ -2,9 +2,7 @@ package fr.free.nrw.commons.upload; import android.content.Context; import android.graphics.drawable.Drawable; -import android.text.Editable; import android.text.TextUtils; -import android.text.TextWatcher; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -107,36 +105,11 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter eventListener.onEvent(value.length() != 0)) ); if (position == 0) { captionItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, getInfoIcon(), @@ -174,13 +147,11 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter uploadMediaDetails.get(position) - .setCaptionText(captionText))); + captionText -> uploadMediaDetails.get(position).setCaptionText(captionText))); initLanguageSpinner(position, uploadMediaDetail); descItemEditText.addTextChangedListener(new AbstractTextWatcher( - descriptionText -> uploadMediaDetails.get(position) - .setDescriptionText(descriptionText))); + descriptionText -> uploadMediaDetails.get(position).setDescriptionText(descriptionText))); initLanguageSpinner(position, uploadMediaDetail); //If the description was manually added by the user, it deserves focus, if not, let the user decide diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index a87277507..0f84da7fb 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -7,7 +7,6 @@ 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.MimeTypeMapWrapper; import fr.free.nrw.commons.filepicker.UploadableFile; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.nearby.Place; @@ -18,15 +17,17 @@ import io.reactivex.Single; 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.Locale; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import org.jetbrains.annotations.NotNull; import timber.log.Timber; @Singleton @@ -37,23 +38,23 @@ public class UploadModel { private final Context context; private String license; private final Map licensesByName; - private List items = new ArrayList<>(); - private CompositeDisposable compositeDisposable = new CompositeDisposable(); + private final List items = new ArrayList<>(); + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); - private SessionManager sessionManager; - private FileProcessor fileProcessor; + private final SessionManager sessionManager; + private final FileProcessor fileProcessor; private final ImageProcessingService imageProcessingService; private List selectedCategories; private ArrayList selectedDepictions; @Inject - UploadModel(@Named("licenses") List licenses, - @Named("default_preferences") JsonKvStore store, - @Named("licenses_by_name") Map licensesByName, - Context context, - SessionManager sessionManager, - FileProcessor fileProcessor, - ImageProcessingService imageProcessingService) { + UploadModel(@Named("licenses") final List licenses, + @Named("default_preferences") final JsonKvStore store, + @Named("licenses_by_name") final Map licensesByName, + final Context context, + final SessionManager sessionManager, + final FileProcessor fileProcessor, + final ImageProcessingService imageProcessingService) { this.licenses = licenses; this.store = store; this.license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); @@ -80,31 +81,29 @@ public class UploadModel { } public void setSelectedCategories(List selectedCategories) { - if (null == selectedCategories) { - selectedCategories = new ArrayList<>(); - } - this.selectedCategories = selectedCategories; + this.selectedCategories = newListOf(selectedCategories); } /** * pre process a one item at a time */ - public Observable preProcessImage(UploadableFile uploadableFile, - Place place, - String source, - SimilarImageInterface similarImageInterface) { - return Observable.just(getUploadItem(uploadableFile, place, source, similarImageInterface)); + public Observable preProcessImage(final UploadableFile uploadableFile, + final Place place, + final String source, + final SimilarImageInterface similarImageInterface) { + return Observable.just( + createAndAddUploadItem(uploadableFile, place, source, similarImageInterface)); } - public Single getImageQuality(UploadItem uploadItem) { + public Single getImageQuality(final UploadItem uploadItem) { return imageProcessingService.validateImage(uploadItem); } - private UploadItem getUploadItem(UploadableFile uploadableFile, - Place place, - String source, - SimilarImageInterface similarImageInterface) { - UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile + private UploadItem createAndAddUploadItem(final UploadableFile uploadableFile, + final Place place, + final String source, + final SimilarImageInterface similarImageInterface) { + final UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile .getFileCreatedDate(context); long fileCreatedDate = -1; String createdTimestampSource = ""; @@ -113,23 +112,15 @@ public class UploadModel { createdTimestampSource = dateTimeWithSource.getSource(); } Timber.d("File created date is %d", fileCreatedDate); - ImageCoordinates imageCoordinates = fileProcessor + final ImageCoordinates imageCoordinates = fileProcessor .processFileCoordinates(similarImageInterface, uploadableFile.getFilePath()); - UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(), + final UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(), Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, imageCoordinates, place, fileCreatedDate, createdTimestampSource); if (place != null) { uploadItem.title.setTitleText(place.name); - if(uploadItem.uploadMediaDetails.isEmpty()) { - uploadItem.uploadMediaDetails.add(new UploadMediaDetail()); - } - uploadItem.uploadMediaDetails.get(0).setDescriptionText(place.getLongDescription()); - uploadItem.uploadMediaDetails.get(0).setLanguageCode("en"); - String languageCode = Locale.getDefault().getLanguage(); - uploadItem.uploadMediaDetails.get(0).setDescriptionText(place.getLongDescription()); - uploadItem.uploadMediaDetails.get(0).setLanguageCode(languageCode); - uploadItem.uploadMediaDetails.get(0).setCaptionText(place.name); + uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place)); } if (!items.contains(uploadItem)) { items.add(uploadItem); @@ -153,7 +144,7 @@ public class UploadModel { return license; } - public void setSelectedLicense(String licenseName) { + public void setSelectedLicense(final String licenseName) { this.license = licensesByName.get(licenseName); store.putString(Prefs.DEFAULT_LICENSE, license); } @@ -161,11 +152,11 @@ public class UploadModel { public Observable buildContributions() { return Observable.fromIterable(items).map(item -> { - Contribution contribution = new Contribution(item.mediaUri, null, + 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, selectedDepictions, item.gpsCoords.getDecimalCoords()); + CommonsApplication.DEFAULT_EDIT_SUMMARY, new ArrayList<>(selectedDepictions), item.gpsCoords.getDecimalCoords()); if (item.place != null) { contribution.setWikiDataEntityId(item.place.getWikiDataEntityId()); contribution.setWikiItemName(item.place.getName()); @@ -196,8 +187,8 @@ public class UploadModel { }); } - public void deletePicture(String filePath) { - Iterator iterator = items.iterator(); + public void deletePicture(final String filePath) { + final Iterator iterator = items.iterator(); while (iterator.hasNext()) { if (iterator.next().mediaUri.toString().contains(filePath)) { iterator.remove(); @@ -213,20 +204,22 @@ public class UploadModel { return items; } - public void updateUploadItem(int index, UploadItem uploadItem) { - UploadItem uploadItem1 = items.get(index); + public void updateUploadItem(final int index, final UploadItem uploadItem) { + final UploadItem uploadItem1 = items.get(index); uploadItem1.setMediaDetails(uploadItem.uploadMediaDetails); uploadItem1.setTitle(uploadItem.title); } - public void setSelectedDepictions(List selectedDepictions) { - if (null == selectedDepictions) { - selectedDepictions = new ArrayList<>(); - } - this.selectedDepictions = (ArrayList) selectedDepictions; + public void setSelectedDepictions(final List selectedDepictions) { + this.selectedDepictions = newListOf(selectedDepictions); } - public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) { + @NotNull + private ArrayList newListOf(final List items) { + return items != null ? new ArrayList<>(items) : new ArrayList<>(); + } + + public void useSimilarPictureCoordinates(final ImageCoordinates imageCoordinates, final int uploadItemIndex) { fileProcessor.useImageCoords(imageCoordinates); items.get(uploadItemIndex).setGpsCoords(imageCoordinates); } @@ -239,29 +232,23 @@ public class UploadModel { private final String mimeType; private final String source; private ImageCoordinates gpsCoords; - - public void setGpsCoords(ImageCoordinates gpsCoords) { - this.gpsCoords = gpsCoords; - } - private Title title; private List uploadMediaDetails; - private Place place; - private long createdTimestamp; - private String createdTimestampSource; - private BehaviorSubject imageQuality; - + private final Place place; + private final long createdTimestamp; + private final String createdTimestampSource; + private final BehaviorSubject imageQuality; @SuppressLint("CheckResult") - UploadItem(Uri originalContentUri, - Uri mediaUri, String mimeType, String source, ImageCoordinates gpsCoords, - Place place, - long createdTimestamp, - String createdTimestampSource) { + UploadItem(final Uri originalContentUri, + final Uri mediaUri, final String mimeType, final String source, final ImageCoordinates gpsCoords, + final Place place, + final long createdTimestamp, + final String createdTimestampSource) { this.originalContentUri = originalContentUri; this.createdTimestampSource = createdTimestampSource; title = new Title(); - uploadMediaDetails = new ArrayList<>(); - uploadMediaDetails.add(new UploadMediaDetail()); + uploadMediaDetails = Collections.singletonList(new UploadMediaDetail()); + uploadMediaDetails = new ArrayList<>(Arrays.asList(new UploadMediaDetail())); this.place = place; this.mediaUri = mediaUri; this.mimeType = mimeType; @@ -303,7 +290,7 @@ public class UploadModel { return this.imageQuality.getValue(); } - public void setImageQuality(int imageQuality) { + public void setImageQuality(final int imageQuality) { this.imageQuality.onNext(imageQuality); } @@ -311,12 +298,12 @@ public class UploadModel { return place; } - public void setTitle(Title title) { + public void setTitle(final Title title) { this.title = title; } - public void setMediaDetails(List uploadMediaDetails) { + public void setMediaDetails(final List uploadMediaDetails) { this.uploadMediaDetails = uploadMediaDetails; } @@ -325,7 +312,7 @@ public class UploadModel { } @Override - public boolean equals(@Nullable Object obj) { + public boolean equals(@Nullable final Object obj) { if (!(obj instanceof UploadItem)) { return false; } @@ -345,6 +332,10 @@ public class UploadModel { public String getFileName() { return uploadMediaDetails.get(0).getCaptionText(); } - } + public void setGpsCoords(final ImageCoordinates gpsCoords) { + this.gpsCoords = gpsCoords; + } + + } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java index 0378f7bda..09e13eb9a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java @@ -13,6 +13,7 @@ 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; @@ -35,18 +36,8 @@ import fr.free.nrw.commons.utils.CommonsDateUtil; import fr.free.nrw.commons.wikidata.WikidataEditService; import io.reactivex.Observable; import io.reactivex.Scheduler; -import io.reactivex.SingleObserver; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.inject.Inject; import timber.log.Timber; public class UploadService extends HandlerService { @@ -275,55 +266,59 @@ public class UploadService extends HandlerService { uploadStash.getFilekey()); } }) - .subscribe(uploadResult -> { - Timber.d("Stash upload response 2 is %s", uploadResult.toString()); - - notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS); - - String resultStatus = uploadResult.getResult(); - if (!resultStatus.equals("Success")) { - 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( - wikiDataEntityId, canonicalFilename, contribution.getCaptions()); - contribution.setFilename(canonicalFilename); - contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl()); - contribution.setState(Contribution.STATE_COMPLETED); - contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatShort() - .parse(uploadResult.getImageinfo().getTimestamp())); - compositeDisposable.add(contributionDao - .save(contribution) - .subscribeOn(ioThreadScheduler) - .observeOn(mainThreadScheduler) - .subscribe()); - } - }, throwable -> { + .subscribe( + uploadResult -> onUpload(contribution, notificationTag, uploadResult), + throwable -> { Timber.w(throwable, "Exception during upload"); notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS); showFailedNotification(contribution); }); } + private void onUpload(Contribution contribution, String notificationTag, + UploadResult uploadResult) throws ParseException { + Timber.d("Stash upload response 2 is %s", uploadResult.toString()); + + notificationManager.cancel(notificationTag, NOTIFICATION_UPLOAD_IN_PROGRESS); + + String resultStatus = uploadResult.getResult(); + if (!resultStatus.equals("Success")) { + 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); + contribution.setImageUrl(uploadResult.getImageinfo().getOriginalUrl()); + contribution.setState(Contribution.STATE_COMPLETED); + contribution.setDateUploaded(CommonsDateUtil.getIso8601DateFormatShort() + .parse(uploadResult.getImageinfo().getTimestamp())); + compositeDisposable.add(contributionDao + .save(contribution) + .subscribeOn(ioThreadScheduler) + .observeOn(mainThreadScheduler) + .subscribe()); + } + } + @SuppressLint("StringFormatInvalid") @SuppressWarnings("deprecation") private void showFailedNotification(Contribution contribution) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java index 5d7a45785..4ab51a3b6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java @@ -32,14 +32,12 @@ import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.settings.Prefs; -import fr.free.nrw.commons.upload.Description; -//import fr.free.nrw.commons.upload.DescriptionsAdapter; -import fr.free.nrw.commons.upload.UploadMediaDetail; -import fr.free.nrw.commons.upload.UploadMediaDetailAdapter; import fr.free.nrw.commons.upload.ImageCoordinates; import fr.free.nrw.commons.upload.SimilarImageDialogFragment; import fr.free.nrw.commons.upload.Title; import fr.free.nrw.commons.upload.UploadBaseFragment; +import fr.free.nrw.commons.upload.UploadMediaDetail; +import fr.free.nrw.commons.upload.UploadMediaDetailAdapter; import fr.free.nrw.commons.upload.UploadModel; import fr.free.nrw.commons.upload.UploadModel.UploadItem; import fr.free.nrw.commons.utils.DialogUtil; @@ -55,6 +53,8 @@ import javax.inject.Named; import org.apache.commons.lang3.StringUtils; import timber.log.Timber; +//import fr.free.nrw.commons.upload.DescriptionsAdapter; + public class UploadMediaDetailFragment extends UploadBaseFragment implements UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener { @@ -282,10 +282,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements @Override public void onImageProcessed(UploadItem uploadItem, Place place) { this.uploadItem = uploadItem; - if (uploadItem.getFileName() != null) { - setDescriptionsInAdapter(uploadItem.getUploadMediaDetails()); - } - descriptions = uploadItem.getUploadMediaDetails(); photoViewBackgroundImage.setImageURI(uploadItem.getMediaUri()); setDescriptionsInAdapter(descriptions); @@ -306,10 +302,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements place.getName()), () -> { etTitle.setText(place.getName()); - UploadMediaDetail description = new UploadMediaDetail(); - description.setLanguageCode("en"); - description.setDescriptionText(place.getLongDescription()); - descriptions = Arrays.asList(description); + descriptions = new ArrayList<>(Arrays.asList(new UploadMediaDetail())); setDescriptionsInAdapter(descriptions); }, () -> { @@ -431,13 +424,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements } private void setDescriptionsInAdapter(List uploadMediaDetails){ - if(uploadMediaDetails==null){ - uploadMediaDetails=new ArrayList<>(); - } - - if(uploadMediaDetails.size()==0){ - uploadMediaDetails.add(new UploadMediaDetail()); - } uploadMediaDetailAdapter.setItems(uploadMediaDetails); } } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java index 873b44da4..bd1a3e995 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -10,7 +10,6 @@ import com.google.gson.JsonObject; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.R; import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.media.MediaClient; import fr.free.nrw.commons.upload.mediaDetails.CaptionInterface; import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -26,7 +25,6 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.wikipedia.csrf.CsrfTokenClient; -import org.wikipedia.dataclient.Service; import timber.log.Timber; /** @@ -46,42 +44,31 @@ public class WikidataEditService { private final CaptionInterface captionInterface; private final WikiBaseClient wikiBaseClient; private final WikidataClient wikidataClient; - private final MediaClient mediaClient; private final CsrfTokenClient csrfTokenClient; - private final Service service; @Inject - public WikidataEditService(Context context, - WikidataEditListener wikidataEditListener, - MediaClient mediaClient, - @Named("default_preferences") JsonKvStore directKvStore, - WikiBaseClient wikiBaseClient, - CaptionInterface captionInterface, - WikidataClient wikidataClient, - @Named("commons-csrf") CsrfTokenClient csrfTokenClient, - @Named("commons-service") Service service) { + 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) { this.context = context; this.wikidataEditListener = wikidataEditListener; this.directKvStore = directKvStore; this.captionInterface = captionInterface; this.wikiBaseClient = wikiBaseClient; - this.mediaClient = mediaClient; this.wikidataClient = wikidataClient; this.csrfTokenClient = csrfTokenClient; - this.service = service; - } + } /** * Create a P18 claim and log the edit with custom tag -<<<<<<< HEAD * - * @param wikidataEntityId - * @param fileName -======= * @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 ->>>>>>> origin/master */ public void createClaimWithLogging(String wikidataEntityId, String wikiItemName, String fileName, String p18Value) { if (wikidataEntityId == null) { @@ -104,8 +91,8 @@ public class WikidataEditService { return; } - editWikidataProperty(wikidataEntityId, wikiItemName, fileName); - //editWikiBaseDepictsProperty(wikidataEntityId, fileName); + editWikidataProperty(wikidataEntityId, wikiItemName, fileName);; + editWikiBaseDepictsProperty(wikidataEntityId, fileName); } @@ -122,7 +109,7 @@ public class WikidataEditService { Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId); Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId); - String propertyValue = getFileName(fileName); + final String propertyValue = getFileName(fileName); Timber.d("Entity id is %s and property value is %s", wikidataEntityId, propertyValue); wikidataClient.createClaim(wikidataEntityId, propertyValue) @@ -148,7 +135,7 @@ public class WikidataEditService { * @param fileName */ @SuppressLint("CheckResult") - private void editWikiBaseDepictsProperty(String wikidataEntityId, String fileName) { + private void editWikiBaseDepictsProperty(final String wikidataEntityId, final String fileName) { wikiBaseClient.getFileEntityId(fileName) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -166,37 +153,37 @@ public class WikidataEditService { } @SuppressLint("CheckResult") - private void addDepictsProperty(String entityId, String fileEntityId) { + private void addDepictsProperty(String entityId, final String fileEntityId) { if (ConfigUtils.isBetaFlavour()) { entityId = "Q10"; // Wikipedia:Sandbox (Q10) } - JsonObject value = new JsonObject(); + final JsonObject value = new JsonObject(); value.addProperty("entity-type", "item"); value.addProperty("numeric-id", entityId.replace("Q", "")); value.addProperty("id", entityId); - JsonObject dataValue = new JsonObject(); + final JsonObject dataValue = new JsonObject(); dataValue.add("value", value); dataValue.addProperty("type", "wikibase-entityid"); - JsonObject mainSnak = new JsonObject(); + final JsonObject mainSnak = new JsonObject(); mainSnak.addProperty("snaktype", "value"); mainSnak.addProperty("property", BuildConfig.DEPICTS_PROPERTY); mainSnak.add("datavalue", dataValue); - JsonObject claim = new JsonObject(); + final JsonObject claim = new JsonObject(); claim.add("mainsnak", mainSnak); claim.addProperty("type", "statement"); claim.addProperty("rank", "preferred"); - JsonArray claims = new JsonArray(); + final JsonArray claims = new JsonArray(); claims.add(claim); - JsonObject jsonData = new JsonObject(); + final JsonObject jsonData = new JsonObject(); jsonData.add("claims", claims); - String data = jsonData.toString(); + final String data = jsonData.toString(); Observable.defer((Callable>) () -> wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, data)) @@ -253,18 +240,19 @@ public class WikidataEditService { * Adding captions as labels after image is successfully uploaded */ @SuppressLint("CheckResult") - public void createLabelforWikidataEntity(String wikiDataEntityId, String fileName, Map captions) { + public void createLabelforWikidataEntity(final String fileName, + final Map captions) { Observable.fromCallable(() -> wikiBaseClient.getFileEntityId(fileName)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(fileEntityId -> { if (fileEntityId != null) { - for (Map.Entry entry : captions.entrySet()) { - Map caption = new HashMap<>(); + for (final Map.Entry entry : captions.entrySet()) { + final Map caption = new HashMap<>(); caption.put(entry.getKey(), entry.getValue()); try { - wikidataAddLabels(wikiDataEntityId, fileEntityId.toString(), caption); - } catch (Throwable throwable) { + wikidataAddLabels(fileEntityId.toString(), caption); + } catch (final Throwable throwable) { throwable.printStackTrace(); } } @@ -280,13 +268,12 @@ public class WikidataEditService { /** * Adds label to Wikidata using the fileEntityId and the edit token, obtained from csrfTokenClient * - * @param wikiDataEntityId entityId for the current contribution * @param fileEntityId * @param caption */ @SuppressLint("CheckResult") - private void wikidataAddLabels(String wikiDataEntityId, String fileEntityId, Map caption) throws Throwable { + private void wikidataAddLabels(final String fileEntityId, final Map caption) { Observable.fromCallable(() -> { try { return csrfTokenClient.getTokenBlocking(); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt index 39b9a7bf2..25e83c0cd 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt @@ -2,12 +2,14 @@ package fr.free.nrw.commons.wikidata import android.content.Context import com.nhaarman.mockitokotlin2.verifyZeroInteractions +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.wikidata.model.AddEditTagResponse import io.reactivex.Observable 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.* @@ -15,19 +17,19 @@ import org.mockito.MockitoAnnotations class WikidataEditServiceTest { @Mock - internal var context: Context? = null - @Mock - internal var wikidataEditListener: WikidataEditListener? = null - @Mock - internal var directKvStore: JsonKvStore? = null - @Mock - internal var wikidataClient: WikidataClient? = null + internal lateinit var context: Context @Mock - internal var wikibaseClient: WikiBaseClient? = null + internal lateinit var directKvStore: JsonKvStore + + @Mock + internal lateinit var wikidataClient: WikidataClient + + @Mock + internal lateinit var wikibaseClient: WikiBaseClient @InjectMocks - var wikidataEditService: WikidataEditService? = null + lateinit var wikidataEditService: WikidataEditService @Before @Throws(Exception::class) @@ -37,40 +39,40 @@ class WikidataEditServiceTest { @Test fun noClaimsWhenEntityIdIsNull() { - wikidataEditService!!.createClaimWithLogging(null, null,"Test.jpg","") - verifyZeroInteractions(wikidataClient!!) + wikidataEditService.createClaimWithLogging(null, null,"Test.jpg","") + verifyZeroInteractions(wikidataClient) } @Test fun noClaimsWhenFileNameIsNull() { - wikidataEditService!!.createClaimWithLogging("Q1", "Test", null,"") - verifyZeroInteractions(wikidataClient!!) + wikidataEditService.createClaimWithLogging("Q1", "Test", null,"") + verifyZeroInteractions(wikidataClient) } @Test fun noClaimsWhenP18IsNotEmpty() { - wikidataEditService!!.createClaimWithLogging("Q1", "Test","Test.jpg","Previous.jpg") - verifyZeroInteractions(wikidataClient!!) + wikidataEditService.createClaimWithLogging("Q1", "Test","Test.jpg","Previous.jpg") + verifyZeroInteractions(wikidataClient) } @Test fun noClaimsWhenLocationIsNotCorrect() { - `when`(directKvStore!!.getBoolean("Picture_Has_Correct_Location", true)) - .thenReturn(false) - wikidataEditService!!.createClaimWithLogging("Q1", "","Test.jpg","") - verifyZeroInteractions(wikidataClient!!) + whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true)) + .thenReturn(false) + wikidataEditService.createClaimWithLogging("Q1", "", "Test.jpg", "") + verifyZeroInteractions(wikidataClient) } @Test fun createClaimWithLogging() { - `when`(directKvStore!!.getBoolean("Picture_Has_Correct_Location", true)) - .thenReturn(true) - `when`(wikidataClient!!.createClaim(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(1L)) - `when`(wikidataClient!!.addEditTag(anyLong(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(mock(AddEditTagResponse::class.java))) - wikidataEditService!!.createClaimWithLogging("Q1", "Test","Test.jpg","") - verify(wikidataClient!!, times(1)) - .createClaim(ArgumentMatchers.anyString(), ArgumentMatchers.anyString()) + whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true)) + .thenReturn(true) + whenever(wikidataClient.createClaim(anyString(), anyString())) + .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()) } -} \ No newline at end of file +}