Merge remote-tracking branch 'remotes/cemrich/refactoring-NewsDetailFragment'

# Conflicts:
#	News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java
This commit is contained in:
David Luhmer 2017-11-07 22:53:44 +01:00
commit 64a73121f9
7 changed files with 398 additions and 375 deletions

View file

@ -21,7 +21,6 @@
package de.luhmer.owncloudnewsreader; package de.luhmer.owncloudnewsreader;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -30,11 +29,7 @@ import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.customtabs.CustomTabsCallback;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentPagerAdapter;
@ -59,7 +54,6 @@ import java.util.Set;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import de.luhmer.owncloudnewsreader.chrometabs.CustomTabActivityManager;
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm.SORT_DIRECTION; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm.SORT_DIRECTION;
import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.database.model.RssItem;
@ -239,18 +233,9 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
{ {
NewsDetailFragment ndf = getNewsDetailFragmentAtPosition(currentPosition);//(NewsDetailFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + currentPosition); NewsDetailFragment ndf = getNewsDetailFragmentAtPosition(currentPosition);//(NewsDetailFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + currentPosition);
if(ndf != null && ndf.mWebView != null) if(ndf != null && ndf.canNavigateBack()) {
{ ndf.navigateBack();
if (ndf.urls.size() > 1) { return true;
ndf.urls.remove(0);
ndf.mWebView.loadUrl(ndf.urls.get(0));
return true;
} else if(ndf.urls.size() == 1) {
ndf.urls.remove(0);
ndf.startLoadRssItemToWebViewTask();
Log.v(TAG, "Load rssitem to webview again");
return true;
}
} }
} }

View file

@ -22,60 +22,46 @@
package de.luhmer.owncloudnewsreader; package de.luhmer.owncloudnewsreader;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.text.format.DateUtils;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.webkit.ConsoleMessage; import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient; import android.webkit.WebHistoryItem;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import com.nostra13.universalimageloader.cache.disc.DiskCache;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.apache.commons.lang3.StringEscapeUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import de.luhmer.owncloudnewsreader.database.model.Feed; import de.luhmer.owncloudnewsreader.adapter.ProgressBarWebChromeClient;
import de.luhmer.owncloudnewsreader.async_tasks.RssItemToHtmlTask;
import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.database.model.RssItem;
import de.luhmer.owncloudnewsreader.helper.AsyncTaskHelper; import de.luhmer.owncloudnewsreader.helper.AsyncTaskHelper;
import de.luhmer.owncloudnewsreader.helper.ColorHelper; import de.luhmer.owncloudnewsreader.helper.ColorHelper;
import de.luhmer.owncloudnewsreader.helper.ImageHandler;
import de.luhmer.owncloudnewsreader.helper.ThemeChooser; import de.luhmer.owncloudnewsreader.helper.ThemeChooser;
public class NewsDetailFragment extends Fragment { public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Listener {
public static final String ARG_SECTION_NUMBER = "ARG_SECTION_NUMBER";
public static final String ARG_SECTION_NUMBER = "ARG_SECTION_NUMBER";
private static final String RSS_ITEM_PAGE_URL = "about:blank";
public final String TAG = getClass().getCanonicalName(); public final String TAG = getClass().getCanonicalName();
@ -87,7 +73,6 @@ public class NewsDetailFragment extends Fragment {
private int section_number; private int section_number;
public List<String> urls = new ArrayList<>();
protected String html; protected String html;
@ -136,11 +121,32 @@ public class NewsDetailFragment extends Fragment {
} }
} }
/**
* @return true when calls to NewsDetailFragment#navigateBack()
* can be processed right now
* @see NewsDetailFragment#navigateBack()
*/
public boolean canNavigateBack() {
return !isCurrentPageRssItem();
}
/**
* Navigates back to the last displayed page. Call NewsDetailFragment#canNavigateBack()
* to check if back navigation is possible right now. Use e.g. for back button handling.
* @see NewsDetailFragment#navigateBack()
*/
public void navigateBack() {
if (isLastPageRssItem()) {
mWebView.clearHistory();
startLoadRssItemToWebViewTask();
} else if (!isCurrentPageRssItem()){
mWebView.goBack();
}
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_news_detail, container, false); View rootView = inflater.inflate(R.layout.fragment_news_detail, container, false);
section_number = (Integer) getArguments().get(ARG_SECTION_NUMBER); section_number = (Integer) getArguments().get(ARG_SECTION_NUMBER);
@ -151,50 +157,34 @@ public class NewsDetailFragment extends Fragment {
return rootView; return rootView;
} }
public void startLoadRssItemToWebViewTask() { private void startLoadRssItemToWebViewTask() {
AsyncTaskHelper.StartAsyncTask(new LoadRssItemToWebViewAsyncTask()); mWebView.setVisibility(View.GONE);
mProgressBarLoading.setVisibility(View.VISIBLE);
NewsDetailActivity ndActivity = ((NewsDetailActivity)getActivity());
if(background_color != Integer.MIN_VALUE && ThemeChooser.isDarkTheme(ndActivity))
{
mWebView.setBackgroundColor(background_color);
ndActivity.mViewPager.setBackgroundColor(background_color);
}
init_webView();
RssItem rssItem = ndActivity.rssItems.get(section_number);
RssItemToHtmlTask task = new RssItemToHtmlTask(ndActivity, rssItem, this);
AsyncTaskHelper.StartAsyncTask(task);
} }
private class LoadRssItemToWebViewAsyncTask extends AsyncTask<Void, Void, String> { @Override
public void onRssItemParsed(String htmlPage) {
mWebView.setVisibility(View.VISIBLE);
mProgressBarLoading.setVisibility(View.GONE);
@Override setSoftwareRenderModeForWebView(htmlPage, mWebView);
protected void onPreExecute() {
NewsDetailActivity ndActivity = ((NewsDetailActivity)getActivity());
if(background_color != Integer.MIN_VALUE && ThemeChooser.isDarkTheme(ndActivity)) html = htmlPage;
{ mWebView.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", RSS_ITEM_PAGE_URL);
mWebView.setBackgroundColor(background_color);
ndActivity.mViewPager.setBackgroundColor(background_color);
}
init_webView();
mWebView.setVisibility(View.GONE);
mProgressBarLoading.setVisibility(View.VISIBLE);
super.onPreExecute();
}
@Override
protected String doInBackground(Void... voids) {
NewsDetailActivity ndActivity = ((NewsDetailActivity)getActivity());
RssItem rssItem = ndActivity.rssItems.get(section_number);
return getHtmlPage(ndActivity, rssItem, true);
}
@Override
protected void onPostExecute(String htmlPage) {
mWebView.setVisibility(View.VISIBLE);
mProgressBarLoading.setVisibility(View.GONE);
SetSoftwareRenderModeForWebView(htmlPage, mWebView);
html = htmlPage;
mWebView.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", "");
super.onPostExecute(htmlPage);
}
} }
/** /**
@ -202,17 +192,14 @@ public class NewsDetailFragment extends Fragment {
* @param htmlPage * @param htmlPage
* @param webView * @param webView
*/ */
public static void SetSoftwareRenderModeForWebView(String htmlPage, WebView webView) { private void setSoftwareRenderModeForWebView(String htmlPage, WebView webView) {
if (htmlPage.contains(".gif")) { if (htmlPage.contains(".gif")) {
webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null); webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
Log.v("NewsDetailFragment", "Using LAYER_TYPE_SOFTWARE"); Log.v("NewsDetailFragment", "Using LAYER_TYPE_SOFTWARE");
} else { } else {
//webView.setLayerType(WebView.LAYER_TYPE_HARDWARE, null); if (webView.getLayerType() == WebView.LAYER_TYPE_HARDWARE) {
//Log.v("NewsDetailFragment", "Using LAYER_TYPE_HARDWARE");
if(webView.getLayerType() == WebView.LAYER_TYPE_HARDWARE) {
Log.v("NewsDetailFragment", "Using LAYER_TYPE_HARDWARE"); Log.v("NewsDetailFragment", "Using LAYER_TYPE_HARDWARE");
} else if (webView.getLayerType() == WebView.LAYER_TYPE_SOFTWARE){ } else if (webView.getLayerType() == WebView.LAYER_TYPE_SOFTWARE) {
Log.v("NewsDetailFragment", "Using LAYER_TYPE_SOFTWARE"); Log.v("NewsDetailFragment", "Using LAYER_TYPE_SOFTWARE");
} else { } else {
Log.v("NewsDetailFragment", "Using LAYER_TYPE_DEFAULT"); Log.v("NewsDetailFragment", "Using LAYER_TYPE_DEFAULT");
@ -240,35 +227,7 @@ public class NewsDetailFragment extends Fragment {
registerForContextMenu(mWebView); registerForContextMenu(mWebView);
mWebView.setWebChromeClient(new WebChromeClient() { mWebView.setWebChromeClient(new ProgressBarWebChromeClient(mProgressbarWebView));
@Override
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.v(TAG, cm.message() + " at " + cm.sourceId() + ":" + cm.lineNumber());
return true;
}
@Override
public void onProgressChanged(WebView view, int progress) {
if (progress < 100 && mProgressbarWebView.getVisibility() == ProgressBar.GONE) {
mProgressbarWebView.setVisibility(ProgressBar.VISIBLE);
}
mProgressbarWebView.setProgress(progress);
if (progress == 100) {
mProgressbarWebView.setVisibility(ProgressBar.GONE);
//The following three lines are a workaround for websites which don't use a background color
int bgColor = ContextCompat.getColor(getContext(), R.color.slider_listview_text_color_dark_theme);
NewsDetailActivity ndActivity = ((NewsDetailActivity) getActivity());
mWebView.setBackgroundColor(bgColor);
ndActivity.mViewPager.setBackgroundColor(bgColor);
if (ThemeChooser.isDarkTheme(getActivity())) {
mWebView.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
}
}
}
});
mWebView.setWebViewClient(new WebViewClient() { mWebView.setWebViewClient(new WebViewClient() {
@ -286,19 +245,17 @@ public class NewsDetailFragment extends Fragment {
} }
@Override @Override
public void onPageStarted(WebView view, String url, Bitmap favicon) { public void onPageFinished(WebView view, String url) {
if (changedUrl) { super.onPageFinished(view, url);
changedUrl = false;
if (!url.equals("file:///android_asset/") && (urls.isEmpty() || !urls.get(0).equals(url))) { // the following lines are a workaround for websites which don't use a background color
urls.add(0, url); NewsDetailActivity ndActivity = ((NewsDetailActivity) getActivity());
int backgroundColor = ColorHelper.getColorFromAttribute(getContext(),
Log.v(TAG, "Page finished (added): " + url); R.attr.news_detail_background_color);
} mWebView.setBackgroundColor(backgroundColor);
} ndActivity.mViewPager.setBackgroundColor(backgroundColor);
super.onPageStarted(view, url, favicon);
} }
}); });
mWebView.setOnTouchListener(new View.OnTouchListener() { mWebView.setOnTouchListener(new View.OnTouchListener() {
@ -314,265 +271,101 @@ public class NewsDetailFragment extends Fragment {
}); });
} }
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
if (!(view instanceof WebView))
return;
WebView.HitTestResult result = ((WebView) view).getHitTestResult();
if (result == null)
return;
int type = result.getType();
Document htmlDoc = Jsoup.parse(html);
FragmentTransaction ft = getFragmentManager().beginTransaction();
String text;
DialogFragment newFragment;
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { switch (type) {
if (v instanceof WebView) { case WebView.HitTestResult.IMAGE_TYPE:
WebView.HitTestResult result = ((WebView) v).getHitTestResult(); case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
if (result != null) { String imageUrl = result.getExtra();
int type = result.getType();
Document htmldoc = Jsoup.parse(html); if (imageUrl.startsWith("http") || imageUrl.startsWith("file")) {
URL mImageUrl;
String imgtitle;
String imgaltval;
String imgsrcval;
FragmentTransaction ft = getFragmentManager().beginTransaction(); imgsrcval = imageUrl.substring(imageUrl.lastIndexOf('/') + 1, imageUrl.length());
Elements imgtag = htmlDoc.getElementsByAttributeValueContaining("src", imageUrl);
if (type == WebView.HitTestResult.IMAGE_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
String imageUrl = result.getExtra();
if (imageUrl.startsWith("http") || imageUrl.startsWith("file")) {
URL mImageUrl;
String imgtitle;
String imgaltval;
String imgsrcval;
imgsrcval = imageUrl.substring(imageUrl.lastIndexOf('/') + 1, imageUrl.length());
Elements imgtag = htmldoc.getElementsByAttributeValueContaining("src", imageUrl);
try {
imgtitle = imgtag.first().attr("title");
} catch (NullPointerException e) {
imgtitle = "";
}
try {
imgaltval = imgtag.first().attr("alt");
} catch (NullPointerException e) {
imgaltval = "";
}
try {
mImageUrl = new URL(imageUrl);
} catch (MalformedURLException e) {
return;
}
String title = imgsrcval;
int titleIcon = android.R.drawable.ic_menu_gallery;
String text = (imgtitle.isEmpty()) ? imgaltval : imgtitle;
// Create and show the dialog.
DialogFragment newFragment =
NewsDetailImageDialogFragment.newInstanceImage(title, titleIcon, text, mImageUrl);
newFragment.show(ft, "menu_fragment_dialog");
}
}
else if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
String url = result.getExtra();
URL mUrl;
String text;
try { try {
Elements urltag = htmldoc.getElementsByAttributeValueContaining("href", url); imgtitle = imgtag.first().attr("title");
text = urltag.text(); } catch (NullPointerException e) {
mUrl = new URL(url); imgtitle = "";
}
try {
imgaltval = imgtag.first().attr("alt");
} catch (NullPointerException e) {
imgaltval = "";
}
try {
mImageUrl = new URL(imageUrl);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
return; return;
} }
String title = imgsrcval;
int titleIcon = android.R.drawable.ic_menu_gallery;
text = (imgtitle.isEmpty()) ? imgaltval : imgtitle;
// Create and show the dialog. // Create and show the dialog.
DialogFragment newFragment = newFragment = NewsDetailImageDialogFragment.newInstanceImage(title, titleIcon, text, mImageUrl);
NewsDetailImageDialogFragment.newInstanceUrl(text, mUrl.toString());
newFragment.show(ft, "menu_fragment_dialog"); newFragment.show(ft, "menu_fragment_dialog");
} }
//else if (type == WebView.HitTestResult.EMAIL_TYPE) { } break;
//else if (type == WebView.HitTestResult.GEO_TYPE) { }
//else if (type == WebView.HitTestResult.PHONE_TYPE) { } case WebView.HitTestResult.SRC_ANCHOR_TYPE:
//else if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) { } String url = result.getExtra();
} URL mUrl;
try {
Elements urltag = htmlDoc.getElementsByAttributeValueContaining("href", url);
text = urltag.text();
mUrl = new URL(url);
} catch (MalformedURLException e) {
return;
}
// Create and show the dialog.
newFragment = NewsDetailImageDialogFragment.newInstanceUrl(text, mUrl.toString());
newFragment.show(ft, "menu_fragment_dialog");
break;
case WebView.HitTestResult.EMAIL_TYPE:
case WebView.HitTestResult.GEO_TYPE:
case WebView.HitTestResult.PHONE_TYPE:
case WebView.HitTestResult.EDIT_TEXT_TYPE:
break;
} }
} }
/**
* @return true when the last page on the webview's history stack is
@SuppressLint("SimpleDateFormat") * the original rss item page
public static String getHtmlPage(Context context, RssItem rssItem, boolean showHeader) */
{ private boolean isLastPageRssItem() {
String feedTitle = "Undefined"; WebBackForwardList list = mWebView.copyBackForwardList();
String favIconUrl = null; WebHistoryItem lastItem = list.getItemAtIndex(list.getCurrentIndex() - 1);
return lastItem != null && lastItem.getUrl().equals(RSS_ITEM_PAGE_URL);
Feed feed = rssItem.getFeed();
int[] colors = ColorHelper.getColorsFromAttributes(context,
R.attr.dividerLineColor,
R.attr.rssItemListBackground);
int feedColor = colors[0];
if(feed != null) {
feedTitle = StringEscapeUtils.escapeHtml4(feed.getFeedTitle());
favIconUrl = feed.getFaviconUrl();
if(feed.getAvgColour() != null)
feedColor = Integer.parseInt(feed.getAvgColour());
}
if(favIconUrl != null)
{
DiskCache diskCache = ImageLoader.getInstance().getDiskCache();
File file = diskCache.get(favIconUrl);
if(file != null)
favIconUrl = "file://" + file.getAbsolutePath();
} else {
favIconUrl = "file:///android_res/drawable/default_feed_icon_light.png";
}
String body_id;
if(ThemeChooser.isDarkTheme(context)) {
body_id = "darkTheme";
} else {
body_id = "lightTheme";
}
boolean isRightToLeft = context.getResources().getBoolean(R.bool.is_right_to_left);
String rtlClass = isRightToLeft ? "rtl" : "";
String borderSide = isRightToLeft ? "right" : "left";
StringBuilder builder = new StringBuilder();
builder.append("<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0\" />");
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"web.css\" />");
builder.append("<style type=\"text/css\">");
builder.append(String.format(
"#top_section { border-%s: 4px solid %s; border-bottom: 1px solid %s; background: %s }",
borderSide,
ColorHelper.getCssColor(feedColor),
ColorHelper.getCssColor(colors[0]),
ColorHelper.getCssColor(colors[1]))
);
builder.append("</style>");
builder.append(String.format("</head><body id=\"%s\" class=\"%s\">", body_id, rtlClass));
if(showHeader) {
builder.append("<div id=\"top_section\">");
builder.append("<div id=\"header\">");
String title = StringEscapeUtils.escapeHtml4(rssItem.getTitle());
String linkToFeed = StringEscapeUtils.escapeHtml4(rssItem.getLink());
builder.append(String.format("<a href=\"%s\">%s</a>", linkToFeed, title));
builder.append("</div>");
String authorOfArticle = StringEscapeUtils.escapeHtml4(rssItem.getAuthor());
if (authorOfArticle != null)
if (!authorOfArticle.trim().equals(""))
feedTitle += " - " + authorOfArticle.trim();
builder.append("<div id=\"header_small_text\">");
builder.append("<div id=\"subscription\">");
builder.append(String.format("<img id=\"imgFavicon\" src=\"%s\" />", favIconUrl));
builder.append(feedTitle.trim());
builder.append("</div>");
Date date = rssItem.getPubDate();
if (date != null) {
String dateString = (String) DateUtils.getRelativeTimeSpanString(date.getTime());
builder.append("<div id=\"datetime\">");
builder.append(dateString);
builder.append("</div>");
}
builder.append("</div>");
builder.append("</div>");
}
String description = rssItem.getBody();
description = getDescriptionWithCachedImages(description).trim();
//StopWatch stopWatch = new StopWatch();
// stopWatch.start();
description = removePreloadAttributeFromVideos(description);
//stopWatch.stop();
//Log.d("NewsDetailFragment", "Time needed for removing preload attribute: " + stopWatch.toString() + " - " + feedTitle);
builder.append("<div id=\"content\">");
builder.append(description);
builder.append("</div>");
builder.append("</body></html>");
String htmlData = builder.toString().replaceAll("\"//", "\"https://");
return htmlData;
}
private static Pattern PATTERN_PRELOAD_VIDEOS = Pattern.compile("(<video[^>]*)(preload=\".*?\")");
private static String removePreloadAttributeFromVideos(String text) {
Matcher m = PATTERN_PRELOAD_VIDEOS.matcher(text);
if(m.find()) {
StringBuffer sb = new StringBuffer();
do {
//$1 represents the 1st group
m.appendReplacement(sb, "$1" + "preload=\"none\"");
} while (m.find());
m.appendTail(sb);
text = sb.toString();
}
return text;
} }
private static String getDescriptionWithCachedImages(String text) /**
{ * @return true when the current page on the webview's history stack is
List<String> links = ImageHandler.getImageLinksFromText(text); * the original rss item page
DiskCache diskCache = ImageLoader.getInstance().getDiskCache(); */
private boolean isCurrentPageRssItem() {
for(String link : links) String currentPageUrl = mWebView.copyBackForwardList().getCurrentItem().getOriginalUrl();
{ return currentPageUrl.equals("data:text/html;charset=utf-8;base64,");
link = link.trim(); //return currentPageUrl.equals(RSS_ITEM_PAGE_URL);
try
{
File file = diskCache.get(link);
if(file != null)
text = text.replace(link, "file://" + file.getAbsolutePath());
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
return text;
}
static String getTextFromAssets(String fileName, Context context) {
InputStream input;
try {
input = context.getAssets().open(fileName);
int size = input.available();
byte[] buffer = new byte[size];
input.read(buffer);
input.close();
// byte buffer into a string
return new String(buffer);
} catch(Exception ex) {
ex.printStackTrace();
}
return "";
} }
private static String SearchString(String data, String startString, String endString)
{
int start = data.indexOf(startString) + startString.length();
int end = data.indexOf(endString, start);
if(start != (-1 + startString.length()) && end != -1)
data = data.substring(start, end).trim();
return data;
}
private static String convertHexColorFrom3To6Characters(String color)
{
for(int i = 1; i < 6; i += 2)
color = color.substring(0, i) + color.charAt(i) + color.substring(i);
return color;
}
} }

View file

@ -195,9 +195,9 @@ public class NewsDetailImageDialogFragment extends DialogFragment {
View v = inflater.inflate(R.layout.fragment_dialog_image, container, false); View v = inflater.inflate(R.layout.fragment_dialog_image, container, false);
TextView tvTitle = (TextView) v.findViewById(R.id.ic_menu_title); TextView tvTitle = v.findViewById(R.id.ic_menu_title);
TextView tvText = (TextView) v.findViewById(R.id.ic_menu_item_text); TextView tvText = v.findViewById(R.id.ic_menu_item_text);
ImageView imgTitle = (ImageView) v.findViewById(R.id.ic_menu_gallery); ImageView imgTitle = v.findViewById(R.id.ic_menu_gallery);
tvTitle.setText(mDialogTitle); tvTitle.setText(mDialogTitle);
tvText.setText(mDialogText); tvText.setText(mDialogText);

View file

@ -0,0 +1,40 @@
package de.luhmer.owncloudnewsreader.adapter;
import android.util.Log;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.ProgressBar;
/**
* A very simple WebChromeClient which sets the status of a given
* ProgressBar instance while loading. The ProgressBar instance will
* only be visible during loading.
*/
public class ProgressBarWebChromeClient extends WebChromeClient {
public final String TAG = getClass().getCanonicalName();
private ProgressBar mProgressBar;
public ProgressBarWebChromeClient(ProgressBar progressBar) {
mProgressBar = progressBar;
}
@Override
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.v(TAG, cm.message() + " at " + cm.sourceId() + ":" + cm.lineNumber());
return true;
}
@Override
public void onProgressChanged(WebView view, int progress) {
mProgressBar.setProgress(progress);
if (progress < 100 && mProgressBar.getVisibility() == ProgressBar.GONE) {
mProgressBar.setVisibility(ProgressBar.VISIBLE);
} else if (progress == 100) {
mProgressBar.setVisibility(ProgressBar.GONE);
}
}
}

View file

@ -26,9 +26,9 @@ import org.greenrobot.eventbus.Subscribe;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import de.luhmer.owncloudnewsreader.NewsDetailFragment;
import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.R;
import de.luhmer.owncloudnewsreader.SettingsActivity; import de.luhmer.owncloudnewsreader.SettingsActivity;
import de.luhmer.owncloudnewsreader.async_tasks.RssItemToHtmlTask;
import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.database.model.RssItem;
import de.luhmer.owncloudnewsreader.helper.ColorHelper; import de.luhmer.owncloudnewsreader.helper.ColorHelper;
import de.luhmer.owncloudnewsreader.helper.FavIconHandler; import de.luhmer.owncloudnewsreader.helper.FavIconHandler;
@ -243,7 +243,7 @@ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickL
favIconHandler.loadFavIconForFeed(favIconUrl, imgViewFavIcon); favIconHandler.loadFavIconForFeed(favIconUrl, imgViewFavIcon);
if(webView_body != null) { if(webView_body != null) {
String htmlPage = NewsDetailFragment.getHtmlPage(itemView.getContext(),rssItem,false); String htmlPage = RssItemToHtmlTask.getHtmlPage(itemView.getContext(), rssItem, false);
webView_body.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", ""); webView_body.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", "");
} }
} }

View file

@ -0,0 +1,199 @@
package de.luhmer.owncloudnewsreader.async_tasks;
import android.content.Context;
import android.os.AsyncTask;
import android.text.format.DateUtils;
import com.nostra13.universalimageloader.cache.disc.DiskCache;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.apache.commons.lang3.StringEscapeUtils;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.luhmer.owncloudnewsreader.R;
import de.luhmer.owncloudnewsreader.database.model.Feed;
import de.luhmer.owncloudnewsreader.database.model.RssItem;
import de.luhmer.owncloudnewsreader.helper.ColorHelper;
import de.luhmer.owncloudnewsreader.helper.ImageHandler;
import de.luhmer.owncloudnewsreader.helper.ThemeChooser;
public class RssItemToHtmlTask extends AsyncTask<Void, Void, String> {
public interface Listener {
/**
* The RSS item has successfully been parsed.
* @param htmlPage RSS item as HTML string
*/
void onRssItemParsed(String htmlPage);
}
private Context mContext;
private RssItem mRssItem;
private Listener mListener;
public RssItemToHtmlTask(Context context, RssItem rssItem, Listener listener) {
this.mContext = context;
this.mRssItem = rssItem;
this.mListener = listener;
}
@Override
protected String doInBackground(Void... params) {
return getHtmlPage(mContext, mRssItem, true);
}
@Override
protected void onPostExecute(String htmlPage) {
mListener.onRssItemParsed(htmlPage);
super.onPostExecute(htmlPage);
}
/**
* @param context
* @param rssItem item to parse
* @param showHeader true if a header with item title, feed title, etc. should be included
* @return given RSS item as full HTML page
*/
public static String getHtmlPage(Context context, RssItem rssItem, boolean showHeader) {
String feedTitle = "Undefined";
String favIconUrl = null;
Feed feed = rssItem.getFeed();
int[] colors = ColorHelper.getColorsFromAttributes(context,
R.attr.dividerLineColor,
R.attr.rssItemListBackground);
int feedColor = colors[0];
if (feed != null) {
feedTitle = StringEscapeUtils.escapeHtml4(feed.getFeedTitle());
favIconUrl = feed.getFaviconUrl();
if(feed.getAvgColour() != null) {
feedColor = Integer.parseInt(feed.getAvgColour());
}
}
if (favIconUrl != null) {
DiskCache diskCache = ImageLoader.getInstance().getDiskCache();
File file = diskCache.get(favIconUrl);
if(file != null) {
favIconUrl = "file://" + file.getAbsolutePath();
}
} else {
favIconUrl = "file:///android_res/drawable/default_feed_icon_light.png";
}
String body_id;
if (ThemeChooser.isDarkTheme(context)) {
body_id = "darkTheme";
} else {
body_id = "lightTheme";
}
boolean isRightToLeft = context.getResources().getBoolean(R.bool.is_right_to_left);
String rtlClass = isRightToLeft ? "rtl" : "";
String borderSide = isRightToLeft ? "right" : "left";
StringBuilder builder = new StringBuilder();
builder.append("<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0\" />");
builder.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"web.css\" />");
builder.append("<style type=\"text/css\">");
builder.append(String.format(
"#top_section { border-%s: 4px solid %s; border-bottom: 1px solid %s; background: %s }",
borderSide,
ColorHelper.getCssColor(feedColor),
ColorHelper.getCssColor(colors[0]),
ColorHelper.getCssColor(colors[1]))
);
builder.append("</style>");
builder.append(String.format("</head><body id=\"%s\" class=\"%s\">", body_id, rtlClass));
if (showHeader) {
builder.append("<div id=\"top_section\">");
builder.append("<div id=\"header\">");
String title = StringEscapeUtils.escapeHtml4(rssItem.getTitle());
String linkToFeed = StringEscapeUtils.escapeHtml4(rssItem.getLink());
builder.append(String.format("<a href=\"%s\">%s</a>", linkToFeed, title));
builder.append("</div>");
String authorOfArticle = StringEscapeUtils.escapeHtml4(rssItem.getAuthor());
if (authorOfArticle != null)
if (!authorOfArticle.trim().equals(""))
feedTitle += " - " + authorOfArticle.trim();
builder.append("<div id=\"header_small_text\">");
builder.append("<div id=\"subscription\">");
builder.append(String.format("<img id=\"imgFavicon\" src=\"%s\" />", favIconUrl));
builder.append(feedTitle.trim());
builder.append("</div>");
Date date = rssItem.getPubDate();
if (date != null) {
String dateString = (String) DateUtils.getRelativeTimeSpanString(date.getTime());
builder.append("<div id=\"datetime\">");
builder.append(dateString);
builder.append("</div>");
}
builder.append("</div>");
builder.append("</div>");
}
String description = rssItem.getBody();
description = getDescriptionWithCachedImages(description).trim();
description = removePreloadAttributeFromVideos(description);
builder.append("<div id=\"content\">");
builder.append(description);
builder.append("</div>");
builder.append("</body></html>");
return builder.toString().replaceAll("\"//", "\"https://");
}
private static String getDescriptionWithCachedImages(String text) {
List<String> links = ImageHandler.getImageLinksFromText(text);
DiskCache diskCache = ImageLoader.getInstance().getDiskCache();
for(String link : links) {
link = link.trim();
try {
File file = diskCache.get(link);
if(file != null)
text = text.replace(link, "file://" + file.getAbsolutePath());
} catch(Exception ex) {
ex.printStackTrace();
}
}
return text;
}
private static Pattern PATTERN_PRELOAD_VIDEOS = Pattern.compile("(<video[^>]*)(preload=\".*?\")");
private static String removePreloadAttributeFromVideos(String text) {
Matcher m = PATTERN_PRELOAD_VIDEOS.matcher(text);
if(m.find()) {
StringBuffer sb = new StringBuffer();
do {
//$1 represents the 1st group
m.appendReplacement(sb, "$1" + "preload=\"none\"");
} while (m.find());
m.appendTail(sb);
text = sb.toString();
}
return text;
}
}

View file

@ -22,6 +22,7 @@
package de.luhmer.owncloudnewsreader.helper; package de.luhmer.owncloudnewsreader.helper;
import android.content.Context; import android.content.Context;
import android.os.Environment;
import com.nostra13.universalimageloader.utils.StorageUtils; import com.nostra13.universalimageloader.utils.StorageUtils;
@ -87,4 +88,9 @@ public class FileUtils {
{ {
return getPath(context) + "/podcasts"; return getPath(context) + "/podcasts";
} }
public static boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
return Environment.MEDIA_MOUNTED.equals(state);
}
} }