org.quantumbadger.redreader.fragments.WebViewFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.quantumbadger.redreader.fragments.WebViewFragment.java

Source

/*******************************************************************************
 * 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();
    }
}