mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 13:53:54 +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
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue