mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
With media client APIs migrated to retrofit (#2998)
* With media client APIs migrated to retrofit * Add test cases and java docs * Fix test * Fix build * Fix build and other minor issues * Fix tests
This commit is contained in:
parent
828b5a3cd1
commit
78141cb609
17 changed files with 252 additions and 92 deletions
|
|
@ -4,6 +4,9 @@ import android.net.Uri;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.gallery.ExtMetadata;
|
||||
|
|
@ -20,8 +23,6 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil;
|
||||
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
||||
|
|
@ -156,9 +157,9 @@ public class Media implements Parcelable {
|
|||
page.title(),
|
||||
"",
|
||||
0,
|
||||
safeParseDate(metadata.dateTimeOriginal().value()),
|
||||
safeParseDate(metadata.dateTime().value()),
|
||||
StringUtil.fromHtml(metadata.artist().value()).toString()
|
||||
safeParseDate(metadata.dateTime()),
|
||||
safeParseDate(metadata.dateTime()),
|
||||
StringUtil.fromHtml(metadata.artist()).toString()
|
||||
);
|
||||
|
||||
if (!StringUtils.isBlank(imageInfo.getThumbUrl())) {
|
||||
|
|
@ -170,17 +171,17 @@ public class Media implements Parcelable {
|
|||
language = "default";
|
||||
}
|
||||
|
||||
media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription().value()));
|
||||
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories().value()));
|
||||
String latitude = metadata.gpsLatitude().value();
|
||||
String longitude = metadata.gpsLongitude().value();
|
||||
media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription()));
|
||||
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.getCategories()));
|
||||
String latitude = metadata.getGpsLatitude();
|
||||
String longitude = metadata.getGpsLongitude();
|
||||
|
||||
if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) {
|
||||
LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0);
|
||||
media.setCoordinates(latLng);
|
||||
}
|
||||
|
||||
media.setLicenseInformation(metadata.licenseShortName().value(), metadata.licenseUrl().value());
|
||||
media.setLicenseInformation(metadata.licenseShortName(), metadata.licenseUrl());
|
||||
return media;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import androidx.core.text.HtmlCompat;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import io.reactivex.Single;
|
||||
|
|
@ -19,12 +21,15 @@ import timber.log.Timber;
|
|||
public class MediaDataExtractor {
|
||||
private final MediaWikiApi mediaWikiApi;
|
||||
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
private final MediaClient mediaClient;
|
||||
|
||||
@Inject
|
||||
public MediaDataExtractor(MediaWikiApi mwApi,
|
||||
OkHttpJsonApiClient okHttpJsonApiClient) {
|
||||
OkHttpJsonApiClient okHttpJsonApiClient,
|
||||
MediaClient mediaClient) {
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
this.mediaWikiApi = mwApi;
|
||||
this.mediaClient = mediaClient;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +40,7 @@ public class MediaDataExtractor {
|
|||
*/
|
||||
public Single<Media> fetchMediaDetails(String filename) {
|
||||
Single<Media> mediaSingle = getMediaFromFileName(filename);
|
||||
Single<Boolean> pageExistsSingle = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename);
|
||||
Single<Boolean> pageExistsSingle = mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + filename);
|
||||
Single<String> discussionSingle = getDiscussion(filename);
|
||||
return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, (media, deletionStatus, discussion) -> {
|
||||
media.setDiscussion(discussion);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public final class OkHttpConnectionFactory {
|
|||
return new OkHttpClient.Builder()
|
||||
.cookieJar(SharedPreferenceCookieManager.getInstance())
|
||||
.cache(NET_CACHE)
|
||||
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
|
||||
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
|
||||
.addInterceptor(new UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(new CommonHeaderRequestInterceptor())
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import dagger.Module;
|
|||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.media.MediaInterface;
|
||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
|
|
@ -39,6 +40,8 @@ public class NetworkingModule {
|
|||
|
||||
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
private static final String NAMED_COMMONS_WIKI_SITE = "commons-wikisite";
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public OkHttpClient provideOkHttpClient(Context context,
|
||||
|
|
@ -120,7 +123,13 @@ public class NetworkingModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ReviewInterface provideReviewInterface(@Named("commons-wikisite") WikiSite commonsWikiSite) {
|
||||
public ReviewInterface provideReviewInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
|
||||
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public MediaInterface provideMediaInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
|
||||
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, MediaInterface.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
46
app/src/main/java/fr/free/nrw/commons/media/MediaClient.java
Normal file
46
app/src/main/java/fr/free/nrw/commons/media/MediaClient.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
/**
|
||||
* Media Client to handle custom calls to Commons MediaWiki APIs
|
||||
*/
|
||||
@Singleton
|
||||
public class MediaClient {
|
||||
|
||||
private final MediaInterface mediaInterface;
|
||||
|
||||
@Inject
|
||||
public MediaClient(MediaInterface mediaInterface) {
|
||||
this.mediaInterface = mediaInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a page exists on Commons
|
||||
* The same method can be used to check for file or talk page
|
||||
*
|
||||
* @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg
|
||||
*/
|
||||
public Single<Boolean> checkPageExistsUsingTitle(String title) {
|
||||
return mediaInterface.checkPageExistsUsingTitle(title)
|
||||
.map(mwQueryResponse -> mwQueryResponse
|
||||
.query().firstPage().pageId() > 0)
|
||||
.singleOrError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the fileSha and returns whether a file with a matching SHA exists or not
|
||||
*
|
||||
* @param fileSha SHA of the file to be checked
|
||||
*/
|
||||
public Single<Boolean> checkFileExistsUsingSha(String fileSha) {
|
||||
return mediaInterface.checkFileExistsUsingSha(fileSha)
|
||||
.map(mwQueryResponse -> mwQueryResponse
|
||||
.query().allImages().size() > 0)
|
||||
.singleOrError();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Interface for interacting with Commons media related APIs
|
||||
*/
|
||||
public interface MediaInterface {
|
||||
/**
|
||||
* Checks if a page exists or not.
|
||||
* @param title the title of the page to be checked
|
||||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2")
|
||||
Observable<MwQueryResponse> checkPageExistsUsingTitle(@Query("titles") String title);
|
||||
|
||||
/**
|
||||
* Check if file exists
|
||||
* @param aisha1 the SHA of the media file to be checked
|
||||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&list=allimages")
|
||||
Observable<MwQueryResponse> checkFileExistsUsingSha(@Query("aisha1") String aisha1);
|
||||
}
|
||||
|
|
@ -4,6 +4,9 @@ import android.content.Context;
|
|||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -32,8 +35,6 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -224,23 +225,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
return centralAuthToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fileExistsWithName(String fileName) throws IOException {
|
||||
return api.action("query")
|
||||
.param("prop", "imageinfo")
|
||||
.param("titles", "File:" + fileName)
|
||||
.get()
|
||||
.getNodes("/api/query/pages/page/imageinfo").size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Boolean> pageExists(String pageName) {
|
||||
return Single.fromCallable(() -> Double.parseDouble(api.action("query")
|
||||
.param("titles", pageName)
|
||||
.get()
|
||||
.getString("/api/query/pages/page/@_idx")) != -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean thank(String editToken, long revision) throws IOException {
|
||||
CustomApiResult res = api.action("thank")
|
||||
|
|
@ -749,16 +733,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
return gson.fromJson(queryContinueString, QueryContinue.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existingFile(String fileSha1) throws IOException {
|
||||
return api.action("query")
|
||||
.param("format", "xml")
|
||||
.param("list", "allimages")
|
||||
.param("aisha1", fileSha1)
|
||||
.get()
|
||||
.getNodes("/api/query/allimages/img").size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Single<UploadStash> uploadFile(
|
||||
|
|
|
|||
|
|
@ -30,10 +30,6 @@ public interface MediaWikiApi {
|
|||
|
||||
String getCentralAuthToken() throws IOException;
|
||||
|
||||
boolean fileExistsWithName(String fileName) throws IOException;
|
||||
|
||||
Single<Boolean> pageExists(String pageName);
|
||||
|
||||
List<String> getSubCategoryList(String categoryName);
|
||||
|
||||
List<String> getParentCategoryList(String categoryName);
|
||||
|
|
@ -87,8 +83,6 @@ public interface MediaWikiApi {
|
|||
@Nullable
|
||||
String revisionsByFilename(String filename) throws IOException;
|
||||
|
||||
boolean existingFile(String fileSha1) throws IOException;
|
||||
|
||||
@NonNull
|
||||
LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import javax.inject.Inject;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import io.reactivex.Observable;
|
||||
|
|
@ -26,14 +27,16 @@ public class ReviewHelper {
|
|||
|
||||
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
||||
private final MediaWikiApi mediaWikiApi;
|
||||
private final MediaClient mediaClient;
|
||||
private final ReviewInterface reviewInterface;
|
||||
|
||||
@Inject
|
||||
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient,
|
||||
MediaWikiApi mediaWikiApi,
|
||||
ReviewInterface reviewInterface) {
|
||||
MediaClient mediaClient, ReviewInterface reviewInterface) {
|
||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||
this.mediaWikiApi = mediaWikiApi;
|
||||
this.mediaClient = mediaClient;
|
||||
this.reviewInterface = reviewInterface;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +89,7 @@ public class ReviewHelper {
|
|||
*/
|
||||
private Single<Media> getRandomMediaFromRecentChange(RecentChange recentChange) {
|
||||
return Single.just(recentChange)
|
||||
.flatMap(change -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + change.getTitle()))
|
||||
.flatMap(change -> mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + change.getTitle()))
|
||||
.flatMap(isDeleted -> {
|
||||
if (isDeleted) {
|
||||
return Single.just(new Media(""));
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import java.io.IOException;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
|
|
@ -34,18 +35,20 @@ public class ImageProcessingService {
|
|||
private final MediaWikiApi mwApi;
|
||||
private final ReadFBMD readFBMD;
|
||||
private final EXIFReader EXIFReader;
|
||||
private final MediaClient mediaClient;
|
||||
private final Context context;
|
||||
|
||||
@Inject
|
||||
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
|
||||
ImageUtilsWrapper imageUtilsWrapper,
|
||||
MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader,
|
||||
Context context) {
|
||||
MediaClient mediaClient, Context context) {
|
||||
this.fileUtilsWrapper = fileUtilsWrapper;
|
||||
this.imageUtilsWrapper = imageUtilsWrapper;
|
||||
this.mwApi = mwApi;
|
||||
this.readFBMD = readFBMD;
|
||||
this.EXIFReader = EXIFReader;
|
||||
this.mediaClient = mediaClient;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +131,7 @@ public class ImageProcessingService {
|
|||
return Single.just(EMPTY_TITLE);
|
||||
}
|
||||
|
||||
return Single.fromCallable(() -> mwApi.fileExistsWithName(uploadItem.getFileName()))
|
||||
return mediaClient.checkPageExistsUsingTitle("File:" + uploadItem.getFileName())
|
||||
.map(doesFileExist -> {
|
||||
Timber.d("Result for valid title is %s", doesFileExist);
|
||||
return doesFileExist ? FILE_NAME_EXISTS : IMAGE_OK;
|
||||
|
|
@ -146,7 +149,7 @@ public class ImageProcessingService {
|
|||
return Single.fromCallable(() ->
|
||||
fileUtilsWrapper.getFileInputStream(filePath))
|
||||
.map(fileUtilsWrapper::getSHA1)
|
||||
.map(mwApi::existingFile)
|
||||
.flatMap(mediaClient::checkFileExistsUsingSha)
|
||||
.map(b -> {
|
||||
Timber.d("Result for duplicate image %s", b);
|
||||
return b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import android.content.Intent;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
|
@ -33,6 +34,7 @@ import fr.free.nrw.commons.contributions.Contribution;
|
|||
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditService;
|
||||
import io.reactivex.Single;
|
||||
|
|
@ -54,6 +56,8 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
@Inject WikidataEditService wikidataEditService;
|
||||
@Inject SessionManager sessionManager;
|
||||
@Inject ContributionDao contributionDao;
|
||||
@Inject
|
||||
MediaClient mediaClient;
|
||||
|
||||
private NotificationManagerCompat notificationManager;
|
||||
private NotificationCompat.Builder curNotification;
|
||||
|
|
@ -337,7 +341,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
sequenceFileName = regexMatcher.replaceAll("$1 " + sequenceNumber + "$2");
|
||||
}
|
||||
}
|
||||
if (!mwApi.fileExistsWithName(sequenceFileName)
|
||||
if (!mediaClient.checkPageExistsUsingTitle(sequenceFileName).blockingGet()
|
||||
&& !unfinishedUploads.contains(sequenceFileName)) {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue