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