com.linkbubble.util.PageInspector.java Source code

Java tutorial

Introduction

Here is the source code for com.linkbubble.util.PageInspector.java

Source

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package com.linkbubble.util;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.os.Handler;
import android.util.Log;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;

import com.linkbubble.Constant;
import com.linkbubble.MainController;
import com.linkbubble.Settings;
import com.linkbubble.articlerender.ArticleContent;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Transformation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;

public class PageInspector {

    private static final String TAG = "PageInspector";

    String mScriptCache = null;

    private static final String JS_VARIABLE = "LinkBubble";
    private static final String UNKNOWN_TAG = "unknown"; // the tag Chrome/WebView uses for unknown elements

    private Context mContext;
    private String mWebViewUrl;
    private WebView mWebView;
    private Handler mHandler;
    private JSEmbedHandler mJSEmbedHandler;
    private YouTubeEmbedHelper mYouTubeEmbedHelper;
    private String mLastYouTubeEmbedResultString = null;
    private OnItemFoundListener mOnItemFoundListener;

    private static final int MAX_FAVICON_ENTRIES = 4;
    private TouchIconEntry[] mTouchIconEntries = new TouchIconEntry[MAX_FAVICON_ENTRIES];
    private int mTouchIconEntryCount = 0;
    private String mLastTouchIconResultString = null;
    private TouchIconTransformation sTouchIconTransformation = null;

    public interface OnItemFoundListener {
        void onYouTubeEmbeds();

        void onTouchIconLoaded(Bitmap bitmap, String pageUrl);

        void onFetchHtml(String html);

        void onThemeColor(int color);
    }

    public PageInspector(Context context, WebView webView, OnItemFoundListener listener) {
        mContext = context;
        mWebView = webView;
        mHandler = new Handler();
        mJSEmbedHandler = new JSEmbedHandler();
        mOnItemFoundListener = listener;
        webView.addJavascriptInterface(mJSEmbedHandler, JS_VARIABLE);
    }

    public void run(WebView webView, String adInsert) {
        mWebViewUrl = webView.getUrl();

        if (mScriptCache == null) {
            mScriptCache = "javascript:(function() {\n";

            if (MainController.get() == null || !MainController.get().hasStableWebViewForSelects(mContext)) {
                mScriptCache += getFileContents("SelectElements");
            }

            mScriptCache += getFileContents("TouchIcon");

            mScriptCache += getFileContents("YouTube");

            mScriptCache += getFileContents("FetchContent");

            mScriptCache += getFileContents("ThemeColor");

            mScriptCache += getFileContents("HideBrokenImages");
        }

        String scriptToExecute = mScriptCache;

        if (null != adInsert && !adInsert.equals("")) {
            scriptToExecute += "\nvar adInfoObject = " + adInsert + ";\n";
            scriptToExecute += getFileContents("AddInsertion");
        }

        scriptToExecute += "}());";

        webView.loadUrl(scriptToExecute);
    }

    public String getFileContents(String pageScript) {
        String fullPageScript = "pagescripts/" + pageScript + ".js";
        AssetManager assetManager = mContext.getResources().getAssets();
        InputStream inputStream = null;
        final StringBuilder stringBuilder = new StringBuilder();

        try {
            inputStream = assetManager.open(fullPageScript);

            final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            boolean done = false;

            while (!done) {
                final String line = reader.readLine();
                done = (line == null);

                if (line != null) {
                    stringBuilder.append(line);
                }
            }

            reader.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.toString();
    }

    public void reset() {
        if (mYouTubeEmbedHelper != null) {
            mYouTubeEmbedHelper.clear();
        }
    }

    public YouTubeEmbedHelper getYouTubeEmbedHelper() {
        return mYouTubeEmbedHelper;
    }

    private static class TouchIconEntry {
        String mRel;
        URL mUrl;
        int mSize;

        public String toString() {
            return "rel:" + mRel + ", url:" + mUrl.toString() + ", size:" + mSize;
        }
    }

    // For security reasons, all callbacks should be in a self contained class
    private class JSEmbedHandler {

        @JavascriptInterface
        public void onTouchIconLinks(String string) {
            if (mLastTouchIconResultString != null && mLastTouchIconResultString.equals(string)) {
                return;
            }
            mLastTouchIconResultString = string;

            if (string == null || string.length() == 0) {
                return;
            }

            Log.d(TAG, "onFaviconLinks() - " + string);

            mTouchIconEntryCount = 0;

            String[] items = string.split("@@@");
            for (String s : items) {
                if (mTouchIconEntryCount == MAX_FAVICON_ENTRIES) {
                    break;
                }
                s = s.replace("###", "");
                if (s.length() > 0) {
                    String[] vars = s.split(",");
                    if (vars == null || vars.length < 2) {
                        continue;
                    }

                    String rel = vars[0];
                    if (rel.equals(UNKNOWN_TAG)) {
                        continue;
                    }
                    String href = vars[1];
                    if (href.equals(UNKNOWN_TAG)) {
                        continue;
                    }
                    int size = -1;
                    String sizes = vars.length > 2 ? vars[2] : null;
                    if (sizes != null && sizes.equals(UNKNOWN_TAG) == false) {
                        String[] splitSizes = sizes.split("x"); // specified as 'sizes="128x128"' (http://goo.gl/tGV50j)
                        if (splitSizes.length > 0) {
                            try {
                                // just pick the first one...
                                size = Integer.valueOf(splitSizes[0]);
                            } catch (NumberFormatException e) {
                                // do nothing...
                            }
                        }
                    }

                    try {
                        TouchIconEntry touchIconEntry = mTouchIconEntries[mTouchIconEntryCount];
                        if (touchIconEntry == null) {
                            touchIconEntry = new TouchIconEntry();
                            mTouchIconEntries[mTouchIconEntryCount] = touchIconEntry;
                        }
                        touchIconEntry.mRel = rel;
                        touchIconEntry.mUrl = new URL(href);
                        touchIconEntry.mSize = size;
                        mTouchIconEntryCount++;
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    }
                }
            }

            if (mTouchIconEntryCount > 0) {
                // pick the first one for now
                TouchIconEntry touchIconEntry = mTouchIconEntries[0];
                if (sTouchIconTransformation == null) {
                    sTouchIconTransformation = new TouchIconTransformation();
                }
                sTouchIconTransformation.setListener(mOnItemFoundListener);
                sTouchIconTransformation.mTouchIconPageUrl = mWebViewUrl;
                Picasso.with(mContext).load(touchIconEntry.mUrl.toString()).transform(sTouchIconTransformation)
                        .fetch();
            }

        }

        @JavascriptInterface
        public void onSelectElementInteract(String optionString) {
            if (optionString == null || optionString.length() == 0) {
                return;
            }

            Log.d(TAG, "onSelectElementInteract() - " + optionString);

            final ArrayList<String> optionList = new ArrayList<String>();
            int selectedIndex = 0;
            try {
                JSONArray optionArray = new JSONArray(optionString);
                if (optionArray != null) {
                    int len = optionArray.length();
                    for (int i = 1; i < len; i += 2) {
                        optionList.add(optionArray.get(i).toString());
                    }
                }
                selectedIndex = Integer.parseInt(optionArray.get(0).toString());
            } catch (JSONException e) {
                Log.d(TAG, "error parsing json");
            }

            AlertDialog dialog;
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setSingleChoiceItems(optionList.toArray(new String[optionList.size()]), selectedIndex,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, final int position) {
                            Log.d(TAG, "click position is - " + position);
                            dialog.dismiss();

                            mHandler.postDelayed(new Runnable() {

                                public void run() {
                                    mWebView.loadUrl("javascript:LinkBubble.selectOption(" + position + ")");
                                }

                            }, 1);
                        }
                    });
            dialog = builder.create();
            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            dialog.show();
        }

        @JavascriptInterface
        public void onYouTubeEmbeds(String string) {
            if (mLastYouTubeEmbedResultString != null && mLastYouTubeEmbedResultString.equals(string)) {
                return;
            }
            mLastYouTubeEmbedResultString = string;

            if (string == null || string.length() == 0) {
                return;
            }

            Log.d(TAG, "onYouTubeEmbeds() - " + string);

            if (mYouTubeEmbedHelper == null) {
                mYouTubeEmbedHelper = new YouTubeEmbedHelper(mContext);
            }

            String[] strings = string.split(",");
            if (mYouTubeEmbedHelper.onEmbeds(strings)) {
                if (mOnItemFoundListener != null) {
                    mOnItemFoundListener.onYouTubeEmbeds();
                }
            }
        }

        @JavascriptInterface
        public void onThemeColor(String string) {
            Log.e("themecolor", "onThemeColor():" + string);
            if (string != null) {
                string = string.replace("#", "");
                if (mOnItemFoundListener != null) {
                    try {
                        int color = Integer.parseInt(string, 16);
                        color |= 0xff000000;
                        mOnItemFoundListener.onThemeColor(color);
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
        }

        @JavascriptInterface
        public void fetchHtml(String html, String windowUrl) {
            //Log.d(TAG, "fetchHtml() - " + html);

            boolean fetchPageHtml = false;
            try {
                URL currentUrl = new URL(windowUrl);
                fetchPageHtml = (Settings.get().getArticleModeEnabled()
                        || Settings.get().getArticleModeOnWearEnabled())
                        && ArticleContent.tryForArticleContent(currentUrl);
            } catch (MalformedURLException e) {
            }

            if (fetchPageHtml == true && mOnItemFoundListener != null) {
                mOnItemFoundListener.onFetchHtml(html);
            }
        }
    }

    private static class TouchIconTransformation implements Transformation {

        private WeakReference<OnItemFoundListener> mListener;
        String mTouchIconPageUrl = null;

        void setListener(OnItemFoundListener listener) {
            if (mListener == null || mListener.get() != listener) {
                mListener = new WeakReference<OnItemFoundListener>(listener);
            }
        }

        @Override
        public Bitmap transform(Bitmap source) {
            int w = source.getWidth();

            Bitmap result = source;
            if (w > Constant.TOUCH_ICON_MAX_SIZE) {
                try {
                    result = Bitmap.createScaledBitmap(source, Constant.TOUCH_ICON_MAX_SIZE,
                            Constant.TOUCH_ICON_MAX_SIZE, true);
                } catch (OutOfMemoryError e) {

                }
            }

            if (result != null && mListener != null) {
                OnItemFoundListener listener = mListener.get();
                if (listener != null) {
                    listener.onTouchIconLoaded(result, mTouchIconPageUrl);
                }
            }

            // return null. No need for Picasso to cache this, as we're already doing so elsewhere
            return null;
        }

        @Override
        public String key() {
            return "faviconTransformation()";
        }
    }
}