#3780 Create media using a combination of Entities & MwQueryResult (#3786)

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadCategoryDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - update BaseAdapter to be easier to use

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchImagesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchCategoriesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace NotificationRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadDepictsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace PlaceRenderer

* #3756 Convert SearchDepictionsFragment to use Pagination - convert SearchDepictionsFragment

* #3756 Convert SearchDepictionsFragment to use Pagination - fix presenter unit tests now that view is not nullable - fix Category prefix imports

* #3756 Convert SearchDepictionsFragment to use Pagination - test DataSource related classes

* #3756 Convert SearchDepictionsFragment to use Pagination - reset rx scheduler - ignore failing test

* #3760 Convert SearchCategoriesFragment to use Pagination - extract functionality of pagination to base classes - add category pagination

* #3772 Convert SearchImagesFragment to use Pagination  - convert SearchImagesFragment - tidy up showing the empty view - make search fragments show snackbar with appropriate text

* #3772 Convert SearchImagesFragment to use Pagination  - allow viewpager to load more data

* #3760 remove test that got re-added by merge

* #3760 remove duplicate dependency

* #3772 fix compilation

* #3780 Create media using a combination of Entities & MwQueryResult - construct media with an entity - move fields from media down to contribution - move dynamic fields outside of media - remove unused constructors - remove all unnecessary fetching of captions/descriptions - bump database version

* #3808 Construct media objects that depict an item id correctly - use generator to construct media for DepictedImages

* #3780 Create media using a combination of Entities & MwQueryResult - update wikicode to align with expected behaviour

* #3780 Create media using a combination of Entities & MwQueryResult - replace old site of thumbnail title with most relevant caption
This commit is contained in:
Seán Mac Gillicuddy 2020-06-25 08:20:01 +01:00 committed by GitHub
parent bf4b7e2efc
commit 4b22583b60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 803 additions and 1532 deletions

View file

@ -1,30 +0,0 @@
package fr.free.nrw.commons.media
import android.os.Parcelable
import androidx.annotation.WorkerThread
import fr.free.nrw.commons.wikidata.WikidataProperties.DEPICTS
import kotlinx.android.parcel.Parcelize
import org.wikipedia.wikidata.DataValue.EntityId
import org.wikipedia.wikidata.Entities
import java.util.*
@Parcelize
data class Depictions(val depictions: List<IdAndLabel>) : Parcelable {
companion object {
@JvmStatic
@WorkerThread
fun from(entities: Entities, mediaClient: MediaClient) =
Depictions(
entities.first?.statements
?.getOrElse(DEPICTS.propertyName, { emptyList() })
?.map { statement ->
(statement.mainSnak.dataValue as EntityId).value.id
}
?.map { id -> IdAndLabel(id, fetchLabel(mediaClient, id)) }
?: emptyList()
)
private fun fetchLabel(mediaClient: MediaClient, id: String) =
mediaClient.getLabelForDepiction(id, Locale.getDefault().language).blockingGet()
}
}

View file

@ -0,0 +1,4 @@
package fr.free.nrw.commons.media
data class IdAndCaptions(val id: String, val captions: Map<String, String>)

View file

@ -1,14 +0,0 @@
package fr.free.nrw.commons.media
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import org.wikipedia.wikidata.Entities
@Parcelize
data class IdAndLabel(val entityId: String, val entityLabel: String) : Parcelable {
constructor(entityId: String, entities: MutableMap<String, Entities.Entity>) : this(
entityId,
entities.values.first().labels().values.first().value()
)
}

View file

@ -1,18 +1,17 @@
package fr.free.nrw.commons.media
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.media.Depictions.Companion.from
import fr.free.nrw.commons.depictions.Media.DepictedImagesFragment.PAGE_ID_PREFIX
import fr.free.nrw.commons.explore.media.MediaConverter
import fr.free.nrw.commons.utils.CommonsDateUtil
import io.reactivex.Observable
import io.reactivex.Single
import org.wikipedia.dataclient.mwapi.MwQueryPage
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.wikidata.Entities
import timber.log.Timber
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
/**
* Media Client to handle custom calls to Commons MediaWiki APIs
@ -21,12 +20,16 @@ import kotlin.collections.ArrayList
class MediaClient @Inject constructor(
private val mediaInterface: MediaInterface,
private val pageMediaInterface: PageMediaInterface,
private val mediaDetailInterface: MediaDetailInterface
private val mediaDetailInterface: MediaDetailInterface,
private val mediaConverter: MediaConverter
) {
fun getMediaById(id: String) =
responseToMediaList(mediaInterface.getMediaById(id)).map { it.first() }
//OkHttpJsonApiClient used JsonKvStore for this. I don't know why.
private val continuationStore: MutableMap<String, Map<String, String>?>
private val continuationExists: MutableMap<String, Boolean>
private val continuationStore: MutableMap<String, Map<String, String>?> = mutableMapOf()
private val continuationExists: MutableMap<String, Boolean> = mutableMapOf()
/**
* Checks if a page exists on Commons
@ -36,11 +39,7 @@ class MediaClient @Inject constructor(
*/
fun checkPageExistsUsingTitle(title: String?): Single<Boolean> {
return mediaInterface.checkPageExistsUsingTitle(title)
.map { mwQueryResponse: MwQueryResponse ->
mwQueryResponse
.query()!!.firstPage()!!.pageId() > 0
}
.singleOrError()
.map { it.query()!!.firstPage()!!.pageId() > 0 }
}
/**
@ -50,11 +49,7 @@ class MediaClient @Inject constructor(
*/
fun checkFileExistsUsingSha(fileSha: String?): Single<Boolean> {
return mediaInterface.checkFileExistsUsingSha(fileSha)
.map { mwQueryResponse: MwQueryResponse ->
mwQueryResponse
.query()!!.allImages().size > 0
}
.singleOrError()
.map { it.query()!!.allImages().size > 0 }
}
/**
@ -66,18 +61,13 @@ class MediaClient @Inject constructor(
*/
fun getMediaListFromCategory(category: String): Single<List<Media>> {
return responseToMediaList(
if (continuationStore.containsKey("category_$category")) mediaInterface.getMediaListFromCategory(
mediaInterface.getMediaListFromCategory(
category,
10,
continuationStore["category_$category"]
) else //if true
mediaInterface.getMediaListFromCategory(
category,
10,
emptyMap()
),
continuationStore["category_$category"] ?: emptyMap()
),
"category_$category"
) //if false
)
}
/**
@ -89,25 +79,16 @@ class MediaClient @Inject constructor(
* @return
*/
fun getMediaListForUser(userName: String): Single<List<Media>> {
val continuation =
if (continuationStore.containsKey("user_$userName")) continuationStore["user_$userName"] else emptyMap()
return responseToMediaList(
mediaInterface
.getMediaListForUser(userName, 10, continuation), "user_$userName"
mediaInterface.getMediaListForUser(
userName,
10,
continuationStore["user_$userName"] ?: Collections.emptyMap()
),
"user_$userName"
)
}
/**
* Check if media for user has reached the end of the list.
* @param userName
* @return
*/
fun doesMediaListForUserHaveMorePages(userName: String): Boolean {
val key = "user_$userName"
return if (continuationExists.containsKey(key)) {
continuationExists[key]!!
} else true
}
/**
* This method takes a keyword as input and returns a list of Media objects filtered using image generator query
@ -118,40 +99,45 @@ class MediaClient @Inject constructor(
* @param offset
* @return
*/
fun getMediaListFromSearch(
keyword: String?,
limit: Int,
offset: Int
): Single<MwQueryResponse> {
return mediaInterface.getMediaListFromSearch(keyword, limit, offset)
fun getMediaListFromSearch(keyword: String?, limit: Int, offset: Int) =
responseToMediaList(mediaInterface.getMediaListFromSearch(keyword, limit, offset))
/**
* @return list of images for a particular depict entity
*/
fun fetchImagesForDepictedItem(query: String, sroffset: Int): Single<List<Media>> {
return responseToMediaList(
mediaInterface.fetchImagesForDepictedItem(
"haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query,
sroffset.toString()
)
)
}
private fun responseToMediaList(
response: Observable<MwQueryResponse>,
key: String
response: Single<MwQueryResponse>,
key: String? = null
): Single<List<Media>> {
return response.flatMap { mwQueryResponse: MwQueryResponse? ->
if (null == mwQueryResponse || null == mwQueryResponse.query() || null == mwQueryResponse.query()!!
.pages()
) {
return@flatMap Observable.empty<MwQueryPage>()
return response.map {
if (key != null) {
continuationExists[key] =
it.continuation()?.let { continuation ->
continuationStore[key] = continuation
true
} ?: false
}
if (mwQueryResponse.continuation() != null) {
continuationStore[key] = mwQueryResponse.continuation()
continuationExists[key] = true
} else {
continuationExists[key] = false
it.query()?.pages() ?: emptyList()
}.flatMap(::mediaFromPageAndEntity)
}
private fun mediaFromPageAndEntity(pages: List<MwQueryPage>): Single<List<Media>> {
return getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" })
.map {
pages.zip(it.entities().values)
.map { (page, entity) -> mediaConverter.convert(page, entity) }
}
Observable.fromIterable(mwQueryResponse.query()!!.pages())
}
.map { page: MwQueryPage? -> Media.from(page) }
.collect(
{ ArrayList() }
) { obj: MutableList<Media>, e: Media ->
obj.add(
e
)
}.map { it.toList() }
}
/**
@ -161,17 +147,8 @@ class MediaClient @Inject constructor(
* @return
*/
fun getMedia(titles: String?): Single<Media> {
return mediaInterface.getMedia(titles)
.flatMap { mwQueryResponse: MwQueryResponse? ->
if (null == mwQueryResponse || null == mwQueryResponse.query() || null == mwQueryResponse.query()!!
.firstPage()
) {
return@flatMap Observable.empty<MwQueryPage>()
}
Observable.just(mwQueryResponse.query()!!.firstPage())
}
.map { page: MwQueryPage? -> Media.from(page) }
.single(Media.EMPTY)
return responseToMediaList(mediaInterface.getMedia(titles))
.map { it.first() }
}
/**
@ -179,122 +156,37 @@ class MediaClient @Inject constructor(
*
* @return Media object corresponding to the picture of the day
*/
val pictureOfTheDay: Single<Media>
get() {
val date =
CommonsDateUtil.getIso8601DateFormatShort().format(Date())
Timber.d("Current date is %s", date)
val template = "Template:Potd/$date"
return mediaInterface.getMediaWithGenerator(template)
.flatMap { mwQueryResponse: MwQueryResponse? ->
if (null == mwQueryResponse || null == mwQueryResponse.query() || null == mwQueryResponse.query()!!
.firstPage()
) {
return@flatMap Observable.empty<MwQueryPage>()
}
Observable.just(mwQueryResponse.query()!!.firstPage())
}
.map { page: MwQueryPage? -> Media.from(page) }
.single(Media.EMPTY)
}
fun getPictureOfTheDay(): Single<Media> {
val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date())
return responseToMediaList(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() }
}
fun getPageHtml(title: String?): Single<String> {
return mediaInterface.getPageHtml(title)
.filter { obj: MwParseResponse -> obj.success() }
.map { obj: MwParseResponse -> obj.parse() }
.map { obj: MwParseResult? -> obj!!.text() }
.first("")
.map { obj: MwParseResponse -> obj.parse()?.text() ?: "" }
}
fun getEntities(entityIds: List<String>): Single<Entities> {
return if (entityIds.isEmpty())
Single.error(Exception("empty list passed for ids"))
else
mediaDetailInterface.getEntity(entityIds.joinToString("|"))
}
/**
* @return caption for image using wikibaseIdentifier
* Check if media for user has reached the end of the list.
* @param userName
* @return
*/
fun getCaptionByWikibaseIdentifier(wikibaseIdentifier: String?): Single<String> {
return mediaDetailInterface.getEntityForImage(
Locale.getDefault().language,
wikibaseIdentifier
)
.map { mediaDetailResponse: Entities ->
if (isSuccess(mediaDetailResponse)) {
for (wikibaseItem in mediaDetailResponse.entities().values) {
for (label in wikibaseItem.labels().values) {
return@map label.value()
}
}
}
NO_CAPTION
}
.singleOrError()
fun doesMediaListForUserHaveMorePages(userName: String): Boolean {
val key = "user_$userName"
return if (continuationExists.containsKey(key)) continuationExists[key]!! else true
}
fun doesPageContainMedia(title: String?): Single<Boolean> {
return pageMediaInterface.getMediaList(title)
.map { it.items.isNotEmpty() }
}
private fun isSuccess(response: Entities?): Boolean {
return response != null && response.success == 1 && response.entities() != null
}
/**
* Fetches Structured data from API
*
* @param filename
* @return a map containing caption and depictions (empty string in the map if no caption/depictions)
*/
fun getDepictions(filename: String?): Single<Depictions> {
return mediaDetailInterface.fetchEntitiesByFileName(
Locale.getDefault().language, filename
)
.map { entities: Entities? ->
from(
entities!!,
this
)
}
.singleOrError()
}
/**
* Gets labels for Depictions using Entity Id from MediaWikiAPI
*
* @param entityId EntityId (Ex: Q81566) of the depict entity
* @return label
*/
fun getLabelForDepiction(
entityId: String?,
language: String
): Single<String> {
return mediaDetailInterface.getEntity(entityId)
.map { entities: Entities ->
if (isSuccess(entities)) {
for (entity in entities.entities().values) {
val languageToLabelMap =
entity.labels()
if (languageToLabelMap.containsKey(language)) {
return@map languageToLabelMap[language]!!.value()
}
for (label in languageToLabelMap.values) {
return@map label.value()
}
}
}
throw RuntimeException("failed getEntities")
}
}
fun getEntities(entityId: String?): Single<Entities> {
return mediaDetailInterface.getEntity(entityId)
}
companion object {
const val NO_CAPTION = "No caption"
private const val NO_DEPICTION = "No depiction"
}
init {
continuationStore =
HashMap()
continuationExists = HashMap()
}
}

View file

@ -54,11 +54,12 @@ import fr.free.nrw.commons.ui.widget.HtmlTextView;
import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.wikipedia.util.DateUtil;
@ -70,7 +71,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
private boolean isCategoryImage;
private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
private int index;
private Locale locale;
private boolean isDeleted = false;
@ -141,7 +141,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
@BindView(R.id.mediaDetailScrollView)
ScrollView scrollView;
private ArrayList<String> categoryNames;
/**
* Depicts is a feature part of Structured data. Multiple Depictions can be added for an image just like categories.
* However unlike categories depictions is multi-lingual
@ -150,10 +149,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
private ImageInfo imageInfoCache;
private int oldWidthOfImageView;
private int newWidthOfImageView;
private Depictions depictions;
private boolean categoriesLoaded = false;
private boolean categoriesPresent = false;
private boolean depictionLoaded = false;
private boolean heightVerifyingBoolean = true; // helps in maintaining aspect ratio
private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
@ -203,9 +198,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
reasonList.add(getString(R.string.deletion_reason_no_longer_want_public));
reasonList.add(getString(R.string.deletion_reason_bad_for_my_privacy));
categoryNames = new ArrayList<>();
categoryNames.add(getString(R.string.detail_panel_cats_loading));
final View view = inflater.inflate(R.layout.fragment_media_detail, container, false);
ButterKnife.bind(this,view);
@ -217,7 +209,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
authorLayout.setVisibility(GONE);
}
locale = getResources().getConfiguration().locale;
return view;
}
@ -291,19 +282,55 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
}
private void displayMediaDetails() {
//Always load image from Internet to allow viewing the desc, license, and cats
setupImageView();
title.setText(media.getDisplayTitle());
desc.setHtmlText(media.getDescription());
license.setText(media.getLicense());
Disposable disposable = mediaDataExtractor.fetchMediaDetails(media.getFilename(), media.getPageId())
setTextFields(media);
compositeDisposable.addAll(
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::setTextFields);
compositeDisposable.add(disposable);
.subscribe(this::onDepictionsLoaded, Timber::e),
mediaDataExtractor.checkDeletionRequestExists(media)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onDeletionPageExists, Timber::e),
mediaDataExtractor.fetchDiscussion(media)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onDiscussionLoaded, Timber::e),
mediaDataExtractor.refresh(media)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onMediaRefreshed, Timber::e)
);
}
private void onMediaRefreshed(Media media) {
setTextFields(media);
compositeDisposable.addAll(
mediaDataExtractor.fetchDepictionIdsAndLabels(media)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onDepictionsLoaded, Timber::e)
);
}
private void onDiscussionLoaded(String discussion) {
mediaDiscussion.setText(prettyDiscussion(discussion.trim()));
}
private void onDeletionPageExists(Boolean deletionPageExists) {
if (deletionPageExists){
delete.setVisibility(GONE);
nominatedForDeletion.setVisibility(VISIBLE);
} else if (!isCategoryImage) {
delete.setVisibility(VISIBLE);
nominatedForDeletion.setVisibility(GONE);
}
}
private void onDepictionsLoaded(List<IdAndCaptions> idAndCaptions){
depictsLayout.setVisibility(idAndCaptions.isEmpty() ? GONE : VISIBLE);
buildDepictionList(idAndCaptions);
}
/**
* The imageSpacer is Basically a transparent overlay for the SimpleDraweeView
* which holds the image to be displayed( moreover this image is out of
@ -370,58 +397,45 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
}
private void setTextFields(Media media) {
this.media = media;
setupImageView();
title.setText(media.getDisplayTitle());
desc.setHtmlText(prettyDescription(media));
license.setText(prettyLicense(media));
coordinates.setText(prettyCoordinates(media));
uploadedDate.setText(prettyUploadedDate(media));
mediaDiscussion.setText(prettyDiscussion(media));
if (prettyCaption(media).equals(getContext().getString(R.string.detail_caption_empty))) {
captionLayout.setVisibility(GONE);
} else mediaCaption.setText(prettyCaption(media));
} else {
mediaCaption.setText(prettyCaption(media));
}
categoryNames.clear();
categoryNames.addAll(media.getCategories());
depictions=media.getDepiction();
depictionLoaded = true;
categoriesLoaded = true;
categoriesPresent = (categoryNames.size() > 0);
if (!categoriesPresent) {
final List<String> categories = media.getCategories();
if (categories.isEmpty()) {
// Stick in a filler element.
categoryNames.add(getString(R.string.detail_panel_cats_none));
categories.add(getString(R.string.detail_panel_cats_none));
}
rebuildCatList();
rebuildCatList(categories);
if(depictions != null) {
rebuildDepictionList();
}
else depictsLayout.setVisibility(GONE);
if (media.getCreator() == null || media.getCreator().equals("")) {
authorLayout.setVisibility(GONE);
} else {
author.setText(media.getCreator());
}
checkDeletion(media);
}
/**
* Populates media details fragment with depiction list
* @param idAndCaptions
*/
private void rebuildDepictionList() {
private void buildDepictionList(List<IdAndCaptions> idAndCaptions) {
depictionContainer.removeAllViews();
for (IdAndLabel depiction : depictions.getDepictions()) {
depictionContainer.addView(
buildDepictLabel(
depiction.getEntityLabel(),
depiction.getEntityId(),
for (IdAndCaptions idAndCaption : idAndCaptions) {
depictionContainer.addView(buildDepictLabel(
idAndCaption.getCaptions().values().iterator().next(),
idAndCaption.getId(),
depictionContainer
));
}
@ -446,7 +460,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
@OnClick(R.id.copyWikicode)
public void onCopyWikicodeClicked(){
String data = "[[" + media.getFilename() + "|thumb|" + media.getDescription() + "]]";
String data = "[[" + media.getFilename() + "|thumb|" + media.getFallbackDescription() + "]]";
Utils.copy("wikiCode",data,getContext());
Timber.d("Generated wikidata copy code: %s", data);
@ -573,42 +587,37 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
}
}
private void rebuildCatList() {
private void rebuildCatList(List<String> categories) {
categoryContainer.removeAllViews();
// @fixme add the category items
//As per issue #1826(see https://github.com/commons-app/apps-android-commons/issues/1826), some categories come suffixed with strings prefixed with |. As per the discussion
//that was meant for alphabetical sorting of the categories and can be safely removed.
for (int i = 0; i < categoryNames.size(); i++) {
String categoryName = categoryNames.get(i);
//Removed everything after '|'
int indexOfPipe = categoryName.indexOf('|');
if (indexOfPipe != -1) {
categoryName = categoryName.substring(0, indexOfPipe);
//Set the updated category to the list as well
categoryNames.set(i, categoryName);
}
View catLabel = buildCatLabel(categoryName, categoryContainer);
categoryContainer.addView(catLabel);
for (String category : categories) {
categoryContainer.addView(buildCatLabel(sanitise(category), categoryContainer));
}
}
//As per issue #1826(see https://github.com/commons-app/apps-android-commons/issues/1826), some categories come suffixed with strings prefixed with |. As per the discussion
//that was meant for alphabetical sorting of the categories and can be safely removed.
private String sanitise(String category) {
int indexOfPipe = category.indexOf('|');
if (indexOfPipe != -1) {
//Removed everything after '|'
return category.substring(0, indexOfPipe);
}
return category;
}
/**
* Add view to depictions obtained also tapping on depictions should open the url
*/
private View buildDepictLabel(String depictionName, String entityId, LinearLayout depictionContainer) {
final View item = LayoutInflater.from(getContext()).inflate(R.layout.detail_category_item, depictionContainer, false);
final View item = LayoutInflater.from(getContext()).inflate(R.layout.detail_category_item, depictionContainer,false);
final TextView textView = item.findViewById(R.id.mediaDetailCategoryItemText);
textView.setText(depictionName);
if (depictionLoaded) {
item.setOnClickListener(view -> {
Intent intent = new Intent(getContext(), WikidataItemDetailsActivity.class);
intent.putExtra("wikidataItemName", depictionName);
intent.putExtra("entityId", entityId);
getContext().startActivity(intent);
});
}
item.setOnClickListener(view -> {
Intent intent = new Intent(getContext(), WikidataItemDetailsActivity.class);
intent.putExtra("wikidataItemName", depictionName);
intent.putExtra("entityId", entityId);
getContext().startActivity(intent);
});
return item;
}
@ -617,7 +626,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
final TextView textView = item.findViewById(R.id.mediaDetailCategoryItemText);
textView.setText(catName);
if (categoriesLoaded && categoriesPresent) {
if(!getString(R.string.detail_panel_cats_none).equals(catName)) {
textView.setOnClickListener(view -> {
// Open Category Details page
String selectedCategoryTitle = CATEGORY_PREFIX + catName;
@ -636,30 +645,36 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
* @return caption as string
*/
private String prettyCaption(Media media) {
String caption = media.getCaption().trim();
if (caption.equals("")) {
return getString(R.string.detail_caption_empty);
} else {
return caption;
for (String caption : media.getCaptions().values()) {
if (caption.equals("")) {
return getString(R.string.detail_caption_empty);
} else {
return caption;
}
}
return getString(R.string.detail_caption_empty);
}
private String prettyDescription(Media media) {
// @todo use UI language when multilingual descs are available
String desc = media.getDescription();
if (desc.equals("")) {
return getString(R.string.detail_description_empty);
} else {
return desc;
}
final String description = chooseDescription(media);
return description.isEmpty() ? getString(R.string.detail_description_empty)
: description;
}
private String prettyDiscussion(Media media) {
String disc = media.getDiscussion().trim();
if (disc.equals("")) {
return getString(R.string.detail_discussion_empty);
} else {
return disc;
private String chooseDescription(Media media) {
final Map<String, String> descriptions = media.getDescriptions();
final String multilingualDesc = descriptions.get(Locale.getDefault().getLanguage());
if (multilingualDesc != null) {
return multilingualDesc;
}
for (String description : descriptions.values()) {
return description;
}
return media.getFallbackDescription();
}
private String prettyDiscussion(String discussion) {
return discussion.isEmpty() ? getString(R.string.detail_discussion_empty) : discussion;
}
private String prettyLicense(Media media) {
@ -691,14 +706,4 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
return media.getCoordinates().getPrettyCoordinateString();
}
private void checkDeletion(Media media){
if (media.isRequestedDeletion()){
delete.setVisibility(GONE);
nominatedForDeletion.setVisibility(VISIBLE);
} else if (!isCategoryImage) {
delete.setVisibility(VISIBLE);
nominatedForDeletion.setVisibility(GONE);
}
}
}

View file

@ -1,7 +1,5 @@
package fr.free.nrw.commons.media;
import fr.free.nrw.commons.depictions.models.DepictionResponse;
import io.reactivex.Observable;
import io.reactivex.Single;
import java.util.Map;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
@ -24,7 +22,7 @@ public interface MediaInterface {
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2")
Observable<MwQueryResponse> checkPageExistsUsingTitle(@Query("titles") String title);
Single<MwQueryResponse> checkPageExistsUsingTitle(@Query("titles") String title);
/**
* Check if file exists
@ -33,7 +31,7 @@ public interface MediaInterface {
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2&list=allimages")
Observable<MwQueryResponse> checkFileExistsUsingSha(@Query("aisha1") String aisha1);
Single<MwQueryResponse> checkFileExistsUsingSha(@Query("aisha1") String aisha1);
/**
* This method retrieves a list of Media objects filtered using image generator query
@ -46,7 +44,8 @@ public interface MediaInterface {
@GET("w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
"&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc" + //Category parameters
MEDIA_PARAMS)
Observable<MwQueryResponse> getMediaListFromCategory(@Query("gcmtitle") String category, @Query("gcmlimit") int itemLimit, @QueryMap Map<String, String> continuation);
Single<MwQueryResponse> getMediaListFromCategory(@Query("gcmtitle") String category, @Query("gcmlimit") int itemLimit, @QueryMap Map<String, String> continuation);
/**
* This method retrieves a list of Media objects for a given user name
@ -58,7 +57,7 @@ public interface MediaInterface {
*/
@GET("w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
"&generator=allimages&gaisort=timestamp&gaidir=older" + MEDIA_PARAMS)
Observable<MwQueryResponse> getMediaListForUser(@Query("gaiuser") String username,
Single<MwQueryResponse> getMediaListForUser(@Query("gaiuser") String username,
@Query("gailimit") int itemLimit, @QueryMap(encoded = true) Map<String, String> continuation);
/**
@ -82,7 +81,17 @@ public interface MediaInterface {
*/
@GET("w/api.php?action=query&format=json&formatversion=2" +
MEDIA_PARAMS)
Observable<MwQueryResponse> getMedia(@Query("titles") String title);
Single<MwQueryResponse> getMedia(@Query("titles") String title);
/**
* Fetches Media object from the imageInfo API
*
* @param pageIds the ids to be searched for
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2" +
MEDIA_PARAMS)
Single<MwQueryResponse> getMediaById(@Query("pageids") String pageIds);
/**
* Fetches Media object from the imageInfo API
@ -93,21 +102,29 @@ public interface MediaInterface {
*/
@GET("w/api.php?action=query&format=json&formatversion=2&generator=images" +
MEDIA_PARAMS)
Observable<MwQueryResponse> getMediaWithGenerator(@Query("titles") String title);
Single<MwQueryResponse> getMediaWithGenerator(@Query("titles") String title);
@GET("w/api.php?format=json&action=parse&prop=text")
Observable<MwParseResponse> getPageHtml(@Query("page") String title);
Single<MwParseResponse> getPageHtml(@Query("page") String title);
/**
* Fetches list of images from a depiction entity
*
* @param query depictionEntityId
* @param sroffset number od depictions already fetched, this is useful in implementing
* pagination
*/
* Fetches caption using file name
*
* @param filename name of the file to be used for fetching captions
* */
@GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1")
Single<MwQueryResponse> fetchCaptionByFilename(@Query("language") String language, @Query("titles") String filename);
@GET("w/api.php?action=query&list=search&format=json&srnamespace=6")
Observable<DepictionResponse> fetchImagesForDepictedItem(@Query("srsearch") String query,
@Query("sroffset") String sroffset);
/**
* Fetches list of images from a depiction entity
*
* @param query depictionEntityId
* @param sroffset number od depictions already fetched, this is useful in implementing pagination
*/
@GET("w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
"&generator=search&gsrnamespace=6" + //Search parameters
MEDIA_PARAMS)
Single<MwQueryResponse> fetchImagesForDepictedItem(@Query("gsrsearch") String query, @Query("gsroffset") String sroffset);
}