diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailActivity.java index c9716a39..e4e516b7 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailActivity.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailActivity.java @@ -21,7 +21,6 @@ package de.luhmer.owncloudnewsreader; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -30,11 +29,7 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.customtabs.CustomTabsCallback; -import android.support.customtabs.CustomTabsClient; 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.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; @@ -59,7 +54,6 @@ import java.util.Set; import butterknife.BindView; import butterknife.ButterKnife; -import de.luhmer.owncloudnewsreader.chrometabs.CustomTabActivityManager; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm.SORT_DIRECTION; 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); - if(ndf != null && ndf.mWebView != null) - { - if (ndf.urls.size() > 1) { - 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; - } + if(ndf != null && ndf.canNavigateBack()) { + ndf.navigateBack(); + return true; } } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java index 2351f792..64ffe963 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java @@ -22,60 +22,46 @@ package de.luhmer.owncloudnewsreader; import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Bitmap; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.support.customtabs.CustomTabsIntent; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.content.ContextCompat; -import android.text.format.DateUtils; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.webkit.ConsoleMessage; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; +import android.webkit.WebBackForwardList; +import android.webkit.WebHistoryItem; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; 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.nodes.Document; import org.jsoup.select.Elements; -import java.io.File; -import java.io.InputStream; import java.net.MalformedURLException; 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.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.helper.AsyncTaskHelper; import de.luhmer.owncloudnewsreader.helper.ColorHelper; -import de.luhmer.owncloudnewsreader.helper.ImageHandler; import de.luhmer.owncloudnewsreader.helper.ThemeChooser; -public class NewsDetailFragment extends Fragment { - public static final String ARG_SECTION_NUMBER = "ARG_SECTION_NUMBER"; +public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Listener { + + 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(); @@ -87,7 +73,6 @@ public class NewsDetailFragment extends Fragment { private int section_number; - public List urls = new ArrayList<>(); 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 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); @@ -151,50 +157,34 @@ public class NewsDetailFragment extends Fragment { return rootView; } - public void startLoadRssItemToWebViewTask() { - AsyncTaskHelper.StartAsyncTask(new LoadRssItemToWebViewAsyncTask()); + private void startLoadRssItemToWebViewTask() { + 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 { + @Override + public void onRssItemParsed(String htmlPage) { + mWebView.setVisibility(View.VISIBLE); + mProgressBarLoading.setVisibility(View.GONE); - @Override - protected void onPreExecute() { - NewsDetailActivity ndActivity = ((NewsDetailActivity)getActivity()); + setSoftwareRenderModeForWebView(htmlPage, mWebView); - if(background_color != Integer.MIN_VALUE && ThemeChooser.isDarkTheme(ndActivity)) - { - 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); - } + html = htmlPage; + mWebView.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", RSS_ITEM_PAGE_URL); } /** @@ -202,17 +192,14 @@ public class NewsDetailFragment extends Fragment { * @param htmlPage * @param webView */ - public static void SetSoftwareRenderModeForWebView(String htmlPage, WebView webView) { + private void setSoftwareRenderModeForWebView(String htmlPage, WebView webView) { if (htmlPage.contains(".gif")) { webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null); Log.v("NewsDetailFragment", "Using LAYER_TYPE_SOFTWARE"); } else { - //webView.setLayerType(WebView.LAYER_TYPE_HARDWARE, null); - //Log.v("NewsDetailFragment", "Using LAYER_TYPE_HARDWARE"); - - if(webView.getLayerType() == WebView.LAYER_TYPE_HARDWARE) { + if (webView.getLayerType() == WebView.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"); } else { Log.v("NewsDetailFragment", "Using LAYER_TYPE_DEFAULT"); @@ -240,35 +227,7 @@ public class NewsDetailFragment extends Fragment { registerForContextMenu(mWebView); - mWebView.setWebChromeClient(new WebChromeClient() { - @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.setWebChromeClient(new ProgressBarWebChromeClient(mProgressbarWebView)); mWebView.setWebViewClient(new WebViewClient() { @@ -286,19 +245,17 @@ public class NewsDetailFragment extends Fragment { } @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - if (changedUrl) { - changedUrl = false; + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); - if (!url.equals("file:///android_asset/") && (urls.isEmpty() || !urls.get(0).equals(url))) { - urls.add(0, url); - - Log.v(TAG, "Page finished (added): " + url); - } - } - - super.onPageStarted(view, url, favicon); + // the following lines are a workaround for websites which don't use a background color + NewsDetailActivity ndActivity = ((NewsDetailActivity) getActivity()); + int backgroundColor = ColorHelper.getColorFromAttribute(getContext(), + R.attr.news_detail_background_color); + mWebView.setBackgroundColor(backgroundColor); + ndActivity.mViewPager.setBackgroundColor(backgroundColor); } + }); 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) { - if (v instanceof WebView) { - WebView.HitTestResult result = ((WebView) v).getHitTestResult(); - if (result != null) { - int type = result.getType(); + switch (type) { + case WebView.HitTestResult.IMAGE_TYPE: + case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE: + String imageUrl = result.getExtra(); - 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 { - Elements urltag = htmldoc.getElementsByAttributeValueContaining("href", url); - text = urltag.text(); - mUrl = new URL(url); + 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; + text = (imgtitle.isEmpty()) ? imgaltval : imgtitle; + // Create and show the dialog. - DialogFragment newFragment = - NewsDetailImageDialogFragment.newInstanceUrl(text, mUrl.toString()); + newFragment = NewsDetailImageDialogFragment.newInstanceImage(title, titleIcon, text, mImageUrl); newFragment.show(ft, "menu_fragment_dialog"); } - //else if (type == WebView.HitTestResult.EMAIL_TYPE) { } - //else if (type == WebView.HitTestResult.GEO_TYPE) { } - //else if (type == WebView.HitTestResult.PHONE_TYPE) { } - //else if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) { } - } + break; + + case WebView.HitTestResult.SRC_ANCHOR_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; } } - - - @SuppressLint("SimpleDateFormat") - 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(""); - builder.append(""); - builder.append(""); - builder.append(String.format("", body_id, rtlClass)); - - if(showHeader) { - builder.append("
"); - builder.append("
"); - String title = StringEscapeUtils.escapeHtml4(rssItem.getTitle()); - String linkToFeed = StringEscapeUtils.escapeHtml4(rssItem.getLink()); - builder.append(String.format("%s", linkToFeed, title)); - builder.append("
"); - - String authorOfArticle = StringEscapeUtils.escapeHtml4(rssItem.getAuthor()); - if (authorOfArticle != null) - if (!authorOfArticle.trim().equals("")) - feedTitle += " - " + authorOfArticle.trim(); - - builder.append("
"); - - builder.append("
"); - builder.append(String.format("", favIconUrl)); - builder.append(feedTitle.trim()); - builder.append("
"); - - Date date = rssItem.getPubDate(); - if (date != null) { - String dateString = (String) DateUtils.getRelativeTimeSpanString(date.getTime()); - builder.append("
"); - builder.append(dateString); - builder.append("
"); - } - - builder.append("
"); - - builder.append("
"); - } - - 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("
"); - builder.append(description); - builder.append("
"); - - builder.append(""); - - String htmlData = builder.toString().replaceAll("\"//", "\"https://"); - - return htmlData; - } - - - private static Pattern PATTERN_PRELOAD_VIDEOS = Pattern.compile("(]*)(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; + /** + * @return true when the last page on the webview's history stack is + * the original rss item page + */ + private boolean isLastPageRssItem() { + WebBackForwardList list = mWebView.copyBackForwardList(); + WebHistoryItem lastItem = list.getItemAtIndex(list.getCurrentIndex() - 1); + return lastItem != null && lastItem.getUrl().equals(RSS_ITEM_PAGE_URL); } - private static String getDescriptionWithCachedImages(String text) - { - List 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; - } - - - - - - - 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 ""; + /** + * @return true when the current page on the webview's history stack is + * the original rss item page + */ + private boolean isCurrentPageRssItem() { + String currentPageUrl = mWebView.copyBackForwardList().getCurrentItem().getOriginalUrl(); + return currentPageUrl.equals("data:text/html;charset=utf-8;base64,"); + //return currentPageUrl.equals(RSS_ITEM_PAGE_URL); } - - 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; - } } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java index b36e11a5..ff01ff5a 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java @@ -195,9 +195,9 @@ public class NewsDetailImageDialogFragment extends DialogFragment { View v = inflater.inflate(R.layout.fragment_dialog_image, container, false); - TextView tvTitle = (TextView) v.findViewById(R.id.ic_menu_title); - TextView tvText = (TextView) v.findViewById(R.id.ic_menu_item_text); - ImageView imgTitle = (ImageView) v.findViewById(R.id.ic_menu_gallery); + TextView tvTitle = v.findViewById(R.id.ic_menu_title); + TextView tvText = v.findViewById(R.id.ic_menu_item_text); + ImageView imgTitle = v.findViewById(R.id.ic_menu_gallery); tvTitle.setText(mDialogTitle); tvText.setText(mDialogText); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ProgressBarWebChromeClient.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ProgressBarWebChromeClient.java new file mode 100644 index 00000000..c0423600 --- /dev/null +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ProgressBarWebChromeClient.java @@ -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); + } + } +} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ViewHolder.java index ed0b95e2..24352b2a 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/ViewHolder.java @@ -26,9 +26,9 @@ import org.greenrobot.eventbus.Subscribe; import butterknife.BindView; import butterknife.ButterKnife; -import de.luhmer.owncloudnewsreader.NewsDetailFragment; import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.SettingsActivity; +import de.luhmer.owncloudnewsreader.async_tasks.RssItemToHtmlTask; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.helper.ColorHelper; import de.luhmer.owncloudnewsreader.helper.FavIconHandler; @@ -243,7 +243,7 @@ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickL favIconHandler.loadFavIconForFeed(favIconUrl, imgViewFavIcon); 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", ""); } } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java new file mode 100644 index 00000000..b644bbb6 --- /dev/null +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java @@ -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 { + + 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(""); + builder.append(""); + builder.append(""); + builder.append(String.format("", body_id, rtlClass)); + + if (showHeader) { + builder.append("
"); + builder.append("
"); + String title = StringEscapeUtils.escapeHtml4(rssItem.getTitle()); + String linkToFeed = StringEscapeUtils.escapeHtml4(rssItem.getLink()); + builder.append(String.format("%s", linkToFeed, title)); + builder.append("
"); + + String authorOfArticle = StringEscapeUtils.escapeHtml4(rssItem.getAuthor()); + if (authorOfArticle != null) + if (!authorOfArticle.trim().equals("")) + feedTitle += " - " + authorOfArticle.trim(); + + builder.append("
"); + + builder.append("
"); + builder.append(String.format("", favIconUrl)); + builder.append(feedTitle.trim()); + builder.append("
"); + + Date date = rssItem.getPubDate(); + if (date != null) { + String dateString = (String) DateUtils.getRelativeTimeSpanString(date.getTime()); + builder.append("
"); + builder.append(dateString); + builder.append("
"); + } + + builder.append("
"); + + builder.append("
"); + } + + String description = rssItem.getBody(); + + description = getDescriptionWithCachedImages(description).trim(); + description = removePreloadAttributeFromVideos(description); + + builder.append("
"); + builder.append(description); + builder.append("
"); + + builder.append(""); + + return builder.toString().replaceAll("\"//", "\"https://"); + } + + private static String getDescriptionWithCachedImages(String text) { + List 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("(]*)(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; + } +} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FileUtils.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FileUtils.java index c0a7aa2f..78b6fc27 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FileUtils.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FileUtils.java @@ -22,6 +22,7 @@ package de.luhmer.owncloudnewsreader.helper; import android.content.Context; +import android.os.Environment; import com.nostra13.universalimageloader.utils.StorageUtils; @@ -87,4 +88,9 @@ public class FileUtils { { return getPath(context) + "/podcasts"; } + + public static boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + return Environment.MEDIA_MOUNTED.equals(state); + } }