mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-11-03 08:13:55 +01:00
* #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:
parent
bf4b7e2efc
commit
4b22583b60
46 changed files with 803 additions and 1532 deletions
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
data class IdAndCaptions(val id: String, val captions: Map<String, String>)
|
||||
|
||||
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue