mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Fixed 4616 : Option for editing depictions (#4725)
* Dialog can't be dismissed * Dialog can't be dismissed * Option for editing depiction * Java docs added * Minor issues fixed * Lining done * "Depictions not updating instantly" issue resolved * Existing Depicts on the top * Existing Depicts on the top * Back press handled * Previous depictions unchecked * Whole Screen issue fixed * Nearby banner removed * Test fixed * Upload Wizard issue fixed * Upload Wizard issue fixed * Previous depicts issue fixed * Previous depicts issue fixed * All issues fixed * Fixed late loading of updated depicts * Depiction is removable * Test fixed * Back button press handled after losing focus for edittext * RequiresApi removed * RequiresApi removed * Test fixed * Requested changes * Test added * Test added * UploadModelUnitTest added * DepictEditHelperUnitTest added * DepictEditHelperUnitTest added * Test added * More test added * Indentation Reversed * Indentation reversed * Update MediaDetailFragment.java * Indentation reversed * Update MediaDetailFragment.java * Indentation reversed * Indentation reversed * Indentation reversed * Indentation reversed * More test added * More test added * Minor fixes * Minor fixes * Minor fixes
This commit is contained in:
parent
e58322ed63
commit
bd9531b969
24 changed files with 1261 additions and 75 deletions
|
|
@ -125,6 +125,16 @@ public class MainActivity extends BaseActivity
|
||||||
toolbar.setNavigationOnClickListener(view -> {
|
toolbar.setNavigationOnClickListener(view -> {
|
||||||
onSupportNavigateUp();
|
onSupportNavigateUp();
|
||||||
});
|
});
|
||||||
|
/*
|
||||||
|
"first_edit_depict" is a key for getting information about opening the depiction editor
|
||||||
|
screen for the first time after opening the app.
|
||||||
|
|
||||||
|
Getting true by the key means the depiction editor screen is opened for the first time
|
||||||
|
after opening the app.
|
||||||
|
Getting false by the key means the depiction editor screen is not opened for the first time
|
||||||
|
after opening the app.
|
||||||
|
*/
|
||||||
|
applicationKvStore.putBoolean("first_edit_depict", true);
|
||||||
if (applicationKvStore.getBoolean("login_skipped") == true) {
|
if (applicationKvStore.getBoolean("login_skipped") == true) {
|
||||||
setTitle(getString(R.string.navigation_item_explore));
|
setTitle(getString(R.string.navigation_item_explore));
|
||||||
setUpLoggedOutPager();
|
setUpLoggedOutPager();
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,8 @@ import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_D
|
||||||
import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT;
|
import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT;
|
||||||
import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT;
|
import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT;
|
||||||
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
|
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
|
||||||
import android.content.res.Resources;
|
|
||||||
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
|
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
@ -45,6 +43,8 @@ import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
|
|
@ -86,6 +86,7 @@ import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
import fr.free.nrw.commons.nearby.Label;
|
import fr.free.nrw.commons.nearby.Label;
|
||||||
import fr.free.nrw.commons.profile.ProfileActivity;
|
import fr.free.nrw.commons.profile.ProfileActivity;
|
||||||
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
||||||
|
import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
||||||
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
@ -93,7 +94,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
@ -176,6 +176,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||||
LinearLayout captionLayout;
|
LinearLayout captionLayout;
|
||||||
@BindView(R.id.depicts_layout)
|
@BindView(R.id.depicts_layout)
|
||||||
LinearLayout depictsLayout;
|
LinearLayout depictsLayout;
|
||||||
|
@BindView(R.id.depictionsEditButton)
|
||||||
|
Button depictEditButton;
|
||||||
@BindView(R.id.media_detail_caption)
|
@BindView(R.id.media_detail_caption)
|
||||||
TextView mediaCaption;
|
TextView mediaCaption;
|
||||||
@BindView(R.id.mediaDetailDesc)
|
@BindView(R.id.mediaDetailDesc)
|
||||||
|
|
@ -239,7 +241,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||||
@BindView(R.id.description_label)
|
@BindView(R.id.description_label)
|
||||||
TextView descriptionLabel;
|
TextView descriptionLabel;
|
||||||
@BindView(R.id.pb_circular)
|
@BindView(R.id.pb_circular)
|
||||||
ProgressBar progressBar;
|
ProgressBar progressBar;
|
||||||
String descriptionHtmlCode;
|
String descriptionHtmlCode;
|
||||||
@BindView(R.id.progressBarDeletion)
|
@BindView(R.id.progressBarDeletion)
|
||||||
ProgressBar progressBarDeletion;
|
ProgressBar progressBarDeletion;
|
||||||
|
|
@ -467,10 +469,10 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||||
private void displayMediaDetails() {
|
private void displayMediaDetails() {
|
||||||
setTextFields(media);
|
setTextFields(media);
|
||||||
compositeDisposable.addAll(
|
compositeDisposable.addAll(
|
||||||
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
|
mediaDataExtractor.refresh(media)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::onDepictionsLoaded, Timber::e),
|
.subscribe(this::onMediaRefreshed, Timber::e),
|
||||||
mediaDataExtractor.checkDeletionRequestExists(media)
|
mediaDataExtractor.checkDeletionRequestExists(media)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|
@ -478,15 +480,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||||
mediaDataExtractor.fetchDiscussion(media)
|
mediaDataExtractor.fetchDiscussion(media)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::onDiscussionLoaded, Timber::e),
|
.subscribe(this::onDiscussionLoaded, Timber::e)
|
||||||
mediaDataExtractor.refresh(media)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(this::onMediaRefreshed, Timber::e)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMediaRefreshed(Media media) {
|
private void onMediaRefreshed(Media media) {
|
||||||
|
this.media = media;
|
||||||
setTextFields(media);
|
setTextFields(media);
|
||||||
compositeDisposable.addAll(
|
compositeDisposable.addAll(
|
||||||
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
|
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
|
||||||
|
|
@ -517,8 +516,26 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDepictionsLoaded(List<IdAndCaptions> idAndCaptions){
|
private void onDepictionsLoaded(List<IdAndCaptions> idAndCaptions){
|
||||||
depictsLayout.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
|
depictsLayout.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
|
||||||
buildDepictionList(idAndCaptions);
|
depictEditButton.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
|
||||||
|
buildDepictionList(idAndCaptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By clicking on the edit depictions button, it will send user to depict fragment
|
||||||
|
*/
|
||||||
|
@OnClick(R.id.depictionsEditButton)
|
||||||
|
public void onDepictionsEditButtonClicked() {
|
||||||
|
depictionContainer.removeAllViews();
|
||||||
|
depictEditButton.setVisibility(GONE);
|
||||||
|
final Fragment depictsFragment = new DepictsFragment();
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
bundle.putParcelable("Existing_Depicts", media);
|
||||||
|
depictsFragment.setArguments(bundle);
|
||||||
|
final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||||
|
transaction.replace(R.id.mediaDetailFrameLayout, depictsFragment);
|
||||||
|
transaction.addToBackStack(null);
|
||||||
|
transaction.commit();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* The imageSpacer is Basically a transparent overlay for the SimpleDraweeView
|
* The imageSpacer is Basically a transparent overlay for the SimpleDraweeView
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ public class NotificationHelper {
|
||||||
public static final int NOTIFICATION_EDIT_CATEGORY = 2;
|
public static final int NOTIFICATION_EDIT_CATEGORY = 2;
|
||||||
public static final int NOTIFICATION_EDIT_COORDINATES = 3;
|
public static final int NOTIFICATION_EDIT_COORDINATES = 3;
|
||||||
public static final int NOTIFICATION_EDIT_DESCRIPTION = 4;
|
public static final int NOTIFICATION_EDIT_DESCRIPTION = 4;
|
||||||
|
public static final int NOTIFICATION_EDIT_DEPICTIONS = 5;
|
||||||
|
|
||||||
private NotificationManager notificationManager;
|
private NotificationManager notificationManager;
|
||||||
private NotificationCompat.Builder notificationBuilder;
|
private NotificationCompat.Builder notificationBuilder;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package fr.free.nrw.commons.repository;
|
package fr.free.nrw.commons.repository;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.category.CategoriesModel;
|
import fr.free.nrw.commons.category.CategoriesModel;
|
||||||
import fr.free.nrw.commons.category.CategoryItem;
|
import fr.free.nrw.commons.category.CategoryItem;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
|
@ -233,8 +234,8 @@ public class UploadRepository {
|
||||||
uploadModel.setSelectedLicense(licenseName);
|
uploadModel.setSelectedLicense(licenseName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
public void onDepictItemClicked(DepictedItem depictedItem, final Media media) {
|
||||||
uploadModel.onDepictItemClicked(depictedItem);
|
uploadModel.onDepictItemClicked(depictedItem, media);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -247,6 +248,23 @@ public class UploadRepository {
|
||||||
return uploadModel.getSelectedDepictions();
|
return uploadModel.getSelectedDepictions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides selected existing depicts
|
||||||
|
*
|
||||||
|
* @return selected existing depicts
|
||||||
|
*/
|
||||||
|
public List<String> getSelectedExistingDepictions() {
|
||||||
|
return uploadModel.getSelectedExistingDepictions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize existing depicts
|
||||||
|
*
|
||||||
|
* @param selectedExistingDepictions existing depicts
|
||||||
|
*/
|
||||||
|
public void setSelectedExistingDepictions(final List<String> selectedExistingDepictions) {
|
||||||
|
uploadModel.setSelectedExistingDepictions(selectedExistingDepictions);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Search all depictions from
|
* Search all depictions from
|
||||||
*
|
*
|
||||||
|
|
@ -275,6 +293,39 @@ public class UploadRepository {
|
||||||
return depictModel.getPlaceDepictions(new ArrayList<>(qids));
|
return depictModel.getPlaceDepictions(new ArrayList<>(qids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes depict IDs as a parameter, converts into a slash separated String and Gets DepictItem
|
||||||
|
* from the server
|
||||||
|
*
|
||||||
|
* @param depictionsQIDs IDs of Depiction
|
||||||
|
* @return Flowable<List<DepictedItem>>
|
||||||
|
*/
|
||||||
|
public Flowable<List<DepictedItem>> getDepictions(final List<String> depictionsQIDs){
|
||||||
|
final String ids = joinQIDs(depictionsQIDs);
|
||||||
|
return depictModel.getDepictions(ids).toFlowable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a string by joining all IDs divided by "|"
|
||||||
|
*
|
||||||
|
* @param depictionsQIDs IDs of depiction ex. ["Q11023","Q1356"]
|
||||||
|
* @return string ex. "Q11023|Q1356"
|
||||||
|
*/
|
||||||
|
private String joinQIDs(final List<String> depictionsQIDs) {
|
||||||
|
if (depictionsQIDs != null && !depictionsQIDs.isEmpty()) {
|
||||||
|
final StringBuilder buffer = new StringBuilder(depictionsQIDs.get(0));
|
||||||
|
|
||||||
|
if (depictionsQIDs.size() > 1) {
|
||||||
|
for (int i = 1; i < depictionsQIDs.size(); i++) {
|
||||||
|
buffer.append("|");
|
||||||
|
buffer.append(depictionsQIDs.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns nearest place matching the passed latitude and longitude
|
* Returns nearest place matching the passed latitude and longitude
|
||||||
* @param decLatitude
|
* @param decLatitude
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||||
|
|
@ -40,6 +41,10 @@ public class UploadModel {
|
||||||
private final ImageProcessingService imageProcessingService;
|
private final ImageProcessingService imageProcessingService;
|
||||||
private final List<String> selectedCategories = new ArrayList<>();
|
private final List<String> selectedCategories = new ArrayList<>();
|
||||||
private final List<DepictedItem> selectedDepictions = new ArrayList<>();
|
private final List<DepictedItem> selectedDepictions = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* Existing depicts which are selected
|
||||||
|
*/
|
||||||
|
private List<String> selectedExistingDepictions = new ArrayList<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UploadModel(@Named("licenses") final List<String> licenses,
|
UploadModel(@Named("licenses") final List<String> licenses,
|
||||||
|
|
@ -68,6 +73,7 @@ public class UploadModel {
|
||||||
items.clear();
|
items.clear();
|
||||||
selectedCategories.clear();
|
selectedCategories.clear();
|
||||||
selectedDepictions.clear();
|
selectedDepictions.clear();
|
||||||
|
selectedExistingDepictions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedCategories(List<String> selectedCategories) {
|
public void setSelectedCategories(List<String> selectedCategories) {
|
||||||
|
|
@ -185,11 +191,33 @@ public class UploadModel {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
public void onDepictItemClicked(DepictedItem depictedItem, Media media) {
|
||||||
if (depictedItem.isSelected()) {
|
if (media == null) {
|
||||||
selectedDepictions.add(depictedItem);
|
if (depictedItem.isSelected()) {
|
||||||
|
selectedDepictions.add(depictedItem);
|
||||||
|
} else {
|
||||||
|
selectedDepictions.remove(depictedItem);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
selectedDepictions.remove(depictedItem);
|
if (depictedItem.isSelected()) {
|
||||||
|
if (media.getDepictionIds().contains(depictedItem.getId())) {
|
||||||
|
selectedExistingDepictions.add(depictedItem.getId());
|
||||||
|
} else {
|
||||||
|
selectedDepictions.add(depictedItem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (media.getDepictionIds().contains(depictedItem.getId())) {
|
||||||
|
selectedExistingDepictions.remove(depictedItem.getId());
|
||||||
|
if (!media.getDepictionIds().contains(depictedItem.getId())) {
|
||||||
|
final List<String> depictsList = new ArrayList<>();
|
||||||
|
depictsList.add(depictedItem.getId());
|
||||||
|
depictsList.addAll(media.getDepictionIds());
|
||||||
|
media.setDepictionIds(depictsList);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedDepictions.remove(depictedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,4 +235,21 @@ public class UploadModel {
|
||||||
return selectedDepictions;
|
return selectedDepictions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides selected existing depicts
|
||||||
|
*
|
||||||
|
* @return selected existing depicts
|
||||||
|
*/
|
||||||
|
public List<String> getSelectedExistingDepictions() {
|
||||||
|
return selectedExistingDepictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize existing depicts
|
||||||
|
*
|
||||||
|
* @param selectedExistingDepictions existing depicts
|
||||||
|
*/
|
||||||
|
public void setSelectedExistingDepictions(final List<String> selectedExistingDepictions) {
|
||||||
|
this.selectedExistingDepictions = selectedExistingDepictions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,21 @@ public interface WikiBaseInterface {
|
||||||
@NonNull @Field("token") String editToken,
|
@NonNull @Field("token") String editToken,
|
||||||
@NonNull @Field("data") String data);
|
@NonNull @Field("data") String data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads depicts for a file in the server
|
||||||
|
*
|
||||||
|
* @param filename name of the file
|
||||||
|
* @param editToken editToken for the file
|
||||||
|
* @param data data of the depicts to be uploaded
|
||||||
|
* @return Observable<MwPostResponse>
|
||||||
|
*/
|
||||||
|
@Headers("Cache-Control: no-cache")
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST(MW_API_PREFIX + "action=wbeditentity&site=commonswiki&clear=1")
|
||||||
|
Observable<MwPostResponse> postEditEntityByFilename(@NonNull @Field("title") String filename,
|
||||||
|
@NonNull @Field("token") String editToken,
|
||||||
|
@NonNull @Field("data") String data);
|
||||||
|
|
||||||
@GET(MW_API_PREFIX + "action=query&prop=info")
|
@GET(MW_API_PREFIX + "action=query&prop=info")
|
||||||
Observable<MwQueryResponse> getFileEntityId(@Query("titles") String fileName);
|
Observable<MwQueryResponse> getFileEntityId(@Query("titles") String fileName);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
package fr.free.nrw.commons.upload.depicts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import fr.free.nrw.commons.BuildConfig
|
||||||
|
import fr.free.nrw.commons.Media
|
||||||
|
import fr.free.nrw.commons.R
|
||||||
|
import fr.free.nrw.commons.notification.NotificationHelper
|
||||||
|
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||||
|
import fr.free.nrw.commons.wikidata.WikidataEditService
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class DepictEditHelper @Inject constructor (notificationHelper: NotificationHelper,
|
||||||
|
wikidataEditService: WikidataEditService,
|
||||||
|
viewUtilWrapper: ViewUtilWrapper) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for making post operations
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
lateinit var wikidataEditService: WikidataEditService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for creating notification
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
lateinit var notificationHelper: NotificationHelper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for showing toast
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
lateinit var viewUtilWrapper: ViewUtilWrapper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public interface to edit depictions
|
||||||
|
*
|
||||||
|
* @param context context
|
||||||
|
* @param media media
|
||||||
|
* @param depictions selected depictions to be added ex: ["Q12", "Q234"]
|
||||||
|
* @return Single<Boolean>
|
||||||
|
*/
|
||||||
|
fun makeDepictionEdit(
|
||||||
|
context: Context,
|
||||||
|
media: Media,
|
||||||
|
depictions: List<String>
|
||||||
|
): Observable<Boolean> {
|
||||||
|
viewUtilWrapper.showShortToast(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.depictions_edit_helper_make_edit_toast)
|
||||||
|
)
|
||||||
|
return addDepiction(media, depictions)
|
||||||
|
.flatMap { result: Boolean ->
|
||||||
|
Observable.just(
|
||||||
|
showDepictionEditNotification(context, media, result)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends new depictions
|
||||||
|
*
|
||||||
|
* @param media media
|
||||||
|
* @param depictions to be added
|
||||||
|
* @return Observable<Boolean>
|
||||||
|
*/
|
||||||
|
private fun addDepiction(media: Media, depictions: List<String>): Observable<Boolean> {
|
||||||
|
Timber.d("thread is adding depiction %s", Thread.currentThread().name)
|
||||||
|
return wikidataEditService.updateDepictsProperty(media.filename, depictions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helps to create notification about condition of editing depictions
|
||||||
|
*
|
||||||
|
* @param context context
|
||||||
|
* @param media media
|
||||||
|
* @param result response of result
|
||||||
|
* @return Single<Boolean>
|
||||||
|
*/
|
||||||
|
private fun showDepictionEditNotification(
|
||||||
|
context: Context,
|
||||||
|
media: Media,
|
||||||
|
result: Boolean
|
||||||
|
): Boolean {
|
||||||
|
val message: String
|
||||||
|
var title = context.getString(R.string.depictions_edit_helper_show_edit_title)
|
||||||
|
if (result) {
|
||||||
|
title += ": " + context.getString(R.string.category_edit_helper_show_edit_title_success)
|
||||||
|
val depictsInMessage = StringBuilder()
|
||||||
|
val depictIdList = media.depictionIds
|
||||||
|
for (depiction in depictIdList) {
|
||||||
|
depictsInMessage.append(depiction)
|
||||||
|
if (depiction == depictIdList[depictIdList.size - 1]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
depictsInMessage.append(",")
|
||||||
|
}
|
||||||
|
message = context.resources.getQuantityString(
|
||||||
|
R.plurals.depictions_edit_helper_show_edit_message_if,
|
||||||
|
depictIdList.size,
|
||||||
|
depictsInMessage.toString()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
title += ": " + context.getString(R.string.depictions_edit_helper_show_edit_title)
|
||||||
|
message = context.getString(R.string.depictions_edit_helper_edit_message_else)
|
||||||
|
}
|
||||||
|
val urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.filename
|
||||||
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile))
|
||||||
|
notificationHelper.showNotification(
|
||||||
|
context,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
NotificationHelper.NOTIFICATION_EDIT_DEPICTIONS,
|
||||||
|
browserIntent
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package fr.free.nrw.commons.upload.depicts;
|
package fr.free.nrw.commons.upload.depicts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import fr.free.nrw.commons.BasePresenter;
|
import fr.free.nrw.commons.BasePresenter;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -40,6 +43,36 @@ public interface DepictsContract {
|
||||||
* add depictions to list
|
* add depictions to list
|
||||||
*/
|
*/
|
||||||
void setDepictsList(List<DepictedItem> depictedItemList);
|
void setDepictsList(List<DepictedItem> depictedItemList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns required context
|
||||||
|
*/
|
||||||
|
Context getFragmentContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns to previous fragment
|
||||||
|
*/
|
||||||
|
void goBackToPreviousScreen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets existing depictions IDs from media
|
||||||
|
*/
|
||||||
|
List<String> getExistingDepictions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the progress dialog
|
||||||
|
*/
|
||||||
|
void showProgressDialog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the progress dialog
|
||||||
|
*/
|
||||||
|
void dismissProgressDialog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the depictions
|
||||||
|
*/
|
||||||
|
void updateDepicts();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserActionListener extends BasePresenter<View> {
|
interface UserActionListener extends BasePresenter<View> {
|
||||||
|
|
@ -71,6 +104,21 @@ public interface DepictsContract {
|
||||||
*/
|
*/
|
||||||
void verifyDepictions();
|
void verifyDepictions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears previous selections
|
||||||
|
*/
|
||||||
|
void clearPreviousSelection();
|
||||||
|
|
||||||
LiveData<List<DepictedItem>> getDepictedItems();
|
LiveData<List<DepictedItem>> getDepictedItems();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the depictions
|
||||||
|
*/
|
||||||
|
void updateDepictions(Media media);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches view and media
|
||||||
|
*/
|
||||||
|
void onAttachViewWithMedia(@NonNull View view, Media media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
package fr.free.nrw.commons.upload.depicts;
|
package fr.free.nrw.commons.upload.depicts;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
|
|
@ -18,7 +24,11 @@ import butterknife.OnClick;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
import com.jakewharton.rxbinding2.view.RxView;
|
import com.jakewharton.rxbinding2.view.RxView;
|
||||||
import com.jakewharton.rxbinding2.widget.RxTextView;
|
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||||
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
|
import fr.free.nrw.commons.media.MediaDetailFragment;
|
||||||
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText;
|
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText;
|
||||||
import fr.free.nrw.commons.upload.UploadActivity;
|
import fr.free.nrw.commons.upload.UploadActivity;
|
||||||
import fr.free.nrw.commons.upload.UploadBaseFragment;
|
import fr.free.nrw.commons.upload.UploadBaseFragment;
|
||||||
|
|
@ -27,8 +37,10 @@ import fr.free.nrw.commons.utils.DialogUtil;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
|
@ -52,11 +64,26 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
RecyclerView depictsRecyclerView;
|
RecyclerView depictsRecyclerView;
|
||||||
@BindView(R.id.tooltip)
|
@BindView(R.id.tooltip)
|
||||||
ImageView tooltip;
|
ImageView tooltip;
|
||||||
|
@BindView(R.id.depicts_next)
|
||||||
|
Button btnNext;
|
||||||
|
@BindView(R.id.depicts_previous)
|
||||||
|
Button btnPrevious;
|
||||||
|
@Inject
|
||||||
|
@Named("default_preferences")
|
||||||
|
public
|
||||||
|
JsonKvStore applicationKvStore;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DepictsContract.UserActionListener presenter;
|
DepictsContract.UserActionListener presenter;
|
||||||
private UploadDepictsAdapter adapter;
|
private UploadDepictsAdapter adapter;
|
||||||
private Disposable subscribe;
|
private Disposable subscribe;
|
||||||
|
private Media media;
|
||||||
|
private ProgressDialog progressDialog;
|
||||||
|
/**
|
||||||
|
* Determines each encounter of edit depicts
|
||||||
|
*/
|
||||||
|
private int count;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public android.view.View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
public android.view.View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
|
@ -67,7 +94,13 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull android.view.View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull android.view.View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
Bundle bundle = getArguments();
|
||||||
|
if (bundle != null) {
|
||||||
|
media = bundle.getParcelable("Existing_Depicts");
|
||||||
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
presenter.getDepictedItems().observe(getViewLifecycleOwner(), this::setDepictsList);
|
presenter.getDepictedItems().observe(getViewLifecycleOwner(), this::setDepictsList);
|
||||||
}
|
}
|
||||||
|
|
@ -76,13 +109,27 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
* Initialize presenter and views
|
* Initialize presenter and views
|
||||||
*/
|
*/
|
||||||
private void init() {
|
private void init() {
|
||||||
depictsTitle.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
|
|
||||||
callback.getTotalNumberOfSteps(), getString(R.string.depicts_step_title)));
|
if (media == null) {
|
||||||
|
depictsTitle
|
||||||
|
.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
|
||||||
|
callback.getTotalNumberOfSteps(), getString(R.string.depicts_step_title)));
|
||||||
|
} else {
|
||||||
|
depictsTitle.setText(R.string.edit_depictions);
|
||||||
|
depictsSubTitle.setVisibility(View.GONE);
|
||||||
|
btnNext.setText(R.string.menu_save_categories);
|
||||||
|
btnPrevious.setText(R.string.menu_cancel_upload);
|
||||||
|
}
|
||||||
|
|
||||||
setDepictsSubTitle();
|
setDepictsSubTitle();
|
||||||
tooltip.setOnClickListener(v -> DialogUtil
|
tooltip.setOnClickListener(v -> DialogUtil
|
||||||
.showAlertDialog(getActivity(), getString(R.string.depicts_step_title),
|
.showAlertDialog(getActivity(), getString(R.string.depicts_step_title),
|
||||||
getString(R.string.depicts_tooltip), getString(android.R.string.ok), null, true));
|
getString(R.string.depicts_tooltip), getString(android.R.string.ok), null, true));
|
||||||
presenter.onAttachView(this);
|
if (media == null) {
|
||||||
|
presenter.onAttachView(this);
|
||||||
|
} else {
|
||||||
|
presenter.onAttachViewWithMedia(this, media);
|
||||||
|
}
|
||||||
initRecyclerView();
|
initRecyclerView();
|
||||||
addTextChangeListenerToSearchBox();
|
addTextChangeListenerToSearchBox();
|
||||||
}
|
}
|
||||||
|
|
@ -105,10 +152,17 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
* Initialise recyclerView and set adapter
|
* Initialise recyclerView and set adapter
|
||||||
*/
|
*/
|
||||||
private void initRecyclerView() {
|
private void initRecyclerView() {
|
||||||
adapter = new UploadDepictsAdapter(item -> {
|
if (media == null) {
|
||||||
presenter.onDepictItemClicked(item);
|
adapter = new UploadDepictsAdapter(categoryItem -> {
|
||||||
return Unit.INSTANCE;
|
presenter.onDepictItemClicked(categoryItem);
|
||||||
});
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
adapter = new UploadDepictsAdapter(item -> {
|
||||||
|
presenter.onDepictItemClicked(item);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
}
|
||||||
depictsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
depictsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
depictsRecyclerView.setAdapter(adapter);
|
depictsRecyclerView.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
@ -133,19 +187,28 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noDepictionSelected() {
|
public void noDepictionSelected() {
|
||||||
DialogUtil.showAlertDialog(getActivity(),
|
if (media == null) {
|
||||||
getString(R.string.no_depictions_selected),
|
DialogUtil.showAlertDialog(getActivity(),
|
||||||
getString(R.string.no_depictions_selected_warning_desc),
|
getString(R.string.no_depictions_selected),
|
||||||
getString(R.string.continue_message),
|
getString(R.string.no_depictions_selected_warning_desc),
|
||||||
getString(R.string.cancel),
|
getString(R.string.continue_message),
|
||||||
this::goToNextScreen,
|
getString(R.string.cancel),
|
||||||
null
|
this::goToNextScreen,
|
||||||
);
|
null
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(requireContext(), getString(R.string.no_depictions_selected),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
presenter.clearPreviousSelection();
|
||||||
|
updateDepicts();
|
||||||
|
goBackToPreviousScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
media = null;
|
||||||
presenter.onDetachView();
|
presenter.onDetachView();
|
||||||
subscribe.dispose();
|
subscribe.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -166,18 +229,98 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDepictsList(List<DepictedItem> depictedItemList) {
|
public void setDepictsList(List<DepictedItem> depictedItemList) {
|
||||||
adapter.setItems(depictedItemList);
|
|
||||||
|
if (applicationKvStore.getBoolean("first_edit_depict")) {
|
||||||
|
count = 1;
|
||||||
|
applicationKvStore.putBoolean("first_edit_depict", false);
|
||||||
|
adapter.setItems(depictedItemList);
|
||||||
|
} else {
|
||||||
|
if ((count == 0) && (!depictedItemList.isEmpty())) {
|
||||||
|
adapter.setItems(null);
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
adapter.setItems(depictedItemList);
|
||||||
|
}
|
||||||
|
}
|
||||||
depictsRecyclerView.smoothScrollToPosition(0);
|
depictsRecyclerView.smoothScrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.depicts_next)
|
/**
|
||||||
public void onNextButtonClicked() {
|
* Returns required context
|
||||||
presenter.verifyDepictions();
|
*/
|
||||||
|
@Override
|
||||||
|
public Context getFragmentContext(){
|
||||||
|
return requireContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns to previous fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void goBackToPreviousScreen() {
|
||||||
|
getFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets existing depictions IDs from media
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getExistingDepictions(){
|
||||||
|
return (media == null) ? null : media.getDepictionIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the progress dialog
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void showProgressDialog() {
|
||||||
|
progressDialog = new ProgressDialog(requireContext());
|
||||||
|
progressDialog.setMessage(getString(R.string.please_wait));
|
||||||
|
progressDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the progress dialog
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void dismissProgressDialog() {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the depicts
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void updateDepicts() {
|
||||||
|
final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment();
|
||||||
|
assert mediaDetailFragment != null;
|
||||||
|
mediaDetailFragment.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the calling fragment by media nullability and act accordingly
|
||||||
|
*/
|
||||||
|
@OnClick(R.id.depicts_next)
|
||||||
|
public void onNextButtonClicked() {
|
||||||
|
if(media != null){
|
||||||
|
presenter.updateDepictions(media);
|
||||||
|
} else {
|
||||||
|
presenter.verifyDepictions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the calling fragment by media nullability and act accordingly
|
||||||
|
*/
|
||||||
@OnClick(R.id.depicts_previous)
|
@OnClick(R.id.depicts_previous)
|
||||||
public void onPreviousButtonClicked() {
|
public void onPreviousButtonClicked() {
|
||||||
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
|
if(media != null){
|
||||||
|
presenter.clearPreviousSelection();
|
||||||
|
updateDepicts();
|
||||||
|
goBackToPreviousScreen();
|
||||||
|
} else {
|
||||||
|
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,4 +343,63 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
||||||
private void searchForDepictions(final String query) {
|
private void searchForDepictions(final String query) {
|
||||||
presenter.searchForDepictions(query);
|
presenter.searchForDepictions(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the action bar while opening editing fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (media != null) {
|
||||||
|
depictsSearch.setOnKeyListener((v, keyCode, event) -> {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
depictsSearch.clearFocus();
|
||||||
|
presenter.clearPreviousSelection();
|
||||||
|
updateDepicts();
|
||||||
|
goBackToPreviousScreen();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
Objects.requireNonNull(getView()).setFocusableInTouchMode(true);
|
||||||
|
getView().requestFocus();
|
||||||
|
getView().setOnKeyListener((v, keyCode, event) -> {
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
presenter.clearPreviousSelection();
|
||||||
|
updateDepicts();
|
||||||
|
goBackToPreviousScreen();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
Objects.requireNonNull(
|
||||||
|
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
|
||||||
|
.hide();
|
||||||
|
|
||||||
|
if (getParentFragment().getParentFragment().getParentFragment()
|
||||||
|
instanceof ContributionsFragment) {
|
||||||
|
((ContributionsFragment) (getParentFragment()
|
||||||
|
.getParentFragment().getParentFragment())).nearbyNotificationCardView
|
||||||
|
.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the action bar while closing editing fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (media != null) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
package fr.free.nrw.commons.upload.depicts
|
package fr.free.nrw.commons.upload.depicts
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||||
import fr.free.nrw.commons.repository.UploadRepository
|
import fr.free.nrw.commons.repository.UploadRepository
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||||
import fr.free.nrw.commons.wikidata.WikidataDisambiguationItems
|
import fr.free.nrw.commons.wikidata.WikidataDisambiguationItems
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.Scheduler
|
import io.reactivex.Scheduler
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.processors.PublishProcessor
|
import io.reactivex.processors.PublishProcessor
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.reflect.Proxy
|
import java.lang.reflect.Proxy
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -35,8 +39,11 @@ class DepictsPresenter @Inject constructor(
|
||||||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||||
private val searchTerm: PublishProcessor<String> = PublishProcessor.create()
|
private val searchTerm: PublishProcessor<String> = PublishProcessor.create()
|
||||||
private val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
|
private val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
|
||||||
|
private var media: Media? = null
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var depictsDao: DepictsDao;
|
lateinit var depictsDao: DepictsDao
|
||||||
|
@Inject
|
||||||
|
lateinit var depictsHelper: DepictEditHelper
|
||||||
|
|
||||||
override fun onAttachView(view: DepictsContract.View) {
|
override fun onAttachView(view: DepictsContract.View) {
|
||||||
this.view = view
|
this.view = view
|
||||||
|
|
@ -71,16 +78,37 @@ class DepictsPresenter @Inject constructor(
|
||||||
if (querystring.isEmpty()) {
|
if (querystring.isEmpty()) {
|
||||||
recentDepictedItemList = getRecentDepictedItems();
|
recentDepictedItemList = getRecentDepictedItems();
|
||||||
}
|
}
|
||||||
return repository.searchAllEntities(querystring)
|
|
||||||
.subscribeOn(ioScheduler)
|
|
||||||
.map { repository.selectedDepictions + it + recentDepictedItemList }
|
|
||||||
.map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } }
|
|
||||||
.map { it.distinctBy(DepictedItem::id) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (media == null) {
|
||||||
|
return repository.searchAllEntities(querystring)
|
||||||
|
.subscribeOn(ioScheduler)
|
||||||
|
.map { repository.selectedDepictions + it + recentDepictedItemList }
|
||||||
|
.map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } }
|
||||||
|
.map { it.distinctBy(DepictedItem::id) }
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Flowable.zip(repository.getDepictions(repository.selectedExistingDepictions)
|
||||||
|
.map { list -> list.map {
|
||||||
|
DepictedItem(it.name, it.description, it.imageUrl, it.instanceOfs,
|
||||||
|
it.commonsCategories, true, it.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
repository.searchAllEntities(querystring),
|
||||||
|
{ it1, it2 ->
|
||||||
|
it1 + it2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribeOn(ioScheduler)
|
||||||
|
.map { repository.selectedDepictions + it + recentDepictedItemList }
|
||||||
|
.map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } }
|
||||||
|
.map { it.distinctBy(DepictedItem::id) }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDetachView() {
|
override fun onDetachView() {
|
||||||
view = DUMMY
|
view = DUMMY
|
||||||
|
media = null
|
||||||
compositeDisposable.clear()
|
compositeDisposable.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +130,7 @@ class DepictsPresenter @Inject constructor(
|
||||||
private fun selectNewDepictions(toSelect: List<DepictedItem>) {
|
private fun selectNewDepictions(toSelect: List<DepictedItem>) {
|
||||||
toSelect.forEach {
|
toSelect.forEach {
|
||||||
it.isSelected = true
|
it.isSelected = true
|
||||||
repository.onDepictItemClicked(it)
|
repository.onDepictItemClicked(it, media)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new selections to the list of depicted items so that the selections appear
|
// Add the new selections to the list of depicted items so that the selections appear
|
||||||
|
|
@ -113,12 +141,16 @@ class DepictsPresenter @Inject constructor(
|
||||||
?.let { depictedItems.value = it }
|
?.let { depictedItems.value = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearPreviousSelection() {
|
||||||
|
repository.cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPreviousButtonClicked() {
|
override fun onPreviousButtonClicked() {
|
||||||
view.goToPreviousScreen()
|
view.goToPreviousScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDepictItemClicked(depictedItem: DepictedItem) {
|
override fun onDepictItemClicked(depictedItem: DepictedItem) {
|
||||||
repository.onDepictItemClicked(depictedItem)
|
repository.onDepictItemClicked(depictedItem, media)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDepictedItems(): LiveData<List<DepictedItem>> {
|
override fun getDepictedItems(): LiveData<List<DepictedItem>> {
|
||||||
|
|
@ -149,6 +181,76 @@ class DepictsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the selected depicts and send them for posting to the server
|
||||||
|
* and saves them in local storage
|
||||||
|
*/
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
override fun updateDepictions(media: Media) {
|
||||||
|
if (repository.selectedDepictions.isNotEmpty()
|
||||||
|
|| repository.selectedExistingDepictions.size != view.existingDepictions.size
|
||||||
|
) {
|
||||||
|
view.showProgressDialog()
|
||||||
|
val selectedDepictions: MutableList<String> =
|
||||||
|
(repository.selectedDepictions.map { it.id }.toMutableList()
|
||||||
|
+ repository.selectedExistingDepictions).toMutableList()
|
||||||
|
|
||||||
|
if (selectedDepictions.isNotEmpty()) {
|
||||||
|
if (::depictsDao.isInitialized) {
|
||||||
|
//save all the selected Depicted item in room Database
|
||||||
|
depictsDao.savingDepictsInRoomDataBase(repository.selectedDepictions)
|
||||||
|
}
|
||||||
|
|
||||||
|
compositeDisposable.add(
|
||||||
|
depictsHelper.makeDepictionEdit(view.fragmentContext, media, selectedDepictions)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
media.depictionIds = selectedDepictions
|
||||||
|
repository.cleanup()
|
||||||
|
view.dismissProgressDialog()
|
||||||
|
view.updateDepicts()
|
||||||
|
view.goBackToPreviousScreen()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Timber.e(
|
||||||
|
"Failed to update depictions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repository.cleanup()
|
||||||
|
view.noDepictionSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachViewWithMedia(view: DepictsContract.View, media: Media) {
|
||||||
|
this.view = view
|
||||||
|
this.media = media
|
||||||
|
repository.selectedExistingDepictions = view.existingDepictions
|
||||||
|
compositeDisposable.add(
|
||||||
|
searchTerm
|
||||||
|
.observeOn(mainThreadScheduler)
|
||||||
|
.doOnNext { view.showProgress(true) }
|
||||||
|
.switchMap(::searchResultsWithTerm)
|
||||||
|
.observeOn(mainThreadScheduler)
|
||||||
|
.subscribe(
|
||||||
|
{ (results, term) ->
|
||||||
|
view.showProgress(false)
|
||||||
|
view.showError(results.isEmpty() && term.isNotEmpty())
|
||||||
|
depictedItems.value = results
|
||||||
|
},
|
||||||
|
{ t: Throwable? ->
|
||||||
|
view.showProgress(false)
|
||||||
|
view.showError(true)
|
||||||
|
Timber.e(t)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the depicts from DepictsRoomdataBase
|
* Get the depicts from DepictsRoomdataBase
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package fr.free.nrw.commons.wikidata;
|
package fr.free.nrw.commons.wikidata;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.media.MediaClientKt.PAGE_ID_PREFIX;
|
|
||||||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF;
|
||||||
|
import static fr.free.nrw.commons.media.MediaClientKt.PAGE_ID_PREFIX;
|
||||||
|
|
||||||
import fr.free.nrw.commons.upload.UploadResult;
|
import fr.free.nrw.commons.upload.UploadResult;
|
||||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||||
|
|
@ -35,6 +35,20 @@ public class WikiBaseClient {
|
||||||
.map(response -> (response.getSuccessVal() == 1)));
|
.map(response -> (response.getSuccessVal() == 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the server call for posting new depicts
|
||||||
|
*
|
||||||
|
* @param filename name of the file
|
||||||
|
* @param data data of the depicts to be uploaded
|
||||||
|
* @return Observable<Boolean>
|
||||||
|
*/
|
||||||
|
public Observable<Boolean> postEditEntityByFilename(final String filename, final String data) {
|
||||||
|
return csrfToken()
|
||||||
|
.switchMap(editToken -> wikiBaseInterface.postEditEntityByFilename(filename,
|
||||||
|
editToken, data)
|
||||||
|
.map(response -> (response.getSuccessVal() == 1)));
|
||||||
|
}
|
||||||
|
|
||||||
public Observable<Long> getFileEntityId(UploadResult uploadResult) {
|
public Observable<Long> getFileEntityId(UploadResult uploadResult) {
|
||||||
return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName())
|
return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName())
|
||||||
.map(response -> (long) (response.query().pages().get(0).pageId()));
|
.map(response -> (long) (response.query().pages().get(0).pageId()));
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import fr.free.nrw.commons.upload.WikidataPlace;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -73,11 +72,12 @@ public class WikidataEditService {
|
||||||
*/
|
*/
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
private Observable<Boolean> addDepictsProperty(final String fileEntityId,
|
private Observable<Boolean> addDepictsProperty(final String fileEntityId,
|
||||||
final WikidataItem depictedItem) {
|
final List<String> depictedItems) {
|
||||||
|
|
||||||
final EditClaim data = editClaim(
|
final EditClaim data = editClaim(
|
||||||
ConfigUtils.isBetaFlavour() ? "Q10" // Wikipedia:Sandbox (Q10)
|
ConfigUtils.isBetaFlavour() ? Collections.singletonList("Q10")
|
||||||
: depictedItem.getId()
|
// Wikipedia:Sandbox (Q10)
|
||||||
|
: depictedItems
|
||||||
);
|
);
|
||||||
|
|
||||||
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, gson.toJson(data))
|
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, gson.toJson(data))
|
||||||
|
|
@ -95,8 +95,42 @@ public class WikidataEditService {
|
||||||
.subscribeOn(Schedulers.io());
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EditClaim editClaim(final String entityId) {
|
/**
|
||||||
return EditClaim.from(entityId, WikidataProperties.DEPICTS.getPropertyName());
|
* Takes depicts ID as a parameter and create a uploadable data with the Id
|
||||||
|
* and send the data for POST operation
|
||||||
|
*
|
||||||
|
* @param filename name of the file
|
||||||
|
* @param depictedItems ID of the selected depict item
|
||||||
|
* @return Observable<Boolean>
|
||||||
|
*/
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
public Observable<Boolean> updateDepictsProperty(final String filename,
|
||||||
|
final List<String> depictedItems) {
|
||||||
|
|
||||||
|
final EditClaim data = editClaim(
|
||||||
|
ConfigUtils.isBetaFlavour() ? Collections.singletonList("Q10")
|
||||||
|
// Wikipedia:Sandbox (Q10)
|
||||||
|
: depictedItems
|
||||||
|
);
|
||||||
|
|
||||||
|
return wikiBaseClient.postEditEntityByFilename(filename,
|
||||||
|
gson.toJson(data))
|
||||||
|
.doOnNext(success -> {
|
||||||
|
if (success) {
|
||||||
|
Timber.d("DEPICTS property was set successfully for %s", filename);
|
||||||
|
} else {
|
||||||
|
Timber.d("Unable to set DEPICTS property for %s", filename);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.doOnError(throwable -> {
|
||||||
|
Timber.e(throwable, "Error occurred while setting DEPICTS property");
|
||||||
|
ViewUtil.showLongToast(context, throwable.toString());
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditClaim editClaim(final List<String> entityIds) {
|
||||||
|
return EditClaim.from(entityIds, WikidataProperties.DEPICTS.getPropertyName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -209,7 +243,12 @@ public class WikidataEditService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<Boolean> depictionEdits(Contribution contribution, Long fileEntityId) {
|
private Observable<Boolean> depictionEdits(Contribution contribution, Long fileEntityId) {
|
||||||
return Observable.fromIterable(contribution.getDepictedItems())
|
final List<String> depictIDs = new ArrayList<>();
|
||||||
.concatMap(wikidataItem -> addDepictsProperty(fileEntityId.toString(), wikidataItem));
|
for (final WikidataItem wikidataItem :
|
||||||
|
contribution.getDepictedItems()) {
|
||||||
|
depictIDs.add(wikidataItem.getId());
|
||||||
|
}
|
||||||
|
return addDepictsProperty(fileEntityId.toString(), depictIDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,15 @@
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/depictionsEditButton"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_margin="@dimen/standard_gap"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="@drawable/ic_baseline_edit_24" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
style="@style/MediaDetailContainer"
|
style="@style/MediaDetailContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:showIn="@layout/activity_upload">
|
android:background="?attr/mainBackground">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -524,6 +524,14 @@ Upload your first media by tapping on the add button.</string>
|
||||||
<string name="category_edit_helper_edit_message_else">Could not add categories.</string>
|
<string name="category_edit_helper_edit_message_else">Could not add categories.</string>
|
||||||
<string name="category_edit_button_text">Update categories</string>
|
<string name="category_edit_button_text">Update categories</string>
|
||||||
|
|
||||||
|
<string name="depictions_edit_helper_make_edit_toast">Trying to update depictions.</string>
|
||||||
|
<string name="depictions_edit_helper_show_edit_title">Edit depictions</string>
|
||||||
|
<plurals name="depictions_edit_helper_show_edit_message_if">
|
||||||
|
<item quantity="one">Depiction %1$s is added.</item>
|
||||||
|
<item quantity="other">Depictions %1$s are added.</item>
|
||||||
|
</plurals>
|
||||||
|
<string name="depictions_edit_helper_edit_message_else">Could not add depictions.</string>
|
||||||
|
|
||||||
<string name="coordinates_edit_helper_make_edit_toast">Trying to update coordinates.</string>
|
<string name="coordinates_edit_helper_make_edit_toast">Trying to update coordinates.</string>
|
||||||
<string name="coordinates_edit_helper_show_edit_title">Coordinates update</string>
|
<string name="coordinates_edit_helper_show_edit_title">Coordinates update</string>
|
||||||
<string name="description_edit_helper_show_edit_title">Description update</string>
|
<string name="description_edit_helper_show_edit_title">Description update</string>
|
||||||
|
|
@ -681,6 +689,7 @@ Upload your first media by tapping on the add button.</string>
|
||||||
<string name="contributions_of_user">Contributions of User: %s</string>
|
<string name="contributions_of_user">Contributions of User: %s</string>
|
||||||
<string name="achievements_of_user">Achievements of User: %s</string>
|
<string name="achievements_of_user">Achievements of User: %s</string>
|
||||||
<string name="menu_view_user_page">View user page</string>
|
<string name="menu_view_user_page">View user page</string>
|
||||||
|
<string name="edit_depictions">Edit depictions</string>
|
||||||
<string name="advanced_options">Advanced Options</string>
|
<string name="advanced_options">Advanced Options</string>
|
||||||
<string name="advanced_query_info_text">You can customize the Nearby query. If you get errors, reset and apply.</string>
|
<string name="advanced_query_info_text">You can customize the Nearby query. If you get errors, reset and apply.</string>
|
||||||
<string name="apply">Apply</string>
|
<string name="apply">Apply</string>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import android.os.Bundle
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
|
@ -19,7 +20,6 @@ import com.facebook.drawee.backends.pipeline.Fresco
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchy
|
import com.facebook.drawee.generic.GenericDraweeHierarchy
|
||||||
import com.facebook.drawee.view.SimpleDraweeView
|
import com.facebook.drawee.view.SimpleDraweeView
|
||||||
import com.facebook.soloader.SoLoader
|
import com.facebook.soloader.SoLoader
|
||||||
import fr.free.nrw.commons.*
|
|
||||||
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
|
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
|
||||||
import org.robolectric.Shadows.shadowOf
|
import org.robolectric.Shadows.shadowOf
|
||||||
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter
|
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter
|
||||||
|
|
@ -34,10 +34,8 @@ import fr.free.nrw.commons.TestCommonsApplication
|
||||||
import fr.free.nrw.commons.R
|
import fr.free.nrw.commons.R
|
||||||
import fr.free.nrw.commons.TestAppAdapter
|
import fr.free.nrw.commons.TestAppAdapter
|
||||||
import fr.free.nrw.commons.Media
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.contributions.ContributionViewHolder
|
|
||||||
import fr.free.nrw.commons.delete.DeleteHelper
|
import fr.free.nrw.commons.delete.DeleteHelper
|
||||||
import fr.free.nrw.commons.delete.ReasonBuilder
|
import fr.free.nrw.commons.delete.ReasonBuilder
|
||||||
import fr.free.nrw.commons.utils.ImageUtils
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
@ -218,6 +216,7 @@ class MediaDetailFragmentUnitTests {
|
||||||
Whitebox.setInternalState(fragment, "showCaptionAndDescriptionContainer", linearLayout)
|
Whitebox.setInternalState(fragment, "showCaptionAndDescriptionContainer", linearLayout)
|
||||||
Whitebox.setInternalState(fragment, "updateCategoriesButton", button)
|
Whitebox.setInternalState(fragment, "updateCategoriesButton", button)
|
||||||
Whitebox.setInternalState(fragment, "editDescription", button)
|
Whitebox.setInternalState(fragment, "editDescription", button)
|
||||||
|
Whitebox.setInternalState(fragment, "depictEditButton", button)
|
||||||
Whitebox.setInternalState(fragment, "categoryContainer", linearLayout)
|
Whitebox.setInternalState(fragment, "categoryContainer", linearLayout)
|
||||||
Whitebox.setInternalState(fragment, "categorySearchView", searchView)
|
Whitebox.setInternalState(fragment, "categorySearchView", searchView)
|
||||||
Whitebox.setInternalState(fragment, "progressBarDeletion", progressBarDeletion)
|
Whitebox.setInternalState(fragment, "progressBarDeletion", progressBarDeletion)
|
||||||
|
|
@ -656,4 +655,17 @@ class MediaDetailFragmentUnitTests {
|
||||||
MediaDetailFragment.forMedia(0, true, true, true)
|
MediaDetailFragment.forMedia(0, true, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testOnDepictEditButtonClicked() {
|
||||||
|
fragment.onDepictionsEditButtonClicked()
|
||||||
|
verify(linearLayout).removeAllViews()
|
||||||
|
verify(button).visibility = GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testOnDeleteButtonClicked() {
|
||||||
|
fragment.onDeleteButtonClicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package fr.free.nrw.commons.upload
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
import com.jraska.livedata.test
|
import com.nhaarman.mockitokotlin2.times
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import depictedItem
|
import depictedItem
|
||||||
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||||
import fr.free.nrw.commons.repository.UploadRepository
|
import fr.free.nrw.commons.repository.UploadRepository
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsContract
|
import fr.free.nrw.commons.upload.depicts.DepictsContract
|
||||||
|
|
@ -16,7 +17,10 @@ import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.powermock.reflect.Whitebox
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
|
||||||
class DepictsPresenterTest {
|
class DepictsPresenterTest {
|
||||||
|
|
@ -37,6 +41,9 @@ class DepictsPresenterTest {
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var depictsClient: DepictsClient
|
lateinit var depictsClient: DepictsClient
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var media: Media
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initial setup
|
* initial setup
|
||||||
*/
|
*/
|
||||||
|
|
@ -92,9 +99,6 @@ class DepictsPresenterTest {
|
||||||
testScheduler.triggerActions()
|
testScheduler.triggerActions()
|
||||||
verify(view).showProgress(false)
|
verify(view).showProgress(false)
|
||||||
verify(view).showError(true)
|
verify(view).showError(true)
|
||||||
depictsPresenter.depictedItems
|
|
||||||
.test()
|
|
||||||
.assertValue(emptyList())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -116,7 +120,7 @@ class DepictsPresenterTest {
|
||||||
fun `onDepictItemClicked calls repository`() {
|
fun `onDepictItemClicked calls repository`() {
|
||||||
val depictedItem = depictedItem()
|
val depictedItem = depictedItem()
|
||||||
depictsPresenter.onDepictItemClicked(depictedItem)
|
depictsPresenter.onDepictItemClicked(depictedItem)
|
||||||
verify(repository).onDepictItemClicked(depictedItem)
|
verify(repository).onDepictItemClicked(depictedItem, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -132,4 +136,63 @@ class DepictsPresenterTest {
|
||||||
depictsPresenter.verifyDepictions()
|
depictsPresenter.verifyDepictions()
|
||||||
verify(view).noDepictionSelected()
|
verify(view).noDepictionSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnAttachViewWithMedia() {
|
||||||
|
depictsPresenter.onAttachViewWithMedia(view, Mockito.mock(Media::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUpdateDepicts() {
|
||||||
|
depictsPresenter.updateDepictions(Mockito.mock(Media::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test searchResults when media is null`() {
|
||||||
|
whenever(repository.searchAllEntities("querystring"))
|
||||||
|
.thenReturn(Flowable.just(listOf(depictedItem())))
|
||||||
|
val method: Method = DepictsPresenter::class.java.getDeclaredMethod(
|
||||||
|
"searchResults",
|
||||||
|
String::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(depictsPresenter, "querystring")
|
||||||
|
verify(repository, times(1)).searchAllEntities("querystring")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test searchResults when media is not null`() {
|
||||||
|
Whitebox.setInternalState(depictsPresenter, "media", media)
|
||||||
|
whenever(repository.getDepictions(repository.selectedExistingDepictions))
|
||||||
|
.thenReturn(Flowable.just(listOf(depictedItem())))
|
||||||
|
whenever(repository.searchAllEntities("querystring"))
|
||||||
|
.thenReturn(Flowable.just(listOf(depictedItem())))
|
||||||
|
val method: Method = DepictsPresenter::class.java.getDeclaredMethod(
|
||||||
|
"searchResults",
|
||||||
|
String::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(depictsPresenter, "querystring")
|
||||||
|
verify(repository, times(1)).searchAllEntities("querystring")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSelectNewDepictions() {
|
||||||
|
Whitebox.setInternalState(depictsPresenter, "media", media)
|
||||||
|
val method: Method = DepictsPresenter::class.java.getDeclaredMethod(
|
||||||
|
"selectNewDepictions",
|
||||||
|
List::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(depictsPresenter, listOf(depictedItem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClearPreviousSelection() {
|
||||||
|
val method: Method = DepictsPresenter::class.java.getDeclaredMethod(
|
||||||
|
"clearPreviousSelection"
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(depictsPresenter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
|
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||||
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||||
|
import media
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
class UploadModelUnitTest {
|
||||||
|
|
||||||
|
private lateinit var uploadModel: UploadModel
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
uploadModel = UploadModel(
|
||||||
|
listOf(),
|
||||||
|
mock(JsonKvStore::class.java),
|
||||||
|
mapOf(),
|
||||||
|
mock(Context::class.java),
|
||||||
|
mock(SessionManager::class.java),
|
||||||
|
mock(FileProcessor::class.java),
|
||||||
|
mock(ImageProcessingService::class.java)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when DepictedItem is selected`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
true,
|
||||||
|
"depictionId"
|
||||||
|
), media(filename = "File:Example.jpg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when DepictedItem is not selected`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
false,
|
||||||
|
"depictionId"
|
||||||
|
), media(filename = "File:Example.jpg")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when DepictedItem is not selected and not included in media`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
false,
|
||||||
|
"id"
|
||||||
|
), media(filename = "File:Example.jpg")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when media is null and DepictedItem is not selected`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
false,
|
||||||
|
"id"
|
||||||
|
), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when media is not null and DepictedItem is selected`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
true,
|
||||||
|
"id"
|
||||||
|
), media(filename = "File:Example.jpg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test onDepictItemClicked when media is null and DepictedItem is selected`(){
|
||||||
|
uploadModel.onDepictItemClicked(
|
||||||
|
DepictedItem(
|
||||||
|
"Test",
|
||||||
|
"Test",
|
||||||
|
"test",
|
||||||
|
listOf(),
|
||||||
|
listOf(),
|
||||||
|
true,
|
||||||
|
"id"
|
||||||
|
), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGetSelectedExistingDepictions(){
|
||||||
|
uploadModel.selectedExistingDepictions
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetSelectedExistingDepictions(){
|
||||||
|
uploadModel.selectedExistingDepictions = listOf("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package fr.free.nrw.commons.upload
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
import fr.free.nrw.commons.category.CategoriesModel
|
import fr.free.nrw.commons.category.CategoriesModel
|
||||||
import fr.free.nrw.commons.category.CategoryItem
|
import fr.free.nrw.commons.category.CategoryItem
|
||||||
|
|
@ -13,12 +14,15 @@ import fr.free.nrw.commons.repository.UploadRepository
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictModel
|
import fr.free.nrw.commons.upload.structure.depictions.DepictModel
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
|
import io.reactivex.Single
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
class UploadRepositoryUnitTest {
|
class UploadRepositoryUnitTest {
|
||||||
|
|
||||||
|
|
@ -204,11 +208,17 @@ class UploadRepositoryUnitTest {
|
||||||
assertEquals(repository.setSelectedLicense(""), uploadModel.setSelectedLicense(""))
|
assertEquals(repository.setSelectedLicense(""), uploadModel.setSelectedLicense(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetSelectedExistingDepictions() {
|
||||||
|
assertEquals(repository.setSelectedExistingDepictions(listOf("")),
|
||||||
|
uploadModel.setSelectedExistingDepictions(listOf("")))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOnDepictItemClicked() {
|
fun testOnDepictItemClicked() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
repository.onDepictItemClicked(depictedItem),
|
repository.onDepictItemClicked(depictedItem, mock()),
|
||||||
uploadModel.onDepictItemClicked(depictedItem)
|
uploadModel.onDepictItemClicked(depictedItem, mock())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +227,11 @@ class UploadRepositoryUnitTest {
|
||||||
assertEquals(repository.selectedDepictions, uploadModel.selectedDepictions)
|
assertEquals(repository.selectedDepictions, uploadModel.selectedDepictions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGetSelectedExistingDepictions() {
|
||||||
|
assertEquals(repository.selectedExistingDepictions, uploadModel.selectedExistingDepictions)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSearchAllEntities() {
|
fun testSearchAllEntities() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
@ -292,4 +307,36 @@ class UploadRepositoryUnitTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGetDepictions() {
|
||||||
|
`when`(depictModel.getDepictions("Q12"))
|
||||||
|
.thenReturn(Single.just(listOf(mock(DepictedItem::class.java))))
|
||||||
|
val method: Method = UploadRepository::class.java.getDeclaredMethod(
|
||||||
|
"getDepictions",
|
||||||
|
List::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(repository, listOf("Q12"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJoinIDs() {
|
||||||
|
val method: Method = UploadRepository::class.java.getDeclaredMethod(
|
||||||
|
"joinQIDs",
|
||||||
|
List::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(repository, listOf("Q12", "Q23"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test joinIDs when depictIDs is null`() {
|
||||||
|
val method: Method = UploadRepository::class.java.getDeclaredMethod(
|
||||||
|
"joinQIDs",
|
||||||
|
List::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(repository, null)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package fr.free.nrw.commons.upload.depicts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
|
import fr.free.nrw.commons.Media
|
||||||
|
import fr.free.nrw.commons.R
|
||||||
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
|
import fr.free.nrw.commons.notification.NotificationHelper
|
||||||
|
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||||
|
import fr.free.nrw.commons.wikidata.WikidataEditService
|
||||||
|
import io.reactivex.Observable
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.powermock.reflect.Whitebox
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.RuntimeEnvironment
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import org.robolectric.annotation.LooperMode
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||||
|
@LooperMode(LooperMode.Mode.PAUSED)
|
||||||
|
class DepictEditHelperUnitTest {
|
||||||
|
|
||||||
|
private lateinit var context: Context
|
||||||
|
private lateinit var helper: DepictEditHelper
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var notificationHelper: NotificationHelper
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var wikidataEditService: WikidataEditService
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var viewUtilWrapper: ViewUtilWrapper
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var media: Media
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
context = RuntimeEnvironment.application.applicationContext
|
||||||
|
helper = DepictEditHelper(notificationHelper, wikidataEditService, viewUtilWrapper)
|
||||||
|
Whitebox.setInternalState(helper, "viewUtilWrapper", viewUtilWrapper)
|
||||||
|
Whitebox.setInternalState(helper, "notificationHelper", notificationHelper)
|
||||||
|
Whitebox.setInternalState(helper, "wikidataEditService", wikidataEditService)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun checkNotNull() {
|
||||||
|
Assert.assertNotNull(helper)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testMakeDepictEdit() {
|
||||||
|
whenever(wikidataEditService.updateDepictsProperty(media.filename, listOf("Q12")))
|
||||||
|
.thenReturn(Observable.just(true))
|
||||||
|
helper.makeDepictionEdit(context, media, listOf("Q12"))
|
||||||
|
Mockito.verify(viewUtilWrapper, Mockito.times(1)).showShortToast(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.depictions_edit_helper_make_edit_toast)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testShowCoordinatesEditNotificationCaseTrue() {
|
||||||
|
whenever(media.depictionIds).thenReturn(listOf("id", "id2"))
|
||||||
|
val method: Method = DepictEditHelper::class.java.getDeclaredMethod(
|
||||||
|
"showDepictionEditNotification",
|
||||||
|
Context::class.java,
|
||||||
|
Media::class.java,
|
||||||
|
Boolean::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
Assertions.assertEquals(
|
||||||
|
method.invoke(helper, context, media, true),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testShowCoordinatesEditNotificationCaseFalse() {
|
||||||
|
val method: Method = DepictEditHelper::class.java.getDeclaredMethod(
|
||||||
|
"showDepictionEditNotification",
|
||||||
|
Context::class.java,
|
||||||
|
Media::class.java,
|
||||||
|
Boolean::class.java
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
Assertions.assertEquals(
|
||||||
|
method.invoke(helper, context, media, false),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package fr.free.nrw.commons.upload.depicts
|
package fr.free.nrw.commons.upload.depicts
|
||||||
|
|
||||||
|
import android.app.ProgressDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.Button
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
|
@ -11,9 +13,13 @@ import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
|
import depictedItem
|
||||||
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.R
|
import fr.free.nrw.commons.R
|
||||||
import fr.free.nrw.commons.TestAppAdapter
|
import fr.free.nrw.commons.TestAppAdapter
|
||||||
import fr.free.nrw.commons.TestCommonsApplication
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
|
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||||
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
|
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText
|
||||||
import fr.free.nrw.commons.upload.UploadActivity
|
import fr.free.nrw.commons.upload.UploadActivity
|
||||||
import fr.free.nrw.commons.upload.UploadBaseFragment
|
import fr.free.nrw.commons.upload.UploadBaseFragment
|
||||||
|
|
@ -62,6 +68,9 @@ class DepictsFragmentUnitTests {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var progressBar: ProgressBar
|
private lateinit var progressBar: ProgressBar
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var button: Button
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var textInputLayout: TextInputLayout
|
private lateinit var textInputLayout: TextInputLayout
|
||||||
|
|
||||||
|
|
@ -74,6 +83,15 @@ class DepictsFragmentUnitTests {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var adapter: UploadDepictsAdapter
|
private lateinit var adapter: UploadDepictsAdapter
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var applicationKvStore: JsonKvStore
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var media: Media
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var progressDialog: ProgressDialog
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
@ -95,6 +113,8 @@ class DepictsFragmentUnitTests {
|
||||||
Whitebox.setInternalState(fragment, "depictsTitle", textView)
|
Whitebox.setInternalState(fragment, "depictsTitle", textView)
|
||||||
Whitebox.setInternalState(fragment, "callback", callback)
|
Whitebox.setInternalState(fragment, "callback", callback)
|
||||||
Whitebox.setInternalState(fragment, "tooltip", imageView)
|
Whitebox.setInternalState(fragment, "tooltip", imageView)
|
||||||
|
Whitebox.setInternalState(fragment, "btnNext", button)
|
||||||
|
Whitebox.setInternalState(fragment, "btnPrevious", button)
|
||||||
Whitebox.setInternalState(fragment, "depictsSubTitle", textView)
|
Whitebox.setInternalState(fragment, "depictsSubTitle", textView)
|
||||||
Whitebox.setInternalState(fragment, "depictsRecyclerView", recyclerView)
|
Whitebox.setInternalState(fragment, "depictsRecyclerView", recyclerView)
|
||||||
Whitebox.setInternalState(fragment, "depictsSearch", textInputEditText)
|
Whitebox.setInternalState(fragment, "depictsSearch", textInputEditText)
|
||||||
|
|
@ -126,6 +146,17 @@ class DepictsFragmentUnitTests {
|
||||||
method.invoke(fragment)
|
method.invoke(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun `Test init when media is not null`() {
|
||||||
|
Whitebox.setInternalState(fragment, "media", media)
|
||||||
|
val method: Method = DepictsFragment::class.java.getDeclaredMethod(
|
||||||
|
"init"
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnBecameVisible() {
|
fun testOnBecameVisible() {
|
||||||
|
|
@ -184,6 +215,20 @@ class DepictsFragmentUnitTests {
|
||||||
fragment.setDepictsList(listOf())
|
fragment.setDepictsList(listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun `Test setDepictsList when list is not empty`() {
|
||||||
|
fragment.setDepictsList(listOf(depictedItem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun `Test setDepictsList when applicationKvStore returns true`() {
|
||||||
|
Whitebox.setInternalState(fragment, "applicationKvStore", applicationKvStore)
|
||||||
|
whenever(applicationKvStore.getBoolean("first_edit_depict")).thenReturn(true)
|
||||||
|
fragment.setDepictsList(listOf(depictedItem()))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnNextButtonClicked() {
|
fun testOnNextButtonClicked() {
|
||||||
|
|
@ -207,4 +252,61 @@ class DepictsFragmentUnitTests {
|
||||||
method.invoke(fragment, "")
|
method.invoke(fragment, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testOnResume() {
|
||||||
|
fragment.onResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testOnStop() {
|
||||||
|
fragment.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testInitRecyclerView() {
|
||||||
|
val method: Method = DepictsFragment::class.java.getDeclaredMethod(
|
||||||
|
"initRecyclerView"
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun `Test initRecyclerView when media is not null`() {
|
||||||
|
Whitebox.setInternalState(fragment, "media", media)
|
||||||
|
val method: Method = DepictsFragment::class.java.getDeclaredMethod(
|
||||||
|
"initRecyclerView"
|
||||||
|
)
|
||||||
|
method.isAccessible = true
|
||||||
|
method.invoke(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testGetFragmentContext() {
|
||||||
|
fragment.fragmentContext
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testGoBackToPreviousScreen() {
|
||||||
|
fragment.goBackToPreviousScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testShowProgressDialog() {
|
||||||
|
fragment.showProgressDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testDismissProgressDialog() {
|
||||||
|
Whitebox.setInternalState(fragment, "progressDialog", progressDialog)
|
||||||
|
fragment.dismissProgressDialog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package fr.free.nrw.commons.wikidata
|
||||||
|
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.InjectMocks
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.wikipedia.csrf.CsrfTokenClient
|
||||||
|
|
||||||
|
class WikiBaseClientUnitTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
internal var csrfTokenClient: CsrfTokenClient? = null
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
var wikiBaseClient: WikiBaseClient? = null
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
Mockito.`when`(csrfTokenClient!!.tokenBlocking)
|
||||||
|
.thenReturn("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPostEditEntityByFilename() {
|
||||||
|
wikiBaseClient?.postEditEntityByFilename("File:Example.jpg", "data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,9 @@ import org.mockito.ArgumentMatchers.any
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.InjectMocks
|
import org.mockito.InjectMocks
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.wikipedia.wikidata.EditClaim
|
||||||
|
|
||||||
class WikidataEditServiceTest {
|
class WikidataEditServiceTest {
|
||||||
@Mock
|
@Mock
|
||||||
|
|
@ -48,6 +50,13 @@ class WikidataEditServiceTest {
|
||||||
verifyZeroInteractions(wikidataClient)
|
verifyZeroInteractions(wikidataClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUpdateDepictsProperty() {
|
||||||
|
whenever(wikibaseClient.postEditEntityByFilename("Test.jpg",
|
||||||
|
gson.toJson(Mockito.mock(EditClaim::class.java)))).thenReturn(Observable.just(true))
|
||||||
|
wikidataEditService.updateDepictsProperty("Test.jpg", listOf())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createImageClaim() {
|
fun createImageClaim() {
|
||||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun from(entityId: String, propertyName: String) =
|
fun from(entityIds: List<String>, propertyName: String): EditClaim {
|
||||||
EditClaim(
|
|
||||||
listOf(
|
val list = mutableListOf<Statement_partial>()
|
||||||
|
entityIds.forEach {
|
||||||
|
list.add(
|
||||||
Statement_partial(
|
Statement_partial(
|
||||||
Snak_partial(
|
Snak_partial(
|
||||||
"value",
|
"value",
|
||||||
|
|
@ -17,8 +19,8 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
||||||
EntityId(
|
EntityId(
|
||||||
WikiBaseEntityValue(
|
WikiBaseEntityValue(
|
||||||
"item",
|
"item",
|
||||||
entityId,
|
it,
|
||||||
entityId.removePrefix("Q").toLong()
|
it.removePrefix("Q").toLong()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
@ -26,6 +28,8 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
||||||
"preferred"
|
"preferred"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
return EditClaim(list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue