mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
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:
parent
fc30f1b5ec
commit
86e849683a
10 changed files with 154 additions and 58 deletions
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
app/src/main/java/fr/free/nrw/commons/utils/StringUtils.java
Normal file
15
app/src/main/java/fr/free/nrw/commons/utils/StringUtils.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue