mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 14:53:59 +01:00 
			
		
		
		
	Fix 4615: Option for editing caption and description (#4672)
* DescriptionEditHelper implemented * Description extracted * Description editable * No description condition handled * Code cleanup * Added javadocs * toolbar added * API call done * Caption edit available * Progress dialog added * Log * Problem with ButterKnife * Caption is editable * Removed unused import * Manifest file reverted * Manifest file reverted * Manifest file reverted * View binding added * Post operation test added * Java docs added * Java docs added * MediaDetailFragment unit tests added * Test added
This commit is contained in:
		
							parent
							
								
									e910b1d14f
								
							
						
					
					
						commit
						0269894c64
					
				
					 20 changed files with 855 additions and 14 deletions
				
			
		|  | @ -157,6 +157,17 @@ class MediaClient @Inject constructor( | |||
|     fun resetUserNameContinuation(userName: String) = | ||||
|         resetUserContinuation("user_", userName) | ||||
| 
 | ||||
|     /** | ||||
|      * Get whole WikiText of required file | ||||
|      * @param title : Name of the file | ||||
|      * @return Observable<MwQueryResult> | ||||
|      */ | ||||
|     fun getCurrentWikiText(title: String): Single<String?> { | ||||
|         return mediaDetailInterface.getWikiText(title).map { | ||||
|             it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun responseMapper( | ||||
|         networkResult: Single<MwQueryResponse>, | ||||
|         key: String? | ||||
|  |  | |||
|  | @ -6,6 +6,9 @@ import static android.view.View.GONE; | |||
| import static android.view.View.VISIBLE; | ||||
| import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_NEEDING_CATEGORIES; | ||||
| import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_UNCATEGORISED; | ||||
| import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_DESCRIPTION_AND_CAPTION; | ||||
| import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT; | ||||
| import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.app.AlertDialog; | ||||
|  | @ -71,18 +74,23 @@ import fr.free.nrw.commons.contributions.ContributionsFragment; | |||
| import fr.free.nrw.commons.coordinates.CoordinateEditHelper; | ||||
| import fr.free.nrw.commons.delete.DeleteHelper; | ||||
| import fr.free.nrw.commons.delete.ReasonBuilder; | ||||
| import fr.free.nrw.commons.description.DescriptionEditActivity; | ||||
| import fr.free.nrw.commons.description.DescriptionEditHelper; | ||||
| 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.nearby.Label; | ||||
| import fr.free.nrw.commons.profile.ProfileActivity; | ||||
| import fr.free.nrw.commons.ui.widget.HtmlTextView; | ||||
| import fr.free.nrw.commons.upload.UploadMediaDetail; | ||||
| import fr.free.nrw.commons.utils.ViewUtilWrapper; | ||||
| import io.reactivex.Single; | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
|  | @ -99,6 +107,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements | |||
|     CategoryEditHelper.Callback { | ||||
| 
 | ||||
|     private static final int REQUEST_CODE = 1001 ; | ||||
|     private static final int REQUEST_CODE_EDIT_DESCRIPTION = 1002 ; | ||||
|     private boolean editable; | ||||
|     private boolean isCategoryImage; | ||||
|     private MediaDetailPagerFragment.MediaDetailProvider detailProvider; | ||||
|  | @ -136,6 +145,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements | |||
|     @Inject | ||||
|     CoordinateEditHelper coordinateEditHelper; | ||||
|     @Inject | ||||
|     DescriptionEditHelper descriptionEditHelper; | ||||
|     @Inject | ||||
|     ViewUtilWrapper viewUtil; | ||||
|     @Inject | ||||
|     CategoryClient categoryClient; | ||||
|  | @ -225,6 +236,10 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements | |||
|     String descriptionHtmlCode; | ||||
|     @BindView(R.id.progressBarDeletion) | ||||
|     ProgressBar progressBarDeletion; | ||||
|     @BindView(R.id.progressBarEdit) | ||||
|     ProgressBar progressBarEditDescription; | ||||
|     @BindView(R.id.description_edit) | ||||
|     Button editDescription; | ||||
| 
 | ||||
|     private ArrayList<String> categoryNames = new ArrayList<>(); | ||||
|     private String categorySearchQuery; | ||||
|  | @ -819,8 +834,155 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements | |||
|             .build(getActivity()), REQUEST_CODE); | ||||
|     } | ||||
| 
 | ||||
|     @OnClick(R.id.description_edit) | ||||
|     public void onDescriptionEditClicked() { | ||||
|         progressBarEditDescription.setVisibility(VISIBLE); | ||||
|         editDescription.setVisibility(GONE); | ||||
|         getDescriptionList(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the coordinates and update the existing coordinates. | ||||
|      * Gets descriptions from wikitext | ||||
|      */ | ||||
|     private void getDescriptionList() { | ||||
|         compositeDisposable.add(mediaDataExtractor.getCurrentWikiText( | ||||
|             Objects.requireNonNull(media.getFilename())) | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe(this::extractCaptionDescription, Timber::e)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets captions and descriptions and merge them according to language code and arranges it in a | ||||
|      * single list. | ||||
|      * Send the list to DescriptionEditActivity | ||||
|      * @param s wikitext | ||||
|      */ | ||||
|     private void extractCaptionDescription(final String s) { | ||||
|         final LinkedHashMap<String,String> descriptions = getDescriptions(s); | ||||
|         final LinkedHashMap<String,String> captions = getCaptionsList(); | ||||
| 
 | ||||
|         final ArrayList<UploadMediaDetail> descriptionAndCaptions = new ArrayList<>(); | ||||
| 
 | ||||
|         if(captions.size() >= descriptions.size()) { | ||||
|             for (final Map.Entry mapElement : captions.entrySet()) { | ||||
| 
 | ||||
|                 final String language = (String) mapElement.getKey(); | ||||
|                 if (descriptions.containsKey(language)) { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, | ||||
|                             Objects.requireNonNull(descriptions.get(language)), | ||||
|                             (String) mapElement.getValue()) | ||||
|                     ); | ||||
|                 } else { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, "", | ||||
|                             (String) mapElement.getValue()) | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|             for (final Map.Entry mapElement : descriptions.entrySet()) { | ||||
| 
 | ||||
|                 final String language = (String) mapElement.getKey(); | ||||
|                 if (!captions.containsKey(language)) { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, | ||||
|                             Objects.requireNonNull(descriptions.get(language)), | ||||
|                             "") | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             for (final Map.Entry mapElement : descriptions.entrySet()) { | ||||
| 
 | ||||
|                 final String language = (String) mapElement.getKey(); | ||||
|                 if (captions.containsKey(language)) { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, (String) mapElement.getValue(), | ||||
|                             Objects.requireNonNull(captions.get(language))) | ||||
|                     ); | ||||
|                 } else { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, (String) mapElement.getValue(), | ||||
|                             "") | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|             for (final Map.Entry mapElement : captions.entrySet()) { | ||||
| 
 | ||||
|                 final String language = (String) mapElement.getKey(); | ||||
|                 if (!descriptions.containsKey(language)) { | ||||
|                     descriptionAndCaptions.add( | ||||
|                         new UploadMediaDetail(language, | ||||
|                             "", | ||||
|                             Objects.requireNonNull(descriptions.get(language))) | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         final Intent intent = new Intent(requireContext(), DescriptionEditActivity.class); | ||||
|         final Bundle bundle = new Bundle(); | ||||
|         bundle.putParcelableArrayList(LIST_OF_DESCRIPTION_AND_CAPTION, descriptionAndCaptions); | ||||
|         bundle.putString(WIKITEXT, s); | ||||
|         intent.putExtras(bundle); | ||||
|         startActivityForResult(intent, REQUEST_CODE_EDIT_DESCRIPTION); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters descriptions from current wikiText and arranges it in LinkedHashmap according to the | ||||
|      * language code | ||||
|      * @param s wikitext | ||||
|      * @return LinkedHashMap<LanguageCode,Description> | ||||
|      */ | ||||
|     private LinkedHashMap<String,String> getDescriptions(String s) { | ||||
|         int descriptionIndex = s.indexOf("description="); | ||||
|         if(descriptionIndex == -1){ | ||||
|             descriptionIndex = s.indexOf("Description="); | ||||
|         } | ||||
| 
 | ||||
|         if( descriptionIndex == -1 ){ | ||||
|             return new LinkedHashMap<>(); | ||||
|         } | ||||
|         final String descriptionToEnd = s.substring(descriptionIndex+12); | ||||
|         final int descriptionEndIndex = descriptionToEnd.indexOf("\n"); | ||||
|         final String description = s.substring(descriptionIndex+12, descriptionIndex+12+descriptionEndIndex); | ||||
| 
 | ||||
|         final String[] arr = description.trim().split(","); | ||||
|         final LinkedHashMap<String,String> descriptionList = new LinkedHashMap<>(); | ||||
| 
 | ||||
|         if (!description.equals("")) { | ||||
|             for (final String string : | ||||
|                 arr) { | ||||
|                 final int startCode = string.indexOf("{{"); | ||||
|                 final int endCode = string.indexOf("|"); | ||||
|                 final String languageCode = string.substring(startCode + 2, endCode).trim(); | ||||
|                 final int startDescription = string.indexOf("="); | ||||
|                 final int endDescription = string.indexOf("}}"); | ||||
|                 final String languageDescription = string | ||||
|                     .substring(startDescription + 1, endDescription); | ||||
|                 descriptionList.put(languageCode, languageDescription); | ||||
|             } | ||||
|         } | ||||
|         return descriptionList; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets list of caption and arranges it in a LinkedHashmap according to the language code | ||||
|      * @return LinkedHashMap<LanguageCode,Caption> | ||||
|      */ | ||||
|     private LinkedHashMap<String,String> getCaptionsList() { | ||||
|         final LinkedHashMap<String, String> captionList = new LinkedHashMap<>(); | ||||
|         final Map<String, String> captions = media.getCaptions(); | ||||
|         for (final Map.Entry<String, String> map : captions.entrySet()) { | ||||
|             final String language = map.getKey(); | ||||
|             final String languageCaption = map.getValue(); | ||||
|             captionList.put(language, languageCaption); | ||||
|         } | ||||
|         return captionList; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the result from another activity and act accordingly. | ||||
|      * @param requestCode | ||||
|      * @param resultCode | ||||
|      * @param data | ||||
|  | @ -854,13 +1016,61 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements | |||
|                     updateCoordinates(latitude, longitude, accuracy); | ||||
|                 } | ||||
|             } | ||||
|         } else if (resultCode == RESULT_CANCELED) { | ||||
| 
 | ||||
|         } else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_OK) { | ||||
|             final String updatedWikiText = data.getStringExtra(UPDATED_WIKITEXT); | ||||
|             compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media, | ||||
|                 updatedWikiText) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(s -> { | ||||
|                     Timber.d("Descriptions are added."); | ||||
|                 })); | ||||
| 
 | ||||
|             final ArrayList<UploadMediaDetail> uploadMediaDetails | ||||
|                 = data.getParcelableArrayListExtra(LIST_OF_DESCRIPTION_AND_CAPTION); | ||||
| 
 | ||||
|             LinkedHashMap<String, String> updatedCaptions = new LinkedHashMap<>(); | ||||
|             for (UploadMediaDetail mediaDetail: | ||||
|             uploadMediaDetails) { | ||||
|                 compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media, | ||||
|                     mediaDetail.getLanguageCode(), mediaDetail.getCaptionText()) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe(s -> { | ||||
|                         updateCaptions(mediaDetail, updatedCaptions); | ||||
|                         Timber.d("Caption is added."); | ||||
|                     })); | ||||
|             } | ||||
|             progressBarEditDescription.setVisibility(GONE); | ||||
|             editDescription.setVisibility(VISIBLE); | ||||
| 
 | ||||
|         } else if (requestCode == REQUEST_CODE && resultCode == RESULT_CANCELED) { | ||||
|             viewUtil.showShortToast(getContext(), | ||||
|                 Objects.requireNonNull(getContext()) | ||||
|                     .getString(R.string.coordinates_picking_unsuccessful)); | ||||
| 
 | ||||
|         } else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_CANCELED) { | ||||
|             progressBarEditDescription.setVisibility(GONE); | ||||
|             editDescription.setVisibility(VISIBLE); | ||||
| 
 | ||||
|             viewUtil.showShortToast(getContext(), | ||||
|                 Objects.requireNonNull(getContext()) | ||||
|                     .getString(R.string.descriptions_picking_unsuccessful)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds caption to the map and updates captions | ||||
|      * @param mediaDetail UploadMediaDetail | ||||
|      * @param updatedCaptions updated captionds | ||||
|      */ | ||||
|     private void updateCaptions(UploadMediaDetail mediaDetail, | ||||
|         LinkedHashMap<String, String> updatedCaptions) { | ||||
|         updatedCaptions.put(mediaDetail.getLanguageCode(), mediaDetail.getCaptionText()); | ||||
|         media.setCaptions(updatedCaptions); | ||||
|     } | ||||
| 
 | ||||
|     @OnClick(R.id.update_categories_button) | ||||
|     public void onUpdateCategoriesClicked() { | ||||
|         updateCategories(categoryEditSearchRecyclerViewAdapter.getNewCategories()); | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ package fr.free.nrw.commons.media; | |||
| 
 | ||||
| import io.reactivex.Observable; | ||||
| import io.reactivex.Single; | ||||
| import org.wikipedia.dataclient.Service; | ||||
| import org.wikipedia.dataclient.mwapi.MwQueryResponse; | ||||
| import org.wikipedia.wikidata.Entities; | ||||
| import retrofit2.Call; | ||||
| import retrofit2.http.GET; | ||||
|  | @ -35,4 +37,18 @@ public interface MediaDetailInterface { | |||
|      */ | ||||
|     @GET("/w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1&sites=commonswiki") | ||||
|     Observable<Entities> getEntityForImage(@Query("languages") String language, @Query("ids") String wikibaseIdentifier); | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches current wikitext | ||||
|      * @param title file name | ||||
|      * @return Single<MwQueryResponse> | ||||
|      */ | ||||
|     @GET( | ||||
|         Service.MW_API_PREFIX + | ||||
|             "action=query&prop=revisions&rvprop=content|timestamp&rvlimit=1&converttitles=" | ||||
|     ) | ||||
|     Single<MwQueryResponse> getWikiText( | ||||
|         @Query("titles") String title | ||||
|     ); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ayan Sarkar
						Ayan Sarkar