/**
* technoXist
*
* Copyright (c) 2014-2015 Suyash Bhatt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.technoxist.view;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.technoxist.Constants;
import com.technoxist.R;
import com.technoxist.utils.FileUtils;
import com.technoxist.utils.HtmlUtils;
import com.technoxist.utils.PrefUtils;
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class EntryView extends WebView {
private static final String TEXT_HTML = "text/html";
private static final String HTML_IMG_REGEX = "(?i)<[/]?[ ]?img(.|\n)*?>";
private static final String BACKGROUND_COLOR = "#f6f6f6";
private static final String TEXT_COLOR = "#000000";
private static final String BUTTON_COLOR = "#2b8bf0";
private static final String SUBTITLE_COLOR = "#666666";
private static final String SUBTBORDER_COLOR = "solid #ddd";
private static final String CSS = "<head><style type='text/css'> "
+ "body {max-width: 100%; margin: 1.2em 0.3cm 0.3cm 0.2cm; font-family: sans-serif-light; color: " + TEXT_COLOR + "; background-color:" + BACKGROUND_COLOR + "; line-height: 140%; font-weight:800} "
+ "* {max-width: 100%; word-break: break-word}"
+ "h1, h2 {font-weight: normal; line-height: 130%} "
+ "h1 {font-size: 170%; margin-bottom: 0.1em} "
+ "h2 {font-size: 140%} "
+ "a {color: #0099CC}"
+ "h1 a {color: inherit; text-decoration: none}"
+ "img {height: auto} "
+ "pre {white-space: pre-wrap;} "
+ "blockquote {margin: 0.8em 0 0.8em 1.2em; padding: 0} "
+ "p {margin: 0.8em 0 0.8em 0; text-align: justify} "
+ "input[type=button], .buttons {font-weight:800} "
+ ".buttons {margin: 0.8em 0 0.8em 0; text-align: center} "
+ "p.subtitle {color: " + SUBTITLE_COLOR + "; border-top:1px " + SUBTBORDER_COLOR + "; border-bottom:1px " + SUBTBORDER_COLOR + "; padding-top:2px; padding-bottom:2px; font-weight:800 } "
+ "ul, ol {margin: 0 0 0.8em 0.6em; padding: 0 0 0 1em} "
+ "ul li, ol li {margin: 0 0 0.8em 0; padding: 0} "
+ "div.button-section {padding: 0.4cm 0; margin: 0; text-align: center} "
+ ".button-section p {margin: 0.1cm 0 0.2cm 0}"
+ ".button-section p.marginfix {margin: 0.5cm 0 0.5cm 0}"
+ ".button-section input, .button-section a {font-family: sans-serif-light; font-size: 100%; color: #FFFFFF; background-color: " + BUTTON_COLOR + "; text-decoration: none; border: none; border-radius:0.2cm; padding: 0.3cm} "
+ "</style><meta name='viewport' content='width=device-width'/></head>";
private static final String BODY_START = "<body>";
private static final String BODY_END = "</body>";
private static final String TITLE_START = "<h1><a href='";
private static final String TITLE_MIDDLE = "'>";
private static final String TITLE_END = "</a></h1>";
private static final String SUBTITLE_START = "<p class='subtitle'>";
private static final String SUBTITLE_END = "</p>";
private static final String BUTTON_SECTION_START = "<div class='button-section'>";
private static final String BUTTON_SECTION_END = "</div>";
private static final String BUTTON_START = "<p class='buttons'><input type='button' value='";
private static final String BUTTON_MIDDLE = "' onclick='";
private static final String BUTTON_END = "'/></p>";
// the separate 'marginfix' selector in the following is only needed because the CSS box model treats <input> and <a> elements differently
private static final String LINK_BUTTON_START = "<p class='marginfix buttons'><a href='";
private static final String LINK_BUTTON_MIDDLE = "'>";
private static final String LINK_BUTTON_END = "</a></p>";
private static final String IMAGE_ENCLOSURE = "[@]image/";
private final JavaScriptObject mInjectedJSObject = new JavaScriptObject();
private EntryViewManager mEntryViewMgr;
//For Fullscreen Video view
public View mCustomView;
private FrameLayout videoLayout;
public EntryView(Context context) {
super(context);
init(context, null, 0);
}
public EntryView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public EntryView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
public void setListener(EntryViewManager manager) {
mEntryViewMgr = manager;
}
public void setHtml(long entryId, String title, String link, String contentText, String enclosure, String author, long timestamp, boolean preferFullText) {
if (PrefUtils.getBoolean(PrefUtils.DISPLAY_IMAGES, true)) {
contentText = HtmlUtils.replaceImageURLs(contentText, entryId);
if (getSettings().getBlockNetworkImage()) {
// setBlockNetwortImage(false) calls postSync, which takes time, so we clean up the html first and change the value afterwards
loadData("", TEXT_HTML, Constants.UTF8);
getSettings().setBlockNetworkImage(false);
}
} else {
contentText = contentText.replaceAll(HTML_IMG_REGEX, "");
getSettings().setBlockNetworkImage(true);
}
// Iframe videos to get fullscreen
contentText = contentText.replace("frameborder=\"0\"", "frameborder=\"0\" allowfullscreen");
contentText = contentText.replace("?rel=0&autoplay=0&wmode=opaque&controls=2&autohide=1&showinfo=0", "");
// String baseUrl = "";
// try {
// URL url = new URL(mLink);
// baseUrl = url.getProtocol() + "://" + url.getHost();
// } catch (MalformedURLException ignored) {
// }
// do not put 'null' to the base url...
loadDataWithBaseURL("", generateHtmlContent(title, link, contentText, enclosure, author, timestamp), TEXT_HTML, Constants.UTF8, null);
}
private String generateHtmlContent(String title, String link, String contentText, String enclosure, String author, long timestamp) {
StringBuilder content = new StringBuilder(CSS).append(BODY_START);
if (link == null) {
link = "";
}
content.append(TITLE_START).append(link).append(TITLE_MIDDLE).append(title).append(TITLE_END).append(SUBTITLE_START);
Date date = new Date(timestamp);
Context context = getContext();
StringBuilder dateStringBuilder = new StringBuilder(DateFormat.getLongDateFormat(context).format(date)).append(' ').append(
DateFormat.getTimeFormat(context).format(date));
if (author != null && !author.isEmpty()) {
dateStringBuilder.append(" | ").append(author);
}
content.append(dateStringBuilder).append(SUBTITLE_END).append(contentText).append(BUTTON_SECTION_START);
if (enclosure != null && enclosure.length() > 6 && !enclosure.contains(IMAGE_ENCLOSURE)) {
content.append(BUTTON_START).append(context.getString(R.string.see_enclosure)).append(BUTTON_MIDDLE)
.append("injectedJSObject.onClickEnclosure();").append(BUTTON_END);
}
if (link.length() > 0) {
content.append(LINK_BUTTON_START).append(link).append(LINK_BUTTON_MIDDLE).append(context.getString(R.string.see_link)).append(LINK_BUTTON_END);
}
content.append(BUTTON_SECTION_END).append(BODY_END);
return content.toString();
}
@SuppressLint("SetJavaScriptEnabled")
private void init(Context context, AttributeSet attrs, int defStyle) {
// For scrolling
setHorizontalScrollBarEnabled(false);
getSettings().setUseWideViewPort(false);
// For color
setBackgroundColor(Color.parseColor(BACKGROUND_COLOR));
// Text zoom level from preferences
int fontSize = Integer.parseInt(PrefUtils.getString(PrefUtils.FONT_SIZE, "0"));
if (fontSize != 0) {
getSettings().setTextZoom(100 + (fontSize * 20));
}
// For javascript
getSettings().setJavaScriptEnabled(true);
addJavascriptInterface(mInjectedJSObject, mInjectedJSObject.toString());
// For HTML5 Video
setWebChromeClient(new VideoWebView());
setWebViewClient(new MyWebViewClient());
}
// For HTML5 video
public class VideoWebView extends WebChromeClient {
private CustomViewCallback mCustomViewCallback;
@Override
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
// if a view already exists then immediately terminate the new one
if (mCustomView != null) {
callback.onCustomViewHidden();
return;
}
videoLayout = mEntryViewMgr.getVideoLayout();
videoLayout.addView(view);
mCustomView = view;
mCustomViewCallback = callback;
videoLayout.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
mEntryViewMgr.onStartVideoFullScreen();
}
@Override
public void onHideCustomView() {
super.onHideCustomView();
if (mCustomView == null) {
return;
}
videoLayout = mEntryViewMgr.getVideoLayout();
setVisibility(View.VISIBLE);
// Hide the custom view.
mCustomView.setVisibility(View.GONE);
// Remove the custom view from its container.
videoLayout.removeView(mCustomView);
mCustomView = null;
videoLayout.setVisibility(View.GONE);
mCustomViewCallback.onCustomViewHidden();
mEntryViewMgr.onEndVideoFullScreen();
}
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Context context = getContext();
try {
if (url.startsWith(Constants.FILE_SCHEME)) {
File file = new File(url.replace(Constants.FILE_SCHEME, ""));
File extTmpFile = new File(context.getExternalCacheDir(), "tmp_img.jpg");
FileUtils.copy(file, extTmpFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(extTmpFile), "image/jpeg");
context.startActivity(intent);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, R.string.cant_open_link, Toast.LENGTH_SHORT).show();
}
catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
public interface EntryViewManager {
public void onClickEnclosure();
public void onStartVideoFullScreen();
public void onEndVideoFullScreen();
public FrameLayout getVideoLayout();
}
private class JavaScriptObject {
@Override
@JavascriptInterface
public String toString() {
return "injectedJSObject";
}
@JavascriptInterface
public void onClickEnclosure() {
mEntryViewMgr.onClickEnclosure();
}
}
}