moose.com.ac.ArticleViewActivity.java Source code

Java tutorial

Introduction

Here is the source code for moose.com.ac.ArticleViewActivity.java

Source

package moose.com.ac;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.customtabs.CustomTabsIntent;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.trello.rxlifecycle.ActivityEvent;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;

import moose.com.ac.common.Config;
import moose.com.ac.data.ArticleCollects;
import moose.com.ac.data.DbHelper;
import moose.com.ac.retrofit.Api;
import moose.com.ac.retrofit.article.Article;
import moose.com.ac.retrofit.article.ArticleBodyWrapper;
import moose.com.ac.ui.BaseActivity;
import moose.com.ac.ui.widget.MultiSwipeRefreshLayout;
import moose.com.ac.ui.widget.ObservableWebView;
import moose.com.ac.util.CommonUtil;
import moose.com.ac.util.DisplayUtil;
import moose.com.ac.util.RxUtils;
import moose.com.ac.util.ScrollFABBehavior;
import moose.com.ac.util.SettingPreferences;
import moose.com.ac.util.chrome.CustomTabActivityHelper;
import moose.com.ac.util.chrome.WebviewFallback;
import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
/*
 * Copyright 2015,2016 Farble Dast
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Created by Farble on 2015/8/16 20.
 * View article body
 */
public class ArticleViewActivity extends BaseActivity implements ObservableWebView.OnScrollChangedCallback {
    private static final String TAG = "ArticleViewActivity";
    private static final int FAB_SHOW = 0x0000aa;
    private static final int FAB_HIDE = 0x0000bb;
    private final String TAB_NAME = ArticleCollects.ArticleEntry.TABLE_NAME;
    private ObservableWebView mWeb;
    private FloatingActionButton fab;
    private MenuItem menFav;
    private MultiSwipeRefreshLayout mSwipeRefreshLayout;
    private CoordinatorLayout mContentView;
    private CompositeSubscription subscription = new CompositeSubscription();
    private Api api = RxUtils.createApi(Api.class, Config.ARTICLE_URL);
    private DbHelper dbHelper = AppApplication.getDbHelper();

    private String HtmlBody;//get body from network
    private String HtmlBodyClone;//get body from network
    private WebSettings settings;
    private int articleId;//article id
    private int fabStatus = FAB_SHOW;

    private String title = "";//default
    private String user = "";//default
    private String contend;
    private int toolbarHeight;
    private int level = 1;
    private boolean isFav = false;
    private boolean isWebViewLoading = false;
    private Article article;
    private boolean isRequest = false;
    private HashMap<String, String> htmlImages = new HashMap<>();

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ SMALLEST, SMALLER, NORMAL, LARGER, LARGEST })
    @interface TextSize {
    }

    private static final int SMALLEST = 0;
    private static final int SMALLER = 1;
    private static final int NORMAL = 2;
    private static final int LARGER = 3;
    private static final int LARGEST = 4;

    @Override
    protected void onInitView(Bundle savedInstanceState) {
        setContentView(R.layout.activity_article_view);
        article = (Article) getIntent().getSerializableExtra(Config.ARTICLE);
        /*
        * Uri data = getIntent().getData();
        if(Intent.ACTION_VIEW.equalsIgnoreCase(getIntent().getAction()) && data!=null){
        String scheme = data.getScheme();
        if(scheme.equals("ac")){
            // ac://ac000000
            aid = Integer.parseInt(getIntent().getDataString().substring(7));
        }else if(scheme.equals("http")){
            // http://www.acfun.tv/v/ac123456
            Matcher matcher;
            String path = data.getPath();
            if(path==null){
                finish();
                return;
            }
            if((matcher = sVreg.matcher(path)).find()
                    || (matcher = sAreg.matcher(path)).find()){
                aid = Integer.parseInt(matcher.group(1));
            }
        }
        if(aid != 0) title = "ac"+aid;
        isWebMode = getIntent().getBooleanExtra("webmode", false) && aid == 0;
        }else{
        aid = getIntent().getIntExtra("aid", 0);
        title = getIntent().getStringExtra("title");
        }*/
        isFav = dbHelper.isExits(TAB_NAME, article.isfav);
        articleId = Integer.valueOf(article.contentId);
        toolbarHeight = DisplayUtil.dip2px(this, 56f);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final ActionBar ab = getSupportActionBar();
        if (ab != null) {
            ab.setDisplayHomeAsUpEnabled(true);
        }
        contend = "ac" + articleId;
        getSupportActionBar().setTitle(contend);

        fab = (FloatingActionButton) findViewById(R.id.view_fab);
        fab.setOnClickListener(v -> {
            Intent intent = new Intent(this, BigNewsActivity.class);
            intent.putExtra(Config.CONTENTID, String.valueOf(articleId));
            intent.putExtra(Config.TITLE, title);
            startActivity(intent);
        });

        mSwipeRefreshLayout = (MultiSwipeRefreshLayout) findViewById(R.id.web_swipe);
        mSwipeRefreshLayout.setSwipeableChildren(R.id.view_fab);
        mSwipeRefreshLayout.setColorSchemeResources(R.color.md_white);
        mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.colorPrimary);

        mWeb = (ObservableWebView) findViewById(R.id.view_webview);
        mContentView = (CoordinatorLayout) findViewById(R.id.view_content);
        settings = mWeb.getSettings();
        settings.setJavaScriptEnabled(true);
        //settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        settings.setUserAgentString(RxUtils.UA);
        settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        settings.setDefaultTextEncodingName(Config.TEXT_ENCODING);
        settings.setSupportZoom(true);
        settings.setBuiltInZoomControls(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mWeb.getSettings().setDisplayZoomControls(false);
        }
        mWeb.setWebViewClient(new Client());
        mWeb.addJavascriptInterface(new JsBridge(), "JsBridge");
        if (Build.VERSION.SDK_INT >= 11)
            mWeb.setBackgroundColor(Color.argb(1, 0, 0, 0));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
        }

        mWeb.setOnScrollChangedCallback(this);
        level = CommonUtil.getTextSize();
        setText();
        mSwipeRefreshLayout.post(() -> mSwipeRefreshLayout.setRefreshing(true));
        initData();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_view_article, menu);
        menFav = menu.findItem(R.id.action_store);
        menFav.setTitle(isFav ? getString(R.string.store_it) : getString(R.string.cancel_store));
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            doExitActivity();
            return true;
        case R.id.action_share:
            String shareUrl = article.title + " " + Config.WEB_URL + articleId + getString(R.string.share_content);
            Intent shareIntent = new Intent();
            shareIntent.setAction(Intent.ACTION_SEND);
            shareIntent.putExtra(Intent.EXTRA_TEXT, shareUrl);
            shareIntent.setType("text/plain");
            startActivity(Intent.createChooser(shareIntent, getString(R.string.share_to)));
            return true;
        case R.id.action_module_wap:
            if (!SettingPreferences.externalBrowserEnabled(mContext)) {
                Intent intent = new Intent();
                intent.setAction("android.intent.action.VIEW");
                Uri content_url = Uri.parse(Config.WEB_URL + articleId + "/");
                intent.setData(content_url);
                startActivity(intent);
            } else {
                String url = Config.WAP_URL + "v#ac=" + articleId + ";type=article";
                CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build();
                CustomTabActivityHelper.openCustomTab(this, customTabsIntent, Uri.parse(url),
                        new WebviewFallback());
            }
            return true;
        case R.id.action_front_view:
            createTextSizeDialog().show();
            return true;
        case R.id.action_module_view:
            createModeDialog().show();
            return true;
        case R.id.action_store:
            if (isFav) {//cancel store
                boolean deleteSuc = AppApplication.getDbHelper()
                        .deleteArticle(ArticleCollects.ArticleEntry.TABLE_NAME, String.valueOf(articleId));
                menFav.setTitle(deleteSuc ? getString(R.string.cancel_store) : getString(R.string.store_it));
                isFav = !deleteSuc;
                snackStore(deleteSuc ? getString(R.string.cancel_success) : getString(R.string.cancel_fal));
            } else {//store it
                article.isfav = Config.STORE;//set not fav
                article.savedate = String.valueOf(System.currentTimeMillis());//set save date
                boolean insertSuc = dbHelper.insertArticle(article, TAB_NAME, article.channelId);
                menFav.setTitle(insertSuc ? getString(R.string.store_it) : getString(R.string.cancel_store));
                isFav = insertSuc;
                snackStore(isFav ? getString(R.string.store_success) : getString(R.string.store_fal));
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void initData() {
        isRequest = true;
        HtmlBody = "";
        HtmlBodyClone = "";
        subscription.add(api.getNewUrlArticleBody(articleId).observeOn(AndroidSchedulers.mainThread()).retry(1)
                .compose(bindUntilEvent(ActivityEvent.DESTROY)).subscribe(new Observer<ArticleBodyWrapper>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        isRequest = true;
                        e.printStackTrace();
                        mSwipeRefreshLayout.setRefreshing(false);//show progressbar
                        mSwipeRefreshLayout.setEnabled(true);
                        snack(e.getMessage());
                    }

                    @Override
                    public void onNext(ArticleBodyWrapper articleBody) {
                        isRequest = true;
                        if (TextUtils.isEmpty(articleBody.message)
                                || !TextUtils.equals(articleBody.message.toUpperCase(), "OK")) {
                            snack(articleBody.message);
                        } else {
                            HtmlBody = articleBody.data.article.content;
                            title = articleBody.data.title;
                            user = articleBody.data.owner.name;

                            //fix this issues https://github.com/Harlber/Moose/issues/8
                            rx.Observable.create(subscriber -> {
                                dealBody(HtmlBody);
                                addHead();
                                filterImg(HtmlBody);
                                subscriber.onNext(HtmlBody);
                            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                                    .subscribe(s -> {
                                        mWeb.loadDataWithBaseURL("", HtmlBody, "text/html", "UTF-8", "");
                                    });
                        }
                    }

                }));
    }

    private void addHead() {
        StringBuilder head = new StringBuilder();
        head.append(getResources().getString(R.string.article_head));
        head.append(title);
        head.append("</h2>\n");
        head.append("<p class=\"name\"><span>");
        //noinspection StringConcatenationInsideStringBufferAppend
        head.append(getString(R.string.up_author) + user);

        head.append("</span></p>\n");
        head.append("</div>\n");
        head.append("<div>");

        StringBuilder body = new StringBuilder();
        body.append("</div>");
        body.append("</body>");
        body.append("</html>");
        String index = HtmlBody;
        HtmlBody = head.toString() + index + body.toString();
        HtmlBodyClone = head.toString() + index + body.toString();
        Log.i(TAG, "html:" + HtmlBodyClone);
    }

    /**
     * <li>deal image <img src=\"http://n.sinaimg.cn/transform/20150817/seQF-fxfxzzn7510940.jpg\" /></li>
     * into <li><img src="http://n.sinaimg.cn/transform/20150817/seQF-fxfxzzn7510940.jpg" /></li>
     * <li>\n</li>
     * <li>\r</li>
     * <li>other </li>
     */
    private void dealBody(String html) {
        //noinspection ResultOfMethodCallIgnored
        html.replaceAll(Config.IMAGE_REG, "");
    }

    private void filterImg(String str) {
        Document mDocument = Jsoup.parse(str);

        Elements imgs = mDocument.select("img");
        for (int imgIndex = 0; imgIndex < imgs.size(); imgIndex++) {
            Element img = imgs.get(imgIndex);
            String src = img.attr("src").trim();
            if (TextUtils.isEmpty(src))
                continue;
            Uri parsedUri = Uri.parse(src);
            if ("file".equals(parsedUri.getScheme()))
                continue;
            if (parsedUri.getPath() == null)
                continue;
            if (!"http".equals(parsedUri.getScheme())) {
                parsedUri = parsedUri.buildUpon().scheme("http").authority("www.acfun.tv").build();
            }
            // url may have encoded path
            parsedUri = parsedUri.buildUpon().path(parsedUri.getPath()).build();
            src = parsedUri.toString();
            Log.i(TAG, "image src:" + src);
            img.attr("org", src);
            if (CommonUtil.getMode() == 1 && !CommonUtil.isWifiConnected(mContext)) {//
                Log.i(TAG, "[?]");
                img.after("<div style=\"width: 100%;text-align: center;\"><br><p>[]</p></div>");
            } else {
                Log.i(TAG, "[?]");
                StringBuilder builder = new StringBuilder();
                builder.append("<div style='text-align: center;'><br>")
                        .append("<img src='file:///android_asset/loading.gif'").append("name = '").append(src)
                        .append("'\n;onclick = window.JsBridge.showImage('").append(src).append("')")
                        .append(" alt=' '/>\n").append("</div>");
                img.after(builder.toString());
                Log.i(TAG, "image:table:-" + builder.toString());
            }
            /*if (CommonUtil.getMode() == 1 && !CommonUtil.isWifiConnected(mContext)) {
            img.after("<p >[]</p>");
            } else if (!src.contains(Config.AC_EMOTION)) {
            StringBuilder builder = new StringBuilder();
            builder.append("<div style=\"width: 100%;text-align: center;\"><br><img src=\"")
                    .append(src)
                    .append("\" width=: 100%;height:auto\"")
                    .append(" alt=\" \"/>\n")
                    .append("</div>");
            Log.i(TAG, "index image:" + builder.toString());
            img.after(builder.toString());
            } else {
            img.after("<img src=\"" + src + "\" alt=\" \"/>\n");
            }*/

            img.remove();
            //img.removeAttr("style");
            HtmlBody = mDocument.toString();
            Log.i(TAG, "??html:" + HtmlBody);
        }
    }

    private void snack(String msg) {
        Snackbar snackBar = Snackbar.make(mContentView, msg, Snackbar.LENGTH_SHORT);
        snackBar.setAction(R.string.reload_action, v -> {
            initData();
        });
        snackBar.getView().setBackgroundResource(R.color.colorPrimary);
        snackBar.show();
    }

    private void snackStore(String msg) {
        Snackbar snackBar = Snackbar.make(mContentView, msg, Snackbar.LENGTH_SHORT);
        snackBar.setAction(R.string.snackbar_action, v -> {
        });
        snackBar.getView().setBackgroundResource(R.color.colorPrimary);
        snackBar.show();
    }

    @SuppressWarnings("RedundantCast")
    private Dialog createTextSizeDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(ArticleViewActivity.this);
        builder.setTitle(R.string.text_size)
                .setSingleChoiceItems(R.array.font_text_size, CommonUtil.getTextSize(), (dialog, which) -> {
                    //zoom text
                    level = settings.getTextZoom();
                    setZoom(which);
                }).setPositiveButton(R.string.ok, (dialog, id) -> {
                    CommonUtil.setTextSize(level);
                    setText();
                }).setNegativeButton(R.string.cancel, (DialogInterface.OnClickListener) (dialog, id) -> {
                });

        return builder.create();
    }

    @SuppressWarnings("RedundantCast")
    private Dialog createModeDialog() {
        int oldMode = CommonUtil.getMode();
        AlertDialog.Builder builder = new AlertDialog.Builder(ArticleViewActivity.this);
        builder.setTitle(R.string.text_mode)
                .setSingleChoiceItems(R.array.mode, CommonUtil.getMode(), (dialog, which) -> {
                    CommonUtil.setMode(which);
                }).setPositiveButton(R.string.ok, (dialog, id) -> {
                    if (isWebViewLoading)//cancel loading
                        mWeb.stopLoading();
                    if (oldMode != CommonUtil.getMode()) {
                        mWeb.stopLoading();//maybe load image then ANR comes
                        if (isRequest) {
                            RxUtils.unsubscribeIfNotNull(subscription);
                            subscription = RxUtils.getNewCompositeSubIfUnsubscribed(subscription);
                        }
                        snack(getString(R.string.progress));
                        initData();
                    }
                }).setNegativeButton(R.string.cancel,
                        (DialogInterface.OnClickListener) (dialog, id) -> CommonUtil.setMode(oldMode));

        return builder.create();
    }

    private void setZoom(int lev) {
        level = lev;
    }

    @SuppressWarnings("deprecation")
    private void setText() {
        switch (level) {
        case SMALLEST:
            mWeb.getSettings().setTextSize(WebSettings.TextSize.SMALLEST);
            break;
        case SMALLER:
            mWeb.getSettings().setTextSize(WebSettings.TextSize.SMALLER);
            break;
        case NORMAL:
            mWeb.getSettings().setTextSize(WebSettings.TextSize.NORMAL);
            break;
        case LARGER:
            mWeb.getSettings().setTextSize(WebSettings.TextSize.LARGER);
            break;
        default:
            mWeb.getSettings().setTextSize(WebSettings.TextSize.LARGEST);
            break;
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            if (level > 4) {
                level = 3;
                CommonUtil.setTextSize(level + 1);//save text size
            }
            mWeb.getSettings().setTextSize(WebSettings.TextSize.values()[level + 1]);
        } else
            mWeb.getSettings().setTextZoom(100 + level * 25);
    }

    @SuppressWarnings("ConstantConditions")
    @Override
    public void onScroll(int l, int t, int oldl, int oldt) {
        if (t > toolbarHeight) {
            getSupportActionBar().setTitle(title);
        } else {
            getSupportActionBar().setTitle(contend);
        }
        if (t - oldt > 0) {
            if (fabStatus == FAB_SHOW) {
                //hide fab
                ScrollFABBehavior.animateOut(fab);
                fabStatus = FAB_HIDE;
            }
        } else {
            if (fabStatus == FAB_HIDE) {
                //show fab
                ScrollFABBehavior.animateIn(fab);
                fabStatus = FAB_SHOW;
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            doExitActivity();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    private void doExitActivity() {
        Intent intent = new Intent(getString(R.string.store_action));
        intent.putExtra(Config.CONTENTID, String.valueOf(articleId));
        intent.putExtra(Config.STORE, isFav);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
        finish();
    }

    private class Client extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            isWebViewLoading = true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            isWebViewLoading = false;
            mSwipeRefreshLayout.setRefreshing(false);
            mSwipeRefreshLayout.setEnabled(false);
        }
    }

    private class JsBridge {

        public JsBridge() {
        }

        @JavascriptInterface
        public boolean isViewMode() {
            return !(CommonUtil.getMode() == 1 && !CommonUtil.isWifiConnected(mContext));
        }

        /**
         * @param id image id
         */
        @JavascriptInterface
        public void showImage(String id) {
            snack(id);
        }

        public String getImageUrlById(String id) {
            return htmlImages.get(id);
        }
    }
}