mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Fixes 4620 : Editing categories of an existing picture: Reuse categories selection UI from the Upload Wizard (#4928)
* Entry to new UI * Getting existing categories * Hidden categories managed * Category edit updated * Category Edition implemented * Java docs added * Java docs added * Java docs added * Previous UI discarded * Test added * More test added * More test added * More test added * More test added * More java docs added * Minor changes
This commit is contained in:
parent
48343035d3
commit
11292ab514
32 changed files with 977 additions and 913 deletions
|
|
@ -206,10 +206,6 @@ public class BookmarkListRootFragment extends CommonsDaggerSupportFragment imple
|
|||
//check mediaDetailPage fragment is not null then we check mediaDetail.is Visible or not to avoid NullPointerException
|
||||
if (mediaDetails != null) {
|
||||
if (mediaDetails.isVisible()) {
|
||||
if (mediaDetails.backButtonClicked()) {
|
||||
// mediaDetails handled the back clicked , no further action required.
|
||||
return true;
|
||||
}
|
||||
// todo add get list fragment
|
||||
((BookmarkFragment) getParentFragment()).setupTabLayout();
|
||||
ArrayList<Integer> removed = mediaDetails.getRemovedItems();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.utils.StringSortingUtils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.functions.Function4
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
|
@ -21,6 +21,11 @@ class CategoriesModel @Inject constructor(
|
|||
) {
|
||||
private val selectedCategories: MutableList<CategoryItem> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Existing categories which are selected
|
||||
*/
|
||||
private var selectedExistingCategories: MutableList<String> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Returns if the item contains an year
|
||||
* @param item
|
||||
|
|
@ -119,6 +124,41 @@ class CategoriesModel @Inject constructor(
|
|||
}.toList().toObservable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches details of every category by their name, converts them into
|
||||
* CategoryItem and returns them in a list.
|
||||
*
|
||||
* @param categoryNames selected Categories
|
||||
* @return List of CategoryItem
|
||||
*/
|
||||
fun getCategoriesByName(categoryNames: List<String>):
|
||||
Observable<MutableList<CategoryItem>>? {
|
||||
return Observable.fromIterable(categoryNames)
|
||||
.map { categoryName ->
|
||||
buildCategories(categoryName)
|
||||
}.toList().toObservable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the categories and converts them into CategoryItem
|
||||
*/
|
||||
fun buildCategories(categoryName: String): CategoryItem {
|
||||
return categoryClient.getCategoriesByName(categoryName,
|
||||
categoryName, SEARCH_CATS_LIMIT).map {
|
||||
if(it.isNotEmpty()) {
|
||||
CategoryItem(
|
||||
it[0].name, it[0].description,
|
||||
it[0].thumbnail, it[0].isSelected
|
||||
)
|
||||
} else {
|
||||
CategoryItem(
|
||||
"Hidden", "Hidden",
|
||||
"hidden", false
|
||||
)
|
||||
}
|
||||
}.blockingGet()
|
||||
}
|
||||
|
||||
private fun combine(
|
||||
depictionCategories: List<CategoryItem>,
|
||||
locationCategories: List<CategoryItem>,
|
||||
|
|
@ -154,12 +194,35 @@ class CategoriesModel @Inject constructor(
|
|||
* Handles category item selection
|
||||
* @param item
|
||||
*/
|
||||
fun onCategoryItemClicked(item: CategoryItem) {
|
||||
if (item.isSelected) {
|
||||
selectedCategories.add(item)
|
||||
updateCategoryCount(item)
|
||||
fun onCategoryItemClicked(item: CategoryItem, media: Media?) {
|
||||
if (media == null) {
|
||||
if (item.isSelected) {
|
||||
selectedCategories.add(item)
|
||||
updateCategoryCount(item)
|
||||
} else {
|
||||
selectedCategories.remove(item)
|
||||
}
|
||||
} else {
|
||||
selectedCategories.remove(item)
|
||||
if (item.isSelected) {
|
||||
if (media.categories?.contains(item.name) == true) {
|
||||
selectedExistingCategories.add(item.name)
|
||||
} else {
|
||||
selectedCategories.add(item)
|
||||
updateCategoryCount(item)
|
||||
}
|
||||
} else {
|
||||
if (media.categories?.contains(item.name) == true) {
|
||||
selectedExistingCategories.remove(item.name)
|
||||
if (!media.categories?.contains(item.name)!!) {
|
||||
val categoriesList: MutableList<String> = ArrayList()
|
||||
categoriesList.add(item.name)
|
||||
categoriesList.addAll(media.categories!!)
|
||||
media.categories = categoriesList
|
||||
}
|
||||
} else {
|
||||
selectedCategories.remove(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,9 +239,28 @@ class CategoriesModel @Inject constructor(
|
|||
*/
|
||||
fun cleanUp() {
|
||||
selectedCategories.clear()
|
||||
selectedExistingCategories.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SEARCH_CATS_LIMIT = 25
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides selected existing categories
|
||||
*
|
||||
* @return selected existing categories
|
||||
*/
|
||||
fun getSelectedExistingCategories(): List<String> {
|
||||
return selectedExistingCategories
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize existing categories
|
||||
*
|
||||
* @param selectedExistingCategories existing categories
|
||||
*/
|
||||
fun setSelectedExistingCategories(selectedExistingCategories: MutableList<String>) {
|
||||
this.selectedExistingCategories = selectedExistingCategories
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,12 +216,6 @@ public class CategoryDetailsActivity extends BaseActivity
|
|||
@Override
|
||||
public void onBackPressed() {
|
||||
if (supportFragmentManager.getBackStackEntryCount() == 1){
|
||||
|
||||
// the back press is handled by the mediaDetails , no further action required.
|
||||
if(mediaDetails.backButtonClicked()){
|
||||
return;
|
||||
}
|
||||
|
||||
tabLayout.setVisibility(View.VISIBLE);
|
||||
viewPager.setVisibility(View.VISIBLE);
|
||||
mediaContainer.setVisibility(View.GONE);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ public class CategoryEditHelper {
|
|||
public final PageEditClient pageEditClient;
|
||||
private final ViewUtilWrapper viewUtil;
|
||||
private final String username;
|
||||
private Callback callback;
|
||||
|
||||
@Inject
|
||||
public CategoryEditHelper(NotificationHelper notificationHelper,
|
||||
|
|
@ -44,35 +43,40 @@ public class CategoryEditHelper {
|
|||
* @param categories
|
||||
* @return
|
||||
*/
|
||||
public Single<Boolean> makeCategoryEdit(Context context, Media media, List<String> categories, Callback callback) {
|
||||
public Single<Boolean> makeCategoryEdit(Context context, Media media, List<String> categories,
|
||||
final String wikiText) {
|
||||
viewUtil.showShortToast(context, context.getString(R.string.category_edit_helper_make_edit_toast));
|
||||
return addCategory(media, categories)
|
||||
return addCategory(media, categories, wikiText)
|
||||
.flatMapSingle(result -> Single.just(showCategoryEditNotification(context, media, result)))
|
||||
.firstOrError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends new categories
|
||||
* Rebuilds the WikiText with new categpries and post it on server
|
||||
*
|
||||
* @param media
|
||||
* @param categories to be added
|
||||
* @return
|
||||
*/
|
||||
private Observable<Boolean> addCategory(Media media, List<String> categories) {
|
||||
private Observable<Boolean> addCategory(Media media, List<String> categories,
|
||||
final String wikiText) {
|
||||
Timber.d("thread is category adding %s", Thread.currentThread().getName());
|
||||
String summary = "Adding categories";
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
|
||||
final String wikiTextWithoutCategory
|
||||
= wikiText.substring(0, wikiText.indexOf("[[Category"));
|
||||
|
||||
if (categories != null && categories.size() != 0) {
|
||||
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
buffer.append("\n[[Category:").append(categories.get(i)).append("]]");
|
||||
buffer.append("[[Category:").append(categories.get(i)).append("]]\n");
|
||||
}
|
||||
} else {
|
||||
buffer.append("{{subst:unc}}");
|
||||
}
|
||||
String appendText = buffer.toString();
|
||||
return pageEditClient.appendEdit(media.getFilename(), appendText + "\n", summary);
|
||||
final String appendText = wikiTextWithoutCategory + buffer;
|
||||
return pageEditClient.edit(media.getFilename(), appendText + "\n", summary);
|
||||
}
|
||||
|
||||
private boolean showCategoryEditNotification(Context context, Media media, boolean result) {
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter.RecyclerViewHolder;
|
||||
import fr.free.nrw.commons.nearby.Label;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CategoryEditSearchRecyclerViewAdapter
|
||||
extends RecyclerView.Adapter<RecyclerViewHolder>
|
||||
implements Filterable {
|
||||
|
||||
private List<String> displayedCategories;
|
||||
private List<String> categories = new ArrayList<>();
|
||||
private List<String> newCategories = new ArrayList<>();
|
||||
private final LayoutInflater inflater;
|
||||
private CategoryClient categoryClient;
|
||||
private Context context;
|
||||
|
||||
private Callback callback;
|
||||
|
||||
public CategoryEditSearchRecyclerViewAdapter(Context context, ArrayList<Label> labels,
|
||||
RecyclerView categoryRecyclerView, CategoryClient categoryClient, Callback callback) {
|
||||
this.context = context;
|
||||
inflater = LayoutInflater.from(context);
|
||||
this.categoryClient = categoryClient;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void addToCategories(List<String> categories) {
|
||||
for(String category : categories) {
|
||||
if (!this.categories.contains(category)) {
|
||||
this.categories.add(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFromNewCategories(String categoryToBeRemoved) {
|
||||
if (newCategories.contains(categoryToBeRemoved)) {
|
||||
newCategories.remove(categoryToBeRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToNewCategories(String addedCategory) {
|
||||
if (!newCategories.contains(addedCategory)) {
|
||||
newCategories.add(addedCategory);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
public List<String> getNewCategories() {
|
||||
return newCategories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new Filter() {
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
FilterResults results = new FilterResults();
|
||||
List<CategoryItem> resultCategories = categoryClient
|
||||
.searchCategories(constraint.toString(), 10).blockingGet();
|
||||
final List<String> namesOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
resultCategories) {
|
||||
namesOfCommonsCategories.add(category.getName());
|
||||
}
|
||||
results.values = namesOfCommonsCategories;
|
||||
results.count = resultCategories.size();
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
List<String> resultList = (List<String>)results.values;
|
||||
// Do not re-add already added categories
|
||||
for (String category : categories) {
|
||||
if (resultList.contains(category)) {
|
||||
resultList.remove(category);
|
||||
}
|
||||
}
|
||||
|
||||
displayedCategories = resultList;
|
||||
notifyDataSetChanged();
|
||||
if (displayedCategories.size()==0) {
|
||||
callback.noResultsFound();
|
||||
} else {
|
||||
callback.someResultsFound();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
|
||||
public CheckBox categoryCheckBox;
|
||||
public TextView categoryTextView;
|
||||
|
||||
public RecyclerViewHolder(View view) {
|
||||
super(view);
|
||||
categoryCheckBox = view.findViewById(R.id.category_checkbox);
|
||||
categoryTextView = view.findViewById(R.id.category_text);
|
||||
categoryCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
if (isChecked) {
|
||||
addToNewCategories(categoryTextView.getText().toString());
|
||||
} else {
|
||||
removeFromNewCategories(categoryTextView.getText().toString());
|
||||
}
|
||||
List<String> allCategories = new ArrayList<>();
|
||||
allCategories.addAll(categories);
|
||||
allCategories.addAll(newCategories);
|
||||
callback.updateSelectedCategoriesTextView(allCategories);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View itemView = inflater.inflate(R.layout.layout_edit_category_item , parent, false);
|
||||
return new RecyclerViewHolder(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
|
||||
holder.categoryTextView.setText(displayedCategories.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return displayedCategories.get(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return (displayedCategories == null) ? 0 : displayedCategories.size();
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void updateSelectedCategoriesTextView(List<String> selectedCategories);
|
||||
void noResultsFound();
|
||||
void someResultsFound();
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
|
|||
featuredArguments.putString("categoryName", title);
|
||||
listFragment.setArguments(featuredArguments);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
|
|
@ -185,18 +185,12 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
|
|||
*/
|
||||
public boolean backPressed() {
|
||||
if (null != mediaDetails && mediaDetails.isVisible()) {
|
||||
// todo add get list fragment
|
||||
if (mediaDetails.backButtonClicked()) {
|
||||
// MediaDetails handled the event no further action required.
|
||||
return true;
|
||||
} else {
|
||||
((ExploreFragment) getParentFragment()).tabLayout.setVisibility(View.VISIBLE);
|
||||
removeFragment(mediaDetails);
|
||||
((ExploreFragment) getParentFragment()).setScroll(true);
|
||||
setFragment(listFragment, mediaDetails);
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
return true;
|
||||
}
|
||||
((ExploreFragment) getParentFragment()).tabLayout.setVisibility(View.VISIBLE);
|
||||
removeFragment(mediaDetails);
|
||||
((ExploreFragment) getParentFragment()).setScroll(true);
|
||||
setFragment(listFragment, mediaDetails);
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
return true;
|
||||
} else {
|
||||
((MainActivity) getActivity()).setSelectedItemId(NavTab.CONTRIBUTIONS.code());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,12 +259,6 @@ public class SearchActivity extends BaseActivity
|
|||
@Override
|
||||
public void onBackPressed() {
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() == 1){
|
||||
|
||||
// the back press is handled by the mediaDetails , no further action required.
|
||||
if(mediaDetails.backButtonClicked()){
|
||||
return;
|
||||
}
|
||||
|
||||
// back to search so show search toolbar and hide navigation toolbar
|
||||
searchView.setVisibility(View.VISIBLE);//set the searchview
|
||||
tabLayout.setVisibility(View.VISIBLE);
|
||||
|
|
|
|||
|
|
@ -183,12 +183,6 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
@Override
|
||||
public void onBackPressed() {
|
||||
if (supportFragmentManager.getBackStackEntryCount() == 1){
|
||||
|
||||
// back pressed is handled by the mediaDetails , no further action required.
|
||||
if(mediaDetailPagerFragment.backButtonClicked()){
|
||||
return;
|
||||
}
|
||||
|
||||
tabLayout.setVisibility(View.VISIBLE);
|
||||
viewPager.setVisibility(View.VISIBLE);
|
||||
mediaContainer.setVisibility(View.GONE);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import android.graphics.drawable.Animatable;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -38,15 +37,12 @@ import android.widget.LinearLayout;
|
|||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.SearchView;
|
||||
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;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
|
@ -57,8 +53,6 @@ import com.facebook.drawee.interfaces.DraweeController;
|
|||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
import com.jakewharton.rxbinding2.widget.RxSearchView;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import fr.free.nrw.commons.LocationPicker.LocationPicker;
|
||||
|
|
@ -71,8 +65,6 @@ import fr.free.nrw.commons.auth.SessionManager;
|
|||
import fr.free.nrw.commons.category.CategoryClient;
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||
import fr.free.nrw.commons.category.CategoryEditHelper;
|
||||
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter;
|
||||
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter.Callback;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.coordinates.CoordinateEditHelper;
|
||||
import fr.free.nrw.commons.delete.DeleteHelper;
|
||||
|
|
@ -83,9 +75,9 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
|||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
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.categories.UploadCategoriesFragment;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
||||
import fr.free.nrw.commons.upload.UploadMediaDetail;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
|
|
@ -99,7 +91,6 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -107,7 +98,7 @@ import org.wikipedia.language.AppLanguageLookUpTable;
|
|||
import org.wikipedia.util.DateUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class MediaDetailFragment extends CommonsDaggerSupportFragment implements Callback,
|
||||
public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
||||
CategoryEditHelper.Callback {
|
||||
|
||||
private static final int REQUEST_CODE = 1001 ;
|
||||
|
|
@ -212,24 +203,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
LinearLayout toDoLayout;
|
||||
@BindView(R.id.toDoReason)
|
||||
TextView toDoReason;
|
||||
@BindView(R.id.category_edit_layout)
|
||||
LinearLayout categoryEditLayout;
|
||||
@BindView(R.id.et_search)
|
||||
SearchView categorySearchView;
|
||||
@BindView(R.id.rv_categories)
|
||||
RecyclerView categoryRecyclerView;
|
||||
@BindView(R.id.update_categories_button)
|
||||
Button updateCategoriesButton;
|
||||
@BindView(R.id.coordinate_edit)
|
||||
Button coordinateEditButton;
|
||||
@BindView(R.id.dummy_category_edit_container)
|
||||
LinearLayout dummyCategoryEditContainer;
|
||||
@BindView(R.id.pb_categories)
|
||||
ProgressBar progressbarCategories;
|
||||
@BindView(R.id.existing_categories)
|
||||
TextView existingCategories;
|
||||
@BindView(R.id.no_results_found)
|
||||
TextView noResultsFound;
|
||||
@BindView(R.id.dummy_caption_description_container)
|
||||
LinearLayout showCaptionAndDescriptionContainer;
|
||||
@BindView(R.id.show_caption_description_textview)
|
||||
|
|
@ -247,6 +222,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
ProgressBar progressBarDeletion;
|
||||
@BindView(R.id.progressBarEdit)
|
||||
ProgressBar progressBarEditDescription;
|
||||
@BindView(R.id.progressBarEditCategory)
|
||||
ProgressBar progressBarEditCategory;
|
||||
@BindView(R.id.description_edit)
|
||||
Button editDescription;
|
||||
|
||||
|
|
@ -263,7 +240,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
private int newWidthOfImageView;
|
||||
private boolean heightVerifyingBoolean = true; // helps in maintaining aspect ratio
|
||||
private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
|
||||
private CategoryEditSearchRecyclerViewAdapter categoryEditSearchRecyclerViewAdapter;
|
||||
|
||||
//Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose
|
||||
private Media media;
|
||||
|
|
@ -396,11 +372,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
categoryEditSearchRecyclerViewAdapter =
|
||||
new CategoryEditSearchRecyclerViewAdapter(getContext(), new ArrayList<>(
|
||||
Label.valuesAsList()), categoryRecyclerView, categoryClient, this);
|
||||
categoryRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
categoryRecyclerView.setAdapter(categoryEditSearchRecyclerViewAdapter);
|
||||
// detail provider is null when fragment is shown in review activity
|
||||
if (detailProvider != null) {
|
||||
media = detailProvider.getMediaAtPosition(index);
|
||||
|
|
@ -473,6 +444,11 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::onMediaRefreshed, Timber::e),
|
||||
mediaDataExtractor.getCurrentWikiText(
|
||||
Objects.requireNonNull(media.getFilename()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::updateCategoryList, Timber::e),
|
||||
mediaDataExtractor.checkDeletionRequestExists(media)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -485,6 +461,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
}
|
||||
|
||||
private void onMediaRefreshed(Media media) {
|
||||
media.setCategories(this.media.getCategories());
|
||||
this.media = media;
|
||||
setTextFields(media);
|
||||
compositeDisposable.addAll(
|
||||
|
|
@ -494,7 +471,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
.subscribe(this::onDepictionsLoaded, Timber::e)
|
||||
);
|
||||
// compositeDisposable.add(disposable);
|
||||
setupToDo();
|
||||
}
|
||||
|
||||
private void onDiscussionLoaded(String discussion) {
|
||||
|
|
@ -602,30 +578,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
image.setController(controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays layout about missing actions to inform user
|
||||
* - Images that they uploaded with no categories/descriptions, so that they can add them
|
||||
* - Images that can be added to associated Wikipedia articles that have no pictures
|
||||
*/
|
||||
private void setupToDo() {
|
||||
updateToDoWarning();
|
||||
compositeDisposable.add(RxSearchView.queryTextChanges(categorySearchView)
|
||||
.takeUntil(RxView.detaches(categorySearchView))
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(query -> {
|
||||
this.categorySearchQuery = query.toString();
|
||||
//update image list
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
if (categoryEditLayout.getVisibility() == VISIBLE) {
|
||||
((CategoryEditSearchRecyclerViewAdapter) categoryRecyclerView.getAdapter()).
|
||||
getFilter().filter(query.toString());
|
||||
}
|
||||
}
|
||||
}, Timber::e
|
||||
));
|
||||
}
|
||||
|
||||
private void updateToDoWarning() {
|
||||
String toDoMessage = "";
|
||||
boolean toDoNeeded = false;
|
||||
|
|
@ -685,11 +637,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
|
||||
categoryNames.clear();
|
||||
categoryNames.addAll(media.getCategories());
|
||||
categoryEditSearchRecyclerViewAdapter.addToCategories(media.getCategories());
|
||||
updateSelectedCategoriesTextView(categoryEditSearchRecyclerViewAdapter.getCategories());
|
||||
|
||||
categoryRecyclerView.setVisibility(GONE);
|
||||
updateCategoryList();
|
||||
|
||||
if (media.getAuthor() == null || media.getAuthor().equals("")) {
|
||||
authorLayout.setVisibility(GONE);
|
||||
|
|
@ -698,20 +645,35 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
}
|
||||
}
|
||||
|
||||
private void updateCategoryList() {
|
||||
List<String> allCategories = new ArrayList<String>( media.getCategories());
|
||||
if (media.getAddedCategories() != null) {
|
||||
// TODO this added categories logic should be removed.
|
||||
// It is just a short term hack. Categories should be fetch everytime they are updated.
|
||||
// if media.getCategories contains addedCategory, then do not re-add them
|
||||
for (String addedCategory : media.getAddedCategories()) {
|
||||
if (allCategories.contains(addedCategory)) {
|
||||
media.setAddedCategories(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
allCategories.addAll(media.getAddedCategories());
|
||||
/**
|
||||
* Gets new categories from the WikiText and updates it on the UI
|
||||
*
|
||||
* @param s WikiText
|
||||
*/
|
||||
private void updateCategoryList(final String s) {
|
||||
final List<String> allCategories = new ArrayList<String>();
|
||||
int i = s.indexOf("[[Category:");
|
||||
while(i != -1){
|
||||
final String category = s.substring(i+11, s.indexOf("]]", i));
|
||||
allCategories.add(category);
|
||||
i = s.indexOf("]]", i);
|
||||
i = s.indexOf("[[Category:", i);
|
||||
}
|
||||
media.setCategories(allCategories);
|
||||
if (allCategories.isEmpty()) {
|
||||
// Stick in a filler element.
|
||||
allCategories.add(getString(R.string.detail_panel_cats_none));
|
||||
}
|
||||
categoryEditButton.setVisibility(VISIBLE);
|
||||
rebuildCatList(allCategories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the categories
|
||||
*/
|
||||
public void updateCategories() {
|
||||
List<String> allCategories = new ArrayList<String>(media.getAddedCategories());
|
||||
media.setCategories(allCategories);
|
||||
if (allCategories.isEmpty()) {
|
||||
// Stick in a filler element.
|
||||
allCategories.add(getString(R.string.detail_panel_cats_none));
|
||||
|
|
@ -720,35 +682,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
rebuildCatList(allCategories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSelectedCategoriesTextView(List<String> selectedCategories) {
|
||||
if (selectedCategories == null || selectedCategories.size() == 0) {
|
||||
updateCategoriesButton.setClickable(false);
|
||||
updateCategoriesButton.setAlpha(.5f);
|
||||
} else {
|
||||
existingCategories.setText(StringUtils.join(selectedCategories,", "));
|
||||
if (selectedCategories.equals(media.getCategories())) {
|
||||
updateCategoriesButton.setClickable(false);
|
||||
updateCategoriesButton.setAlpha(.5f);
|
||||
} else {
|
||||
updateCategoriesButton.setClickable(true);
|
||||
updateCategoriesButton.setAlpha(1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noResultsFound() {
|
||||
categoryRecyclerView.setVisibility(GONE);
|
||||
noResultsFound.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void someResultsFound() {
|
||||
categoryRecyclerView.setVisibility(VISIBLE);
|
||||
noResultsFound.setVisibility(GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates media details fragment with depiction list
|
||||
* @param idAndCaptions
|
||||
|
|
@ -802,41 +735,41 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
Toast.makeText(getContext(), getString(R.string.wikicode_copied), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@OnClick(R.id.dummy_category_edit_container)
|
||||
public void onOutsideOfCategoryEditClicked() {
|
||||
if (dummyCategoryEditContainer.getVisibility() == VISIBLE) {
|
||||
dummyCategoryEditContainer.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.categoryEditButton)
|
||||
public void onCategoryEditButtonClicked(){
|
||||
displayHideCategorySearch();
|
||||
progressBarEditCategory.setVisibility(VISIBLE);
|
||||
categoryEditButton.setVisibility(GONE);
|
||||
getWikiText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the categoryEditContainer.
|
||||
* returns true after closing the categoryEditContainer if open, implying that event was handled.
|
||||
* else returns false
|
||||
* @return
|
||||
* Gets WikiText from the server and send it to catgory editor
|
||||
*/
|
||||
public boolean hideCategoryEditContainerIfOpen(){
|
||||
if (dummyCategoryEditContainer.getVisibility() == VISIBLE) {
|
||||
// editCategory is open, close it and return true as the event was handled.
|
||||
dummyCategoryEditContainer.setVisibility(GONE);
|
||||
return true;
|
||||
}
|
||||
// Event was not handled.
|
||||
return false;
|
||||
private void getWikiText() {
|
||||
compositeDisposable.add(mediaDataExtractor.getCurrentWikiText(
|
||||
Objects.requireNonNull(media.getFilename()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::gotoCategoryEditor, Timber::e));
|
||||
}
|
||||
|
||||
public void displayHideCategorySearch() {
|
||||
showCaptionAndDescriptionContainer.setVisibility(GONE);
|
||||
if (dummyCategoryEditContainer.getVisibility() != VISIBLE) {
|
||||
dummyCategoryEditContainer.setVisibility(VISIBLE);
|
||||
} else {
|
||||
dummyCategoryEditContainer.setVisibility(GONE);
|
||||
}
|
||||
/**
|
||||
* Opens the category editor
|
||||
*
|
||||
* @param s WikiText
|
||||
*/
|
||||
private void gotoCategoryEditor(final String s) {
|
||||
categoryEditButton.setVisibility(VISIBLE);
|
||||
progressBarEditCategory.setVisibility(GONE);
|
||||
final Fragment categoriesFragment = new UploadCategoriesFragment();
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("Existing_Categories", media);
|
||||
bundle.putString("WikiText", s);
|
||||
categoriesFragment.setArguments(bundle);
|
||||
final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||
transaction.replace(R.id.mediaDetailFrameLayout, categoriesFragment);
|
||||
transaction.addToBackStack(null);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
@OnClick(R.id.coordinate_edit)
|
||||
|
|
@ -1113,29 +1046,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
media.setCaptions(updatedCaptions);
|
||||
}
|
||||
|
||||
@OnClick(R.id.update_categories_button)
|
||||
public void onUpdateCategoriesClicked() {
|
||||
updateCategories(categoryEditSearchRecyclerViewAdapter.getNewCategories());
|
||||
displayHideCategorySearch();
|
||||
}
|
||||
|
||||
@OnClick(R.id.cancel_categories_button)
|
||||
public void onCancelCategoriesClicked() {
|
||||
displayHideCategorySearch();
|
||||
}
|
||||
|
||||
public void updateCategories(List<String> selectedCategories) {
|
||||
compositeDisposable.add(categoryEditHelper.makeCategoryEdit(getContext(), media, selectedCategories, this)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
Timber.d("Categories are added.");
|
||||
onOutsideOfCategoryEditClicked();
|
||||
media.setAddedCategories(selectedCategories);
|
||||
updateCategoryList();
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetched coordinates are replaced with existing coordinates by a POST API call.
|
||||
* @param Latitude to be added
|
||||
|
|
@ -1417,7 +1327,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
|
||||
@OnClick(R.id.show_caption_description_textview)
|
||||
void showCaptionAndDescription() {
|
||||
dummyCategoryEditContainer.setVisibility(GONE);
|
||||
if (showCaptionAndDescriptionContainer.getVisibility() == GONE) {
|
||||
showCaptionAndDescriptionContainer.setVisibility(VISIBLE);
|
||||
setUpCaptionAndDescriptionLayout();
|
||||
|
|
|
|||
|
|
@ -404,16 +404,6 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
public void nominatingForDeletion(int index) {
|
||||
provider.refreshNominatedMedia(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* backButtonClicked is called on a back event in the media details pager.
|
||||
* returns true after closing the categoryEditContainer if open, implying that event was handled.
|
||||
* else returns false
|
||||
* @return
|
||||
*/
|
||||
public boolean backButtonClicked(){
|
||||
return ((MediaDetailFragment)(adapter.getCurrentFragment())).hideCategoryEditContainerIfOpen();
|
||||
}
|
||||
|
||||
public interface MediaDetailProvider {
|
||||
Media getMediaAtPosition(int i);
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@ public class UploadRepository {
|
|||
*
|
||||
* @param categoryItem
|
||||
*/
|
||||
public void onCategoryClicked(CategoryItem categoryItem) {
|
||||
categoriesModel.onCategoryItemClicked(categoryItem);
|
||||
public void onCategoryClicked(CategoryItem categoryItem, final Media media) {
|
||||
categoriesModel.onCategoryItemClicked(categoryItem, media);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -354,4 +354,32 @@ public class UploadRepository {
|
|||
public boolean isWMLSupportedForThisPlace() {
|
||||
return uploadModel.getItems().get(0).isWLMUpload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides selected existing categories
|
||||
*
|
||||
* @return selected existing categories
|
||||
*/
|
||||
public List<String> getSelectedExistingCategories() {
|
||||
return categoriesModel.getSelectedExistingCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize existing categories
|
||||
*
|
||||
* @param selectedExistingCategories existing categories
|
||||
*/
|
||||
public void setSelectedExistingCategories(final List<String> selectedExistingCategories) {
|
||||
categoriesModel.setSelectedExistingCategories(selectedExistingCategories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes category names and Gets CategoryItem from the server
|
||||
*
|
||||
* @param categories names of Category
|
||||
* @return Observable<List<CategoryItem>>
|
||||
*/
|
||||
public Observable<List<CategoryItem>> getCategories(final List<String> categories){
|
||||
return categoriesModel.getCategoriesByName(categories);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package fr.free.nrw.commons.upload.categories;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The contract with with UploadCategoriesFragment and its presenter would talk to each other
|
||||
|
|
@ -24,6 +26,35 @@ public interface CategoriesContract {
|
|||
|
||||
void showNoCategorySelected();
|
||||
|
||||
/**
|
||||
* Gets existing category names from media
|
||||
*/
|
||||
List<String> getExistingCategories();
|
||||
|
||||
/**
|
||||
* Returns required context
|
||||
*/
|
||||
Context getFragmentContext();
|
||||
|
||||
/**
|
||||
* Returns to previous fragment
|
||||
*/
|
||||
void goBackToPreviousScreen();
|
||||
|
||||
/**
|
||||
* Shows the progress dialog
|
||||
*/
|
||||
void showProgressDialog();
|
||||
|
||||
/**
|
||||
* Hides the progress dialog
|
||||
*/
|
||||
void dismissProgressDialog();
|
||||
|
||||
/**
|
||||
* Refreshes the categories
|
||||
*/
|
||||
void refreshCategories();
|
||||
}
|
||||
|
||||
interface UserActionListener extends BasePresenter<View> {
|
||||
|
|
@ -33,6 +64,23 @@ public interface CategoriesContract {
|
|||
void verifyCategories();
|
||||
|
||||
void onCategoryItemClicked(CategoryItem categoryItem);
|
||||
|
||||
/**
|
||||
* Attaches view and media
|
||||
*/
|
||||
void onAttachViewWithMedia(@NonNull CategoriesContract.View view, Media media);
|
||||
|
||||
/**
|
||||
* Clears previous selections
|
||||
*/
|
||||
void clearPreviousSelection();
|
||||
|
||||
/**
|
||||
* Update the categories
|
||||
*/
|
||||
void updateCategories(Media media, String wikiText);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package fr.free.nrw.commons.upload.categories
|
||||
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.category.CategoryEditHelper
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.depicts.proxy
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -31,6 +36,16 @@ class CategoriesPresenter @Inject constructor(
|
|||
var view = DUMMY
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val searchTerms = PublishSubject.create<String>()
|
||||
/**
|
||||
* Current media
|
||||
*/
|
||||
private var media: Media? = null
|
||||
|
||||
/**
|
||||
* helper class for editing categories
|
||||
*/
|
||||
@Inject
|
||||
lateinit var categoryEditHelper: CategoryEditHelper
|
||||
|
||||
override fun onAttachView(view: CategoriesContract.View) {
|
||||
this.view = view
|
||||
|
|
@ -59,10 +74,33 @@ class CategoriesPresenter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun searchResults(term: String) =
|
||||
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map { it.filterNot { categoryItem -> repository.containsYear(categoryItem.name) } }
|
||||
/**
|
||||
* If media is null : Fetches categories from server according to the term
|
||||
* Else : Fetches existing categories by their name, fetches categories from server according
|
||||
* to the term and combines both in a list
|
||||
*/
|
||||
private fun searchResults(term: String): Observable<List<CategoryItem>>? {
|
||||
if (media == null) {
|
||||
return repository.searchAll(term, getImageTitleList(), repository.selectedDepictions)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map { it.filterNot { categoryItem -> repository.containsYear(categoryItem.name) } }
|
||||
} else {
|
||||
return Observable.zip(
|
||||
repository.getCategories(repository.selectedExistingCategories)
|
||||
.map { list -> list.map {
|
||||
CategoryItem(it.name, it.description, it.thumbnail, true)
|
||||
}
|
||||
},
|
||||
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions),
|
||||
{ it1, it2 ->
|
||||
it1 + it2
|
||||
}
|
||||
)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map { it.filterNot { categoryItem -> repository.containsYear(categoryItem.name) } }
|
||||
.map { it.filterNot { categoryItem -> categoryItem.thumbnail == "hidden" } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachView() {
|
||||
view = DUMMY
|
||||
|
|
@ -106,6 +144,88 @@ class CategoriesPresenter @Inject constructor(
|
|||
* @param categoryItem
|
||||
*/
|
||||
override fun onCategoryItemClicked(categoryItem: CategoryItem) {
|
||||
repository.onCategoryClicked(categoryItem)
|
||||
repository.onCategoryClicked(categoryItem, media)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches view and media
|
||||
*/
|
||||
override fun onAttachViewWithMedia(view: CategoriesContract.View, media: Media) {
|
||||
this.view = view
|
||||
this.media = media
|
||||
repository.selectedExistingCategories = view.existingCategories
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
view.showError(null)
|
||||
view.setCategories(null)
|
||||
}
|
||||
.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
view.setCategories(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty()) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
},
|
||||
Timber::e
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears previous selections
|
||||
*/
|
||||
override fun clearPreviousSelection() {
|
||||
repository.cleanup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected categories and send them for posting to the server
|
||||
*
|
||||
* @param media media
|
||||
* @param wikiText current WikiText from server
|
||||
*/
|
||||
override fun updateCategories(media: Media, wikiText: String) {
|
||||
if (repository.selectedCategories.isNotEmpty()
|
||||
|| repository.selectedExistingCategories.size != view.existingCategories.size
|
||||
) {
|
||||
val selectedCategories: MutableList<String> =
|
||||
(repository.selectedCategories.map { it.name }.toMutableList()
|
||||
+ repository.selectedExistingCategories).toMutableList()
|
||||
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
view.showProgressDialog()
|
||||
compositeDisposable.add(
|
||||
categoryEditHelper.makeCategoryEdit(view.fragmentContext, media,
|
||||
selectedCategories, wikiText)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
Timber.d("Categories are added.")
|
||||
media.addedCategories = selectedCategories
|
||||
repository.cleanup()
|
||||
view.dismissProgressDialog()
|
||||
view.refreshCategories()
|
||||
view.goBackToPreviousScreen()
|
||||
})
|
||||
{
|
||||
Timber.e(
|
||||
"Failed to update categories"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
} else {
|
||||
repository.cleanup()
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
package fr.free.nrw.commons.upload.categories;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
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;
|
||||
|
|
@ -20,8 +26,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.category.CategoryItem;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
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;
|
||||
|
|
@ -29,6 +38,7 @@ 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 kotlin.Unit;
|
||||
|
|
@ -50,11 +60,27 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
RecyclerView rvCategories;
|
||||
@BindView(R.id.tooltip)
|
||||
ImageView tooltip;
|
||||
@BindView(R.id.btn_next)
|
||||
Button btnNext;
|
||||
@BindView(R.id.btn_previous)
|
||||
Button btnPrevious;
|
||||
|
||||
@Inject
|
||||
CategoriesContract.UserActionListener presenter;
|
||||
private UploadCategoryAdapter adapter;
|
||||
private Disposable subscribe;
|
||||
/**
|
||||
* Current media
|
||||
*/
|
||||
private Media media;
|
||||
/**
|
||||
* Progress Dialog for showing background process
|
||||
*/
|
||||
private ProgressDialog progressDialog;
|
||||
/**
|
||||
* WikiText from the server
|
||||
*/
|
||||
private String wikiText;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
|
|
@ -67,12 +93,26 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
ButterKnife.bind(this, view);
|
||||
final Bundle bundle = getArguments();
|
||||
if (bundle != null) {
|
||||
media = bundle.getParcelable("Existing_Categories");
|
||||
wikiText = bundle.getString("WikiText");
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
tvTitle.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
|
||||
callback.getTotalNumberOfSteps(), getString(R.string.categories_activity_title)));
|
||||
if (media == null) {
|
||||
tvTitle.setText(getString(R.string.step_count, callback.getIndexInViewFlipper(this) + 1,
|
||||
callback.getTotalNumberOfSteps(), getString(R.string.categories_activity_title)));
|
||||
} else {
|
||||
tvTitle.setText(R.string.edit_categories);
|
||||
tvSubTitle.setVisibility(View.GONE);
|
||||
btnNext.setText(R.string.menu_save_categories);
|
||||
btnPrevious.setText(R.string.menu_cancel_upload);
|
||||
}
|
||||
|
||||
setTvSubTitle();
|
||||
tooltip.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
|
|
@ -80,7 +120,11 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
DialogUtil.showAlertDialog(getActivity(), getString(R.string.categories_activity_title), getString(R.string.categories_tooltip), getString(android.R.string.ok), null, true);
|
||||
}
|
||||
});
|
||||
presenter.onAttachView(this);
|
||||
if (media == null) {
|
||||
presenter.onAttachView(this);
|
||||
} else {
|
||||
presenter.onAttachViewWithMedia(this, media);
|
||||
}
|
||||
initRecyclerView();
|
||||
addTextChangeListenerToEtSearch();
|
||||
}
|
||||
|
|
@ -160,24 +204,96 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
|
||||
@Override
|
||||
public void showNoCategorySelected() {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
if (media == null) {
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.no_categories_selected),
|
||||
getString(R.string.no_categories_selected_warning_desc),
|
||||
getString(R.string.continue_message),
|
||||
getString(R.string.cancel),
|
||||
() -> goToNextScreen(),
|
||||
null);
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.no_categories_selected),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
presenter.clearPreviousSelection();
|
||||
goBackToPreviousScreen();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets existing categories from media
|
||||
*/
|
||||
@Override
|
||||
public List<String> getExistingCategories() {
|
||||
return (media == null) ? null : media.getCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns required context
|
||||
*/
|
||||
@Override
|
||||
public Context getFragmentContext() {
|
||||
return requireContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns to previous fragment
|
||||
*/
|
||||
@Override
|
||||
public void goBackToPreviousScreen() {
|
||||
getFragmentManager().popBackStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the categories
|
||||
*/
|
||||
@Override
|
||||
public void refreshCategories() {
|
||||
final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment();
|
||||
assert mediaDetailFragment != null;
|
||||
mediaDetailFragment.updateCategories();
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_next)
|
||||
public void onNextButtonClicked() {
|
||||
presenter.verifyCategories();
|
||||
if (media != null) {
|
||||
presenter.updateCategories(media, wikiText);
|
||||
} else {
|
||||
presenter.verifyCategories();
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.btn_previous)
|
||||
public void onPreviousButtonClicked() {
|
||||
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
|
||||
if (media != null) {
|
||||
presenter.clearPreviousSelection();
|
||||
adapter.setItems(null);
|
||||
final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment();
|
||||
assert mediaDetailFragment != null;
|
||||
mediaDetailFragment.onResume();
|
||||
goBackToPreviousScreen();
|
||||
} else {
|
||||
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -188,4 +304,65 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
|
|||
presenter.searchForCategories(text.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the action bar while opening editing fragment
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (media != null) {
|
||||
etSearch.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
etSearch.clearFocus();
|
||||
presenter.clearPreviousSelection();
|
||||
final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment();
|
||||
assert mediaDetailFragment != null;
|
||||
mediaDetailFragment.onResume();
|
||||
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();
|
||||
final MediaDetailFragment mediaDetailFragment = (MediaDetailFragment) getParentFragment();
|
||||
assert mediaDetailFragment != null;
|
||||
mediaDetailFragment.onResume();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,20 +9,6 @@
|
|||
android:id="@+id/mediaDetailFrameLayout"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dummy_category_edit_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:elevation="35dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical"
|
||||
android:weightSum="10">
|
||||
|
||||
<include
|
||||
layout="@layout/layout_edit_categories" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dummy_caption_description_container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -368,14 +354,39 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/categoryEditButton"
|
||||
android:layout_width="24dp"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="@dimen/standard_gap"
|
||||
android:layout_gravity="end"
|
||||
android:background="@drawable/ic_baseline_edit_24" />
|
||||
android:layout_margin="@dimen/standard_gap">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarEditCategory"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/tiny_gap"
|
||||
android:layout_marginRight="@dimen/tiny_gap"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateOnly="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mediaDetailCategoryContainer"
|
||||
app:layout_constraintBottom_toTopOf="@id/mediaDetailuploadeddate" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/categoryEditButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="end"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/ic_baseline_edit_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/mediaDetailCategoryContainer"
|
||||
app:layout_constraintBottom_toTopOf="@id/mediaDetailuploadeddate"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<LinearLayout
|
||||
style="@style/MediaDetailContainer"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/category_edit_layout"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15dp"
|
||||
android:orientation="vertical"
|
||||
android:background="?attr/mainBackground"
|
||||
android:elevation="30dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/half_standard_height"
|
||||
android:layout_margin="@dimen/quarter_standard_height"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Type categories"
|
||||
android:textSize="@dimen/subtitle_text"
|
||||
android:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/category_search_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_gap"
|
||||
>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/til_container_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/et_search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:queryHint="@string/categories_search_text_hint"
|
||||
android:iconifiedByDefault="false"
|
||||
android:imeOptions="actionSearch"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_categories"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/tiny_gap"
|
||||
android:layout_marginRight="@dimen/tiny_gap"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateOnly="true"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/existing_categories"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/quarter_standard_height"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="@dimen/subtitle_text"
|
||||
android:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_results_found"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/quarter_standard_height"
|
||||
android:gravity="center_vertical"
|
||||
android:text="No results found"
|
||||
android:textSize="@dimen/description_text_size"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_categories"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dimen_200"
|
||||
android:background="?attr/mainBackground"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<Button
|
||||
android:id="@+id/cancel_categories_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="@dimen/quarter_standard_height"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/cancel"
|
||||
android:padding="@dimen/small_gap"
|
||||
android:textColor="@color/white"
|
||||
android:background="@color/opak_middle_grey"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/update_categories_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_margin="@dimen/quarter_standard_height"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_edit_button_text"
|
||||
android:padding="@dimen/small_gap"
|
||||
android:textColor="@color/white"
|
||||
android:background="@color/button_blue"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/category_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/category_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkMark="?android:attr/textCheckMark"
|
||||
android:checked="false"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="@dimen/tiny_gap"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Category"
|
||||
app:layout_constraintLeft_toRightOf="@+id/category_checkbox"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/standard_gap"
|
||||
android:background="?attr/mainBackground"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* StarrySky
|
||||
-->
|
||||
<resources>
|
||||
<string name="crash_dialog_title">維基共享資源已當機</string>
|
||||
<string name="crash_dialog_text">哎呀,出錯了!</string>
|
||||
<string name="crash_dialog_comment_prompt">透過電子郵件告訴我們您先前做了什麼,這將協助我們修復它!</string>
|
||||
<string name="crash_dialog_ok_toast">謝謝您!</string>
|
||||
</resources>
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* Liuxinyu970226
|
||||
* StarrySky
|
||||
-->
|
||||
<resources>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">正在上傳%1$d個檔案</item>
|
||||
<item quantity="other">正在上傳%1$d個檔案</item>
|
||||
</plurals>
|
||||
<plurals name="contributions_subtitle">
|
||||
<item quantity="one">(%1$d)</item>
|
||||
<item quantity="other">(%1$d)</item>
|
||||
</plurals>
|
||||
<string name="starting_uploads">開始上傳</string>
|
||||
<plurals name="starting_multiple_uploads">
|
||||
<item quantity="one">正在處理%d個上傳</item>
|
||||
<item quantity="other">正在處理%d個上傳</item>
|
||||
</plurals>
|
||||
<plurals name="multiple_uploads_title">
|
||||
<item quantity="one">%d個上傳</item>
|
||||
<item quantity="other">%d個上傳</item>
|
||||
</plurals>
|
||||
<plurals name="share_license_summary">
|
||||
<item quantity="one">此圖片會按 %1$s 協議授權上傳</item>
|
||||
<item quantity="other">這些圖片會按 %1$s 協議授權上傳</item>
|
||||
</plurals>
|
||||
<plurals name="upload_count_title">
|
||||
<item quantity="one">%1$d 個上傳</item>
|
||||
<item quantity="other">%1$d 個上傳</item>
|
||||
</plurals>
|
||||
<plurals name="receiving_shared_content">
|
||||
<item quantity="one">接收分享內容。處理圖片的時間取決於圖片大小與您的裝置</item>
|
||||
<item quantity="other">接收分享內容。處理圖片的時間取決於圖片大小與您的裝置</item>
|
||||
</plurals>
|
||||
<string name="navigation_item_explore">探索</string>
|
||||
<string name="preference_category_appearance">外觀</string>
|
||||
<string name="preference_category_general">一般</string>
|
||||
<string name="preference_category_feedback">意見回饋</string>
|
||||
<string name="preference_category_privacy">隱私</string>
|
||||
<string name="app_name">維基共享資源</string>
|
||||
<string name="menu_settings">設定</string>
|
||||
<string name="intent_share_upload_label">上傳至維基共享資源</string>
|
||||
<string name="username">用戶名</string>
|
||||
<string name="password">密碼</string>
|
||||
<string name="login">登入</string>
|
||||
<string name="forgot_password">忘記了密碼?</string>
|
||||
<string name="signup">註冊</string>
|
||||
<string name="logging_in_title">登入中</string>
|
||||
<string name="logging_in_message">請稍候…</string>
|
||||
<string name="updating_caption_title">正在更新說明與描述</string>
|
||||
<string name="updating_caption_message">請等待⋯</string>
|
||||
<string name="login_success">登入成功!</string>
|
||||
<string name="login_failed">登入失敗!</string>
|
||||
<string name="upload_failed">找不到檔案。請試試看其它檔案。</string>
|
||||
<string name="authentication_failed">身份驗證失敗,請重新登入</string>
|
||||
<string name="uploading_started">開始上傳!</string>
|
||||
<string name="uploading_queued">上傳已排定(限制連線模式已啟用)</string>
|
||||
<string name="upload_completed_notification_title">已上傳%1$s!</string>
|
||||
<string name="upload_completed_notification_text">輕觸來檢視您上傳的項目</string>
|
||||
<string name="upload_progress_notification_title_start">開始上傳檔案:%s</string>
|
||||
<string name="upload_progress_notification_title_in_progress">正在上傳%1$s</string>
|
||||
<string name="upload_progress_notification_title_finishing">即將完成上傳%1$s</string>
|
||||
<string name="upload_failed_notification_title">上傳%1$s失敗</string>
|
||||
<string name="upload_paused_notification_title">已暫停上傳%1$s</string>
|
||||
<string name="upload_failed_notification_subtitle">輕觸檢視</string>
|
||||
<string name="upload_paused_notification_subtitle">輕觸檢視</string>
|
||||
<string name="title_activity_contributions">我最近的上傳</string>
|
||||
<string name="contribution_state_queued">佇列</string>
|
||||
<string name="contribution_state_failed">失敗</string>
|
||||
<string name="contribution_state_in_progress">%1$d%%完成</string>
|
||||
<string name="contribution_state_starting">上傳中</string>
|
||||
<string name="menu_from_gallery">自圖庫</string>
|
||||
<string name="menu_from_camera">拍照</string>
|
||||
<string name="menu_nearby">附近</string>
|
||||
<string name="provider_contributions">我的上傳</string>
|
||||
<string name="menu_share">分享</string>
|
||||
<string name="menu_view_file_page">檢視檔案頁面</string>
|
||||
<string name="share_title_hint">說明(必填)</string>
|
||||
<string name="add_caption_toast">請提供此檔案的說明</string>
|
||||
<string name="share_description_hint">描述</string>
|
||||
<string name="share_caption_hint">說明</string>
|
||||
<string name="login_failed_network">無法登入-網路故障</string>
|
||||
<string name="login_failed_throttled">失敗次數過多。請於幾分鐘後重試。</string>
|
||||
<string name="login_failed_blocked">很抱歉,該使用者已被維基共享資源封鎖</string>
|
||||
<string name="login_failed_2fa_needed">必須提供您的雙重驗證代碼。</string>
|
||||
<string name="login_failed_generic">登入失敗!</string>
|
||||
<string name="share_upload_button">上載</string>
|
||||
<string name="multiple_share_base_title">命名此組圖像</string>
|
||||
<string name="provider_modifications">修改</string>
|
||||
<string name="menu_upload_single">上載</string>
|
||||
<string name="categories_search_text_hint">搜尋分類</string>
|
||||
<string name="depicts_search_text_hint">搜尋您的媒體所描寫項目(高山、泰姬瑪、其它等。)</string>
|
||||
<string name="menu_save_categories">儲存</string>
|
||||
<string name="refresh_button">刷新</string>
|
||||
<string name="display_list_button">清單</string>
|
||||
<string name="contributions_subtitle_zero">(尚未上傳)</string>
|
||||
<string name="categories_not_found">沒有發現與 %1$s 相符的分類</string>
|
||||
<string name="depictions_not_found">找不到符合%1$s的維基數據項目</string>
|
||||
<string name="no_child_classes">%1$s沒有子類別</string>
|
||||
<string name="no_parent_classes">%1$s沒有父類別</string>
|
||||
<string name="categories_skip_explanation">為您的圖片添加分類,使別人在維基共享資源更容易找到。\n\n開始輸入以添加分類。</string>
|
||||
<string name="categories_activity_title">分類:</string>
|
||||
<string name="title_activity_settings">設定</string>
|
||||
<string name="title_activity_signup">註冊</string>
|
||||
<string name="title_activity_featured_images">特色圖片</string>
|
||||
<string name="title_activity_custom_selector">自訂選擇器</string>
|
||||
<string name="title_activity_category_details">分類</string>
|
||||
<string name="title_activity_review">同行評審</string>
|
||||
<string name="menu_about">關於</string>
|
||||
<string name="about_license">維基共享資源應用程式是透過維基媒體社群上的受讓人,與志願者們所建立及維護的開源應用程式。維基媒體基金會並不參與此應用程式的建立、開發,與維護。</string>
|
||||
<string name="about_improve">建立新的<a href=\"%1$s\"> GitHub 問題</a>來回報程式錯誤和提出建議。</string>
|
||||
<string name="about_privacy_policy"><u>私隱政策</u></string>
|
||||
<string name="about_credits">製作人員</string>
|
||||
<string name="title_activity_about">關於</string>
|
||||
<string name="menu_feedback">發送回饋 (通過電子郵件)</string>
|
||||
<string name="no_email_client">未安裝電子郵件客戶端</string>
|
||||
<string name="provider_categories">最近使用過的分類</string>
|
||||
<string name="waiting_first_sync">等待首次同步…</string>
|
||||
<string name="no_uploads_yet">您尚未上傳過任何照片。</string>
|
||||
<string name="menu_retry_upload">重試</string>
|
||||
<string name="menu_cancel_upload">取消</string>
|
||||
<string name="media_upload_policy">透過提交此圖片,我宣佈這是我個人創作的成品,且不包含受版權保護或自拍內容,並除此之外遵守<a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">維基媒體共享資源方針</a>。</string>
|
||||
<string name="menu_download">下載</string>
|
||||
<string name="preference_license">預設授權條款</string>
|
||||
<string name="use_previous">使用先前標題及描述</string>
|
||||
<string name="preference_theme">主題</string>
|
||||
<string name="license_name_cc_by_sa_four">姓名標示-相同方式分享4.0</string>
|
||||
<string name="license_name_cc_by_four">姓名標示4.0</string>
|
||||
<string name="license_name_cc_by_sa"> 姓名標示-相同方式分享3.0</string>
|
||||
<string name="license_name_cc_by">姓名標示3.0</string>
|
||||
<string name="tutorial_1_text">維基共享資源管理大部份使用在維基百科的圖片。</string>
|
||||
<string name="tutorial_1_subtext">您的圖片可以幫助教育世界上的人們!</string>
|
||||
<string name="tutorial_2_text">請上傳完全由您自己拍攝或創作的圖片:</string>
|
||||
<string name="tutorial_2_subtext_1">自然事物(花、動物、山峰等)</string>
|
||||
<string name="tutorial_2_subtext_2">可利用的事物(腳踏車、車站等)</string>
|
||||
<string name="tutorial_2_subtext_3">知名人物(您的市長、您所見過的奧運選手等)</string>
|
||||
<string name="tutorial_3_text">請不要上傳:</string>
|
||||
<string name="tutorial_3_subtext_1">您朋友的自拍和圖片</string>
|
||||
<string name="tutorial_3_subtext_2">您從網路下載來的圖片</string>
|
||||
<string name="tutorial_3_subtext_3">專有應用程式的截圖</string>
|
||||
<string name="tutorial_4_text">上傳範例:</string>
|
||||
<string name="tutorial_4_subtext_1">標題:雪梨歌劇院</string>
|
||||
<string name="tutorial_4_subtext_2">描述:從對面海灣所看到的雪梨歌劇院</string>
|
||||
<string name="tutorial_4_subtext_3">分類:Sydney Opera House from the west(雪梨歌劇院的西側)、Sydney Opera House remote views(雪梨歌劇院的遠方外觀)</string>
|
||||
<string name="welcome_wikipedia_text">貢獻您的圖片,使維基百科的文章更加生動!</string>
|
||||
<string name="welcome_wikipedia_subtext">維基百科上的圖像來自維基共享資源。</string>
|
||||
<string name="welcome_copyright_text">您的圖片可以幫助教育世界上的人們!</string>
|
||||
<string name="welcome_copyright_subtext">避免使用受版權保護的材料,例如從網際網路找來的圖片、海報、書籍封面等</string>
|
||||
<string name="welcome_final_text">以上您明白了嗎?</string>
|
||||
<string name="welcome_final_button_text">是!</string>
|
||||
<string name="welcome_help_button_text">更多資訊</string>
|
||||
<string name="detail_panel_cats_label">分類:</string>
|
||||
<string name="detail_panel_cats_loading">載入中…</string>
|
||||
<string name="detail_panel_cats_none">未選擇</string>
|
||||
<string name="detail_caption_empty">沒有說明</string>
|
||||
<string name="detail_description_empty">無描述</string>
|
||||
<string name="detail_discussion_empty">無討論</string>
|
||||
<string name="detail_license_empty">不明授權</string>
|
||||
<string name="menu_refresh">刷新</string>
|
||||
<string name="storage_permission_title">請求儲存裝置權限</string>
|
||||
<string name="read_storage_permission_rationale">必要權限:讀取外部儲存裝置。否則應用程式無法存取您的圖庫。</string>
|
||||
<string name="write_storage_permission_rationale">必要權限:寫入外部存儲裝置。否則應用程式無法取用您的相機/圖庫。</string>
|
||||
<string name="location_permission_title">請求儲存裝置權限</string>
|
||||
<string name="ok">確定</string>
|
||||
<string name="warning">警告!</string>
|
||||
<string name="duplicate_image_found">發現重複圖片</string>
|
||||
<string name="upload">上載</string>
|
||||
<string name="yes">是</string>
|
||||
<string name="no">否</string>
|
||||
<string name="media_detail_caption">說明</string>
|
||||
<string name="media_detail_title">頁面標題</string>
|
||||
<string name="media_detail_depiction">描述</string>
|
||||
<string name="media_detail_description">描述</string>
|
||||
<string name="media_detail_discussion">討論</string>
|
||||
<string name="media_detail_author">製作者</string>
|
||||
<string name="media_detail_uploaded_date">上傳日期</string>
|
||||
<string name="media_detail_license">授權</string>
|
||||
<string name="media_detail_coordinates">座標</string>
|
||||
<string name="media_detail_coordinates_empty">未提供</string>
|
||||
<string name="become_a_tester_title">成為測試人員</string>
|
||||
<string name="become_a_tester_description">選擇加入我們在 Google Play 上的 beta 測試版本,以提早取用新功能及程式修正</string>
|
||||
<string name="_2fa_code">雙重驗證代碼</string>
|
||||
<string name="logout_verification">您確定要登出嗎?</string>
|
||||
<string name="commons_logo">維基共享資源標誌</string>
|
||||
<string name="commons_website">共享資源網站</string>
|
||||
<string name="commons_facebook">維基共享資源臉書頁</string>
|
||||
<string name="commons_github">Github 上的共享資源原始碼</string>
|
||||
<string name="mediaimage_failed">媒體圖片失敗</string>
|
||||
<string name="no_subcategory_found">找不到子分類</string>
|
||||
<string name="no_parentcategory_found">找不到母分類</string>
|
||||
<string name="welcome_image_mount_zao">藏王連峰</string>
|
||||
<string name="welcome_image_llamas">大羊駝</string>
|
||||
<string name="welcome_image_rainbow_bridge">彩虹橋</string>
|
||||
<string name="welcome_image_tulip">鬱金香</string>
|
||||
<string name="welcome_image_welcome_wikipedia">歡迎來到維基百科</string>
|
||||
<string name="welcome_image_welcome_copyright">歡迎版權</string>
|
||||
<string name="welcome_image_sydney_opera_house">雪梨歌劇院</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="navigation_drawer_open">打開</string>
|
||||
<string name="navigation_drawer_close">關閉</string>
|
||||
<string name="navigation_item_home">首頁</string>
|
||||
<string name="navigation_item_upload">上載</string>
|
||||
<string name="navigation_item_nearby">附近</string>
|
||||
<string name="navigation_item_about">關於</string>
|
||||
<string name="navigation_item_settings">設定</string>
|
||||
<string name="navigation_item_feedback">意見回饋</string>
|
||||
<string name="navigation_item_logout">登出</string>
|
||||
<string name="navigation_item_info">教程</string>
|
||||
<string name="navigation_item_notification">通知</string>
|
||||
<string name="navigation_item_review">檢閱</string>
|
||||
<string name="nearby_info_menu_wikidata_article">維基數據項目</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">維基百科條目</string>
|
||||
<string name="description_info">請盡可能描述媒體內容:拍攝於何處?是顯示什麼事物?有什麼脈絡?請描述對象或人物。透露出一些較不易猜測的訊息,例如是風景的話,可以是一天裡的時間。如果媒體顯示出了一些不尋常的事物,請說明不尋常原因。</string>
|
||||
</resources>
|
||||
|
|
@ -691,6 +691,7 @@ Upload your first media by tapping on the add button.</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="edit_categories">Edit categories</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>
|
||||
|
|
|
|||
|
|
@ -294,8 +294,7 @@ class BookmarkListRootFragmentUnitTest {
|
|||
@Throws(Exception::class)
|
||||
fun `testBackPressed Case NonNull isVisible and backButton clicked`() {
|
||||
whenever(mediaDetails.isVisible).thenReturn(true)
|
||||
whenever(mediaDetails.backButtonClicked()).thenReturn(true)
|
||||
Assert.assertEquals(fragment.backPressed(), true)
|
||||
Assert.assertEquals(fragment.backPressed(), false)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -303,7 +302,6 @@ class BookmarkListRootFragmentUnitTest {
|
|||
fun `testBackPressed Case NonNull isVisible and backButton not clicked`() {
|
||||
Whitebox.setInternalState(fragment, "listFragment", mock(BookmarkPicturesFragment::class.java))
|
||||
whenever(mediaDetails.isVisible).thenReturn(true)
|
||||
whenever(mediaDetails.backButtonClicked()).thenReturn(false)
|
||||
whenever(mediaDetails.removedItems).thenReturn(ArrayList(0))
|
||||
Assert.assertEquals(fragment.backPressed(), false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import depictedItem
|
|||
import fr.free.nrw.commons.upload.GpsCategoryModel
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import media
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
|
|
@ -23,10 +24,16 @@ class CategoriesModelTest {
|
|||
@Mock
|
||||
internal lateinit var categoryClient: CategoryClient
|
||||
|
||||
@Mock
|
||||
internal lateinit var gpsCategoryModel: GpsCategoryModel
|
||||
|
||||
private lateinit var categoriesModel: CategoriesModel
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
categoriesModel = CategoriesModel(categoryClient, categoryDao, gpsCategoryModel)
|
||||
}
|
||||
|
||||
// Test Case for verifying that Categories search (MW api calls)
|
||||
|
|
@ -103,4 +110,106 @@ class CategoriesModelTest {
|
|||
verify(categoryClient).searchCategories(it, CategoriesModel.SEARCH_CATS_LIMIT)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetCategoriesByName(){
|
||||
categoriesModel.getCategoriesByName(listOf("Test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test buildCategories when it returns non empty list`(){
|
||||
whenever(categoryClient.getCategoriesByName("Test",
|
||||
"Test", CategoriesModel.SEARCH_CATS_LIMIT
|
||||
)).thenReturn(Single.just(listOf(categoryItem())))
|
||||
categoriesModel.buildCategories("Test")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test buildCategories when it returns empty list`(){
|
||||
whenever(categoryClient.getCategoriesByName("Test",
|
||||
"Test", CategoriesModel.SEARCH_CATS_LIMIT
|
||||
)).thenReturn(Single.just(emptyList()))
|
||||
categoriesModel.buildCategories("Test")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetSelectedExistingCategories(){
|
||||
categoriesModel.getSelectedExistingCategories()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetSelectedExistingCategories(){
|
||||
categoriesModel.setSelectedExistingCategories(mutableListOf("Test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is null and item is selected`(){
|
||||
categoriesModel.onCategoryItemClicked(
|
||||
CategoryItem(
|
||||
"name",
|
||||
"des",
|
||||
"image",
|
||||
true
|
||||
), null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is null and item is not selected`(){
|
||||
categoriesModel.onCategoryItemClicked(categoryItem(), null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is not null and item is selected and media contains category`(){
|
||||
categoriesModel.onCategoryItemClicked(
|
||||
CategoryItem(
|
||||
"categories",
|
||||
"des",
|
||||
"image",
|
||||
true
|
||||
), media())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is not null and item is selected and media does not contains category`(){
|
||||
categoriesModel.onCategoryItemClicked(
|
||||
CategoryItem(
|
||||
"name",
|
||||
"des",
|
||||
"image",
|
||||
true
|
||||
), media())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is not null and item is not selected and media contains category`(){
|
||||
categoriesModel.onCategoryItemClicked(
|
||||
CategoryItem(
|
||||
"categories",
|
||||
"des",
|
||||
"image",
|
||||
false
|
||||
), media())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onCategoryItemClicked when media is not null and item is not selected and media does not contains category`(){
|
||||
categoriesModel.onCategoryItemClicked(
|
||||
CategoryItem(
|
||||
"name",
|
||||
"des",
|
||||
"image",
|
||||
false
|
||||
), media())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import android.content.Context
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.actions.PageEditClient
|
||||
import fr.free.nrw.commons.notification.NotificationHelper
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.LooperMode
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class CategoryEditHelperUnitTests {
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var helper: CategoryEditHelper
|
||||
|
||||
@Mock
|
||||
private lateinit var notificationHelper: NotificationHelper
|
||||
|
||||
@Mock
|
||||
private lateinit var pageEditClient: PageEditClient
|
||||
|
||||
@Mock
|
||||
private lateinit var viewUtilWrapper: ViewUtilWrapper
|
||||
|
||||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
context = RuntimeEnvironment.application.applicationContext
|
||||
helper = CategoryEditHelper(notificationHelper, pageEditClient, viewUtilWrapper,
|
||||
"")
|
||||
Mockito.`when`(media.filename).thenReturn("File:Example.jpg")
|
||||
Mockito.`when`(pageEditClient.getCurrentWikiText(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(""))
|
||||
Mockito.`when`(
|
||||
pageEditClient.edit(
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString()
|
||||
)
|
||||
).thenReturn(Observable.just(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun checkNotNull() {
|
||||
Assert.assertNotNull(helper)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testMakeCategoryEdit() {
|
||||
helper.makeCategoryEdit(context, media, listOf("Test"), "[[Category:Test]]")
|
||||
Mockito.verify(viewUtilWrapper, Mockito.times(1)).showShortToast(
|
||||
context,
|
||||
context.getString(R.string.category_edit_helper_make_edit_toast)
|
||||
)
|
||||
Mockito.verify(pageEditClient, Mockito.times(1)).edit(
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.anyString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.nearby.Label
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.powermock.reflect.Whitebox
|
||||
|
||||
class CategoryEditSearchRecyclerViewAdapterUnitTest {
|
||||
|
||||
private lateinit var adapter: CategoryEditSearchRecyclerViewAdapter
|
||||
|
||||
@Mock
|
||||
private lateinit var context: Context
|
||||
|
||||
@Mock
|
||||
private lateinit var labels: ArrayList<Label>
|
||||
|
||||
@Mock
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
|
||||
@Mock
|
||||
private lateinit var categoryClient: CategoryClient
|
||||
|
||||
@Mock
|
||||
private lateinit var callback: CategoryEditSearchRecyclerViewAdapter.Callback
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
adapter =
|
||||
CategoryEditSearchRecyclerViewAdapter(
|
||||
context,
|
||||
labels,
|
||||
recyclerView,
|
||||
categoryClient,
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddToCategories() {
|
||||
val categories = mutableListOf<String>()
|
||||
Whitebox.setInternalState(adapter, "categories", categories)
|
||||
val testCategories = listOf("someString")
|
||||
adapter.addToCategories(testCategories)
|
||||
assertEquals(categories.size, testCategories.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveFromNewCategories() {
|
||||
val testCategory = "someString"
|
||||
val newCategories = mutableListOf(testCategory)
|
||||
val originalSize = newCategories.size
|
||||
Whitebox.setInternalState(adapter, "newCategories", newCategories)
|
||||
adapter.removeFromNewCategories(testCategory)
|
||||
assertEquals(newCategories.size, originalSize - 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddToNewCategories() {
|
||||
val testCategory = "someString"
|
||||
val newCategories = mutableListOf<String>()
|
||||
val originalSize = newCategories.size
|
||||
Whitebox.setInternalState(adapter, "newCategories", newCategories)
|
||||
adapter.addToNewCategories(testCategory)
|
||||
assertEquals(newCategories.size, originalSize + 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCategories() {
|
||||
val categories = mutableListOf<String>()
|
||||
Whitebox.setInternalState(adapter, "categories", categories)
|
||||
assertEquals(adapter.categories, categories)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetNewCategories() {
|
||||
val newCategories = mutableListOf<String>()
|
||||
Whitebox.setInternalState(adapter, "newCategories", newCategories)
|
||||
assertEquals(adapter.newCategories, newCategories)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -259,7 +259,6 @@ class ExploreListRootFragmentUnitTest {
|
|||
@Throws(Exception::class)
|
||||
fun `testBackPressed_Case null != mediaDetails && mediaDetails_isVisible_backButtonNotClicked`() {
|
||||
`when`(mediaDetails.isVisible).thenReturn(true)
|
||||
`when`(mediaDetails.backButtonClicked()).thenReturn(true)
|
||||
Assert.assertEquals(fragment.backPressed(), true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ class WikidataItemDetailsActivityUnitTests {
|
|||
@Throws(Exception::class)
|
||||
fun testOnBackPressedCaseReturn() {
|
||||
`when`(supportFragmentManager.backStackEntryCount).thenReturn(1)
|
||||
`when`(mediaDetailPagerFragment.backButtonClicked()).thenReturn(true)
|
||||
activity.onBackPressed()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,6 @@ class SearchActivityUnitTests {
|
|||
`when`(supportFragmentManager.backStackEntryCount).thenReturn(1)
|
||||
`when`(mediaDetails.isVisible).thenReturn(true)
|
||||
activity.refreshNominatedMedia(0)
|
||||
verify(mediaDetails).backButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -215,9 +214,7 @@ class SearchActivityUnitTests {
|
|||
`when`(mFragments.supportFragmentManager).thenReturn(supportFragmentManager)
|
||||
`when`(supportFragmentManager.backStackEntryCount).thenReturn(1)
|
||||
`when`(mediaDetails.isVisible).thenReturn(true)
|
||||
`when`(mediaDetails.backButtonClicked()).thenReturn(true)
|
||||
activity.refreshNominatedMedia(0)
|
||||
verify(mediaDetails).backButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -228,12 +225,10 @@ class SearchActivityUnitTests {
|
|||
Whitebox.setInternalState(activity, "mediaDetails", mediaDetails)
|
||||
`when`(mFragments.supportFragmentManager).thenReturn(supportFragmentManager)
|
||||
`when`(supportFragmentManager.backStackEntryCount).thenReturn(1)
|
||||
`when`(mediaDetails.backButtonClicked()).thenReturn(true)
|
||||
`when`(mediaDetails.isVisible).thenReturn(true)
|
||||
val method: Method = SearchActivity::class.java.getDeclaredMethod("onResume")
|
||||
method.isAccessible = true
|
||||
method.invoke(activity)
|
||||
verify(mediaDetails).backButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ 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 com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter
|
||||
import fr.free.nrw.commons.explore.SearchActivity
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
|
|
@ -97,9 +97,6 @@ class MediaDetailFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var locationManager: LocationServiceManager
|
||||
|
||||
@Mock
|
||||
private lateinit var categoryEditSearchRecyclerViewAdapter: CategoryEditSearchRecyclerViewAdapter
|
||||
|
||||
@Mock
|
||||
private lateinit var savedInstanceState: Bundle
|
||||
|
||||
|
|
@ -109,9 +106,6 @@ class MediaDetailFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
@Mock
|
||||
private lateinit var categoryRecyclerView: RecyclerView
|
||||
|
||||
@Mock
|
||||
private lateinit var simpleDraweeView: SimpleDraweeView
|
||||
|
||||
|
|
@ -182,10 +176,8 @@ class MediaDetailFragmentUnitTests {
|
|||
scrollView = view.findViewById(R.id.mediaDetailScrollView)
|
||||
Whitebox.setInternalState(fragment, "scrollView", scrollView)
|
||||
|
||||
categoryRecyclerView = view.findViewById(R.id.rv_categories)
|
||||
progressBarDeletion = view.findViewById(R.id.progressBarDeletion)
|
||||
delete = view.findViewById(R.id.nominateDeletion)
|
||||
Whitebox.setInternalState(fragment, "categoryRecyclerView", categoryRecyclerView)
|
||||
|
||||
Whitebox.setInternalState(fragment, "media", media)
|
||||
Whitebox.setInternalState(fragment, "isDeleted", isDeleted)
|
||||
|
|
@ -212,21 +204,16 @@ class MediaDetailFragmentUnitTests {
|
|||
Whitebox.setInternalState(fragment, "delete", delete)
|
||||
Whitebox.setInternalState(fragment, "depictionContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "toDoLayout", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "dummyCategoryEditContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "authorLayout", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "showCaptionAndDescriptionContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "updateCategoriesButton", button)
|
||||
Whitebox.setInternalState(fragment, "editDescription", button)
|
||||
Whitebox.setInternalState(fragment, "depictEditButton", button)
|
||||
Whitebox.setInternalState(fragment, "categoryEditButton", button)
|
||||
Whitebox.setInternalState(fragment, "categoryContainer", linearLayout)
|
||||
Whitebox.setInternalState(fragment, "categorySearchView", searchView)
|
||||
Whitebox.setInternalState(fragment, "progressBarDeletion", progressBarDeletion)
|
||||
Whitebox.setInternalState(fragment, "progressBarEditCategory", progressBarDeletion)
|
||||
Whitebox.setInternalState(fragment, "mediaDiscussion", textView)
|
||||
Whitebox.setInternalState(fragment, "locationManager", locationManager)
|
||||
Whitebox.setInternalState(
|
||||
fragment,
|
||||
"categoryEditSearchRecyclerViewAdapter",
|
||||
categoryEditSearchRecyclerViewAdapter
|
||||
)
|
||||
|
||||
`when`(simpleDraweeView.hierarchy).thenReturn(genericDraweeHierarchy)
|
||||
val map = HashMap<String, String>()
|
||||
|
|
@ -606,16 +593,6 @@ class MediaDetailFragmentUnitTests {
|
|||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testSetupToDo() {
|
||||
val method: Method = MediaDetailFragment::class.java.getDeclaredMethod(
|
||||
"setupToDo"
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnDiscussionLoaded() {
|
||||
|
|
@ -668,4 +645,46 @@ class MediaDetailFragmentUnitTests {
|
|||
fun testOnDeleteButtonClicked() {
|
||||
fragment.onDeleteButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnCategoryEditButtonClicked() {
|
||||
whenever(media.filename).thenReturn("File:Example.jpg")
|
||||
fragment.onCategoryEditButtonClicked()
|
||||
verify(media, times(1)).filename
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testDisplayMediaDetails() {
|
||||
whenever(media.filename).thenReturn("File:Example.jpg")
|
||||
val method: Method = MediaDetailFragment::class.java.getDeclaredMethod(
|
||||
"displayMediaDetails"
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment)
|
||||
verify(media, times(4)).filename
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGotoCategoryEditor() {
|
||||
val method: Method = MediaDetailFragment::class.java.getDeclaredMethod(
|
||||
"gotoCategoryEditor",
|
||||
String::class.java
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, "[[Category:Test]]")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnMediaRefreshed() {
|
||||
val method: Method = MediaDetailFragment::class.java.getDeclaredMethod(
|
||||
"onMediaRefreshed",
|
||||
Media::class.java
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, media)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,13 @@ import fr.free.nrw.commons.upload.categories.CategoriesContract
|
|||
import fr.free.nrw.commons.upload.categories.CategoriesPresenter
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.schedulers.TestScheduler
|
||||
import media
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.powermock.reflect.Whitebox
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* The class contains unit test cases for CategoriesPresenter
|
||||
|
|
@ -39,6 +42,27 @@ class CategoriesPresenterTest {
|
|||
categoriesPresenter.onAttachView(view)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnAttachViewWithMedia() {
|
||||
categoriesPresenter.onAttachViewWithMedia(view, media())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onAttachViewWithMedia when media is not null`() {
|
||||
Whitebox.setInternalState(categoriesPresenter, "media", media())
|
||||
whenever(repository.getCategories(repository.selectedExistingCategories))
|
||||
.thenReturn(Observable.just(mutableListOf(categoryItem())))
|
||||
whenever(repository.searchAll("mock", emptyList(), repository.selectedDepictions))
|
||||
.thenReturn(Observable.just(mutableListOf(categoryItem())))
|
||||
val method: Method = CategoriesPresenter::class.java.getDeclaredMethod(
|
||||
"searchResults",
|
||||
String::class.java
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(categoriesPresenter, "mock") }
|
||||
|
||||
/**
|
||||
* unit test case for method CategoriesPresenter.searchForCategories
|
||||
*/
|
||||
|
|
@ -122,6 +146,16 @@ class CategoriesPresenterTest {
|
|||
fun onCategoryItemClickedTest() {
|
||||
val categoryItem = categoryItem()
|
||||
categoriesPresenter.onCategoryItemClicked(categoryItem)
|
||||
verify(repository).onCategoryClicked(categoryItem)
|
||||
verify(repository).onCategoryClicked(categoryItem, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClearPreviousSelection() {
|
||||
categoriesPresenter.clearPreviousSelection()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUpdateCategories() {
|
||||
categoriesPresenter.updateCategories(media(), "[[Category:Test]]")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload
|
|||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
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
|
||||
|
|
@ -73,6 +74,9 @@ class UploadRepositoryUnitTest {
|
|||
@Mock
|
||||
private lateinit var imageCoordinates: ImageCoordinates
|
||||
|
||||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
|
@ -142,8 +146,8 @@ class UploadRepositoryUnitTest {
|
|||
|
||||
@Test
|
||||
fun testOnCategoryClicked() {
|
||||
repository.onCategoryClicked(categoryItem)
|
||||
verify(categoriesModel).onCategoryItemClicked(categoryItem)
|
||||
repository.onCategoryClicked(categoryItem, media)
|
||||
verify(categoriesModel).onCategoryItemClicked(categoryItem, media)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -339,4 +343,21 @@ class UploadRepositoryUnitTest {
|
|||
method.invoke(repository, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSelectedExistingCategories() {
|
||||
assertEquals(repository.selectedExistingCategories,
|
||||
categoriesModel.getSelectedExistingCategories())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetSelectedExistingCategories() {
|
||||
assertEquals(repository.setSelectedExistingCategories(listOf("Test")),
|
||||
categoriesModel.setSelectedExistingCategories(mutableListOf("Test")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCategories() {
|
||||
assertEquals(repository.getCategories(listOf("Test")),
|
||||
categoriesModel.getCategoriesByName(mutableListOf("Test")))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package fr.free.nrw.commons.upload.categories
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import android.text.Editable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
|
|
@ -12,6 +14,10 @@ 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.times
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.TestAppAdapter
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
|
|
@ -53,6 +59,9 @@ class UploadCategoriesFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var pbCategories: ProgressBar
|
||||
|
||||
@Mock
|
||||
private lateinit var progressDialog: ProgressDialog
|
||||
|
||||
@Mock
|
||||
private lateinit var tilContainerEtSearch: TextInputLayout
|
||||
|
||||
|
|
@ -74,6 +83,9 @@ class UploadCategoriesFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var editable: Editable
|
||||
|
||||
@Mock
|
||||
private lateinit var button: Button
|
||||
|
||||
@Mock
|
||||
private lateinit var adapter: UploadCategoryAdapter
|
||||
|
||||
|
|
@ -83,6 +95,10 @@ class UploadCategoriesFragmentUnitTests {
|
|||
@Mock
|
||||
private lateinit var presenter: CategoriesContract.UserActionListener
|
||||
|
||||
@Mock
|
||||
private lateinit var media: Media
|
||||
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
|
@ -108,6 +124,10 @@ class UploadCategoriesFragmentUnitTests {
|
|||
Whitebox.setInternalState(fragment, "tvTitle", tvTitle)
|
||||
Whitebox.setInternalState(fragment, "tooltip", tooltip)
|
||||
Whitebox.setInternalState(fragment, "tvSubTitle", tvSubTitle)
|
||||
Whitebox.setInternalState(fragment, "btnNext", button)
|
||||
Whitebox.setInternalState(fragment, "btnPrevious", button)
|
||||
Whitebox.setInternalState(fragment, "progressDialog", progressDialog)
|
||||
Whitebox.setInternalState(fragment, "wikiText", "[[Category:Test]]")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -185,6 +205,51 @@ class UploadCategoriesFragmentUnitTests {
|
|||
fragment.showNoCategorySelected()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetExistingCategories() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
fragment.existingCategories
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetFragmentContext() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
fragment.fragmentContext
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGoBackToPreviousScreen() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
fragment.goBackToPreviousScreen()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testShowProgressDialog() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
fragment.showProgressDialog()
|
||||
verify(progressDialog, times(0)).show()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testDismissProgressDialog() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
fragment.dismissProgressDialog()
|
||||
verify(progressDialog, times(1)).dismiss()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test showNoCategorySelected when media is not null`() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
Whitebox.setInternalState(fragment, "media", media)
|
||||
fragment.showNoCategorySelected()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnNextButtonClicked() {
|
||||
|
|
@ -192,6 +257,14 @@ class UploadCategoriesFragmentUnitTests {
|
|||
fragment.onNextButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test onNextButtonClicked when media is not null`() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
Whitebox.setInternalState(fragment, "media", media)
|
||||
fragment.onNextButtonClicked()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnPreviousButtonClicked() {
|
||||
|
|
@ -256,4 +329,16 @@ class UploadCategoriesFragmentUnitTests {
|
|||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun `Test init when media is not null`() {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
Whitebox.setInternalState(fragment, "media", media)
|
||||
val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod(
|
||||
"init"
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue