/*******************************************************************************
* This file is part of RedReader.
*
* RedReader 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.
*
* RedReader 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 RedReader. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.quantumbadger.redreader.fragments;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.DownloadListener;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.quantumbadger.redreader.R;
import org.quantumbadger.redreader.cache.CacheManager;
import org.quantumbadger.redreader.common.AndroidApi;
import org.quantumbadger.redreader.common.General;
import org.quantumbadger.redreader.common.LinkHandler;
import org.quantumbadger.redreader.reddit.prepared.RedditParsedPost;
import org.quantumbadger.redreader.reddit.prepared.RedditPreparedPost;
import org.quantumbadger.redreader.reddit.things.RedditPost;
import org.quantumbadger.redreader.reddit.url.RedditURLParser;
import org.quantumbadger.redreader.views.RedditPostView;
import org.quantumbadger.redreader.views.WebViewFixed;
import org.quantumbadger.redreader.views.bezelmenu.BezelSwipeOverlay;
import org.quantumbadger.redreader.views.bezelmenu.SideToolbarOverlay;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
public class WebViewFragment extends Fragment implements RedditPostView.PostSelectionListener {
private AppCompatActivity mActivity;
private String mUrl, html;
private volatile String currentUrl;
private volatile boolean goingBack;
private volatile int lastBackDepthAttempt;
private WebViewFixed webView;
private ProgressBar progressView;
private FrameLayout outer;
public static WebViewFragment newInstance(final String url, final RedditPost post) {
final WebViewFragment f = new WebViewFragment();
final Bundle bundle = new Bundle(1);
bundle.putString("url", url);
if (post != null) bundle.putParcelable("post", post);
f.setArguments(bundle);
return f;
}
public static WebViewFragment newInstanceHtml(final String html) {
final WebViewFragment f = new WebViewFragment();
final Bundle bundle = new Bundle(1);
bundle.putString("html", html);
f.setArguments(bundle);
return f;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
// TODO load position/etc?
super.onCreate(savedInstanceState);
mUrl = getArguments().getString("url");
html = getArguments().getString("html");
}
@SuppressLint("NewApi")
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
mActivity = (AppCompatActivity) getActivity();
CookieSyncManager.createInstance(mActivity);
outer = (FrameLayout) inflater.inflate(R.layout.web_view_fragment, null);
final RedditPost src_post = getArguments().getParcelable("post");
final RedditPreparedPost post;
if (src_post != null) {
final RedditParsedPost parsedPost = new RedditParsedPost(src_post, false);
post = new RedditPreparedPost(
mActivity,
CacheManager.getInstance(mActivity),
0,
parsedPost,
-1,
false,
false);
} else {
post = null;
}
webView = (WebViewFixed) outer.findViewById(R.id.web_view_fragment_webviewfixed);
final FrameLayout loadingViewFrame = (FrameLayout) outer.findViewById(R.id.web_view_fragment_loadingview_frame);
/*handle download links show an alert box to load this outside the internal browser*/
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(final String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
{
new AlertDialog.Builder(mActivity)
.setTitle(R.string.download_link_title)
.setMessage(R.string.download_link_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
getContext().startActivity(i);
mActivity.onBackPressed(); //get back from internal browser
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mActivity.onBackPressed(); //get back from internal browser
}
})
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}
}
});
/*handle download links end*/
progressView = new ProgressBar(mActivity, null, android.R.attr.progressBarStyleHorizontal);
loadingViewFrame.addView(progressView);
loadingViewFrame.setPadding(General.dpToPixels(mActivity, 10), 0, General.dpToPixels(mActivity, 10), 0);
final WebSettings settings = webView.getSettings();
settings.setBuiltInZoomControls(true);
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(false);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setDomStorageEnabled(true);
if (AndroidApi.isHoneyCombOrLater()) {
settings.setDisplayZoomControls(false);
}
// TODO handle long clicks
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, final int newProgress) {
super.onProgressChanged(view, newProgress);
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
progressView.setProgress(newProgress);
progressView.setVisibility(newProgress == 100 ? View.GONE : View.VISIBLE);
}
});
}
});
if (mUrl != null) {
webView.loadUrl(mUrl);
} else {
webView.loadDataWithBaseURL("https://reddit.com/", html, "text/html; charset=UTF-8", null, null);
}
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
if (url == null) return false;
if (url.startsWith("data:")) {
// Prevent imgur bug where we're directed to some random data URI
return true;
}
// Go back if loading same page to prevent redirect loops.
if (goingBack && currentUrl != null && url.equals(currentUrl)) {
General.quickToast(mActivity,
String.format(Locale.US, "Handling redirect loop (level %d)", -lastBackDepthAttempt), Toast.LENGTH_SHORT);
lastBackDepthAttempt--;
if (webView.canGoBackOrForward(lastBackDepthAttempt)) {
webView.goBackOrForward(lastBackDepthAttempt);
} else {
mActivity.finish();
}
} else {
if (RedditURLParser.parse(Uri.parse(url)) != null) {
LinkHandler.onLinkClicked(mActivity, url, false);
} else {
webView.loadUrl(url);
currentUrl = url;
}
}
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
if (mUrl != null && url != null) {
final AppCompatActivity activity = mActivity;
if (activity != null) {
activity.setTitle(url);
}
}
}
@Override
public void onPageFinished(final WebView view, final String url) {
super.onPageFinished(view, url);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
if (currentUrl == null || url == null) return;
if (!url.equals(view.getUrl())) return;
if (goingBack && url.equals(currentUrl)) {
General.quickToast(mActivity,
String.format(Locale.US, "Handling redirect loop (level %d)", -lastBackDepthAttempt));
lastBackDepthAttempt--;
if (webView.canGoBackOrForward(lastBackDepthAttempt)) {
webView.goBackOrForward(lastBackDepthAttempt);
} else {
mActivity.finish();
}
} else {
goingBack = false;
}
}
});
}
}, 1000);
}
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
}
});
final FrameLayout outerFrame = new FrameLayout(mActivity);
outerFrame.addView(outer);
if (post != null) {
final SideToolbarOverlay toolbarOverlay = new SideToolbarOverlay(mActivity);
final BezelSwipeOverlay bezelOverlay = new BezelSwipeOverlay(mActivity, new BezelSwipeOverlay.BezelSwipeListener() {
@Override
public boolean onSwipe(@BezelSwipeOverlay.SwipeEdge int edge) {
toolbarOverlay.setContents(post.generateToolbar(mActivity, false, toolbarOverlay));
toolbarOverlay.show(edge == BezelSwipeOverlay.LEFT ?
SideToolbarOverlay.SideToolbarPosition.LEFT : SideToolbarOverlay.SideToolbarPosition.RIGHT);
return true;
}
@Override
public boolean onTap() {
if (toolbarOverlay.isShown()) {
toolbarOverlay.hide();
return true;
}
return false;
}
});
outerFrame.addView(bezelOverlay);
outerFrame.addView(toolbarOverlay);
bezelOverlay.getLayoutParams().width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
bezelOverlay.getLayoutParams().height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
toolbarOverlay.getLayoutParams().width = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
toolbarOverlay.getLayoutParams().height = android.widget.FrameLayout.LayoutParams.MATCH_PARENT;
}
return outerFrame;
}
@Override
public void onDestroyView() {
webView.stopLoading();
webView.loadData("<html></html>", "text/plain", "UTF-8");
webView.reload();
webView.loadUrl("about:blank");
outer.removeAllViews();
webView.destroy();
final CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
super.onDestroyView();
}
public boolean onBackButtonPressed() {
if (webView.canGoBack()) {
goingBack = true;
lastBackDepthAttempt = -1;
webView.goBack();
return true;
}
return false;
}
public void onPostSelected(final RedditPreparedPost post) {
((RedditPostView.PostSelectionListener) mActivity).onPostSelected(post);
}
public void onPostCommentsSelected(final RedditPreparedPost post) {
((RedditPostView.PostSelectionListener) mActivity).onPostCommentsSelected(post);
}
public String getCurrentUrl() {
return (currentUrl != null) ? currentUrl : mUrl;
}
@Override
@SuppressLint("NewApi")
public void onPause() {
super.onPause();
if (AndroidApi.isHoneyCombOrLater()) {
webView.onPause();
}
webView.pauseTimers();
}
@Override
@SuppressLint("NewApi")
public void onResume() {
super.onResume();
webView.resumeTimers();
if (AndroidApi.isHoneyCombOrLater()) {
webView.onResume();
}
}
public void clearCache() {
webView.clearBrowser();
}
}