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 -> {
|
||||
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) {
|
||||
setTitle(getString(R.string.navigation_item_explore));
|
||||
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.WIKITEXT;
|
||||
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 android.annotation.SuppressLint;
|
||||
import java.lang.reflect.Field;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -45,6 +43,8 @@ import android.widget.Spinner;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
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.profile.ProfileActivity;
|
||||
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.utils.ViewUtilWrapper;
|
||||
import io.reactivex.Single;
|
||||
|
|
@ -93,7 +94,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
@ -176,6 +176,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
LinearLayout captionLayout;
|
||||
@BindView(R.id.depicts_layout)
|
||||
LinearLayout depictsLayout;
|
||||
@BindView(R.id.depictionsEditButton)
|
||||
Button depictEditButton;
|
||||
@BindView(R.id.media_detail_caption)
|
||||
TextView mediaCaption;
|
||||
@BindView(R.id.mediaDetailDesc)
|
||||
|
|
@ -239,7 +241,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
@BindView(R.id.description_label)
|
||||
TextView descriptionLabel;
|
||||
@BindView(R.id.pb_circular)
|
||||
ProgressBar progressBar;
|
||||
ProgressBar progressBar;
|
||||
String descriptionHtmlCode;
|
||||
@BindView(R.id.progressBarDeletion)
|
||||
ProgressBar progressBarDeletion;
|
||||
|
|
@ -467,10 +469,10 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
private void displayMediaDetails() {
|
||||
setTextFields(media);
|
||||
compositeDisposable.addAll(
|
||||
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
|
||||
mediaDataExtractor.refresh(media)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::onDepictionsLoaded, Timber::e),
|
||||
.subscribe(this::onMediaRefreshed, Timber::e),
|
||||
mediaDataExtractor.checkDeletionRequestExists(media)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -478,15 +480,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
mediaDataExtractor.fetchDiscussion(media)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::onDiscussionLoaded, Timber::e),
|
||||
mediaDataExtractor.refresh(media)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::onMediaRefreshed, Timber::e)
|
||||
.subscribe(this::onDiscussionLoaded, Timber::e)
|
||||
);
|
||||
}
|
||||
|
||||
private void onMediaRefreshed(Media media) {
|
||||
this.media = media;
|
||||
setTextFields(media);
|
||||
compositeDisposable.addAll(
|
||||
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
|
||||
|
|
@ -517,8 +516,26 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
}
|
||||
|
||||
private void onDepictionsLoaded(List<IdAndCaptions> idAndCaptions){
|
||||
depictsLayout.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
|
||||
buildDepictionList(idAndCaptions);
|
||||
depictsLayout.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public class NotificationHelper {
|
|||
public static final int NOTIFICATION_EDIT_CATEGORY = 2;
|
||||
public static final int NOTIFICATION_EDIT_COORDINATES = 3;
|
||||
public static final int NOTIFICATION_EDIT_DESCRIPTION = 4;
|
||||
public static final int NOTIFICATION_EDIT_DEPICTIONS = 5;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.repository;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.category.CategoriesModel;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
|
|
@ -233,8 +234,8 @@ public class UploadRepository {
|
|||
uploadModel.setSelectedLicense(licenseName);
|
||||
}
|
||||
|
||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
||||
uploadModel.onDepictItemClicked(depictedItem);
|
||||
public void onDepictItemClicked(DepictedItem depictedItem, final Media media) {
|
||||
uploadModel.onDepictItemClicked(depictedItem, media);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,6 +248,23 @@ public class UploadRepository {
|
|||
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
|
||||
*
|
||||
|
|
@ -275,6 +293,39 @@ public class UploadRepository {
|
|||
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
|
||||
* @param decLatitude
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload;
|
|||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
|
|
@ -40,6 +41,10 @@ public class UploadModel {
|
|||
private final ImageProcessingService imageProcessingService;
|
||||
private final List<String> selectedCategories = new ArrayList<>();
|
||||
private final List<DepictedItem> selectedDepictions = new ArrayList<>();
|
||||
/**
|
||||
* Existing depicts which are selected
|
||||
*/
|
||||
private List<String> selectedExistingDepictions = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
UploadModel(@Named("licenses") final List<String> licenses,
|
||||
|
|
@ -68,6 +73,7 @@ public class UploadModel {
|
|||
items.clear();
|
||||
selectedCategories.clear();
|
||||
selectedDepictions.clear();
|
||||
selectedExistingDepictions.clear();
|
||||
}
|
||||
|
||||
public void setSelectedCategories(List<String> selectedCategories) {
|
||||
|
|
@ -185,11 +191,33 @@ public class UploadModel {
|
|||
return items;
|
||||
}
|
||||
|
||||
public void onDepictItemClicked(DepictedItem depictedItem) {
|
||||
if (depictedItem.isSelected()) {
|
||||
selectedDepictions.add(depictedItem);
|
||||
public void onDepictItemClicked(DepictedItem depictedItem, Media media) {
|
||||
if (media == null) {
|
||||
if (depictedItem.isSelected()) {
|
||||
selectedDepictions.add(depictedItem);
|
||||
} else {
|
||||
selectedDepictions.remove(depictedItem);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("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")
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -40,6 +43,36 @@ public interface DepictsContract {
|
|||
* add depictions to list
|
||||
*/
|
||||
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> {
|
||||
|
|
@ -71,6 +104,21 @@ public interface DepictsContract {
|
|||
*/
|
||||
void verifyDepictions();
|
||||
|
||||
/**
|
||||
* Clears previous selections
|
||||
*/
|
||||
void clearPreviousSelection();
|
||||
|
||||
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;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
|
|
@ -18,7 +24,11 @@ import butterknife.OnClick;
|
|||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||
import fr.free.nrw.commons.Media;
|
||||
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.upload.UploadActivity;
|
||||
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.disposables.Disposable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import kotlin.Unit;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -52,11 +64,26 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
RecyclerView depictsRecyclerView;
|
||||
@BindView(R.id.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
|
||||
DepictsContract.UserActionListener presenter;
|
||||
private UploadDepictsAdapter adapter;
|
||||
private Disposable subscribe;
|
||||
private Media media;
|
||||
private ProgressDialog progressDialog;
|
||||
/**
|
||||
* Determines each encounter of edit depicts
|
||||
*/
|
||||
private int count;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public android.view.View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
|
|
@ -67,7 +94,13 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
@Override
|
||||
public void onViewCreated(@NonNull android.view.View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
ButterKnife.bind(this, view);
|
||||
Bundle bundle = getArguments();
|
||||
if (bundle != null) {
|
||||
media = bundle.getParcelable("Existing_Depicts");
|
||||
}
|
||||
|
||||
init();
|
||||
presenter.getDepictedItems().observe(getViewLifecycleOwner(), this::setDepictsList);
|
||||
}
|
||||
|
|
@ -76,13 +109,27 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
* Initialize presenter and views
|
||||
*/
|
||||
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();
|
||||
tooltip.setOnClickListener(v -> DialogUtil
|
||||
.showAlertDialog(getActivity(), getString(R.string.depicts_step_title),
|
||||
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();
|
||||
addTextChangeListenerToSearchBox();
|
||||
}
|
||||
|
|
@ -105,10 +152,17 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
* Initialise recyclerView and set adapter
|
||||
*/
|
||||
private void initRecyclerView() {
|
||||
adapter = new UploadDepictsAdapter(item -> {
|
||||
presenter.onDepictItemClicked(item);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
if (media == null) {
|
||||
adapter = new UploadDepictsAdapter(categoryItem -> {
|
||||
presenter.onDepictItemClicked(categoryItem);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
} else {
|
||||
adapter = new UploadDepictsAdapter(item -> {
|
||||
presenter.onDepictItemClicked(item);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
depictsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
depictsRecyclerView.setAdapter(adapter);
|
||||
}
|
||||
|
|
@ -133,19 +187,28 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
|
||||
@Override
|
||||
public void noDepictionSelected() {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.no_depictions_selected),
|
||||
getString(R.string.no_depictions_selected_warning_desc),
|
||||
getString(R.string.continue_message),
|
||||
getString(R.string.cancel),
|
||||
this::goToNextScreen,
|
||||
null
|
||||
);
|
||||
if (media == null) {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.no_depictions_selected),
|
||||
getString(R.string.no_depictions_selected_warning_desc),
|
||||
getString(R.string.continue_message),
|
||||
getString(R.string.cancel),
|
||||
this::goToNextScreen,
|
||||
null
|
||||
);
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.no_depictions_selected),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
presenter.clearPreviousSelection();
|
||||
updateDepicts();
|
||||
goBackToPreviousScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
media = null;
|
||||
presenter.onDetachView();
|
||||
subscribe.dispose();
|
||||
}
|
||||
|
|
@ -166,18 +229,98 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
|
|||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@OnClick(R.id.depicts_next)
|
||||
public void onNextButtonClicked() {
|
||||
presenter.verifyDepictions();
|
||||
/**
|
||||
* Returns required context
|
||||
*/
|
||||
@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)
|
||||
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) {
|
||||
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
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.wikidata.WikidataDisambiguationItems
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.Proxy
|
||||
import java.util.*
|
||||
|
|
@ -35,8 +39,11 @@ class DepictsPresenter @Inject constructor(
|
|||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
private val searchTerm: PublishProcessor<String> = PublishProcessor.create()
|
||||
private val depictedItems: MutableLiveData<List<DepictedItem>> = MutableLiveData()
|
||||
private var media: Media? = null
|
||||
@Inject
|
||||
lateinit var depictsDao: DepictsDao;
|
||||
lateinit var depictsDao: DepictsDao
|
||||
@Inject
|
||||
lateinit var depictsHelper: DepictEditHelper
|
||||
|
||||
override fun onAttachView(view: DepictsContract.View) {
|
||||
this.view = view
|
||||
|
|
@ -71,16 +78,37 @@ class DepictsPresenter @Inject constructor(
|
|||
if (querystring.isEmpty()) {
|
||||
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() {
|
||||
view = DUMMY
|
||||
media = null
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +130,7 @@ class DepictsPresenter @Inject constructor(
|
|||
private fun selectNewDepictions(toSelect: List<DepictedItem>) {
|
||||
toSelect.forEach {
|
||||
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
|
||||
|
|
@ -113,12 +141,16 @@ class DepictsPresenter @Inject constructor(
|
|||
?.let { depictedItems.value = it }
|
||||
}
|
||||
|
||||
override fun clearPreviousSelection() {
|
||||
repository.cleanup()
|
||||
}
|
||||
|
||||
override fun onPreviousButtonClicked() {
|
||||
view.goToPreviousScreen()
|
||||
}
|
||||
|
||||
override fun onDepictItemClicked(depictedItem: DepictedItem) {
|
||||
repository.onDepictItemClicked(depictedItem)
|
||||
repository.onDepictItemClicked(depictedItem, media)
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
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.media.MediaClientKt.PAGE_ID_PREFIX;
|
||||
|
||||
import fr.free.nrw.commons.upload.UploadResult;
|
||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||
|
|
@ -35,6 +35,20 @@ public class WikiBaseClient {
|
|||
.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) {
|
||||
return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName())
|
||||
.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.ViewUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -73,11 +72,12 @@ public class WikidataEditService {
|
|||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private Observable<Boolean> addDepictsProperty(final String fileEntityId,
|
||||
final WikidataItem depictedItem) {
|
||||
final List<String> depictedItems) {
|
||||
|
||||
final EditClaim data = editClaim(
|
||||
ConfigUtils.isBetaFlavour() ? "Q10" // Wikipedia:Sandbox (Q10)
|
||||
: depictedItem.getId()
|
||||
ConfigUtils.isBetaFlavour() ? Collections.singletonList("Q10")
|
||||
// Wikipedia:Sandbox (Q10)
|
||||
: depictedItems
|
||||
);
|
||||
|
||||
return wikiBaseClient.postEditEntity(PAGE_ID_PREFIX + fileEntityId, gson.toJson(data))
|
||||
|
|
@ -95,8 +95,42 @@ public class WikidataEditService {
|
|||
.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) {
|
||||
return Observable.fromIterable(contribution.getDepictedItems())
|
||||
.concatMap(wikidataItem -> addDepictsProperty(fileEntityId.toString(), wikidataItem));
|
||||
final List<String> depictIDs = new ArrayList<>();
|
||||
for (final WikidataItem wikidataItem :
|
||||
contribution.getDepictedItems()) {
|
||||
depictIDs.add(wikidataItem.getId());
|
||||
}
|
||||
return addDepictsProperty(fileEntityId.toString(), depictIDs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -336,6 +336,15 @@
|
|||
android:orientation="vertical" />
|
||||
</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
|
||||
style="@style/MediaDetailContainer"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical"
|
||||
tools:showIn="@layout/activity_upload">
|
||||
android:background="?attr/mainBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="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_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_show_edit_title">Coordinates 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="achievements_of_user">Achievements of User: %s</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_query_info_text">You can customize the Nearby query. If you get errors, reset and apply.</string>
|
||||
<string name="apply">Apply</string>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.os.Bundle
|
|||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.ViewTreeObserver
|
||||
import android.webkit.WebView
|
||||
import android.widget.*
|
||||
|
|
@ -19,7 +20,6 @@ import com.facebook.drawee.backends.pipeline.Fresco
|
|||
import com.facebook.drawee.generic.GenericDraweeHierarchy
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.facebook.soloader.SoLoader
|
||||
import fr.free.nrw.commons.*
|
||||
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
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.TestAppAdapter
|
||||
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.ReasonBuilder
|
||||
import fr.free.nrw.commons.utils.ImageUtils
|
||||
import io.reactivex.Single
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
|
@ -218,6 +216,7 @@ class MediaDetailFragmentUnitTests {
|
|||
Whitebox.setInternalState(fragment, "showCaptionAndDescriptionContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "updateCategoriesButton", button)
|
||||
Whitebox.setInternalState(fragment, "editDescription", button)
|
||||
Whitebox.setInternalState(fragment, "depictEditButton", button)
|
||||
Whitebox.setInternalState(fragment, "categoryContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "categorySearchView", searchView)
|
||||
Whitebox.setInternalState(fragment, "progressBarDeletion", progressBarDeletion)
|
||||
|
|
@ -656,4 +655,17 @@ class MediaDetailFragmentUnitTests {
|
|||
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
|
||||
|
||||
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.whenever
|
||||
import depictedItem
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsContract
|
||||
|
|
@ -16,7 +17,10 @@ import org.junit.Before
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.powermock.reflect.Whitebox
|
||||
import java.lang.reflect.Method
|
||||
|
||||
|
||||
class DepictsPresenterTest {
|
||||
|
|
@ -37,6 +41,9 @@ class DepictsPresenterTest {
|
|||
@Mock
|
||||
lateinit var depictsClient: DepictsClient
|
||||
|
||||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
/**
|
||||
* initial setup
|
||||
*/
|
||||
|
|
@ -92,9 +99,6 @@ class DepictsPresenterTest {
|
|||
testScheduler.triggerActions()
|
||||
verify(view).showProgress(false)
|
||||
verify(view).showError(true)
|
||||
depictsPresenter.depictedItems
|
||||
.test()
|
||||
.assertValue(emptyList())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -116,7 +120,7 @@ class DepictsPresenterTest {
|
|||
fun `onDepictItemClicked calls repository`() {
|
||||
val depictedItem = depictedItem()
|
||||
depictsPresenter.onDepictItemClicked(depictedItem)
|
||||
verify(repository).onDepictItemClicked(depictedItem)
|
||||
verify(repository).onDepictItemClicked(depictedItem, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -132,4 +136,63 @@ class DepictsPresenterTest {
|
|||
depictsPresenter.verifyDepictions()
|
||||
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
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import fr.free.nrw.commons.category.CategoriesModel
|
||||
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.DepictedItem
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Single
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.lang.reflect.Method
|
||||
|
||||
class UploadRepositoryUnitTest {
|
||||
|
||||
|
|
@ -204,11 +208,17 @@ class UploadRepositoryUnitTest {
|
|||
assertEquals(repository.setSelectedLicense(""), uploadModel.setSelectedLicense(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetSelectedExistingDepictions() {
|
||||
assertEquals(repository.setSelectedExistingDepictions(listOf("")),
|
||||
uploadModel.setSelectedExistingDepictions(listOf("")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnDepictItemClicked() {
|
||||
assertEquals(
|
||||
repository.onDepictItemClicked(depictedItem),
|
||||
uploadModel.onDepictItemClicked(depictedItem)
|
||||
repository.onDepictItemClicked(depictedItem, mock()),
|
||||
uploadModel.onDepictItemClicked(depictedItem, mock())
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -217,6 +227,11 @@ class UploadRepositoryUnitTest {
|
|||
assertEquals(repository.selectedDepictions, uploadModel.selectedDepictions)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSelectedExistingDepictions() {
|
||||
assertEquals(repository.selectedExistingDepictions, uploadModel.selectedExistingDepictions)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSearchAllEntities() {
|
||||
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
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
|
|
@ -11,9 +13,13 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.TestAppAdapter
|
||||
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.upload.UploadActivity
|
||||
import fr.free.nrw.commons.upload.UploadBaseFragment
|
||||
|
|
@ -62,6 +68,9 @@ class DepictsFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
@Mock
|
||||
private lateinit var button: Button
|
||||
|
||||
@Mock
|
||||
private lateinit var textInputLayout: TextInputLayout
|
||||
|
||||
|
|
@ -74,6 +83,15 @@ class DepictsFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var adapter: UploadDepictsAdapter
|
||||
|
||||
@Mock
|
||||
private lateinit var applicationKvStore: JsonKvStore
|
||||
|
||||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
@Mock
|
||||
private lateinit var progressDialog: ProgressDialog
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
|
@ -95,6 +113,8 @@ class DepictsFragmentUnitTests {
|
|||
Whitebox.setInternalState(fragment, "depictsTitle", textView)
|
||||
Whitebox.setInternalState(fragment, "callback", callback)
|
||||
Whitebox.setInternalState(fragment, "tooltip", imageView)
|
||||
Whitebox.setInternalState(fragment, "btnNext", button)
|
||||
Whitebox.setInternalState(fragment, "btnPrevious", button)
|
||||
Whitebox.setInternalState(fragment, "depictsSubTitle", textView)
|
||||
Whitebox.setInternalState(fragment, "depictsRecyclerView", recyclerView)
|
||||
Whitebox.setInternalState(fragment, "depictsSearch", textInputEditText)
|
||||
|
|
@ -126,6 +146,17 @@ class DepictsFragmentUnitTests {
|
|||
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
|
||||
@Throws(Exception::class)
|
||||
fun testOnBecameVisible() {
|
||||
|
|
@ -184,6 +215,20 @@ class DepictsFragmentUnitTests {
|
|||
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
|
||||
@Throws(Exception::class)
|
||||
fun testOnNextButtonClicked() {
|
||||
|
|
@ -207,4 +252,61 @@ class DepictsFragmentUnitTests {
|
|||
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.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.wikipedia.wikidata.EditClaim
|
||||
|
||||
class WikidataEditServiceTest {
|
||||
@Mock
|
||||
|
|
@ -48,6 +50,13 @@ class WikidataEditServiceTest {
|
|||
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
|
||||
fun createImageClaim() {
|
||||
whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true))
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
|||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun from(entityId: String, propertyName: String) =
|
||||
EditClaim(
|
||||
listOf(
|
||||
fun from(entityIds: List<String>, propertyName: String): EditClaim {
|
||||
|
||||
val list = mutableListOf<Statement_partial>()
|
||||
entityIds.forEach {
|
||||
list.add(
|
||||
Statement_partial(
|
||||
Snak_partial(
|
||||
"value",
|
||||
|
|
@ -17,8 +19,8 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
|||
EntityId(
|
||||
WikiBaseEntityValue(
|
||||
"item",
|
||||
entityId,
|
||||
entityId.removePrefix("Q").toLong()
|
||||
it,
|
||||
it.removePrefix("Q").toLong()
|
||||
)
|
||||
)
|
||||
),
|
||||
|
|
@ -26,6 +28,8 @@ data class EditClaim(val claims: List<Statement_partial>) {
|
|||
"preferred"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
return EditClaim(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue