diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java index 568071b13..6841e8e83 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -47,6 +47,7 @@ public class Media implements Parcelable { protected String filename; protected String description; // monolingual description on input... protected String discussion; + protected String caption; protected long dataLength; protected Date dateCreated; protected @Nullable Date dateUploaded; @@ -240,6 +241,22 @@ public class Media implements Parcelable { return imageUrl; } + /** + * Sets the Caption of the file. + * @param caption + */ + public void setCaption(String caption) { + this.caption = caption; + } + + /** + * Gets the file Caption as a string. + * @return file Caption as a string + */ + public String getCaption() { + return caption; + } + /** * Gets the name of the file. * @return file name as a string diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java index 4779e0455..5cdf53137 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java @@ -1,5 +1,9 @@ package fr.free.nrw.commons; +import android.util.Log; + +import java.io.IOException; + import javax.inject.Inject; import javax.inject.Singleton; @@ -20,16 +24,18 @@ public class MediaDataExtractor { private final MediaWikiApi mediaWikiApi; private final OkHttpJsonApiClient okHttpJsonApiClient; + @Inject public MediaDataExtractor(MediaWikiApi mwApi, OkHttpJsonApiClient okHttpJsonApiClient) { this.okHttpJsonApiClient = okHttpJsonApiClient; this.mediaWikiApi = mwApi; + } /** * Simplified method to extract all details required to show media details. - * It fetches media object, deletion status and talk page for the filename + * It fetches media object, deletion status, talk page and caption for the filename * @param filename for which the details are to be fetched * @return full Media object with all details including deletion status and talk page */ @@ -37,8 +43,11 @@ public class MediaDataExtractor { Single mediaSingle = getMediaFromFileName(filename); Single pageExistsSingle = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); Single discussionSingle = getDiscussion(filename); - return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, (media, deletionStatus, discussion) -> { + Single captionSingle = getCaption(filename); + + return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, captionSingle, (media, deletionStatus, discussion, caption) -> { media.setDiscussion(discussion); + media.setCaption(caption); if (deletionStatus) { media.setRequestedDeletion(); } @@ -68,5 +77,19 @@ public class MediaDataExtractor { Timber.e(throwable, "Error occurred while fetching discussion"); return ""; }); + + } + + /** + * Fetch caption from the MediaWiki API + * @param filename the filename we will return the caption for + * @return a single with caption string (an empty string if no caption) + */ + private Single getCaption(String filename) { + return mediaWikiApi.fetchCaptionByFilename(filename) + .onErrorReturn(throwable -> { + Timber.e(throwable, "Error occurred while fetching caption"); + return ""; + }); } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 92c0a4b01..dba890da3 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -101,6 +101,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { SimpleDraweeView image; @BindView(R.id.mediaDetailSpacer) MediaDetailSpacer spacer; + @BindView(R.id.mediaDetailCaption) + TextView mediaCaption; @BindView(R.id.mediaDetailTitle) TextView title; @BindView(R.id.mediaDetailDesc) @@ -315,6 +317,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { coordinates.setText(prettyCoordinates(media)); uploadedDate.setText(prettyUploadedDate(media)); mediaDiscussion.setText(prettyDiscussion(media)); + mediaCaption.setText(prettyCaption(media)); categoryNames.clear(); categoryNames.addAll(media.getCategories()); @@ -516,6 +519,16 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { return desc; } } + + private String prettyCaption(Media media) { + String caption = media.getCaption().trim(); + if (caption.equals("")) { + return getString(R.string.detail_caption_empty); + } else { + return caption; + } + } + private String prettyDiscussion(Media media) { String disc = media.getDiscussion().trim(); if (disc.equals("")) { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index b7f7e57a9..9419a9eff 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -3,6 +3,7 @@ package fr.free.nrw.commons.mwapi; import android.content.Context; import android.net.Uri; import android.text.TextUtils; +import android.util.Log; import com.google.gson.Gson; @@ -303,6 +304,26 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .getString("/api/flow-parsoid-utils/@content")); } + /** + * fetches the Caption of the file with a given name. + * @param filename title of the file + * @return a single with media caption + */ + @Override + public Single fetchCaptionByFilename(String filename) { + return Single.fromCallable(() -> { + CustomApiResult apiResult = api.action("wbgetentities") + .param("sites", "commonswiki") + .param("titles", filename) + .param("props", "labels") + .param("format", "xml") + .param("languages", Locale.getDefault().getLanguage()) + .param("languagefallback", "1") + .get(); + return apiResult.getString("/api/entities/entity/labels/label/@value"); + }); + } + @Override @NonNull public Single fetchMediaByFilename(String filename) { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 2d39740d5..1baba6e11 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -66,6 +66,8 @@ public interface MediaWikiApi { Single parseWikicode(String source); + Single fetchCaptionByFilename(String filename); + @NonNull Single fetchMediaByFilename(String filename); diff --git a/app/src/main/res/layout/fragment_media_detail.xml b/app/src/main/res/layout/fragment_media_detail.xml index 5c325aaaf..8e885d34a 100644 --- a/app/src/main/res/layout/fragment_media_detail.xml +++ b/app/src/main/res/layout/fragment_media_detail.xml @@ -52,6 +52,34 @@ android:orientation="vertical" android:padding="@dimen/standard_gap"> + + + + + + + None selected No description No discussion + No caption Unknown license Refresh Requesting Storage Permission @@ -173,6 +174,7 @@ This file already exists on Commons. Are you sure you want to proceed? Yes No + Caption Title Description Discussion diff --git a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt index 839d7f22c..909ff8ce5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt @@ -7,12 +7,9 @@ import io.reactivex.Single import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test -import org.mockito.ArgumentMatchers -import org.mockito.InjectMocks -import org.mockito.Mock +import org.mockito.* import org.mockito.Mockito.`when` import org.mockito.Mockito.mock -import org.mockito.MockitoAnnotations /** * Test methods in media data extractor @@ -45,6 +42,8 @@ class MediaDataExtractorTest { `when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())) .thenReturn(Single.just(mock(Media::class.java))) + Mockito.`when`(mwApi?.fetchCaptionByFilename(ArgumentMatchers.anyString())).thenReturn(Single.just("test caption")) + `when`(mwApi?.pageExists(ArgumentMatchers.anyString())) .thenReturn(Single.just(true)) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt index 04f20f7b9..2e2ae3f30 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt @@ -6,6 +6,7 @@ import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.utils.ConfigUtils +import io.reactivex.observers.TestObserver import okhttp3.OkHttpClient import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -23,6 +24,7 @@ import org.wikipedia.util.DateUtil import java.net.URLDecoder import java.util.* + @RunWith(RobolectricTestRunner::class) @Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class) class ApacheHttpClientMediaWikiApiTest { @@ -240,6 +242,30 @@ class ApacheHttpClientMediaWikiApiTest { assertFalse(result) } + @Test + fun fetchCaptionByFilename() { + server.enqueue(MockResponse().setBody("")) + + val result = testObject.fetchCaptionByFilename("File:foo") + val testObserver = TestObserver() + result.subscribe(testObserver) + assertBasicRequestParameters(server, "GET").let { request -> + parseQueryParams(request).let { params -> + assertEquals("xml", params["format"]) + assertEquals("wbgetentities", params["action"]) + assertEquals("commonswiki", params["sites"]) + assertEquals("File:foo", params["titles"]) + assertEquals("labels", params["props"]) + assertEquals(Locale.getDefault().getLanguage(), params["languages"]) + assertEquals("1", params["languagefallback"]) + } + } + + testObserver.assertResult("Test") + testObserver.assertNoErrors() + + } + @Test fun isUserBlockedFromCommonsForInfinitelyBlockedUser() { server.enqueue(MockResponse().setBody(""))