public abstract class AbstractNews {
    public abstract String parseHtml(final String link, String content) throws IOException;

    public Boolean isEmptyContent() {
        return false;
    }

    protected String cleaner(String rs) {
        Whitelist wlist = null;
        try {
            wlist = Whitelist.basicWithImages();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return Jsoup.clean(rs, wlist);

    }

} public class NewsViewPresenter extends BasePresenter<NewsViewView> {

    private static final String TAG = NewsViewPresenter.class.getSimpleName();
    private NewsViewView view;
    private Context context;
    private CompositeSubscription subscriptions;

    public NewsViewPresenter(NewsViewView view, Context context) {
        this.view = view;
        this.context = context;
        this.subscriptions = new CompositeSubscription();
    }

    public void getNews(String tabName, int sourceNumber, String url) {
        String charset = Category.getEncoding(tabName, sourceNumber);//"utf-8";
        String newsUrl = Network.checkNewsViewURL(url);

        if (BaseApplication.APPDEBUG) {
            Log.d(TAG, "charset = " + charset);
            Log.d(TAG, "url = " + newsUrl);
        }

        NewsHandler newsHandler = new NewsHandler(newsUrl);

        subscriptions.add(
                newsHandler.getNewsContent(charset)
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Subscriber<String>() {
                            @Override
                            public void onCompleted() {

                            }

                            @Override
                            public void onError(Throwable e) {
                                view.showError(R.string.data_error);
                                Log.e(TAG, "error = " + e.getMessage());
                            }

                            @Override
                            public void onNext(String html) {
                                view.drawHtmlPage(html);
                            }
                        })
        );

    }

    public void menuOpenBrowser(String newsUrl) {
        Uri uri = Uri.parse(newsUrl);
        context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
    }

    public void menuShare(String message) {
        //Todo Shorten URL
        // https://www.learn2crack.com/2014/01/android-using-goo-gl-url-shortener-api.html
        // http://jkoder.com/url-shortening-using-google-api/

        final Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("text/plain");

        intent.putExtra(Intent.EXTRA_SUBJECT, "");
        intent.putExtra(Intent.EXTRA_TEXT, message);
        context.startActivity(Intent.createChooser(intent, context.getString(R.string.sharing_description)));

    }

    @Override
    public void onDestroy() {
        if (subscriptions != null) {
            subscriptions.unsubscribe();
        }
    }
} public interface NewsViewView {

    void showError(int message);
    void drawHtmlPage(String html);

} public class Analytics {


    public static void trackerPage(Activity activity) {
        Tracker t = ((BaseApplication) activity.getApplication()).getTracker(
                BaseApplication.TrackerName.APP_TRACKER);
        t.setScreenName(activity.getClass().getSimpleName());
        t.send(new HitBuilders.AppViewBuilder().build());
    }

    public static void trackEvent(Activity activity
            , String category, String action, String label, long value) {
        Tracker t = ((BaseApplication) activity.getApplication()).getTracker(
                BaseApplication.TrackerName.APP_TRACKER);
        t.send(new HitBuilders.EventBuilder()
                .setCategory(category)
                .setAction(action)
                .setLabel(label)
                .setValue(value)
                .build());
    }


} public class Category {

    private Context context;

    public Category(Context context) {
        this.context = context;
    }

    public String[] getCategory(String tab, int position) {
        String[] category = null;

        if (tab.equals("TW")) {
            switch (position) {
                case 0:
                    category = context.getResources().getStringArray(R.array.newscatsCNA);
                    break;
                case 1:
                    category = context.getResources().getStringArray(R.array.newscatsYahoo);
                    break;
                case 2:
                    category = context.getResources().getStringArray(R.array.newscatsUDN);
                    break;
                case 3:
                    category = context.getResources().getStringArray(R.array.newscatsUDNFN);
                    break;
                case 4:
                    category = context.getResources().getStringArray(R.array.newscatsChinaTimes);
                    break;
                case 5:
                    category = context.getResources().getStringArray(R.array.newscatsCommercial);
                    break;
                case 6:
                    category = context.getResources().getStringArray(R.array.newscatsWant);
                    break;
                case 7:
                    category = context.getResources().getStringArray(R.array.newscatsStorm);
                    break;
                case 8:
                    category = context.getResources().getStringArray(R.array.newscatsEttoday);
                    break;
                case 9:
                    category = context.getResources().getStringArray(R.array.newscatsCNYes);
                    break;
                case 10:
                    category = context.getResources().getStringArray(R.array.newscatsNewsTalk);
                    break;
                case 11:
                    category = context.getResources().getStringArray(R.array.newscatsLibertyTimes);
                    break;
                case 12:
                    category = context.getResources().getStringArray(R.array.newscatsAppDaily);
                    break;
                case 13:
                    category = context.getResources().getStringArray(R.array.newscatsAppDailyRT);
                    break;
                case 14:
                    category = context.getResources().getStringArray(R.array.newsCatsTheNewsLens);
                    break;
            }
        } else if (tab.equals("HK")) {
            switch (position) {
                case 0:
                    category = context.getResources().getStringArray(R.array.newscatsHKAppleDaily);
                    break;
                case 1:
                    category = context.getResources().getStringArray(R.array.newscatsHKAppleDailyRT);
                    break;
                case 2:
                    category = context.getResources().getStringArray(R.array.newscatsHKOrientalDaily);
                    break;
                case 3:
                    //category = context.getResources().getStringArray(R.array.newscatsHKYahoo);
                    //break;
                //case 4:
                    category = context.getResources().getStringArray(R.array.newscatsHKEJ);
                    break;
                case 4:
                    category = context.getResources().getStringArray(R.array.newscatsHKMetro);
                    break;
                case 5:
                    category = context.getResources().getStringArray(R.array.newscatsHKam730);
                    break;
                case 6:
                    category = context.getResources().getStringArray(R.array.newscatsHKheadline);
                    break;
                case 7:
                    category = context.getResources().getStringArray(R.array.newscatsETNet);
                    break;
                case 8:
                    category = context.getResources().getStringArray(R.array.newscatsTheStandNews);
                    break;
                case 9:
                    category = context.getResources().getStringArray(R.array.newscatsInMediaHK);
                    break;
            }
        } else if (tab.equals("SG")) {
            switch (position) {
                case 0:
                    category = context.getResources().getStringArray(R.array.newscatsZaobao);
                    break;
                case 1:
                    category = context.getResources().getStringArray(R.array.newscatsDaliulian);
                    break;
                case 2:
                    category = context.getResources().getStringArray(R.array.newscatsKwongwah);
                    break;
                case 3:
                    category = context.getResources().getStringArray(R.array.newscatsGuangming);
                    break;
            }
        }

        return category;
    }

    public String[] getFeedURL(String tab, int position) {
        String[] feedURL = null;

        if (tab.equals("TW")) {
            switch (position) {
                case 0:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsCNA);
                    break;
                case 1:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsYahoo);
                    break;
                case 2:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsUDN);
                    break;
                case 3:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsUDNFN);
                    break;
                case 4:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsChinaTimes);
                    break;
                case 5:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsCommercial);
                    break;
                case 6:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsWant);
                    break;
                case 7:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsStorm);
                    break;
                case 8:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsEttoday);
                    break;
                case 9:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsCNYes);
                    break;
                case 10:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsNewsTalk);
                    break;
                case 11:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsLibertyTimes);
                    break;
                case 12:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsAppDaily);
                    break;
                case 13:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsAppDailyRT);
                    break;
                case 14:
                    feedURL = context.getResources().getStringArray(R.array.newsFeedsTheNewsLens);
                    break;
            }
        } else if (tab.equals("HK")) {
            switch (position) {
                case 0:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKAppleDaily);
                    break;
                case 1:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKAppleDailyRT);
                    break;
                case 2:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKOrientalDaily);
                    break;
                case 3:
                    //feedURL = context.getResources().getStringArray(R.array.newsfeedsHKYahoo);
                    //break;
                //case 4:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKEJ);
                    break;
                case 4:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKMetro);
                    break;
                case 5:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKam730);
                    break;
                case 6:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsHKheadline);
                    break;
                case 7:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsETNet);
                    break;
                case 8:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsTheStandNews);
                    break;
                case 9:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsInMediaHK);
                    break;
            }
        } else if (tab.equals("SG")) {
            switch (position) {
                case 0:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsZaobao);
                    break;
                case 1:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsDaliulian);
                    break;
                case 2:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsKwongwah);
                    break;
                case 3:
                    feedURL = context.getResources().getStringArray(R.array.newsfeedsGuangming);
                    break;
            }
        }

        return feedURL;
    }

    public AbstractNews getNewsParser(String tab, int position) {
        AbstractNews parser = null;

        if (tab.equals("TW")) {
            switch (position) {
                case 0:
                    parser = new CNA();
                    break;
                case 1:
                    parser = new Yahoo();
                    break;
                case 2:
                case 3:
                    parser = new UDN();
                    break;
                case 4:
                case 5:
                case 6:
                    parser = new ChinaTimes();
                    break;
                case 7:
                    parser = new Storm();
                    break;
                case 8:
                    parser = new ETToday();
                    break;
                case 9:
                    parser = new CNYes();
                    break;
                case 10:
                    parser = new NewTalk();
                    break;
                case 11:
                    parser = new LibertyTimes();
                    break;
                case 12:
                case 13:
                    parser = new AppleDaily();
                    break;
                case 14:
                    parser = new TheNewsLens();
                    break;
            }
        } else if (tab.equals("HK")) {
            switch (position) {
                case 0:
                case 1:
                    parser = new HKAppleDaily();
                    break;
                case 2:
                    parser = new OrientalDaily();
                    break;
                case 3:
                //    parser = new HKYahoo();
                //    break;
                //case 4:
                    parser = new HKEJ();
                    break;
                case 4:
                    parser = new RTHK();
                    break;
                case 5:
                    parser = new AM730();
                    break;
                case 6:
                    parser = new HKHeadline();
                    break;
                case 7:
                    parser = new ETNet();
                    break;
                case 8:
                    parser = new TheStandNews();
                    break;
                case 9:
                    parser = new InMediaHK();
                    break;
            }
        } else if (tab.equals("SG")) {
            switch (position) {
                case 0:
                    parser = new Zaobao();
                    break;
                case 1:
                    parser = new Daliulian();
                    break;
                case 2:
                    parser = new Kwongwah();
                    break;
                case 3:
                    parser = new Guangming();
                    break;
            }
        }

        return parser;
    }

    public static String getEncoding(String tab, int position) {
        String encoding = "utf-8";

        if (tab.equals("HK")) {

          /*
            switch (position) {
                case 6: //HKHeadline
                    encoding = "big-5";
                    break;
            }*/
        }

        return encoding;
    }

    public static boolean customRSSFeed(String url) {

        if (url.contains("appledaily") || // include TW and HK
                url.contains("am730.com.hk") ||
                url.contains("twgreatdaily")) {
            return true;
        } else {
            return false;
        }

    }
} public class Network {

    private final static String TAG = Network.class.getName();

    private static ConnectivityManager connMgr;

    public static boolean isNetworkConnected(Context context) {
        if (null == connMgr) {
            connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        }

        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

        if (networkInfo != null && networkInfo.isConnected()) {
            return true;
        } else {
            return false;
        }

    }

    public static boolean isWifiAvailable(Context context) {
        if (null == connMgr) {
            connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        }
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();

        if (networkInfo != null && networkInfo.getTypeName().equalsIgnoreCase("WIFI")) {
            if (networkInfo.isConnected()) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }

    }

    public static void AdView(VpadnBanner adView, String AdUnitID) {

    }
    /*
    public static void AdView(MoPubView adView, String AdUnitID) {
        adView.setAdUnitId(AdUnitID);
        if (BaseApplication.APPDEBUG) {
            adView.setTesting(true);
        } else {
            adView.loadAd();
            adView.setBannerAdListener(new MoPubView.BannerAdListener() {
                @Override
                public void onBannerLoaded(MoPubView banner) {

                }

                @Override
                public void onBannerFailed(MoPubView banner, MoPubErrorCode errorCode) {

                }

                @Override
                public void onBannerClicked(MoPubView banner) {

                }

                @Override
                public void onBannerExpanded(MoPubView banner) {

                }

                @Override
                public void onBannerCollapsed(MoPubView banner) {

                }
            });
        }
    }
    */

    public static String checkNewsViewURL(String url) {

        //force change TW / HK yahoo url to mobile version.
        if (url.contains("news.yahoo.com")) {
            url = url.replace("news", "mobi");
            Log.d(TAG, "New URL =" + url);

        }

        return url;
    }

    public static String getDomainName(String url) throws URISyntaxException {
        URI uri = new URI(url);
        String domain = uri.getHost();
        return domain.startsWith("www.") ? domain.substring(4) : domain;
    }

} public class PreferenceSetting {

    private static final String TAG = "PreferenceSetting";

    public static void getPreference(Context context) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

        String fontSize = prefs.getString("font_size", "1");
        String fontColor = prefs.getString("font_color", "#000000");
        String bgColor = prefs.getString("bg_color", "#FFFFFF");
        Boolean smartSave = prefs.getBoolean("smart_save", false);
        Boolean enableSmartSave = false;

        Integer defaultTab = Integer.valueOf(prefs.getString("defaulttab", "0"));

        if (smartSave) {
            if (!Network.isWifiAvailable(context)) {
                enableSmartSave = true;
            }
        }
        BaseApplication.setPrefFontSize(fontSize);
        BaseApplication.setPrefFontColor(fontColor);
        BaseApplication.setPretBGColor(bgColor);
        BaseApplication.setPretSmartSave(enableSmartSave);
        BaseApplication.setPretDefaultTab(defaultTab);

    }
} public class UI {

    public static void showErrorSnackBar(CoordinatorLayout container, int message) {

        Snackbar snackbar = Snackbar
                .make(container, message, Snackbar.LENGTH_LONG);
        snackbar.getView().setBackgroundColor(Color.RED);

        TextView tv = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
        tv.setTextColor(Color.WHITE);

        snackbar.show();

    }
} public class Webpage {
    private static final String TAG = "Webpage";

    public static String htmlDrawer(String title, String time, String body) {

        String htmlTagStart = "<body bgcolor=" + BaseApplication.getPrefBGColor()
                + "><font color="+ BaseApplication.getPrefFontColor()+">";

        String htmlTagEnd = "</font></body>";

        String fontSize = "<font size=+"+ BaseApplication.getPrefFontSize() +">";

        String html = htmlTagStart + "<h2>" + title + "</h2>"
                + "<div>" + time + "</div>";

        html = html + "<hr>";


        if (BaseApplication.getPrefSmartSave()) {
            Log.d(TAG, "enable smart save");
            String smartSaveTag = "<small><small>[這是圖片，已啟用智能節費，停止圖片下載]</small></small></br></br>";
            body = body.replace("<img src", smartSaveTag + "<imgsrc");
        }
        html = html + fontSize + body + "</font>" + htmlTagEnd;

        return html;
    }


    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }


    public static double getWidth(NewsView context) {
        int width = 0;
        DisplayMetrics dm = new DisplayMetrics();
        context.getWindowManager().getDefaultDisplay().getMetrics(dm);
        Configuration config = context.getResources().getConfiguration();
        //int vWidth = 0;
        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
            width = dm.heightPixels;
        else
            width = dm.widthPixels;

        return Webpage.px2dip(context, width);

    }
} public class BaseApplication extends MultiDexApplication {

    public static final boolean APPDEBUG = BuildConfig.DEBUG;

    @Override
    public void onCreate() {
        super.onCreate();
        //LeakCanary.install(this);
        //this.registerActivityLifecycleCallbacks(ActivityStack.getInstance());
    }

    /* Global Variables
    * */
    private static String mPrefFontSize = "";
    public static String getPrefFontSize(){
        return mPrefFontSize;
    }
    public static void setPrefFontSize(String s){
        mPrefFontSize = s;
    }

    private static String mPrefFontColor = "";
    public static String getPrefFontColor(){
        return mPrefFontColor;
    }
    public static void setPrefFontColor(String s){
        mPrefFontColor = s;
    }

    private static String mPrefBGColor = "";
    public static String getPrefBGColor(){
        return mPrefBGColor;
    }
    public static void setPretBGColor(String s){
        mPrefBGColor = s;
    }

    private static Boolean mPrefSmartSave = false;
    public static Boolean getPrefSmartSave(){
        return mPrefSmartSave;
    }
    public static void setPretSmartSave(Boolean s){
        mPrefSmartSave = s;
    }

    private static Integer mPrefDefaultTab = 0;
    public static Integer getPrefDefaultTab(){
        return mPrefDefaultTab;
    }
    public static void setPretDefaultTab(Integer s){
        mPrefDefaultTab = s;
    }

    // The following line should be changed to include the correct property id.
    private static final String PROPERTY_ID = "UA-19743390-22";
    public enum TrackerName {
        APP_TRACKER // Tracker used only in this app.
    }
    HashMap<TrackerName, Tracker> mTrackers = new HashMap<TrackerName, Tracker>();

    public synchronized Tracker getTracker(TrackerName trackerId) {
        if (!mTrackers.containsKey(trackerId)) {

            GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
            analytics.getLogger().setLogLevel(Logger.LogLevel.VERBOSE);
            if (APPDEBUG) {
                analytics.getInstance(this).setDryRun(true);
            }
            Tracker t = (trackerId == TrackerName.APP_TRACKER) ? analytics.newTracker(PROPERTY_ID)
                    : analytics.newTracker(R.xml.global_tracker);
            t.enableAdvertisingIdCollection(true);
            mTrackers.put(trackerId, t);
        }
        return mTrackers.get(trackerId);
    }

} public abstract class MVPBaseActivity<V, T extends BasePresenter<V>> extends SwipeBackActivity {
        protected T mPresenter;


    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSwipeBackLayout().setEdgeOrientation(SwipeBackLayout.EDGE_LEFT);
        mPresenter = createPresenter();
        mPresenter.attachView((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.detachView();
    }

    @Override
    protected void onStart() {
        super.onStart();
        GoogleAnalytics.getInstance(this).reportActivityStart(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        GoogleAnalytics.getInstance(this).reportActivityStop(this);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    protected abstract T createPresenter();

}
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.widget.NestedScrollView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.ccjeng.news.R;
import com.ccjeng.news.parser.AbstractNews;
import com.ccjeng.news.presenter.NewsViewPresenter;
import com.ccjeng.news.presenter.NewsViewView;
import com.ccjeng.news.utils.Analytics;
import com.ccjeng.news.utils.Category;
import com.ccjeng.news.utils.Network;
import com.ccjeng.news.utils.PreferenceSetting;
import com.ccjeng.news.utils.UI;
import com.ccjeng.news.utils.Webpage;
import com.ccjeng.news.view.base.BaseApplication;
import com.ccjeng.news.view.base.MVPBaseActivity;
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.pnikosis.materialishprogress.ProgressWheel;
import java.io.IOException;
import butterknife.BindView;
import butterknife.ButterKnife;
package com.ccjeng.news.view;





//todo 加上新聞小幫手檢核 sample: https://github.com/g0v/newshelper-extension/blob/master/background.js

public class NewsView extends MVPBaseActivity<NewsViewView, NewsViewPresenter> implements NewsViewView {

    private static final String TAG = NewsView.class.getSimpleName();

    WebView webView;
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.progress_wheel)
    ProgressWheel progressWheel;
    @BindView(R.id.main)
    NestedScrollView main;
    @BindView(R.id.coordinator)
    CoordinatorLayout coordinator;

    private Analytics ga;

    private String newsName;
    private String newsUrl;
    private String newsTitle;
    //private MoPubView moPubView;
    private int sourceNumber;
    private String tabName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        ButterKnife.bind(this);

        ga = new Analytics();
        ga.trackerPage(this);

        webView = new WebView(getApplicationContext());
        main.addView(webView);

        setSupportActionBar(toolbar);
        if (getSupportActionBar()!=null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }

        PreferenceSetting.getPreference(this);