Refactor code to remove usage of Jsoup and Rssparser (#1888)

* Refactor code to remove usage of Jsoup and Rssparser

* Use current date for picture of the day
This commit is contained in:
Vivek Maskara 2018-09-11 21:14:41 +05:30 committed by GitHub
parent fc30f1b5ec
commit 86e849683a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 154 additions and 58 deletions

View file

@ -6,7 +6,6 @@ apply plugin: 'jacoco-android'
apply from: 'quality.gradle' apply from: 'quality.gradle'
dependencies { dependencies {
implementation 'com.prof.rssparser:rssparser:1.1'
implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07' implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07'
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'in.yuvi:http.fluent:1.3' implementation 'in.yuvi:http.fluent:1.3'
@ -43,7 +42,6 @@ dependencies {
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
implementation 'org.jsoup:jsoup:1.11.3'
implementation 'com.facebook.fresco:fresco:1.10.0' implementation 'com.facebook.fresco:fresco:1.10.0'
implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0'
implementation "com.google.dagger:dagger:$DAGGER_VERSION" implementation "com.google.dagger:dagger:$DAGGER_VERSION"

View file

@ -36,6 +36,7 @@ import javax.inject.Inject;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.category; package fr.free.nrw.commons.category;
import org.jsoup.Jsoup;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@ -15,6 +14,7 @@ import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.utils.StringUtils;
import timber.log.Timber; import timber.log.Timber;
public class CategoryImageUtils { public class CategoryImageUtils {
@ -57,7 +57,7 @@ public class CategoryImageUtils {
* @param node * @param node
* @return * @return
*/ */
private static Media getMediaFromPage(Node node) { public static Media getMediaFromPage(Node node) {
Media media = new Media(null, Media media = new Media(null,
getImageUrl(node), getImageUrl(node),
getFileName(node), getFileName(node),
@ -103,17 +103,12 @@ public class CategoryImageUtils {
/** /**
* Returns the parsed value of artist from the response * Returns the parsed value of artist from the response
* The artist information is returned as a HTML string from the API. Jsoup library parses the HTML string * The artist information is returned as a HTML string from the API. Using HTML parser to parse the HTML
* to extract just the text value
* @param document * @param document
* @return * @return
*/ */
private static String getCreator(Node document) { private static String getCreator(Node document) {
String artist = getMetaDataValue(document, "Artist"); return StringUtils.getParsedStringFromHtml(getMetaDataValue(document, "Artist"));
if (artist != null) {
return Jsoup.parse(artist).text();
}
return null;
} }
/** /**

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.category;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.text.Html;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -10,10 +11,12 @@ import android.widget.TextView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.MediaWikiImageView;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.ui.widget.HtmlTextView;
/** /**
* This is created to only display UI implementation. Needs to be changed in real implementation * This is created to only display UI implementation. Needs to be changed in real implementation
@ -92,7 +95,9 @@ public class GridViewAdapter extends ArrayAdapter {
private void setAuthorView(Media item, TextView author) { private void setAuthorView(Media item, TextView author) {
if (item.getCreator() != null && !item.getCreator().equals("")) { if (item.getCreator() != null && !item.getCreator().equals("")) {
String uploadedByTemplate = context.getString(R.string.image_uploaded_by); String uploadedByTemplate = context.getString(R.string.image_uploaded_by);
author.setText(String.format(uploadedByTemplate, item.getCreator()));
String uploadedBy = String.format(Locale.getDefault(), uploadedByTemplate, item.getCreator());
author.setText(uploadedBy);
} else { } else {
author.setVisibility(View.GONE); author.setVisibility(View.GONE);
} }

View file

@ -15,6 +15,7 @@ import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
import fr.free.nrw.commons.nearby.PlaceRenderer; import fr.free.nrw.commons.nearby.PlaceRenderer;
import fr.free.nrw.commons.upload.FileProcessor; import fr.free.nrw.commons.upload.FileProcessor;
import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.settings.SettingsFragment;
import fr.free.nrw.commons.widget.PicOfDayAppWidget;
@Singleton @Singleton
@ -50,6 +51,8 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
void inject(FileProcessor fileProcessor); void inject(FileProcessor fileProcessor);
void inject(PicOfDayAppWidget picOfDayAppWidget);
@Component.Builder @Component.Builder
@SuppressWarnings({"WeakerAccess", "unused"}) @SuppressWarnings({"WeakerAccess", "unused"})
interface Builder { interface Builder {

View file

@ -34,6 +34,7 @@ import java.net.URL;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -51,6 +52,7 @@ import fr.free.nrw.commons.category.QueryContinue;
import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationUtils; import fr.free.nrw.commons.notification.NotificationUtils;
import fr.free.nrw.commons.utils.ContributionUtils; import fr.free.nrw.commons.utils.ContributionUtils;
import fr.free.nrw.commons.utils.DateUtils;
import in.yuvi.http.fluent.Http; import in.yuvi.http.fluent.Http;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
@ -997,6 +999,44 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
} }
/**
* The method returns the picture of the day
*
* @return Media object corresponding to the picture of the day
*/
@Override
@Nullable
public Single<Media> getPictureOfTheDay() {
return Single.fromCallable(() -> {
CustomApiResult apiResult = null;
try {
String template = "Template:Potd/" + DateUtils.getCurrentDate();
CustomMwApi.RequestBuilder requestBuilder = api.action("query")
.param("generator", "images")
.param("format", "xml")
.param("titles", template)
.param("prop", "imageinfo")
.param("iiprop", "url|extmetadata");
apiResult = requestBuilder.get();
} catch (IOException e) {
Timber.e("Failed to obtain searchCategories", e);
}
if (apiResult == null) {
return null;
}
CustomApiResult imageNode = apiResult.getNode("/api/query/pages/page");
if (imageNode == null
|| imageNode.getDocument() == null) {
return null;
}
return CategoryImageUtils.getMediaFromPage(imageNode.getDocument());
});
}
private Date parseMWDate(String mwDate) { 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 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")); isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

View file

@ -101,6 +101,8 @@ public interface MediaWikiApi {
Single<FeedbackResponse> getAchievements(String userName); Single<FeedbackResponse> getAchievements(String userName);
Single<Media> getPictureOfTheDay();
void logout(); void logout();
interface ProgressListener { interface ProgressListener {

View file

@ -1,7 +1,9 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale;
public class DateUtils { public class DateUtils {
public static String getTimeAgo(Date currDate, Date itemDate) { public static String getTimeAgo(Date currDate, Date itemDate) {
@ -33,4 +35,10 @@ public class DateUtils {
return "0-seconds"; return "0-seconds";
} }
} }
public static String getCurrentDate() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
Date date = new Date();
return dateFormat.format(date);
}
} }

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.utils;
import android.os.Build;
import android.text.Html;
public class StringUtils {
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();
}
}
}

View file

@ -19,39 +19,71 @@ import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.prof.rssparser.Article;
import com.prof.rssparser.Parser;
import org.jsoup.Jsoup; import javax.inject.Inject;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/** /**
* Implementation of App Widget functionality. * Implementation of App Widget functionality.
*/ */
public class PicOfDayAppWidget extends AppWidgetProvider { public class PicOfDayAppWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { private CompositeDisposable compositeDisposable = new CompositeDisposable();
@Inject
MediaWikiApi mediaWikiApi;
void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
loadPictureOfTheDay(context, views, appWidgetManager, appWidgetId);
}
String urlString = BuildConfig.WIKIMEDIA_API_POTD; /**
Parser parser = new Parser(); * Loads the picture of the day using media wiki API
parser.execute(urlString); * @param context
parser.onFinish(new Parser.OnTaskCompleted() { * @param views
@Override * @param appWidgetManager
public void onTaskCompleted(ArrayList<Article> list) { * @param appWidgetId
String desc = list.get(list.size() - 1).getDescription(); */
if (desc != null) { private void loadPictureOfTheDay(Context context,
Document document = Jsoup.parse(desc); RemoteViews views,
Elements elements = document.select("img"); AppWidgetManager appWidgetManager,
String imageUrl = elements.get(0).attr("src"); int appWidgetId) {
if (imageUrl != null && imageUrl.length() > 0) { compositeDisposable.add(mediaWikiApi.getPictureOfTheDay()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
if (response != null) {
loadImageFromUrl(response.getImageUrl(), context, views, appWidgetManager, appWidgetId);
}
},
t -> {
Timber.e(t, "Fetching picture of the day failed");
}
));
}
/**
* Uses Fresco to load an image from Url
* @param imageUrl
* @param context
* @param views
* @param appWidgetManager
* @param appWidgetId
*/
private void loadImageFromUrl(String imageUrl,
Context context,
RemoteViews views,
AppWidgetManager appWidgetManager,
int appWidgetId) {
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl)).build(); ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrl)).build();
ImagePipeline imagePipeline = Fresco.getImagePipeline(); ImagePipeline imagePipeline = Fresco.getImagePipeline();
DataSource<CloseableReference<CloseableImage>> dataSource DataSource<CloseableReference<CloseableImage>> dataSource
@ -75,17 +107,14 @@ public class PicOfDayAppWidget extends AppWidgetProvider {
} }
}, CallerThreadExecutor.getInstance()); }, CallerThreadExecutor.getInstance());
} }
}
}
@Override
public void onError() {
}
});
}
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
ApplicationlessInjection
.getInstance(context
.getApplicationContext())
.getCommonsApplicationComponent()
.inject(this);
// There may be multiple widgets active, so update all of them // There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) { for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId); updateAppWidget(context, appWidgetManager, appWidgetId);