mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Update master with backend overhaul branch (#2829)
* Beginnings of integration with Wikipedia client library. (#2642) * Remove remaining unnecessary API version check. * Roll up sleeves. * Add and integrate the beginnings of app adapter. * Remove vestigial event logging logic. Event logging is no longer used in this app. * Beginnings: remove StringUtils and associated redundancies. * Remove redundant capitalize() method. * Remove redundant urlEncode() method. * Remove redundant (and incomplete) language lists. * Remove redundant usages of SimpleDateFormat. * Remove redundant json type adapter. * Remove redundant MW error model classes. * Rip out redundant MW model classes. * Pass SessionManager into AppAdapter instead of injecting. * Wire up more of the AppAdapter. * Remove redundant Gson initialization and type adapters. * Rip out PageTitle. This was being used in some slightly incorrect/unexpected ways. * Don't need static WikiSite. * Bump data client library version * Bump library version and fix build * Fix tests * Fix build * Fix media of the day * With fixes in recently modified APIs
This commit is contained in:
parent
76e5a30fb5
commit
dcbf076965
76 changed files with 424 additions and 2122 deletions
|
|
@ -28,6 +28,7 @@ dependencies {
|
|||
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
|
||||
implementation 'com.facebook.fresco:fresco:1.13.0'
|
||||
implementation 'com.drewnoakes:metadata-extractor:2.11.0'
|
||||
implementation 'com.dmitrybrant:wikimedia-android-data-client:0.0.12'
|
||||
|
||||
// UI
|
||||
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
|
||||
|
|
@ -191,8 +192,6 @@ android {
|
|||
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\""
|
||||
buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.org\""
|
||||
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\""
|
||||
buildConfigField "String", "EVENTLOG_URL", "\"https://www.wikimedia.org/beacon/event\""
|
||||
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
|
||||
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
|
||||
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
|
||||
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\""
|
||||
|
|
@ -224,8 +223,6 @@ android {
|
|||
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\""
|
||||
buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.beta.wmflabs.org\""
|
||||
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\""
|
||||
buildConfigField "String", "EVENTLOG_URL", "\"https://commons.wikimedia.beta.wmflabs.org/beacon/event\""
|
||||
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
|
||||
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
|
||||
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
|
||||
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\""
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.app.AlertDialog;
|
|||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.Menu;
|
||||
|
|
@ -17,6 +16,8 @@ import android.widget.LinearLayout;
|
|||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
|
@ -32,16 +33,6 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
@BindView(R.id.about_license) HtmlTextView aboutLicenseText;
|
||||
@BindView(R.id.about_faq) TextView faqText;
|
||||
|
||||
String language[] = { "Kazakh", "Afrikaans", "Arabic", "Bengali", "Asturianu", "azərbaycanca", "Bikol Central",
|
||||
"Bulgarain", "বাংলা", "Bosanski", "Brezhoneg","català","کوردی", " čeština", " kaszëbsczi", "Cymraeg", "dansk", "Deutsch"
|
||||
,"Zazaki", "डोटेली","Ελληνικά","euskara","español","فارسی","suomi", "français" ,"Nordfriisk", "galego", "Hawaiʻi"
|
||||
,"हिन्दी","Hunsrik","עברית","hornjoserbsce","magyar","interlingua","Bahasa Indonesia", "íslenska","Italian","japanese",
|
||||
"Basa Jawa", "ქართული", " ភាសាខ្មែរ","ಕನ್ನಡ", "한국어","къарачай-малкъар","Кыргызча", "latina", "Lëtzebuergesch", "lietuvių",
|
||||
"latviešu", "Malagasy", "македонски"," മലയാളം","монгол","मराठी","Bahasa Melayu","Malti", "नेपाली", "norsk bokmål",
|
||||
" Nederlands","occitan","ଓଡ଼ିଆ","ਪੰਜਾਬੀ","polsk","Piemontèis","پښتو","português","română","русский"," سنڌي", " සිංහල",
|
||||
"slovenčina"," سرائیکی", "svenska", "தமிழ்", "ತುಳು"," తెలుగు"," ไทย", "Türkçe","українська", "اردو", "Tiếng Việt",
|
||||
" მარგალური","ייִדיש",};
|
||||
|
||||
/**
|
||||
* This method helps in the creation About screen
|
||||
*
|
||||
|
|
@ -66,11 +57,11 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
TextView credits = findViewById(R.id.about_credits);
|
||||
TextView faq = findViewById(R.id.about_faq);
|
||||
|
||||
rate_us.setText(Html.fromHtml(getString(R.string.about_rate_us)));
|
||||
privacy_policy.setText(Html.fromHtml(getString(R.string.about_privacy_policy)));
|
||||
translate.setText(Html.fromHtml(getString(R.string.about_translate)));
|
||||
credits.setText(Html.fromHtml(getString(R.string.about_credits)));
|
||||
faq.setText(Html.fromHtml(getString(R.string.about_faq)));
|
||||
rate_us.setText(StringUtil.fromHtml(getString(R.string.about_rate_us)));
|
||||
privacy_policy.setText(StringUtil.fromHtml(getString(R.string.about_privacy_policy)));
|
||||
translate.setText(StringUtil.fromHtml(getString(R.string.about_translate)));
|
||||
credits.setText(StringUtil.fromHtml(getString(R.string.about_credits)));
|
||||
faq.setText(StringUtil.fromHtml(getString(R.string.about_faq)));
|
||||
|
||||
initDrawer();
|
||||
}
|
||||
|
|
@ -144,7 +135,7 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
@OnClick(R.id.about_translate)
|
||||
public void launchTranslate(View view) {
|
||||
final ArrayAdapter<String> languageAdapter = new ArrayAdapter<>(AboutActivity.this,
|
||||
android.R.layout.simple_spinner_dropdown_item, language);
|
||||
android.R.layout.simple_spinner_dropdown_item, CommonsApplication.getInstance().getLanguageLookUpTable().getLocalizedNames());
|
||||
final Spinner spinner = new Spinner(AboutActivity.this);
|
||||
spinner.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
spinner.setAdapter(languageAdapter);
|
||||
|
|
@ -156,11 +147,9 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
builder.setTitle(R.string.about_translate_title)
|
||||
.setMessage(R.string.about_translate_message)
|
||||
.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> {
|
||||
String languageSelected = spinner.getSelectedItem().toString();
|
||||
TokensTranslations tokensTranslations = new TokensTranslations();
|
||||
tokensTranslations.initailize();
|
||||
String token = tokensTranslations.getTranslationToken(languageSelected);
|
||||
Utils.handleWebUrl(AboutActivity.this,Uri.parse("https://translatewiki.net/w/i.php?title=Special:Translate&language="+token+"&group=commons-android-strings&filter=%21translated&action=translate ?"));
|
||||
String langCode = CommonsApplication.getInstance().getLanguageLookUpTable().getCodes().get(spinner.getSelectedItemPosition());
|
||||
Utils.handleWebUrl(AboutActivity.this,Uri.parse("https://translatewiki.net/w/i.php?title=Special:Translate&language="
|
||||
+ langCode + "&group=commons-android-strings&filter=%21translated&action=translate ?"));
|
||||
});
|
||||
builder.setNegativeButton(R.string.about_translate_cancel, (dialog, which) -> finish());
|
||||
builder.create().show();
|
||||
|
|
|
|||
84
app/src/main/java/fr/free/nrw/commons/CommonsAppAdapter.java
Normal file
84
app/src/main/java/fr/free/nrw/commons/CommonsAppAdapter.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import org.wikipedia.AppAdapter;
|
||||
import org.wikipedia.dataclient.SharedPreferenceCookieManager;
|
||||
import org.wikipedia.dataclient.WikiSite;
|
||||
import org.wikipedia.json.GsonMarshaller;
|
||||
import org.wikipedia.json.GsonUnmarshaller;
|
||||
import org.wikipedia.login.LoginResult;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class CommonsAppAdapter extends AppAdapter {
|
||||
private final int DEFAULT_THUMB_SIZE = 640;
|
||||
private final String COOKIE_STORE_NAME = "cookie_store";
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
private final JsonKvStore preferences;
|
||||
|
||||
CommonsAppAdapter(@NonNull SessionManager sessionManager, @NonNull JsonKvStore preferences) {
|
||||
this.sessionManager = sessionManager;
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMediaWikiBaseUrl() {
|
||||
return BuildConfig.COMMONS_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRestbaseUriFormat() {
|
||||
return BuildConfig.COMMONS_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpClient getOkHttpClient(@NonNull WikiSite wikiSite) {
|
||||
return OkHttpConnectionFactory.getClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDesiredLeadImageDp() {
|
||||
return DEFAULT_THUMB_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggedIn() {
|
||||
return sessionManager.isUserLoggedIn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserName() {
|
||||
return sessionManager.getUserName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return sessionManager.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAccount(@NonNull LoginResult result) {
|
||||
// TODO: sessionManager.updateAccount(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPreferenceCookieManager getCookies() {
|
||||
if (!preferences.contains(COOKIE_STORE_NAME)) {
|
||||
return null;
|
||||
}
|
||||
return GsonUnmarshaller.unmarshal(SharedPreferenceCookieManager.class, preferences.getString(COOKIE_STORE_NAME, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookies(@NonNull SharedPreferenceCookieManager cookies) {
|
||||
preferences.putString(COOKIE_STORE_NAME, GsonMarshaller.marshal(cookies));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logErrorsInsteadOfCrashing() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ import org.acra.annotation.AcraCore;
|
|||
import org.acra.annotation.AcraDialog;
|
||||
import org.acra.annotation.AcraMailSender;
|
||||
import org.acra.data.StringFormat;
|
||||
import org.wikipedia.AppAdapter;
|
||||
import org.wikipedia.language.AppLanguageLookUpTable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
@ -49,7 +51,12 @@ import io.reactivex.plugins.RxJavaPlugins;
|
|||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.acra.ReportField.*;
|
||||
import static org.acra.ReportField.ANDROID_VERSION;
|
||||
import static org.acra.ReportField.APP_VERSION_CODE;
|
||||
import static org.acra.ReportField.APP_VERSION_NAME;
|
||||
import static org.acra.ReportField.PHONE_MODEL;
|
||||
import static org.acra.ReportField.STACK_TRACE;
|
||||
import static org.acra.ReportField.USER_COMMENT;
|
||||
|
||||
@AcraCore(
|
||||
buildConfigClass = BuildConfig.class,
|
||||
|
|
@ -97,6 +104,15 @@ public class CommonsApplication extends Application {
|
|||
|
||||
private RefWatcher refWatcher;
|
||||
|
||||
private static CommonsApplication INSTANCE;
|
||||
public static CommonsApplication getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private AppLanguageLookUpTable languageLookUpTable;
|
||||
public AppLanguageLookUpTable getLanguageLookUpTable() {
|
||||
return languageLookUpTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to declare and initialize various components and dependencies
|
||||
|
|
@ -104,6 +120,7 @@ public class CommonsApplication extends Application {
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
INSTANCE = this;
|
||||
ACRA.init(this);
|
||||
|
||||
ApplicationlessInjection
|
||||
|
|
@ -111,6 +128,8 @@ public class CommonsApplication extends Application {
|
|||
.getCommonsApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
AppAdapter.set(new CommonsAppAdapter(sessionManager, defaultPrefs));
|
||||
|
||||
initTimber();
|
||||
|
||||
// Set DownsampleEnabled to True to downsample the image in case it's heavy
|
||||
|
|
@ -130,6 +149,8 @@ public class CommonsApplication extends Application {
|
|||
|
||||
createNotificationChannel(this);
|
||||
|
||||
languageLookUpTable = new AppLanguageLookUpTable(this);
|
||||
|
||||
// This handler will catch exceptions thrown from Observables after they are disposed,
|
||||
// or from Observables that are (deliberately or not) missing an onError handler.
|
||||
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
|
||||
|
|
@ -185,6 +206,10 @@ public class CommonsApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return "Commons/" + ConfigUtils.getVersionNameWithSha(this) + " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps in setting up LeakCanary library
|
||||
* @return instance of LeakCanary
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@ import android.net.Uri;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.gallery.ExtMetadata;
|
||||
import org.wikipedia.gallery.ImageInfo;
|
||||
import org.wikipedia.page.PageTitle;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
|
@ -11,17 +20,11 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.media.model.ExtMetadata;
|
||||
import fr.free.nrw.commons.media.model.ImageInfo;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
import fr.free.nrw.commons.utils.DateUtils;
|
||||
import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
public class Media implements Parcelable {
|
||||
|
||||
|
|
@ -37,7 +40,6 @@ public class Media implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
private static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE);
|
||||
// Primary metadata fields
|
||||
protected Uri localUri;
|
||||
protected String imageUrl;
|
||||
|
|
@ -88,7 +90,7 @@ public class Media implements Parcelable {
|
|||
* @param creator Media creator
|
||||
*/
|
||||
public Media(Uri localUri, String imageUrl, String filename, String description,
|
||||
long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
|
||||
long dataLength, Date dateCreated, Date dateUploaded, String creator) {
|
||||
this();
|
||||
this.localUri = localUri;
|
||||
this.imageUrl = imageUrl;
|
||||
|
|
@ -144,26 +146,16 @@ public class Media implements Parcelable {
|
|||
* Gets media display title
|
||||
* @return Media title
|
||||
*/
|
||||
public String getDisplayTitle() {
|
||||
if (filename == null) {
|
||||
return "";
|
||||
}
|
||||
// FIXME: Gross hack because my regex skills suck maybe or I am too lazy who knows
|
||||
String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", "");
|
||||
Matcher matcher = displayTitlePattern.matcher(title);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
@NonNull public String getDisplayTitle() {
|
||||
return filename != null ? getPageTitle().getDisplayTextWithoutNamespace().replaceFirst("[.][^.]+$", "") : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets file page title
|
||||
* @return New media page title
|
||||
*/
|
||||
public PageTitle getFilePageTitle() {
|
||||
return new PageTitle("File:" + getFilename().replaceFirst("^File:", ""));
|
||||
@NonNull public PageTitle getPageTitle() {
|
||||
return Utils.getPageTitle(getFilename());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -344,7 +336,7 @@ public class Media implements Parcelable {
|
|||
@Nullable
|
||||
public static Media from(MwQueryPage page) {
|
||||
ImageInfo imageInfo = page.imageInfo();
|
||||
if(imageInfo == null) {
|
||||
if (imageInfo == null) {
|
||||
return null;
|
||||
}
|
||||
ExtMetadata metadata = imageInfo.getMetadata();
|
||||
|
|
@ -358,13 +350,13 @@ public class Media implements Parcelable {
|
|||
page.title(),
|
||||
"",
|
||||
0,
|
||||
DateUtils.getDateFromString(metadata.dateTimeOriginal().value()),
|
||||
DateUtils.getDateFromString(metadata.dateTime().value()),
|
||||
StringUtils.getParsedStringFromHtml(metadata.artist().value())
|
||||
safeParseDate(metadata.dateTimeOriginal().value()),
|
||||
safeParseDate(metadata.dateTime().value()),
|
||||
StringUtil.fromHtml(metadata.artist().value()).toString()
|
||||
);
|
||||
|
||||
String language = Locale.getDefault().getLanguage();
|
||||
if (StringUtils.isNullOrWhiteSpace(language)) {
|
||||
if (StringUtils.isBlank(language)) {
|
||||
language = "default";
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +365,7 @@ public class Media implements Parcelable {
|
|||
String latitude = metadata.gpsLatitude().value();
|
||||
String longitude = metadata.gpsLongitude().value();
|
||||
|
||||
if(!StringUtils.isNullOrWhiteSpace(latitude) && !StringUtils.isNullOrWhiteSpace(longitude)) {
|
||||
if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) {
|
||||
LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0);
|
||||
media.setCoordinates(latLng);
|
||||
}
|
||||
|
|
@ -467,6 +459,14 @@ public class Media implements Parcelable {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable private static Date safeParseDate(String dateStr) {
|
||||
try {
|
||||
return DateUtil.getIso8601DateFormatShort().parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method of Parcelable interface
|
||||
* @return zero
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import android.util.AttributeSet;
|
|||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
@ -14,7 +16,6 @@ import androidx.collection.LruCache;
|
|||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
|
@ -56,7 +57,7 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(thumbnail -> {
|
||||
if (!StringUtils.isNullOrWhiteSpace(thumbnail)) {
|
||||
if (!StringUtils.isBlank(thumbnail)) {
|
||||
setImageUrl(thumbnail);
|
||||
}
|
||||
}, throwable -> Timber.e(throwable, "Error occurred while fetching thumbnail"));
|
||||
|
|
@ -89,8 +90,10 @@ public class MediaWikiImageView extends SimpleDraweeView {
|
|||
}
|
||||
|
||||
//TODO: refactor the logic for thumbnails. ImageInfo API can be used to fetch thumbnail upfront
|
||||
|
||||
/**
|
||||
* Fetches media thumbnail from the server
|
||||
*
|
||||
* @param media
|
||||
* @return
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import org.wikipedia.dataclient.SharedPreferenceCookieManager;
|
||||
import org.wikipedia.dataclient.okhttp.HttpStatusException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
public final class OkHttpConnectionFactory {
|
||||
private static final String CACHE_DIR_NAME = "okhttp-cache";
|
||||
private static final long NET_CACHE_SIZE = 64 * 1024 * 1024;
|
||||
@NonNull private static final Cache NET_CACHE = new Cache(new File(CommonsApplication.getInstance().getCacheDir(),
|
||||
CACHE_DIR_NAME), NET_CACHE_SIZE);
|
||||
|
||||
@NonNull private static OkHttpClient CLIENT = createClient();
|
||||
|
||||
@NonNull public static OkHttpClient getClient() {
|
||||
return CLIENT;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static OkHttpClient createClient() {
|
||||
return new OkHttpClient.Builder()
|
||||
.cookieJar(SharedPreferenceCookieManager.getInstance())
|
||||
.cache(NET_CACHE)
|
||||
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
|
||||
.addInterceptor(new UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(new CommonHeaderRequestInterceptor())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static class CommonHeaderRequestInterceptor implements Interceptor {
|
||||
@Override @NonNull public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Request request = chain.request().newBuilder()
|
||||
.header("User-Agent", CommonsApplication.getInstance().getUserAgent())
|
||||
.build();
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnsuccessfulResponseInterceptor implements Interceptor {
|
||||
@Override @NonNull public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Response rsp = chain.proceed(chain.request());
|
||||
if (rsp.isSuccessful()) {
|
||||
return rsp;
|
||||
}
|
||||
throw new HttpStatusException(rsp);
|
||||
}
|
||||
}
|
||||
|
||||
private OkHttpConnectionFactory() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class PageTitle {
|
||||
private final String namespace;
|
||||
private final String titleKey;
|
||||
|
||||
/**
|
||||
* Construct from a namespace-prefixed page name.
|
||||
* @param prefixedText namespace-prefixed page name
|
||||
*/
|
||||
public PageTitle(@NonNull String prefixedText) {
|
||||
String[] segments = prefixedText.trim().replace(" ", "_").split(":", 2);
|
||||
|
||||
// Canonicalize and capitalize page title and namespace (if present)
|
||||
if (segments.length == 2) {
|
||||
namespace = Utils.capitalize(segments[0]);
|
||||
titleKey = Utils.capitalize(segments[1]);
|
||||
} else {
|
||||
namespace = "";
|
||||
titleKey = Utils.capitalize(segments[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonicalized title for displaying (such as "File:My example.jpg").
|
||||
*
|
||||
* @return canonical title
|
||||
*/
|
||||
@NonNull
|
||||
public String getPrefixedText() {
|
||||
if (namespace.isEmpty()) {
|
||||
return titleKey;
|
||||
} else {
|
||||
return namespace + ":" + titleKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonical title for DB and URLs (such as "File:My_example.jpg").
|
||||
*
|
||||
* @return canonical title
|
||||
*/
|
||||
@NonNull
|
||||
public String getDisplayText() {
|
||||
return getPrefixedText().replace("_", " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to a URI
|
||||
* (such as "https://commons.wikimedia.org/wiki/File:My_example.jpg").
|
||||
*
|
||||
* @return URI
|
||||
*/
|
||||
@NonNull
|
||||
public Uri getCanonicalUri() {
|
||||
String uriStr = BuildConfig.HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to a mobile URI
|
||||
* (such as "https://commons.m.wikimedia.org/wiki/File:My_example.jpg").
|
||||
*
|
||||
* @return URI
|
||||
*/
|
||||
@NonNull
|
||||
public Uri getMobileUri() {
|
||||
String uriStr = BuildConfig.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
|
||||
return Uri.parse(uriStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonical title without namespace.
|
||||
* @return title
|
||||
*/
|
||||
@NonNull
|
||||
public String getText() {
|
||||
return titleKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the canonicalized title for displaying (such as "File:My example.jpg").
|
||||
* </p>
|
||||
* Essentially equivalent to getPrefixedText
|
||||
* @return canonical title as a String
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getPrefixedText();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by Dell on 3/16/2018.
|
||||
*/
|
||||
|
||||
public class TokensTranslations {
|
||||
HashMap<String,String> translationToken = new HashMap<>();
|
||||
|
||||
public void initailize() {
|
||||
translationToken.put("Kazakh", "ab");
|
||||
translationToken.put("Afrikaans", "af");
|
||||
translationToken.put("Arabic", "ar");
|
||||
translationToken.put("Bengali", "as");
|
||||
translationToken.put("Asturianu", "ast");
|
||||
translationToken.put("azərbaycanca", "az");
|
||||
translationToken.put("Bikol Central", "bcl");
|
||||
translationToken.put("Bulgarain","bg");
|
||||
translationToken.put("বাংলা", "bn");
|
||||
translationToken.put("Brezhoneg", "br");
|
||||
translationToken.put("Bosanski", "bs");
|
||||
translationToken.put("català", "ca");
|
||||
translationToken.put("کوردی","ckb");
|
||||
translationToken.put("čeština", "cs");
|
||||
translationToken.put("kaszëbsczi", "csb");
|
||||
translationToken.put("Cymraeg", "cy");
|
||||
translationToken.put("dansk", "da");
|
||||
translationToken.put("Deutsch", "de");
|
||||
translationToken.put("Zazaki", "diq");
|
||||
translationToken.put("डोटेली","diq");
|
||||
translationToken.put("Ελληνικά","el");
|
||||
translationToken.put("euskara","eu");
|
||||
translationToken.put("español", "es");
|
||||
translationToken.put("فارسی","fa");
|
||||
translationToken.put("suomi", "fi");
|
||||
translationToken.put("føroyskt", "fo");
|
||||
translationToken.put("français", "fr");
|
||||
translationToken.put("Nordfriisk", "frr");
|
||||
translationToken.put("galego", "gr");
|
||||
translationToken.put("Hawaiʻi", "haw");
|
||||
translationToken.put("עברית","he");
|
||||
translationToken.put("हिन्दी","hi");
|
||||
translationToken.put("Hunsrik", "hrx");
|
||||
translationToken.put("hornjoserbsce", "hsb");
|
||||
translationToken.put("magyar","hu");
|
||||
translationToken.put("interlingua","ia");
|
||||
translationToken.put("Bahasa Indonesia", "id");
|
||||
translationToken.put("íslenska","is");
|
||||
translationToken.put("Italian","it");
|
||||
translationToken.put("japanese","ja");
|
||||
translationToken.put("Basa Jawa","jv");
|
||||
translationToken.put("ქართული", "ka");
|
||||
translationToken.put("Taqbaylit","kab");
|
||||
translationToken.put(" ភាសាខ្មែរ","km");
|
||||
translationToken.put("ಕನ್ನಡ", "kn");
|
||||
translationToken.put("한국어", "ko");
|
||||
translationToken.put("къарачай-малкъар","krc");
|
||||
translationToken.put("Кыргызча","ky");
|
||||
translationToken.put("latina","la");
|
||||
translationToken.put("Lëtzebuergesch","lb");
|
||||
translationToken.put("lietuvių", "lt");
|
||||
translationToken.put("latviešu","lv");
|
||||
translationToken.put("Malagasy","mg");
|
||||
translationToken.put("македонски", "mk");
|
||||
translationToken.put("മലയാളം","ml");
|
||||
translationToken.put("монгол","mn");
|
||||
translationToken.put("मराठी","mr");
|
||||
translationToken.put("Bahasa Melayu","ms");
|
||||
translationToken.put("Malti","mt");
|
||||
translationToken.put("norsk bokmål", "nb");
|
||||
translationToken.put("नेपाली","ne");
|
||||
translationToken.put("Nederlands","nl");
|
||||
translationToken.put("occitan","oc");
|
||||
translationToken.put("ଓଡ଼ିଆ","or");
|
||||
translationToken.put("ਪੰਜਾਬੀ","pa");
|
||||
translationToken.put("polsk", "pl");
|
||||
translationToken.put("Piemontèis","pms");
|
||||
translationToken.put("پښتو","ps");
|
||||
translationToken.put("português","pt");
|
||||
translationToken.put("română","ro");
|
||||
translationToken.put("русский","ru");
|
||||
translationToken.put(" سنڌي","sd");
|
||||
translationToken.put(" සිංහල","si");
|
||||
translationToken.put("slovenčina","sk");
|
||||
translationToken.put(" سرائیکی","skr");
|
||||
translationToken.put("Basa Sunda","su");
|
||||
translationToken.put("svenska","sv");
|
||||
translationToken.put("தமிழ்", "ta");
|
||||
translationToken.put("ತುಳು", "tcy");
|
||||
translationToken.put(" తెలుగు","te");
|
||||
translationToken.put(" ไทย","th");
|
||||
translationToken.put("Türkçe","tr");
|
||||
translationToken.put("українська","uk");
|
||||
translationToken.put("اردو","ur");
|
||||
translationToken.put("Tiếng Việt","vi");
|
||||
translationToken.put(" მარგალური", "xmf");
|
||||
translationToken.put("ייִדיש","yi");
|
||||
}
|
||||
|
||||
public String getTranslationToken ( String language){
|
||||
return translationToken.get(language);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,13 +11,11 @@ import android.widget.Toast;
|
|||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.wikipedia.dataclient.WikiSite;
|
||||
import org.wikipedia.page.PageTitle;
|
||||
import org.wikipedia.util.UriUtil;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -32,20 +30,8 @@ import static android.widget.Toast.LENGTH_SHORT;
|
|||
|
||||
public class Utils {
|
||||
|
||||
/**
|
||||
* Strips localization symbols from a string.
|
||||
* Removes the suffix after "@" and quotes.
|
||||
*
|
||||
* @param s string possibly containing localization symbols
|
||||
* @return stripped string
|
||||
*/
|
||||
public static String stripLocalizedString(String s) {
|
||||
Matcher matcher = Pattern.compile("\\\"(.*)\\\"(@\\w+)?").matcher(s);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
public static PageTitle getPageTitle(@NonNull String title) {
|
||||
return new PageTitle(title, new WikiSite(BuildConfig.COMMONS_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,36 +41,9 @@ public class Utils {
|
|||
* @return URL of thumbnail
|
||||
*/
|
||||
public static String makeThumbBaseUrl(@NonNull String filename) {
|
||||
String name = new PageTitle(filename).getPrefixedText();
|
||||
String name = getPageTitle(filename).getPrefixedText();
|
||||
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
|
||||
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL Encode an URL in UTF-8 format
|
||||
* @param url Unformatted URL
|
||||
* @return Encoded URL
|
||||
*/
|
||||
public static String urlEncode(String url) {
|
||||
try {
|
||||
return URLEncoder.encode(url, "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first character of a string.
|
||||
*
|
||||
* @param string String to alter
|
||||
* @return string with capitalized first character
|
||||
*/
|
||||
public static String capitalize(String string) {
|
||||
if (string.length() > 0) {
|
||||
return string.substring(0, 1).toUpperCase(Locale.getDefault()) + string.substring(1);
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), UriUtil.encodeURL(name));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -234,15 +193,6 @@ public class Utils {
|
|||
return bitmap;
|
||||
}
|
||||
|
||||
public static <K,V> Map<K,V> arraysToMap(K[] kArray, V[] vArray){
|
||||
if(kArray.length!=vArray.length)
|
||||
throw new RuntimeException("arraysToMap array sizes don't match");
|
||||
Map<K,V> map=new LinkedHashMap<>();
|
||||
for (int i=0;i<vArray.length;i++){
|
||||
map.put(kArray[i], vArray[i]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
/*
|
||||
*Copies the content to the clipboard
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ package fr.free.nrw.commons;
|
|||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
public class WelcomePagerAdapter extends PagerAdapter {
|
||||
private static final int[] PAGE_LAYOUTS = new int[]{
|
||||
R.layout.welcome_wikipedia,
|
||||
|
|
@ -61,7 +62,7 @@ public class WelcomePagerAdapter extends PagerAdapter {
|
|||
if (position == PAGE_FINAL) {
|
||||
// Add link to more information
|
||||
TextView moreInfo = layout.findViewById(R.id.welcomeInfo);
|
||||
moreInfo.setText(Html.fromHtml(WelcomeActivity.moreInformation));
|
||||
moreInfo.setText(StringUtil.fromHtml(WelcomeActivity.moreInformation));
|
||||
moreInfo.setOnClickListener(view -> {
|
||||
try {
|
||||
Utils.handleWebUrl(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import android.widget.TextView;
|
|||
|
||||
import com.dinuscxj.progressbar.CircleProgressBar;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
|
@ -42,7 +44,6 @@ import fr.free.nrw.commons.Utils;
|
|||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
|
@ -243,7 +244,7 @@ public class AchievementsActivity extends NavigationBaseActivity {
|
|||
@SuppressLint("CheckResult")
|
||||
private void setWikidataEditCount() {
|
||||
String userName = sessionManager.getUserName();
|
||||
if (StringUtils.isNullOrWhiteSpace(userName)) {
|
||||
if (StringUtils.isBlank(userName)) {
|
||||
return;
|
||||
}
|
||||
compositeDisposable.add(okHttpJsonApiClient.getWikidataEdits(userName)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ import butterknife.OnClick;
|
|||
import butterknife.OnEditorAction;
|
||||
import butterknife.OnFocusChange;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.WelcomeActivity;
|
||||
|
|
@ -192,22 +191,6 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL));
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_button)
|
||||
void performLogin() {
|
||||
loginCurrentlyInProgress = true;
|
||||
Timber.d("Login to start!");
|
||||
final String username = canonicializeUsername(usernameEdit.getText().toString());
|
||||
final String rawUsername = Utils.capitalize(usernameEdit.getText().toString().trim());
|
||||
final String password = passwordEdit.getText().toString();
|
||||
String twoFactorCode = twoFactorEdit.getText().toString();
|
||||
|
||||
showLoggingProgressBar();
|
||||
compositeDisposable.add(Observable.fromCallable(() -> login(username, password, twoFactorCode))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> handleLogin(username, rawUsername, password, result)));
|
||||
}
|
||||
|
||||
@OnClick(R.id.sign_up_button)
|
||||
void signUp() {
|
||||
Intent intent = new Intent(this, SignupActivity.class);
|
||||
|
|
@ -259,6 +242,22 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
super.onDestroy();
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_button)
|
||||
public void performLogin() {
|
||||
loginCurrentlyInProgress = true;
|
||||
Timber.d("Login to start!");
|
||||
final String username = usernameEdit.getText().toString();
|
||||
final String rawUsername = usernameEdit.getText().toString().trim();
|
||||
final String password = passwordEdit.getText().toString();
|
||||
String twoFactorCode = twoFactorEdit.getText().toString();
|
||||
|
||||
showLoggingProgressBar();
|
||||
compositeDisposable.add(Observable.fromCallable(() -> login(username, password, twoFactorCode))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> handleLogin(username, rawUsername, password, result)));
|
||||
}
|
||||
|
||||
private String login(String username, String password, String twoFactorCode) {
|
||||
try {
|
||||
if (twoFactorCode.isEmpty()) {
|
||||
|
|
@ -364,16 +363,6 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Because Mediawiki is upercase-first-char-then-case-sensitive :)
|
||||
*
|
||||
* @param username String
|
||||
* @return String canonicial username
|
||||
*/
|
||||
private String canonicializeUsername(String username) {
|
||||
return new PageTitle(username).getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import javax.inject.Singleton;
|
|||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import android.util.AttributeSet;
|
|||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -85,13 +86,11 @@ public class CampaignView extends SwipableCardView {
|
|||
if (campaign != null) {
|
||||
tvTitle.setText(campaign.getTitle());
|
||||
tvDescription.setText(campaign.getDescription());
|
||||
SimpleDateFormat inputDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
SimpleDateFormat outputDateFormat = new SimpleDateFormat("dd MMM");
|
||||
try {
|
||||
Date startDate = inputDateFormat.parse(campaign.getStartDate());
|
||||
Date endDate = inputDateFormat.parse(campaign.getEndDate());
|
||||
tvDates.setText(String.format("%1s - %2s", outputDateFormat.format(startDate),
|
||||
outputDateFormat.format(endDate)));
|
||||
Date startDate = DateUtil.getIso8601DateFormatShort().parse(campaign.getStartDate());
|
||||
Date endDate = DateUtil.getIso8601DateFormatShort().parse(campaign.getEndDate());
|
||||
tvDates.setText(String.format("%1s - %2s", DateUtil.getExtraShortDateString(startDate),
|
||||
DateUtil.getExtraShortDateString(endDate)));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ package fr.free.nrw.commons.campaigns;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
|
@ -71,7 +72,6 @@ public class CampaignsPresenter implements BasePresenter {
|
|||
|
||||
@Override public void onSuccess(CampaignResponseDTO campaignResponseDTO) {
|
||||
List<Campaign> campaigns = campaignResponseDTO.getCampaigns();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
if (campaigns == null || campaigns.isEmpty()) {
|
||||
Timber.e("The campaigns list is empty");
|
||||
view.showCampaigns(null);
|
||||
|
|
@ -79,8 +79,8 @@ public class CampaignsPresenter implements BasePresenter {
|
|||
Collections.sort(campaigns, (campaign, t1) -> {
|
||||
Date date1, date2;
|
||||
try {
|
||||
date1 = dateFormat.parse(campaign.getStartDate());
|
||||
date2 = dateFormat.parse(t1.getStartDate());
|
||||
date1 = DateUtil.getIso8601DateFormatShort().parse(campaign.getStartDate());
|
||||
date2 = DateUtil.getIso8601DateFormatShort().parse(t1.getStartDate());
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
|
|
@ -91,9 +91,8 @@ public class CampaignsPresenter implements BasePresenter {
|
|||
Date currentDate = new Date();
|
||||
try {
|
||||
for (Campaign aCampaign : campaigns) {
|
||||
campaignEndDate = dateFormat.parse(aCampaign.getEndDate());
|
||||
campaignStartDate =
|
||||
dateFormat.parse(aCampaign.getStartDate());
|
||||
campaignEndDate = DateUtil.getIso8601DateFormatShort().parse(aCampaign.getEndDate());
|
||||
campaignStartDate = DateUtil.getIso8601DateFormatShort().parse(aCampaign.getStartDate());
|
||||
if (campaignEndDate.compareTo(currentDate) >= 0
|
||||
&& campaignStartDate.compareTo(currentDate) <= 0) {
|
||||
campaign = aCampaign;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package fr.free.nrw.commons.category;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.DataSetObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
|
@ -22,7 +23,6 @@ import androidx.viewpager.widget.ViewPager;
|
|||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.explore.ViewPagerAdapter;
|
||||
|
|
@ -219,7 +219,7 @@ public class CategoryDetailsActivity extends NavigationBaseActivity
|
|||
// Handle item selection
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_browser_current_category:
|
||||
Utils.handleWebUrl(this, new PageTitle(categoryName).getCanonicalUri());
|
||||
Utils.handleWebUrl(this, Uri.parse(Utils.getPageTitle(categoryName).getCanonicalUri()));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class CategoryImageUtils {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@ package fr.free.nrw.commons.contributions;
|
|||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
|
@ -18,7 +21,6 @@ import fr.free.nrw.commons.Media;
|
|||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
|
|
@ -168,7 +170,7 @@ public class Contribution extends Media {
|
|||
.append("|author=[[User:").append(creator).append("|").append(creator).append("]]\n");
|
||||
|
||||
String templatizedCreatedDate = getTemplatizedCreatedDate();
|
||||
if (!StringUtils.isNullOrWhiteSpace(templatizedCreatedDate)) {
|
||||
if (!StringUtils.isBlank(templatizedCreatedDate)) {
|
||||
buffer.append("|date=").append(templatizedCreatedDate);
|
||||
}
|
||||
|
||||
|
|
@ -201,8 +203,7 @@ public class Contribution extends Media {
|
|||
private String getTemplatizedCreatedDate() {
|
||||
if (dateCreated != null) {
|
||||
if (UploadableFile.DateTimeWithSource.EXIF_SOURCE.equals(dateCreatedSource)) {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
|
||||
return String.format(Locale.ENGLISH, TEMPLATE_DATE_ACC_TO_EXIF, isoFormat.format(dateCreated)) + "\n";
|
||||
return String.format(Locale.ENGLISH, TEMPLATE_DATE_ACC_TO_EXIF, DateUtil.getIso8601DateFormatShort().format(dateCreated)) + "\n";
|
||||
} else {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(dateCreated);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ import android.net.Uri;
|
|||
import android.os.RemoteException;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import java.util.Date;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
|
@ -144,7 +146,7 @@ public class ContributionDao {
|
|||
);
|
||||
|
||||
String wikidataEntityId = cursor.getString(cursor.getColumnIndex(COLUMN_WIKI_DATA_ENTITY_ID));
|
||||
if (!StringUtils.isNullOrWhiteSpace(wikidataEntityId)) {
|
||||
if (!StringUtils.isBlank(wikidataEntityId)) {
|
||||
contribution.setWikiDataEntityId(wikidataEntityId);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,12 @@ import android.os.Bundle;
|
|||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
|
@ -25,7 +24,6 @@ import javax.inject.Named;
|
|||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.mwapi.LogEventResult;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
|
@ -151,13 +149,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
done = true;
|
||||
}
|
||||
}
|
||||
defaultKvStore.putString("lastSyncTimestamp", toMWDate(curTime));
|
||||
defaultKvStore.putString("lastSyncTimestamp", DateUtil.getIso8601DateFormat().format(curTime));
|
||||
Timber.d("Oh hai, everyone! Look, a kitty!");
|
||||
}
|
||||
|
||||
private String toMWDate(Date date) {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
||||
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
return isoFormat.format(date);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ package fr.free.nrw.commons.delete;
|
|||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
|
|
@ -40,8 +41,7 @@ public class ReasonBuilder {
|
|||
if (date == null || date.toString() == null || date.toString().isEmpty()) {
|
||||
return "Uploaded date not available";
|
||||
}
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy", Locale.getDefault());
|
||||
return formatter.format(date);
|
||||
return DateUtil.getDateStringWithSkeletonPattern(date,"dd MMM yyyy");
|
||||
}
|
||||
|
||||
private Single<String> fetchArticleNumber(Media media, String reason) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import android.net.Uri;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import org.wikipedia.json.GsonUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -20,8 +22,6 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
import fr.free.nrw.commons.utils.UriSerializer;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
|
@ -107,10 +107,7 @@ public class NetworkingModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
public Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
return GsonUtil.getDefaultGson();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import android.content.SharedPreferences;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.MediaStore;
|
||||
|
|
@ -65,11 +64,8 @@ public class FilePicker implements Constants {
|
|||
|
||||
private static Intent createGalleryIntent(@NonNull Context context, int type) {
|
||||
storeType(context, type);
|
||||
Intent intent = plainGalleryPickerIntent();
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, configuration(context).allowsMultiplePickingInGallery());
|
||||
}
|
||||
return intent;
|
||||
return plainGalleryPickerIntent()
|
||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, configuration(context).allowsMultiplePickingInGallery());
|
||||
}
|
||||
|
||||
private static Intent createCameraForImageIntent(@NonNull Context context, int type) {
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
package fr.free.nrw.commons.json;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PostProcessingTypeAdapter implements TypeAdapterFactory {
|
||||
public interface PostProcessable {
|
||||
void postProcess();
|
||||
}
|
||||
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
||||
|
||||
return new TypeAdapter<T>() {
|
||||
public void write(JsonWriter out, T value) throws IOException {
|
||||
delegate.write(out, value);
|
||||
}
|
||||
|
||||
public T read(JsonReader in) throws IOException {
|
||||
T obj = delegate.read(in);
|
||||
if (obj instanceof PostProcessable) {
|
||||
((PostProcessable)obj).postProcess();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ import android.database.DataSetObserver;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -23,6 +22,10 @@ import android.widget.Spinner;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
|
@ -44,9 +47,6 @@ import fr.free.nrw.commons.delete.ReasonBuilder;
|
|||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
||||
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
||||
import fr.free.nrw.commons.utils.DateUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
|
@ -183,7 +183,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
final View view = inflater.inflate(R.layout.fragment_media_detail, container, false);
|
||||
|
||||
ButterKnife.bind(this,view);
|
||||
seeMore.setText(Html.fromHtml(getString(R.string.nominated_see_more)));
|
||||
seeMore.setText(StringUtil.fromHtml(getString(R.string.nominated_see_more)));
|
||||
|
||||
if (isCategoryImage){
|
||||
authorLayout.setVisibility(VISIBLE);
|
||||
|
|
@ -319,7 +319,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
@OnClick(R.id.mediaDetailLicense)
|
||||
public void onMediaDetailLicenceClicked(){
|
||||
String url = media.getLicenseUrl();
|
||||
if (!StringUtils.isNullOrWhiteSpace(url) && getActivity() != null) {
|
||||
if (!StringUtils.isBlank(url) && getActivity() != null) {
|
||||
Utils.handleWebUrl(getActivity(), Uri.parse(url));
|
||||
} else {
|
||||
viewUtil.showShortToast(getActivity(), getString(R.string.null_url));
|
||||
|
|
@ -422,7 +422,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
@OnClick(R.id.seeMore)
|
||||
public void onSeeMoreClicked(){
|
||||
if (nominatedForDeletion.getVisibility() == VISIBLE && getActivity() != null) {
|
||||
Utils.handleWebUrl(getActivity(), media.getFilePageTitle().getMobileUri());
|
||||
Utils.handleWebUrl(getActivity(), Uri.parse(media.getPageTitle().getMobileUri()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +516,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
if (date == null || date.toString() == null || date.toString().isEmpty()) {
|
||||
return "Uploaded date not available";
|
||||
}
|
||||
return DateUtils.dateInLocaleFormat(date);
|
||||
return DateUtil.getDateStringWithSkeletonPattern(date, "dd MMM yyyy");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -159,12 +159,12 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
case R.id.menu_share_current_image:
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri());
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, m.getDisplayTitle() + " \n" + m.getPageTitle().getCanonicalUri());
|
||||
startActivity(Intent.createChooser(shareIntent, "Share image via..."));
|
||||
return true;
|
||||
case R.id.menu_browser_current_image:
|
||||
// View in browser
|
||||
handleWebUrl(requireContext(), m.getFilePageTitle().getMobileUri());
|
||||
handleWebUrl(requireContext(), Uri.parse(m.getPageTitle().getMobileUri()));
|
||||
return true;
|
||||
case R.id.menu_download_current_image:
|
||||
// Download
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
package fr.free.nrw.commons.media.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
public class ExtMetadata {
|
||||
@SuppressWarnings("unused") @SerializedName("DateTime") @Nullable
|
||||
private Values dateTime;
|
||||
@SuppressWarnings("unused") @SerializedName("ObjectName") @Nullable private Values objectName;
|
||||
@SuppressWarnings("unused") @SerializedName("CommonsMetadataExtension") @Nullable private Values commonsMetadataExtension;
|
||||
@SuppressWarnings("unused") @SerializedName("Categories") @Nullable private Values categories;
|
||||
@SuppressWarnings("unused") @SerializedName("Assessments") @Nullable private Values assessments;
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("ImageDescription")
|
||||
@Nullable
|
||||
private Values imageDescription;
|
||||
@SuppressWarnings("unused") @SerializedName("GPSLatitude") @Nullable private Values gpsLatitude;
|
||||
@SuppressWarnings("unused") @SerializedName("GPSLongitude") @Nullable private Values gpsLongitude;
|
||||
@SuppressWarnings("unused") @SerializedName("DateTimeOriginal") @Nullable private Values dateTimeOriginal;
|
||||
@SuppressWarnings("unused") @SerializedName("Artist") @Nullable private Values artist;
|
||||
@SuppressWarnings("unused") @SerializedName("Credit") @Nullable private Values credit;
|
||||
@SuppressWarnings("unused") @SerializedName("Permission") @Nullable private Values permission;
|
||||
@SuppressWarnings("unused") @SerializedName("AuthorCount") @Nullable private Values authorCount;
|
||||
@SuppressWarnings("unused") @SerializedName("LicenseShortName") @Nullable private Values licenseShortName;
|
||||
@SuppressWarnings("unused") @SerializedName("UsageTerms") @Nullable private Values usageTerms;
|
||||
@SuppressWarnings("unused") @SerializedName("LicenseUrl") @Nullable private Values licenseUrl;
|
||||
@SuppressWarnings("unused") @SerializedName("AttributionRequired") @Nullable private Values attributionRequired;
|
||||
@SuppressWarnings("unused") @SerializedName("Copyrighted") @Nullable private Values copyrighted;
|
||||
@SuppressWarnings("unused") @SerializedName("Restrictions") @Nullable private Values restrictions;
|
||||
@SuppressWarnings("unused") @SerializedName("License") @Nullable private Values license;
|
||||
|
||||
@NonNull public Values dateTime() {
|
||||
return dateTime != null ? dateTime : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values dateTimeOriginal() {
|
||||
return dateTimeOriginal != null ? dateTimeOriginal : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values licenseShortName() {
|
||||
return licenseShortName != null ? licenseShortName : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values licenseUrl() {
|
||||
return licenseUrl != null ? licenseUrl : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values license() {
|
||||
return license != null ? license : new Values();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Values imageDescription() {
|
||||
return imageDescription != null ? imageDescription : new Values();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Values categories() {
|
||||
return categories != null ? categories : new Values();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Values gpsLatitude() {
|
||||
return gpsLatitude != null ? gpsLatitude : new Values();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Values gpsLongitude() {
|
||||
return gpsLongitude != null ? gpsLongitude : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values objectName() {
|
||||
return objectName != null ? objectName : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values usageTerms() {
|
||||
return usageTerms != null ? usageTerms : new Values();
|
||||
}
|
||||
|
||||
@NonNull public Values artist() {
|
||||
return artist != null ? artist : new Values();
|
||||
}
|
||||
|
||||
public class Values {
|
||||
@SuppressWarnings("unused,NullableProblems") @Nullable private String value;
|
||||
@SuppressWarnings("unused,NullableProblems") @Nullable private String source;
|
||||
|
||||
@NonNull public String value() {
|
||||
return StringUtils.defaultString(value);
|
||||
}
|
||||
|
||||
@NonNull public String source() {
|
||||
return StringUtils.defaultString(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package fr.free.nrw.commons.media.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* Gson POJO for a standard image info object as returned by the API ImageInfo module
|
||||
*/
|
||||
public class ImageInfo implements Serializable {
|
||||
@SuppressWarnings("unused") private int size;
|
||||
@SuppressWarnings("unused") private int width;
|
||||
@SuppressWarnings("unused") private int height;
|
||||
@SuppressWarnings("unused,NullableProblems") @Nullable
|
||||
private String source;
|
||||
@SuppressWarnings("unused") @SerializedName("thumburl") @Nullable private String thumbUrl;
|
||||
@SuppressWarnings("unused") @SerializedName("thumbwidth") private int thumbWidth;
|
||||
@SuppressWarnings("unused") @SerializedName("thumbheight") private int thumbHeight;
|
||||
@SuppressWarnings("unused") @SerializedName("url") @Nullable private String originalUrl;
|
||||
@SuppressWarnings("unused") @SerializedName("descriptionurl") @Nullable private String descriptionUrl;
|
||||
@SuppressWarnings("unused") @SerializedName("descriptionshorturl") @Nullable private String descriptionShortUrl;
|
||||
@SuppressWarnings("unused,NullableProblems") @SerializedName("mime") @NonNull
|
||||
private String mimeType = "*/*";
|
||||
@SuppressWarnings("unused") @SerializedName("extmetadata")@Nullable private ExtMetadata metadata;
|
||||
|
||||
@NonNull
|
||||
public String getSource() {
|
||||
return StringUtils.defaultString(source);
|
||||
}
|
||||
|
||||
public void setSource(@Nullable String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@NonNull public String getThumbUrl() {
|
||||
return StringUtils.defaultString(thumbUrl);
|
||||
}
|
||||
|
||||
@NonNull public String getOriginalUrl() {
|
||||
return StringUtils.defaultString(originalUrl);
|
||||
}
|
||||
|
||||
@Nullable public ExtMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
package fr.free.nrw.commons.media.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* A class representing a standard page object as returned by the MediaWiki API.
|
||||
*/
|
||||
public class MwQueryPage {
|
||||
@SuppressWarnings("unused")
|
||||
private int pageid;
|
||||
@SuppressWarnings("unused")
|
||||
private int ns;
|
||||
@SuppressWarnings("unused")
|
||||
private int index;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@NonNull
|
||||
private String title;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private List<LangLink> langlinks;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private List<Revision> revisions;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private List<Coordinates> coordinates;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private List<Category> categories;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private PageProps pageprops;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String extract;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private Thumbnail thumbnail;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String description;
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("descriptionsource")
|
||||
@Nullable
|
||||
private String descriptionSource;
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("imageinfo")
|
||||
@Nullable
|
||||
private List<ImageInfo> imageInfo;
|
||||
@Nullable
|
||||
private String redirectFrom;
|
||||
@Nullable
|
||||
private String convertedFrom;
|
||||
@Nullable
|
||||
private String convertedTo;
|
||||
|
||||
@NonNull
|
||||
public String title() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<LangLink> langLinks() {
|
||||
return langlinks;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Revision> revisions() {
|
||||
return revisions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Category> categories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<Coordinates> coordinates() {
|
||||
// TODO: Handle null values in lists during deserialization, perhaps with a new
|
||||
// @RequiredElements annotation and corresponding TypeAdapter
|
||||
if (coordinates != null) {
|
||||
coordinates.removeAll(Collections.singleton(null));
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
public int pageId() {
|
||||
return pageid;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PageProps pageProps() {
|
||||
return pageprops;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String extract() {
|
||||
return extract;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String thumbUrl() {
|
||||
return thumbnail != null ? thumbnail.source() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String descriptionSource() {
|
||||
return descriptionSource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ImageInfo imageInfo() {
|
||||
return imageInfo != null ? imageInfo.get(0) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String redirectFrom() {
|
||||
return redirectFrom;
|
||||
}
|
||||
|
||||
public void redirectFrom(@Nullable String from) {
|
||||
redirectFrom = from;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String convertedFrom() {
|
||||
return convertedFrom;
|
||||
}
|
||||
|
||||
public void convertedFrom(@Nullable String from) {
|
||||
convertedFrom = from;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String convertedTo() {
|
||||
return convertedTo;
|
||||
}
|
||||
|
||||
public void convertedTo(@Nullable String to) {
|
||||
convertedTo = to;
|
||||
}
|
||||
|
||||
public void appendTitleFragment(@Nullable String fragment) {
|
||||
title += "#" + fragment;
|
||||
}
|
||||
|
||||
public static class Revision {
|
||||
private String revid;
|
||||
private String user;
|
||||
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@SerializedName("contentformat")
|
||||
@NonNull
|
||||
private String contentFormat;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@SerializedName("contentmodel")
|
||||
@NonNull
|
||||
private String contentModel;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@SerializedName("timestamp")
|
||||
@NonNull
|
||||
private String timeStamp;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@NonNull
|
||||
private String content;
|
||||
|
||||
|
||||
|
||||
@NonNull
|
||||
public String content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String timeStamp() {
|
||||
return StringUtils.defaultString(timeStamp);
|
||||
}
|
||||
|
||||
public String getRevid() {
|
||||
return revid;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LangLink {
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@NonNull
|
||||
private String lang;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@NonNull
|
||||
private String title;
|
||||
|
||||
@NonNull
|
||||
public String lang() {
|
||||
return lang;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String title() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Coordinates {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private Double lat;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private Double lon;
|
||||
|
||||
@Nullable
|
||||
public Double lat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double lon() {
|
||||
return lon;
|
||||
}
|
||||
}
|
||||
|
||||
static class Thumbnail {
|
||||
@SuppressWarnings("unused")
|
||||
private String source;
|
||||
@SuppressWarnings("unused")
|
||||
private int width;
|
||||
@SuppressWarnings("unused")
|
||||
private int height;
|
||||
|
||||
String source() {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PageProps {
|
||||
@SuppressWarnings("unused")
|
||||
@SerializedName("wikibase_item")
|
||||
@Nullable
|
||||
private String wikiBaseItem;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String displaytitle;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String disambiguation;
|
||||
|
||||
@Nullable
|
||||
public String getDisplayTitle() {
|
||||
return displaytitle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getWikiBaseItem() {
|
||||
return StringUtils.defaultString(wikiBaseItem);
|
||||
}
|
||||
|
||||
public boolean isDisambiguation() {
|
||||
return disambiguation != null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Category {
|
||||
@SuppressWarnings("unused")
|
||||
private int ns;
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@Nullable
|
||||
private String title;
|
||||
@SuppressWarnings("unused")
|
||||
private boolean hidden;
|
||||
|
||||
public int ns() {
|
||||
return ns;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String title() {
|
||||
return StringUtils.defaultString(title);
|
||||
}
|
||||
|
||||
public boolean hidden() {
|
||||
return hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,11 @@ package fr.free.nrw.commons.mwapi;
|
|||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.scheme.PlainSocketFactory;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
|
|
@ -21,23 +20,22 @@ import org.apache.http.params.CoreProtocolPNames;
|
|||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
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;
|
||||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.category.CategoryImageUtils;
|
||||
|
|
@ -45,10 +43,7 @@ import fr.free.nrw.commons.category.QueryContinue;
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.notification.Notification;
|
||||
import fr.free.nrw.commons.notification.NotificationUtils;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import in.yuvi.http.fluent.Http;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import timber.log.Timber;
|
||||
|
|
@ -79,7 +74,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
|
||||
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
|
||||
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
|
||||
params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent());
|
||||
params.setParameter(CoreProtocolPNames.USER_AGENT, CommonsApplication.getInstance().getUserAgent());
|
||||
httpClient = new DefaultHttpClient(cm, params);
|
||||
if (BuildConfig.DEBUG) {
|
||||
httpClient.addRequestInterceptor(NetworkInterceptors.getHttpRequestInterceptor());
|
||||
|
|
@ -90,12 +85,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
this.gson = gson;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getUserAgent() {
|
||||
return "Commons/" + ConfigUtils.getVersionNameWithSha(context) + " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param username String
|
||||
* @param password String
|
||||
|
|
@ -254,11 +243,11 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean thank(String editToken, String revision) throws IOException {
|
||||
public boolean thank(String editToken, long revision) throws IOException {
|
||||
CustomApiResult res = api.action("thank")
|
||||
.param("rev", revision)
|
||||
.param("token", editToken)
|
||||
.param("source", getUserAgent())
|
||||
.param("source", CommonsApplication.getInstance().getUserAgent())
|
||||
.post();
|
||||
String r = res.getString("/api/result/@success");
|
||||
// Does this correctly check the success/failure?
|
||||
|
|
@ -587,7 +576,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
notfilter = "!read";
|
||||
}
|
||||
String language=Locale.getDefault().getLanguage();
|
||||
if(StringUtils.isNullOrWhiteSpace(language)){
|
||||
if(StringUtils.isBlank(language)){
|
||||
//if no language is set we use the default user language defined on wikipedia
|
||||
language="user";
|
||||
}
|
||||
|
|
@ -624,7 +613,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.post()
|
||||
.getString("/api/query/echomarkread/@result");
|
||||
|
||||
if (StringUtils.isNullOrWhiteSpace(result)) {
|
||||
if (StringUtils.isBlank(result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -783,29 +772,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.getNodes("/api/query/allimages/img").size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logEvents(LogBuilder[] logBuilders) {
|
||||
boolean allSuccess = true;
|
||||
// Not using the default URL connection, since that seems to have different behavior than the rest of the code
|
||||
for (LogBuilder logBuilder : logBuilders) {
|
||||
try {
|
||||
URL url = logBuilder.toUrl();
|
||||
HttpResponse response = Http.get(url.toString()).use(httpClient).asResponse();
|
||||
|
||||
if (response.getStatusLine().getStatusCode() != 204) {
|
||||
allSuccess = false;
|
||||
}
|
||||
Timber.d("EventLog hit %s", url);
|
||||
|
||||
} catch (IOException e) {
|
||||
// Probably just ignore for now. Can be much more robust with a service, etc later on.
|
||||
Timber.d("IO Error, EventLog hit skipped");
|
||||
}
|
||||
}
|
||||
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Single<UploadStash> uploadFile(
|
||||
|
|
@ -903,10 +869,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
private Date parseMWDate(String mwDate) {
|
||||
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); // Assuming MW always gives me UTC
|
||||
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
try {
|
||||
return isoFormat.parse(mwDate);
|
||||
return DateUtil.getIso8601DateFormat().parse(mwDate);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package fr.free.nrw.commons.mwapi;
|
|||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
@ -11,9 +14,6 @@ import java.util.Set;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.mwapi.model.ApiResponse;
|
||||
import fr.free.nrw.commons.mwapi.model.Page;
|
||||
import fr.free.nrw.commons.mwapi.model.PageCategory;
|
||||
import io.reactivex.Single;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
|
@ -54,12 +54,14 @@ public class CategoryApi {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
ApiResponse apiResponse = gson.fromJson(body.charStream(), ApiResponse.class);
|
||||
MwQueryResponse apiResponse = gson.fromJson(body.charStream(), MwQueryResponse.class);
|
||||
Set<String> categories = new LinkedHashSet<>();
|
||||
if (apiResponse != null && apiResponse.hasPages()) {
|
||||
for (Page page : apiResponse.query.pages) {
|
||||
for (PageCategory category : page.getCategories()) {
|
||||
categories.add(category.withoutPrefix());
|
||||
if (apiResponse != null && apiResponse.query() != null && apiResponse.query().pages() != null) {
|
||||
for (MwQueryPage page : apiResponse.query().pages()) {
|
||||
if (page.categories() != null) {
|
||||
for (MwQueryPage.Category category : page.categories()) {
|
||||
categories.add(category.title().replace("Category:", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
|
||||
public class EventLog {
|
||||
static final String DEVICE;
|
||||
|
||||
static {
|
||||
if (Build.MODEL.startsWith(Build.MANUFACTURER)) {
|
||||
DEVICE = Utils.capitalize(Build.MODEL);
|
||||
} else {
|
||||
DEVICE = Utils.capitalize(Build.MANUFACTURER) + " " + Build.MODEL;
|
||||
}
|
||||
}
|
||||
|
||||
private static LogBuilder schema(String schema, long revision, MediaWikiApi mwApi, JsonKvStore prefs, Context context) {
|
||||
return new LogBuilder(schema, revision, mwApi, prefs, context);
|
||||
}
|
||||
|
||||
public static LogBuilder schema(Object[] scid, MediaWikiApi mwApi, JsonKvStore prefs, Context context) {
|
||||
if (scid.length != 2) {
|
||||
throw new IllegalArgumentException("Needs an object array with schema as first param and revision as second");
|
||||
}
|
||||
return schema((String) scid[0], (Long) scid[1], mwApi, prefs, context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class LogBuilder {
|
||||
private final MediaWikiApi mwApi;
|
||||
private final JSONObject data;
|
||||
private final long rev;
|
||||
private final String schema;
|
||||
private final JsonKvStore prefs;
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Main constructor of LogBuilder
|
||||
*
|
||||
* @param schema Log schema
|
||||
* @param revision Log revision
|
||||
* @param mwApi Wiki media API instance
|
||||
* @param prefs Instance of SharedPreferences
|
||||
*/
|
||||
LogBuilder(String schema,
|
||||
long revision,
|
||||
MediaWikiApi mwApi,
|
||||
JsonKvStore prefs,
|
||||
Context context) {
|
||||
this.prefs = prefs;
|
||||
this.data = new JSONObject();
|
||||
this.schema = schema;
|
||||
this.rev = revision;
|
||||
this.mwApi = mwApi;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data to preferences
|
||||
* @param key Log key
|
||||
* @param value Log object value
|
||||
* @return LogBuilder
|
||||
*/
|
||||
public LogBuilder param(String key, Object value) {
|
||||
try {
|
||||
data.put(key, value);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes JSON object to URL
|
||||
* @return URL to JSON object
|
||||
*/
|
||||
URL toUrl() {
|
||||
JSONObject fullData = new JSONObject();
|
||||
try {
|
||||
fullData.put("schema", schema);
|
||||
fullData.put("revision", rev);
|
||||
fullData.put("wiki", BuildConfig.EVENTLOG_WIKI);
|
||||
data.put("device", EventLog.DEVICE);
|
||||
data.put("platform", "Android/" + Build.VERSION.RELEASE);
|
||||
data.put("appversion", "Android/" + ConfigUtils.getVersionNameWithSha(context));
|
||||
fullData.put("event", data);
|
||||
return new URL(BuildConfig.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
|
||||
} catch (MalformedURLException | JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// force param disregards user preference
|
||||
// Use *only* for tracking the user preference change for EventLogging
|
||||
// Attempting to use anywhere else will cause kitten explosions
|
||||
public void log(boolean force) {
|
||||
if (!prefs.getBoolean(Prefs.TRACKING_ENABLED, true) && !force) {
|
||||
return; // User has disabled tracking
|
||||
}
|
||||
LogTask logTask = new LogTask(mwApi);
|
||||
logTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this);
|
||||
}
|
||||
|
||||
public void log() {
|
||||
log(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
class LogTask extends AsyncTask<LogBuilder, Void, Boolean> {
|
||||
|
||||
private final MediaWikiApi mwApi;
|
||||
|
||||
/**
|
||||
* Main constructor of LogTask
|
||||
*
|
||||
* @param mwApi Media wiki API instance
|
||||
*/
|
||||
public LogTask(MediaWikiApi mwApi) {
|
||||
this.mwApi = mwApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs events in background
|
||||
* @param logBuilders LogBuilder instance
|
||||
* @return Background success state ( TRUE or FALSE )
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(LogBuilder... logBuilders) {
|
||||
return mwApi.logEvents(logBuilders);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.Single;
|
||||
|
||||
public interface MediaWikiApi {
|
||||
String getUserAgent();
|
||||
|
||||
String getAuthCookie();
|
||||
|
||||
|
|
@ -37,8 +36,6 @@ public interface MediaWikiApi {
|
|||
|
||||
Single<String> findThumbnailByFilename(String filename);
|
||||
|
||||
boolean logEvents(LogBuilder[] logBuilders);
|
||||
|
||||
List<String> getSubCategoryList(String categoryName);
|
||||
|
||||
List<String> getParentCategoryList(String categoryName);
|
||||
|
|
@ -103,7 +100,7 @@ public interface MediaWikiApi {
|
|||
|
||||
// Single<CampaignResponseDTO> getCampaigns();
|
||||
|
||||
boolean thank(String editToken, String revision) throws IOException;
|
||||
boolean thank(String editToken, long revision) throws IOException;
|
||||
|
||||
interface ProgressListener {
|
||||
void onProgress(long transferred, long total);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ package fr.free.nrw.commons.mwapi;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
import org.wikipedia.dataclient.mwapi.RecentChange;
|
||||
import org.wikipedia.util.DateUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -18,21 +24,15 @@ import javax.inject.Singleton;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
import fr.free.nrw.commons.achievements.FeaturedImages;
|
||||
import fr.free.nrw.commons.achievements.FeedbackResponse;
|
||||
import fr.free.nrw.commons.campaigns.CampaignResponseDTO;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
import fr.free.nrw.commons.mwapi.model.MwQueryResponse;
|
||||
import fr.free.nrw.commons.mwapi.model.RecentChange;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyResponse;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
||||
import fr.free.nrw.commons.upload.FileUtils;
|
||||
import fr.free.nrw.commons.utils.DateUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
|
@ -135,7 +135,7 @@ public class OkHttpJsonApiClient {
|
|||
String url = String.format(
|
||||
Locale.ENGLISH,
|
||||
fetchAchievementUrlTemplate,
|
||||
new PageTitle(userName).getText());
|
||||
userName);
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||
urlBuilder.addQueryParameter("user", userName);
|
||||
Timber.i("Url %s", urlBuilder.toString());
|
||||
|
|
@ -221,14 +221,16 @@ public class OkHttpJsonApiClient {
|
|||
*/
|
||||
@Nullable
|
||||
public Single<Media> getPictureOfTheDay() {
|
||||
String template = "Template:Potd/" + DateUtils.getCurrentDate();
|
||||
String date = DateUtil.getIso8601DateFormatShort().format(new Date());
|
||||
Timber.d("Current date is %s", date);
|
||||
String template = "Template:Potd/" + date;
|
||||
return getMedia(template, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API
|
||||
*
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @param useGenerator specifies if a image generator parameter needs to be passed or not
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -265,6 +267,7 @@ public class OkHttpJsonApiClient {
|
|||
/**
|
||||
* Whenever imageInfo is fetched, these common properties can be specified for the API call
|
||||
* https://www.mediawiki.org/wiki/API:Imageinfo
|
||||
*
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -274,7 +277,7 @@ public class OkHttpJsonApiClient {
|
|||
.addQueryParameter("iiextmetadatafilter", "DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal|Artist|LicenseShortName|LicenseUrl");
|
||||
|
||||
String language = Locale.getDefault().getLanguage();
|
||||
if (!StringUtils.isNullOrWhiteSpace(language)) {
|
||||
if (!StringUtils.isBlank(language)) {
|
||||
builder.addQueryParameter("iiextmetadatalanguage", language);
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +337,8 @@ public class OkHttpJsonApiClient {
|
|||
|
||||
/**
|
||||
* Append params for search query.
|
||||
* @param query the search query to be sent to the API
|
||||
*
|
||||
* @param query the search query to be sent to the API
|
||||
* @param urlBuilder builder for HttpUrl
|
||||
*/
|
||||
private void appendSearchParam(String query, HttpUrl.Builder urlBuilder) {
|
||||
|
|
@ -347,6 +351,7 @@ public class OkHttpJsonApiClient {
|
|||
|
||||
/**
|
||||
* It takes a urlBuilder and appends all the continue values as query parameters
|
||||
*
|
||||
* @param query
|
||||
* @param urlBuilder
|
||||
*/
|
||||
|
|
@ -361,8 +366,9 @@ public class OkHttpJsonApiClient {
|
|||
|
||||
/**
|
||||
* Append parameters for category image generator
|
||||
*
|
||||
* @param categoryName name of the category
|
||||
* @param urlBuilder HttpUrl builder
|
||||
* @param urlBuilder HttpUrl builder
|
||||
*/
|
||||
private void appendCategoryParams(String categoryName, HttpUrl.Builder urlBuilder) {
|
||||
urlBuilder.addQueryParameter("generator", "categorymembers")
|
||||
|
|
@ -376,6 +382,7 @@ public class OkHttpJsonApiClient {
|
|||
/**
|
||||
* Stores the continue values for action=query
|
||||
* These values are sent to the server in the subsequent call to fetch results after this point
|
||||
*
|
||||
* @param keyword
|
||||
* @param values
|
||||
*/
|
||||
|
|
@ -386,6 +393,7 @@ public class OkHttpJsonApiClient {
|
|||
/**
|
||||
* Retrieves a map of continue values from shared preferences.
|
||||
* These values are appended to the next API call
|
||||
*
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -395,6 +403,7 @@ public class OkHttpJsonApiClient {
|
|||
|
||||
/**
|
||||
* Returns recent changes on commons
|
||||
*
|
||||
* @return list of recent changes made
|
||||
*/
|
||||
@Nullable
|
||||
|
|
@ -405,6 +414,7 @@ public class OkHttpJsonApiClient {
|
|||
Date now = new Date();
|
||||
Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L);
|
||||
|
||||
String rcStart = DateUtil.getIso8601DateFormat().format(startDate);
|
||||
HttpUrl.Builder urlBuilder = HttpUrl
|
||||
.parse(commonsBaseUrl)
|
||||
.newBuilder()
|
||||
|
|
@ -412,7 +422,7 @@ public class OkHttpJsonApiClient {
|
|||
.addQueryParameter("format", "json")
|
||||
.addQueryParameter("formatversion", "2")
|
||||
.addQueryParameter("list", "recentchanges")
|
||||
.addQueryParameter("rcstart", DateUtils.formatMWDate(startDate))
|
||||
.addQueryParameter("rcstart", rcStart)
|
||||
.addQueryParameter("rcnamespace", FILE_NAMESPACE)
|
||||
.addQueryParameter("rcprop", "title|ids")
|
||||
.addQueryParameter("rctype", "new|log")
|
||||
|
|
@ -427,7 +437,7 @@ public class OkHttpJsonApiClient {
|
|||
if (response.body() != null && response.isSuccessful()) {
|
||||
String json = response.body().string();
|
||||
MwQueryResponse mwQueryPage = gson.fromJson(json, MwQueryResponse.class);
|
||||
return mwQueryPage.query().getRecentchanges();
|
||||
return mwQueryPage.query().getRecentChanges();
|
||||
}
|
||||
return new ArrayList<>();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class ApiResponse {
|
||||
public Query query;
|
||||
|
||||
public ApiResponse() {
|
||||
}
|
||||
|
||||
public boolean hasPages() {
|
||||
return query != null && query.pages != null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class MwException extends RuntimeException {
|
||||
@SuppressWarnings("unused")
|
||||
@NonNull
|
||||
private final MwServiceError error;
|
||||
|
||||
public MwException(@NonNull MwServiceError error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MwServiceError getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return error.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
return error.getDetails();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MwQueryResponse extends MwResponse {
|
||||
|
||||
@SuppressWarnings("unused") @SerializedName("batchcomplete") private boolean batchComplete;
|
||||
|
||||
@SuppressWarnings("unused") @SerializedName("continue") @Nullable
|
||||
private Map<String, String> continuation;
|
||||
|
||||
@Nullable private MwQueryResult query;
|
||||
|
||||
public boolean batchComplete() {
|
||||
return batchComplete;
|
||||
}
|
||||
|
||||
@Nullable public Map<String, String> continuation() {
|
||||
return continuation;
|
||||
}
|
||||
|
||||
@Nullable public MwQueryResult query() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public boolean success() {
|
||||
return query != null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected void setQuery(@Nullable MwQueryResult query) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.media.model.ImageInfo;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
|
||||
public class MwQueryResult {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private List<MwQueryPage> pages;
|
||||
private List<RecentChange> recentchanges;
|
||||
|
||||
@NonNull
|
||||
public List<MwQueryPage> pages() {
|
||||
if (pages == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
public List<RecentChange> getRecentchanges() {
|
||||
return recentchanges;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MwQueryPage firstPage() {
|
||||
return pages().get(0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Map<String, ImageInfo> images() {
|
||||
Map<String, ImageInfo> result = new HashMap<>();
|
||||
if (pages != null) {
|
||||
for (MwQueryPage page : pages()) {
|
||||
if (page.imageInfo() != null) {
|
||||
result.put(page.title(), page.imageInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import fr.free.nrw.commons.json.PostProcessingTypeAdapter;
|
||||
|
||||
public abstract class MwResponse implements PostProcessingTypeAdapter.PostProcessable {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private MwServiceError error;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private Map<String, Warning> warnings;
|
||||
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@SerializedName("servedby")
|
||||
@NonNull
|
||||
private String servedBy;
|
||||
|
||||
@Override
|
||||
public void postProcess() {
|
||||
if (error != null) {
|
||||
throw new MwException(error);
|
||||
}
|
||||
}
|
||||
|
||||
private class Warning {
|
||||
@SuppressWarnings("unused,NullableProblems")
|
||||
@NonNull
|
||||
private String warnings;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* Gson POJO for a MediaWiki API error.
|
||||
*/
|
||||
public class MwServiceError implements ServiceError {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String code;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String info;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String docref;
|
||||
@SuppressWarnings("unused")
|
||||
@NonNull
|
||||
private List<Message> messages = Collections.emptyList();
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getTitle() {
|
||||
return StringUtils.defaultString(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getDetails() {
|
||||
return StringUtils.defaultString(info);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDocRef() {
|
||||
return docref;
|
||||
}
|
||||
|
||||
public boolean badToken() {
|
||||
return "badtoken".equals(code);
|
||||
}
|
||||
|
||||
public boolean badLoginState() {
|
||||
return "assertuserfailed".equals(code);
|
||||
}
|
||||
|
||||
public boolean hasMessageName(@NonNull String messageName) {
|
||||
for (Message msg : messages) {
|
||||
if (messageName.equals(msg.name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMessageHtml(@NonNull String messageName) {
|
||||
for (Message msg : messages) {
|
||||
if (messageName.equals(msg.name)) {
|
||||
return msg.html();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class Message {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String name;
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable
|
||||
private String html;
|
||||
|
||||
@NonNull
|
||||
private String html() {
|
||||
return StringUtils.defaultString(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Page {
|
||||
public String title;
|
||||
public PageCategory[] categories;
|
||||
public PageCategory category;
|
||||
|
||||
public Page() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PageCategory[] getCategories() {
|
||||
return categories != null ? categories : new PageCategory[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class PageCategory {
|
||||
public String title;
|
||||
|
||||
public PageCategory() {
|
||||
}
|
||||
|
||||
public String withoutPrefix() {
|
||||
return title != null ? title.replace("Category:", "") : "";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class Query {
|
||||
public Page[] pages;
|
||||
|
||||
public Query() {
|
||||
pages = new Page[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class RecentChange {
|
||||
private final String type;
|
||||
private final String title;
|
||||
@SerializedName("old_revid")
|
||||
private final String oldRevisionId;
|
||||
|
||||
public RecentChange(String type, String title, String oldRevisionId) {
|
||||
this.type = type;
|
||||
this.title = title;
|
||||
this.oldRevisionId = oldRevisionId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getOldRevisionId() {
|
||||
return oldRevisionId;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* The API reported an error in the payload.
|
||||
*/
|
||||
public interface ServiceError {
|
||||
@NonNull
|
||||
String getTitle();
|
||||
|
||||
@NonNull String getDetails();
|
||||
}
|
||||
|
|
@ -3,12 +3,14 @@ package fr.free.nrw.commons.nearby;
|
|||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyResultItem;
|
||||
import fr.free.nrw.commons.utils.PlaceUtils;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -47,7 +49,7 @@ public class Place implements Parcelable {
|
|||
public static Place from(NearbyResultItem item) {
|
||||
String itemClass = item.getClassName().getValue();
|
||||
String classEntityId = "";
|
||||
if(!StringUtils.isNullOrWhiteSpace(itemClass)) {
|
||||
if(!StringUtils.isBlank(itemClass)) {
|
||||
classEntityId = itemClass.replace("http://www.wikidata.org/entity/", "");
|
||||
}
|
||||
return new Place(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.notification;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -14,6 +13,8 @@ import com.daimajia.swipe.SwipeLayout;
|
|||
import com.google.android.material.animation.ArgbEvaluatorCompat;
|
||||
import com.pedrogomez.renderers.Renderer;
|
||||
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
|
|
@ -106,7 +107,7 @@ public class NotificationRenderer extends Renderer<Notification> {
|
|||
*/
|
||||
private void setTitle(String notificationText) {
|
||||
notificationText = notificationText.trim().replaceAll("(^\\s*)|(\\s*$)", "");
|
||||
notificationText = Html.fromHtml(notificationText).toString();
|
||||
notificationText = StringUtil.fromHtml(notificationText).toString();
|
||||
if (notificationText.length() > 280) {
|
||||
notificationText = notificationText.substring(0, 279);
|
||||
notificationText = notificationText.concat("...");
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ public class ReviewActivity extends AuthenticatedActivity {
|
|||
.subscribe(revision -> {
|
||||
reviewController.firstRevision = revision;
|
||||
reviewPagerAdapter.updateFileInformation(fileName);
|
||||
((TextView) imageCaption).setText(fileName + " is uploaded by: " + revision.getUser());
|
||||
((TextView) imageCaption).setText(fileName + " is uploaded by: " + revision.content());
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}));
|
||||
reviewPager.setCurrentItem(0);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import android.content.Context;
|
|||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -21,7 +23,6 @@ import fr.free.nrw.commons.R;
|
|||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.delete.DeleteHelper;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
|
@ -31,21 +32,19 @@ import timber.log.Timber;
|
|||
@Singleton
|
||||
public class ReviewController {
|
||||
private String fileName;
|
||||
public static final int NOTIFICATION_SEND_THANK = 0x102;
|
||||
protected static ArrayList<String> categories;
|
||||
public static final int NOTIFICATION_CHECK_CATEGORY = 0x101;
|
||||
private final DeleteHelper deleteHelper;
|
||||
@Nullable
|
||||
public MwQueryPage.Revision firstRevision; // TODO: maybe we can expand this class to include fileName
|
||||
protected static ArrayList<String> categories;
|
||||
public static final int NOTIFICATION_SEND_THANK = 0x102;
|
||||
public static final int NOTIFICATION_CHECK_CATEGORY = 0x101;
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
private Media media;
|
||||
|
||||
@Inject
|
||||
MediaWikiApi mwApi;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
private final DeleteHelper deleteHelper;
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
private Media media;
|
||||
|
||||
private ViewPager viewPager;
|
||||
private ReviewActivity reviewActivity;
|
||||
|
|
@ -201,7 +200,7 @@ public class ReviewController {
|
|||
}
|
||||
publishProgress(context, 1);
|
||||
assert firstRevision != null;
|
||||
mwApi.thank(editToken, firstRevision.getRevid());
|
||||
mwApi.thank(editToken, firstRevision.getRevisionId());
|
||||
publishProgress(context, 2);
|
||||
} catch (Exception e) {
|
||||
Timber.d(e);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package fr.free.nrw.commons.review;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.RecentChange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
|
|
@ -9,10 +12,8 @@ import javax.inject.Singleton;
|
|||
|
||||
import androidx.core.util.Pair;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.mwapi.model.RecentChange;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
|
|
@ -36,6 +37,7 @@ public class ReviewHelper {
|
|||
* - Picks a random file from those changes
|
||||
* - Checks if the file is nominated for deletion
|
||||
* - Retries upto 5 times for getting a file which is not nominated for deletion
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Single<Media> getRandomMedia() {
|
||||
|
|
@ -75,7 +77,7 @@ public class ReviewHelper {
|
|||
for (int i = 0; i < count; i++) {
|
||||
int randomIndex = randomIndexes[i];
|
||||
RecentChange recentChange = recentChanges.get(randomIndex);
|
||||
if (recentChange.getType().equals("log") && !recentChange.getOldRevisionId().equals("0")) {
|
||||
if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) {
|
||||
// For log entries, we only want ones where old_revid is zero, indicating a new file
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import android.widget.Button;
|
|||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
|
||||
public class ReviewImageFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
|
|
@ -130,6 +131,7 @@ public class ReviewImageFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
return layoutView;
|
||||
}
|
||||
|
||||
private ReviewActivity getReviewActivity() {
|
||||
return (ReviewActivity) requireActivity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.os.Bundle;
|
|||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage;
|
||||
|
||||
public class ReviewPagerAdapter extends FragmentStatePagerAdapter {
|
||||
ReviewImageFragment[] reviewImageFragments;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
package fr.free.nrw.commons.ui.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.wikipedia.util.StringUtil;
|
||||
|
||||
/**
|
||||
* An {@link AppCompatTextView} which formats the text to HTML displayable text and makes any
|
||||
* links clickable.
|
||||
|
|
@ -23,7 +22,7 @@ public class HtmlTextView extends AppCompatTextView {
|
|||
super(context, attrs);
|
||||
|
||||
setMovementMethod(LinkMovementMethod.getInstance());
|
||||
setText(fromHtml(getText().toString()));
|
||||
setText(StringUtil.fromHtml(getText().toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -31,21 +30,6 @@ public class HtmlTextView extends AppCompatTextView {
|
|||
* @param newText the text to be displayed
|
||||
*/
|
||||
public void setHtmlText(String newText) {
|
||||
setText(fromHtml(newText));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix Html.fromHtml is deprecated problem
|
||||
*
|
||||
* @param source provided Html string
|
||||
* @return returned Spanned of appropriate method according to version check
|
||||
*/
|
||||
private static Spanned fromHtml(String source) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
return Html.fromHtml(source);
|
||||
}
|
||||
setText(StringUtil.fromHtml(newText));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package fr.free.nrw.commons.upload;
|
|||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -12,7 +14,6 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
|||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import fr.free.nrw.commons.utils.ImageUtilsWrapper;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import io.reactivex.Single;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -172,13 +173,13 @@ public class ImageProcessingService {
|
|||
*/
|
||||
private Single<Integer> checkImageGeoLocation(Place place, String filePath) {
|
||||
Timber.d("Checking for image geolocation %s", filePath);
|
||||
if (place == null || StringUtils.isNullOrWhiteSpace(place.getWikiDataEntityId())) {
|
||||
if (place == null || StringUtils.isBlank(place.getWikiDataEntityId())) {
|
||||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
return Single.fromCallable(() -> filePath)
|
||||
.map(fileUtilsWrapper::getGeolocationOfFile)
|
||||
.flatMap(geoLocation -> {
|
||||
if (StringUtils.isNullOrWhiteSpace(geoLocation)) {
|
||||
if (StringUtils.isBlank(geoLocation)) {
|
||||
return Single.just(ImageUtils.IMAGE_OK);
|
||||
}
|
||||
return imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, place.getLocation());
|
||||
|
|
|
|||
|
|
@ -11,12 +11,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
|
|
@ -61,6 +56,7 @@ import fr.free.nrw.commons.location.LatLng;
|
|||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||
|
|
@ -124,8 +120,7 @@ public class UploadActivity extends BaseActivity implements UploadView, SimilarI
|
|||
|
||||
// Final Submission
|
||||
@BindView(R.id.license_title) TextView licenseTitle;
|
||||
@BindView(R.id.share_license_summary) TextView licenseSummary;
|
||||
@BindView(R.id.media_upload_policy) TextView licensePolicy;
|
||||
@BindView(R.id.share_license_summary) HtmlTextView licenseSummary;
|
||||
@BindView(R.id.license_list) Spinner licenseSpinner;
|
||||
@BindView(R.id.submit) Button submit;
|
||||
@BindView(R.id.license_previous) Button licensePrevious;
|
||||
|
|
@ -152,7 +147,6 @@ public class UploadActivity extends BaseActivity implements UploadView, SimilarI
|
|||
configureNavigationButtons();
|
||||
configureCategories();
|
||||
configureLicenses();
|
||||
configurePolicy();
|
||||
|
||||
presenter.init();
|
||||
|
||||
|
|
@ -271,8 +265,7 @@ public class UploadActivity extends BaseActivity implements UploadView, SimilarI
|
|||
public void updateLicenseSummary(String selectedLicense, int imageCount) {
|
||||
String licenseHyperLink = "<a href='" + Utils.licenseUrlFor(selectedLicense) + "'>" +
|
||||
getString(Utils.licenseNameFor(selectedLicense)) + "</a><br>";
|
||||
|
||||
setTextViewHTML(licenseSummary, getResources().getQuantityString(R.plurals.share_license_summary, imageCount, licenseHyperLink));
|
||||
licenseSummary.setHtmlText(getResources().getQuantityString(R.plurals.share_license_summary, imageCount, licenseHyperLink));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -438,47 +431,6 @@ public class UploadActivity extends BaseActivity implements UploadView, SimilarI
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses links from HTML string, and makes the links clickable in the specified TextView.<br>
|
||||
* Uses {@link #makeLinkClickable(SpannableStringBuilder, URLSpan)}.
|
||||
* @see <a href="https://stackoverflow.com/questions/12418279/android-textview-with-clickable-links-how-to-capture-clicks">Source</a>
|
||||
*/
|
||||
private void setTextViewHTML(TextView text, String html)
|
||||
{
|
||||
CharSequence sequence = Html.fromHtml(html);
|
||||
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
|
||||
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
|
||||
for (URLSpan span : urls) {
|
||||
makeLinkClickable(strBuilder, span);
|
||||
}
|
||||
text.setText(strBuilder);
|
||||
text.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets onClick handler to launch browser for the specified URLSpan.
|
||||
* @see <a href="https://stackoverflow.com/questions/12418279/android-textview-with-clickable-links-how-to-capture-clicks">Source</a>
|
||||
*/
|
||||
private void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span)
|
||||
{
|
||||
int start = strBuilder.getSpanStart(span);
|
||||
int end = strBuilder.getSpanEnd(span);
|
||||
int flags = strBuilder.getSpanFlags(span);
|
||||
ClickableSpan clickable = new ClickableSpan() {
|
||||
public void onClick(View view) {
|
||||
// Handle hyperlink click
|
||||
String hyperLink = span.getURL();
|
||||
launchBrowser(hyperLink);
|
||||
}
|
||||
};
|
||||
strBuilder.setSpan(clickable, start, end, flags);
|
||||
strBuilder.removeSpan(span);
|
||||
}
|
||||
|
||||
private void launchBrowser(String hyperLink) {
|
||||
Utils.handleWebUrl(this, Uri.parse(hyperLink));
|
||||
}
|
||||
|
||||
private void configureLicenses() {
|
||||
licenseSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
|
|
@ -559,10 +511,6 @@ public class UploadActivity extends BaseActivity implements UploadView, SimilarI
|
|||
categoriesList.setAdapter(categoriesAdapter);
|
||||
}
|
||||
|
||||
private void configurePolicy() {
|
||||
setTextViewHTML(licensePolicy, getString(R.string.media_upload_policy));
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private void updateCategoryList(String filter) {
|
||||
List<String> imageTitleList = presenter.getImageTitleList();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,17 @@ package fr.free.nrw.commons.upload;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoriesModel;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
|
|
@ -11,7 +22,6 @@ import fr.free.nrw.commons.location.LatLng;
|
|||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.CustomProxy;
|
||||
import fr.free.nrw.commons.utils.StringUtils;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
|
@ -166,7 +176,7 @@ public class UploadPresenter {
|
|||
break;
|
||||
default:
|
||||
String errorMessageForResult = getErrorMessageForResult(context, errorCode);
|
||||
if (StringUtils.isNullOrWhiteSpace(errorMessageForResult)) {
|
||||
if (StringUtils.isBlank(errorMessageForResult)) {
|
||||
return;
|
||||
}
|
||||
view.showBadPicturePopup(errorMessageForResult);
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DateUtils {
|
||||
private static final SimpleDateFormat isoFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
|
||||
|
||||
public static String getTimeAgo(Date currDate, Date itemDate) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(currDate);
|
||||
int yearNow = c.get(Calendar.YEAR);
|
||||
int monthNow = c.get(Calendar.MONTH);
|
||||
int dayNow = c.get(Calendar.DAY_OF_MONTH);
|
||||
int hourNow = c.get(Calendar.HOUR_OF_DAY);
|
||||
int minuteNow = c.get(Calendar.MINUTE);
|
||||
c.setTime(itemDate);
|
||||
int videoYear = c.get(Calendar.YEAR);
|
||||
int videoMonth = c.get(Calendar.MONTH);
|
||||
int videoDays = c.get(Calendar.DAY_OF_MONTH);
|
||||
int videoHour = c.get(Calendar.HOUR_OF_DAY);
|
||||
int videoMinute = c.get(Calendar.MINUTE);
|
||||
|
||||
if (yearNow != videoYear) {
|
||||
return (String.valueOf(yearNow - videoYear) + "-" + "years");
|
||||
} else if (monthNow != videoMonth) {
|
||||
return (String.valueOf(monthNow - videoMonth) + "-" + "months");
|
||||
} else if (dayNow != videoDays) {
|
||||
return (String.valueOf(dayNow - videoDays) + "-" + "days");
|
||||
} else if (hourNow != videoHour) {
|
||||
return (String.valueOf(hourNow - videoHour) + "-" + "hours");
|
||||
} else if (minuteNow != videoMinute) {
|
||||
return (String.valueOf(minuteNow - videoMinute) + "-" + "minutes");
|
||||
} else {
|
||||
return "0-seconds";
|
||||
}
|
||||
}
|
||||
|
||||
public static Date getDateFromString(String dateString) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
try {
|
||||
return dateFormat.parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCurrentDate() {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||
Date date = new Date();
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
public static String dateInLocaleFormat(Date date){
|
||||
return new SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "dd MMM yyyy"), Locale.getDefault()).format(date);
|
||||
}
|
||||
|
||||
public static String formatMWDate(Date date) {
|
||||
return isoFormat.format(date);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import android.app.Dialog;
|
|||
import android.content.DialogInterface;
|
||||
import android.view.View;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -59,7 +61,7 @@ public class DialogUtil {
|
|||
builder.setTitle(title);
|
||||
builder.setMessage(message);
|
||||
|
||||
if (!StringUtils.isNullOrWhiteSpace(positiveButtonText)) {
|
||||
if (!StringUtils.isBlank(positiveButtonText)) {
|
||||
builder.setPositiveButton(positiveButtonText, (dialogInterface, i) -> {
|
||||
dialogInterface.dismiss();
|
||||
if (onPositiveBtnClick != null) {
|
||||
|
|
@ -68,7 +70,7 @@ public class DialogUtil {
|
|||
});
|
||||
}
|
||||
|
||||
if (!StringUtils.isNullOrWhiteSpace(negativeButtonText)) {
|
||||
if (!StringUtils.isBlank(negativeButtonText)) {
|
||||
builder.setNegativeButton(negativeButtonText, (DialogInterface dialogInterface, int i) -> {
|
||||
dialogInterface.dismiss();
|
||||
if (onNegativeBtnClick != null) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
|
|
@ -33,13 +35,13 @@ public class MediaDataExtractorUtil {
|
|||
* @return
|
||||
*/
|
||||
public static List<String> extractCategoriesFromList(String source) {
|
||||
if (StringUtils.isNullOrWhiteSpace(source)) {
|
||||
if (StringUtils.isBlank(source)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
String[] cats = source.split("\\|");
|
||||
List<String> categories = new ArrayList<>();
|
||||
for (String category : cats) {
|
||||
if (!StringUtils.isNullOrWhiteSpace(category.trim())) {
|
||||
if (!StringUtils.isBlank(category.trim())) {
|
||||
categories.add(category);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
|
||||
public class StringUtils {
|
||||
public static final String EMPTY = "";
|
||||
|
||||
public static String getParsedStringFromHtml(String source) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
return Html.fromHtml(source).toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNullOrWhiteSpace(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
// Defaults
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Returns either the passed in String,
|
||||
* or if the String is {@code null}, an empty String ("").</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.defaultString(null) = ""
|
||||
* StringUtils.defaultString("") = ""
|
||||
* StringUtils.defaultString("bat") = "bat"
|
||||
* </pre>
|
||||
*
|
||||
* @see String#valueOf(Object)
|
||||
* @param str the String to check, may be null
|
||||
* @return the passed in String, or the empty String if it
|
||||
* was {@code null}
|
||||
*/
|
||||
public static String defaultString(final String str) {
|
||||
return defaultString(str, EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns either the passed in String, or if the String is
|
||||
* {@code null}, the value of {@code defaultStr}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.defaultString(null, "NULL") = "NULL"
|
||||
* StringUtils.defaultString("", "NULL") = ""
|
||||
* StringUtils.defaultString("bat", "NULL") = "bat"
|
||||
* </pre>
|
||||
*
|
||||
* @see String#valueOf(Object)
|
||||
* @param str the String to check, may be null
|
||||
* @param defaultStr the default String to return
|
||||
* if the input is {@code null}, may be null
|
||||
* @return the passed in String, or the default if it was {@code null}
|
||||
*/
|
||||
public static String defaultString(final String str, final String defaultStr) {
|
||||
return str == null ? defaultStr : str;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class UriDeserializer implements JsonDeserializer<Uri> {
|
||||
@Override
|
||||
public Uri deserialize(final JsonElement src, final Type srcType,
|
||||
final JsonDeserializationContext context) throws JsonParseException {
|
||||
return Uri.parse(src.getAsString());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
|
||||
public class UriSerializer implements JsonSerializer<Uri> {
|
||||
public JsonElement serialize(Uri src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ public class PicOfDayAppWidget extends AppWidgetProvider {
|
|||
// View in browser
|
||||
Intent viewIntent = new Intent();
|
||||
viewIntent.setAction(ACTION_VIEW);
|
||||
viewIntent.setData(response.getFilePageTitle().getMobileUri());
|
||||
viewIntent.setData(Uri.parse(response.getPageTitle().getMobileUri()));
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0);
|
||||
views.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
android:layout_below="@id/license_subtitle"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
<fr.free.nrw.commons.ui.widget.HtmlTextView
|
||||
android:id="@+id/share_license_summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import java.net.URLEncoder
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
|
||||
class PageTitleTest {
|
||||
@Test
|
||||
fun displayTextShouldNotBeUnderscored() {
|
||||
assertEquals("Ex 1", PageTitle("Ex_1").displayText)
|
||||
assertEquals("Ex 1", PageTitle("Ex_1 ").displayText)
|
||||
assertEquals("Ex 1", PageTitle("Ex 1").displayText)
|
||||
assertEquals("Ex 1", PageTitle("Ex 1 ").displayText)
|
||||
assertEquals("File:Ex 1 2", PageTitle("File:Ex_1_2 ").displayText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prefixedTextShouldBeUnderscored() {
|
||||
assertEquals("Ex_1", PageTitle("Ex_1").prefixedText)
|
||||
assertEquals("Ex_1", PageTitle("Ex_1 ").prefixedText)
|
||||
assertEquals("Ex_1", PageTitle("Ex 1").prefixedText)
|
||||
assertEquals("Ex_1", PageTitle("Ex 1 ").prefixedText)
|
||||
assertEquals("File:Ex_1_2", PageTitle("File:Ex 1 2 ").prefixedText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fileNameWithOneColon() {
|
||||
val pageTitle = PageTitle("File:sample:a.jpg")
|
||||
assertEquals("File:Sample:a.jpg", pageTitle.prefixedText)
|
||||
assertEquals("File:Sample:a.jpg", pageTitle.displayText)
|
||||
assertEquals("Sample:a.jpg", pageTitle.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fileNameWithMoreThanOneColon() {
|
||||
var pageTitle = PageTitle("File:sample:a:b.jpg")
|
||||
assertEquals("File:Sample:a:b.jpg", pageTitle.prefixedText)
|
||||
assertEquals("File:Sample:a:b.jpg", pageTitle.displayText)
|
||||
assertEquals("Sample:a:b.jpg", pageTitle.text)
|
||||
|
||||
pageTitle = PageTitle("File:sample:a:b:c.jpg")
|
||||
assertEquals("File:Sample:a:b:c.jpg", pageTitle.prefixedText)
|
||||
assertEquals("File:Sample:a:b:c.jpg", pageTitle.displayText)
|
||||
assertEquals("Sample:a:b:c.jpg", pageTitle.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun keyShouldNotIncludeNamespace() {
|
||||
val pageTitle = PageTitle("File:Sample.jpg")
|
||||
assertEquals("Sample.jpg", pageTitle.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun capitalizeNamespace() {
|
||||
val pageTitle = PageTitle("file:Sample.jpg")
|
||||
assertEquals("File:Sample.jpg", pageTitle.prefixedText)
|
||||
assertEquals("File:Sample.jpg", pageTitle.displayText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun capitalizeKey() {
|
||||
val pageTitle = PageTitle("File:sample.jpg")
|
||||
assertEquals("File:Sample.jpg", pageTitle.prefixedText)
|
||||
assertEquals("File:Sample.jpg", pageTitle.displayText)
|
||||
assertEquals("Sample.jpg", pageTitle.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getMobileUriForTest() {
|
||||
val pageTitle = PageTitle("Test")
|
||||
assertEquals(BuildConfig.MOBILE_HOME_URL + "Test", pageTitle.mobileUri.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun spaceBecomesUnderscoreInUri() {
|
||||
val pageTitle = PageTitle("File:Ex 1.jpg")
|
||||
assertEquals(BuildConfig.HOME_URL + "File:Ex_1.jpg", pageTitle.canonicalUri.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun leaveSubpageNamesUncapitalizedInUri() {
|
||||
val pageTitle = PageTitle("User:Ex/subpage")
|
||||
assertEquals(BuildConfig.HOME_URL + "User:Ex/subpage", pageTitle.canonicalUri.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unicodeUri() {
|
||||
val pageTitle = PageTitle("User:例")
|
||||
assertEquals(BuildConfig.HOME_URL + "User:" + URLEncoder.encode("例", "utf-8"), pageTitle.canonicalUri.toString())
|
||||
}
|
||||
}
|
||||
|
|
@ -19,8 +19,8 @@ import org.mockito.Mockito.mock
|
|||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
import org.wikipedia.util.DateUtil
|
||||
import java.net.URLDecoder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
|
|
@ -262,9 +262,7 @@ class ApacheHttpClientMediaWikiApiTest {
|
|||
fun isUserBlockedFromCommonsForTimeBlockedUser() {
|
||||
val currentDate = Date()
|
||||
val expiredDate = Date(currentDate.time + 10000)
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"))
|
||||
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"" + dateFormat.format(expiredDate) + "\"></userinfo></query></api>"))
|
||||
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"" + DateUtil.getIso8601DateFormat().format(expiredDate) + "\"></userinfo></query></api>"))
|
||||
|
||||
val result = testObject.isUserBlockedFromCommons()
|
||||
|
||||
|
|
@ -284,9 +282,7 @@ class ApacheHttpClientMediaWikiApiTest {
|
|||
fun isUserBlockedFromCommonsForExpiredBlockedUser() {
|
||||
val currentDate = Date()
|
||||
val expiredDate = Date(currentDate.time - 10000)
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"))
|
||||
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"" + dateFormat.format(expiredDate) + "\"></userinfo></query></api>"))
|
||||
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"" + DateUtil.getIso8601DateFormat().format(expiredDate) + "\"></userinfo></query></api>"))
|
||||
|
||||
val result = testObject.isUserBlockedFromCommons()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,178 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi
|
||||
|
||||
import com.google.gson.Gson
|
||||
import fr.free.nrw.commons.mwapi.model.Page
|
||||
import fr.free.nrw.commons.mwapi.model.PageCategory
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class CategoryApiTest {
|
||||
private lateinit var server: MockWebServer
|
||||
private lateinit var url: String
|
||||
private lateinit var categoryApi: CategoryApi
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
server = MockWebServer()
|
||||
url = "http://${server.hostName}:${server.port}/"
|
||||
categoryApi = CategoryApi(OkHttpClient.Builder().build(), Gson(), HttpUrl.parse(url))
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
server.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apiReturnsEmptyListWhenError() {
|
||||
server.enqueue(MockResponse().setResponseCode(400).setBody(""))
|
||||
|
||||
assertTrue(categoryApi.request("foo").blockingGet().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apiReturnsEmptyWhenTheresNoQuery() {
|
||||
server.success(emptyMap())
|
||||
|
||||
assertTrue(categoryApi.request("foo").blockingGet().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apiReturnsEmptyWhenQueryHasNoPages() {
|
||||
server.success(mapOf("query" to emptyMap<String, Any>()))
|
||||
|
||||
assertTrue(categoryApi.request("foo").blockingGet().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apiReturnsEmptyWhenQueryHasPagesButTheyreEmpty() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to emptyList<String>())))
|
||||
|
||||
assertTrue(categoryApi.request("foo").blockingGet().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun singlePageSingleCategory() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to listOf(
|
||||
page(listOf("one"))
|
||||
))))
|
||||
|
||||
val response = categoryApi.request("foo").blockingGet()
|
||||
|
||||
assertEquals(1, response.size)
|
||||
assertEquals("one", response[0])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multiplePagesSingleCategory() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to listOf(
|
||||
page(listOf("one")),
|
||||
page(listOf("two"))
|
||||
))))
|
||||
|
||||
val response = categoryApi.request("foo").blockingGet()
|
||||
|
||||
assertEquals(2, response.size)
|
||||
assertEquals("one", response[0])
|
||||
assertEquals("two", response[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun singlePageMultipleCategories() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to listOf(
|
||||
page(listOf("one", "two"))
|
||||
))))
|
||||
|
||||
val response = categoryApi.request("foo").blockingGet()
|
||||
|
||||
assertEquals(2, response.size)
|
||||
assertEquals("one", response[0])
|
||||
assertEquals("two", response[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multiplePagesMultipleCategories() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to listOf(
|
||||
page(listOf("one", "two")),
|
||||
page(listOf("three", "four"))
|
||||
))))
|
||||
|
||||
val response = categoryApi.request("foo").blockingGet()
|
||||
|
||||
assertEquals(4, response.size)
|
||||
assertEquals("one", response[0])
|
||||
assertEquals("two", response[1])
|
||||
assertEquals("three", response[2])
|
||||
assertEquals("four", response[3])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multiplePagesMultipleCategories_duplicatesRemoved() {
|
||||
server.success(mapOf("query" to
|
||||
mapOf("pages" to listOf(
|
||||
page(listOf("one", "two", "three")),
|
||||
page(listOf("three", "four", "one"))
|
||||
))))
|
||||
|
||||
val response = categoryApi.request("foo").blockingGet()
|
||||
|
||||
assertEquals(4, response.size)
|
||||
assertEquals("one", response[0])
|
||||
assertEquals("two", response[1])
|
||||
assertEquals("three", response[2])
|
||||
assertEquals("four", response[3])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestSendsWhatWeExpect() {
|
||||
server.success(mapOf("query" to mapOf("pages" to emptyList<String>())))
|
||||
|
||||
val coords = "foo,bar"
|
||||
categoryApi.request(coords).blockingGet()
|
||||
|
||||
server.takeRequest().let { request ->
|
||||
assertEquals("GET", request.method)
|
||||
assertEquals("/w/api.php", request.requestUrl.encodedPath())
|
||||
request.requestUrl.let { url ->
|
||||
assertEquals("query", url.queryParameter("action"))
|
||||
assertEquals("categories|coordinates|pageprops", url.queryParameter("prop"))
|
||||
assertEquals("json", url.queryParameter("format"))
|
||||
assertEquals("!hidden", url.queryParameter("clshow"))
|
||||
assertEquals("type|name|dim|country|region|globe", url.queryParameter("coprop"))
|
||||
assertEquals(coords, url.queryParameter("codistancefrompoint"))
|
||||
assertEquals("geosearch", url.queryParameter("generator"))
|
||||
assertEquals(coords, url.queryParameter("ggscoord"))
|
||||
assertEquals("10000", url.queryParameter("ggsradius"))
|
||||
assertEquals("10", url.queryParameter("ggslimit"))
|
||||
assertEquals("6", url.queryParameter("ggsnamespace"))
|
||||
assertEquals("type|name|dim|country|region|globe", url.queryParameter("ggsprop"))
|
||||
assertEquals("all", url.queryParameter("ggsprimary"))
|
||||
assertEquals("2", url.queryParameter("formatversion"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun page(catList: List<String>) = Page().apply {
|
||||
categories = catList.map {
|
||||
PageCategory().apply {
|
||||
title = "Category:$it"
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
fun MockWebServer.success(response: Map<String, Any>) {
|
||||
enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(response)))
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import fr.free.nrw.commons.Media
|
|||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient.mapType
|
||||
import fr.free.nrw.commons.utils.DateUtils
|
||||
import junit.framework.Assert.assertEquals
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
|
|
@ -22,6 +21,8 @@ import org.mockito.Mockito
|
|||
import org.mockito.Mockito.`when`
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.wikipedia.util.DateUtil
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
|
|
@ -189,7 +190,7 @@ class OkHttpJsonApiClientTest {
|
|||
*/
|
||||
@Test
|
||||
fun getImageWithGenerator() {
|
||||
val template = "Template:Potd/" + DateUtils.getCurrentDate()
|
||||
val template = "Template:Potd/" + DateUtil.getIso8601DateFormatShort().format(Date())
|
||||
server.enqueue(getMediaList("", "", "", 1))
|
||||
|
||||
val media = testObject.getMedia(template, true)!!.blockingGet()
|
||||
|
|
@ -215,7 +216,7 @@ class OkHttpJsonApiClientTest {
|
|||
*/
|
||||
@Test
|
||||
fun getPictureOfTheDay() {
|
||||
val template = "Template:Potd/" + DateUtils.getCurrentDate()
|
||||
val template = "Template:Potd/" + DateUtil.getIso8601DateFormatShort().format(Date())
|
||||
server.enqueue(getMediaList("", "", "", 1))
|
||||
|
||||
val media = testObject.pictureOfTheDay?.blockingGet()
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
|
||||
class ApiResponseTest {
|
||||
@Test
|
||||
fun hasPages_whenQueryIsNull() {
|
||||
val response = ApiResponse()
|
||||
assertFalse(response.hasPages())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasPages_whenPagesIsNull() {
|
||||
val response = ApiResponse()
|
||||
response.query = Query()
|
||||
response.query.pages = null
|
||||
assertFalse(response.hasPages())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hasPages_defaultsToSafeValue() {
|
||||
val response = ApiResponse()
|
||||
response.query = Query()
|
||||
assertNotNull(response.query.pages)
|
||||
assertTrue(response.hasPages())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class PageCategoryTest {
|
||||
@Test
|
||||
fun stripPrefix_whenPresent() {
|
||||
val testObject = PageCategory()
|
||||
testObject.title = "Category:Foo"
|
||||
assertEquals("Foo", testObject.withoutPrefix())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stripPrefix_prefixAbsent() {
|
||||
val testObject = PageCategory()
|
||||
testObject.title = "Foo_Bar"
|
||||
assertEquals("Foo_Bar", testObject.withoutPrefix())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package fr.free.nrw.commons.mwapi.model
|
||||
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
|
||||
class PageTest {
|
||||
@Test
|
||||
fun categoriesDefaultToSafeValue() {
|
||||
val page = Page()
|
||||
assertNotNull(page.getCategories())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
package fr.free.nrw.commons.review
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.media.model.MwQueryPage
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.mwapi.model.RecentChange
|
||||
import io.reactivex.Single
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
|
|
@ -15,6 +13,8 @@ import org.mockito.Mock
|
|||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage
|
||||
import org.wikipedia.dataclient.mwapi.RecentChange
|
||||
|
||||
/**
|
||||
* Test class for ReviewHelper
|
||||
|
|
@ -43,10 +43,11 @@ class ReviewHelperTest {
|
|||
*/
|
||||
@Test
|
||||
fun getRandomMedia() {
|
||||
val recentChange = getMockRecentChange("test", "File:Test1.jpeg", 0)
|
||||
val recentChange1 = getMockRecentChange("test", "File:Test2.png", 0)
|
||||
val recentChange2 = getMockRecentChange("test", "File:Test3.jpg", 0)
|
||||
`when`(okHttpJsonApiClient?.recentFileChanges)
|
||||
.thenReturn(Single.just(listOf(RecentChange("test", "File:Test1.jpeg", "0"),
|
||||
RecentChange("test", "File:Test2.png", "0"),
|
||||
RecentChange("test", "File:Test3.jpg", "0"))))
|
||||
.thenReturn(Single.just(listOf(recentChange, recentChange1, recentChange2)))
|
||||
|
||||
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString()))
|
||||
.thenReturn(Single.just(true))
|
||||
|
|
@ -55,6 +56,14 @@ class ReviewHelperTest {
|
|||
assertTrue(randomMedia is Media)
|
||||
}
|
||||
|
||||
fun getMockRecentChange(type: String, title: String, oldRevisionId: Long): RecentChange {
|
||||
val recentChange = mock(RecentChange::class.java)
|
||||
`when`(recentChange!!.type).thenReturn(type)
|
||||
`when`(recentChange.title).thenReturn(title)
|
||||
`when`(recentChange.oldRevisionId).thenReturn(oldRevisionId)
|
||||
return recentChange
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for getting first revision of file
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import fr.free.nrw.commons.Utils
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.hamcrest.CoreMatchers.`is` as _is
|
||||
|
||||
class UtilsTest {
|
||||
@Test fun `strip nothing from non-localized string`() {
|
||||
Assert.assertThat(Utils.stripLocalizedString("Hello"), _is("Hello"))
|
||||
}
|
||||
|
||||
@Test fun `strip tag from Japanese string`() {
|
||||
Assert.assertThat(Utils.stripLocalizedString("\"こんにちは\"@ja"), _is("こんにちは"))
|
||||
}
|
||||
|
||||
@Test fun `capitalize first letter`() {
|
||||
Assert.assertThat(Utils.capitalize("hello"), _is("Hello"))
|
||||
}
|
||||
|
||||
@Test fun `capitalize - pass all-capital string as it is`() {
|
||||
Assert.assertThat(Utils.capitalize("HELLO"), _is("HELLO"))
|
||||
}
|
||||
|
||||
@Test fun `capitalize - pass numbers`() {
|
||||
Assert.assertThat(Utils.capitalize("12x"), _is("12x"))
|
||||
}
|
||||
|
||||
@Test fun `capitalize - pass Japanese characters`() {
|
||||
Assert.assertThat(Utils.capitalize("こんにちは"), _is("こんにちは"))
|
||||
}
|
||||
|
||||
@Test fun `capitalize does not fail on empty string`() {
|
||||
Assert.assertThat(Utils.capitalize(""), _is(""))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue