+ [android] support communication between weex and web
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/fbaffef2 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/fbaffef2 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/fbaffef2 Branch: refs/heads/master Commit: fbaffef2e304448761a494451f505cf2c6c04bc9 Parents: 4a3b952 Author: åéº <haonan....@alibaba-inc.com> Authored: Wed Mar 7 19:09:53 2018 +0800 Committer: misakuo <misa...@apache.org> Committed: Tue Mar 20 12:36:08 2018 +0800 ---------------------------------------------------------------------- .../java/com/taobao/weex/common/Constants.java | 6 +- .../com/taobao/weex/ui/component/WXWeb.java | 42 +++++- .../taobao/weex/ui/module/WXWebViewModule.java | 21 ++- .../java/com/taobao/weex/ui/view/IWebView.java | 7 + .../java/com/taobao/weex/ui/view/WXWebView.java | 138 ++++++++++++++++++- 5 files changed, 201 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fbaffef2/android/sdk/src/main/java/com/taobao/weex/common/Constants.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/common/Constants.java b/android/sdk/src/main/java/com/taobao/weex/common/Constants.java index a994663..4873f1f 100644 --- a/android/sdk/src/main/java/com/taobao/weex/common/Constants.java +++ b/android/sdk/src/main/java/com/taobao/weex/common/Constants.java @@ -108,6 +108,7 @@ public class Constants { String FILTER = "filter"; String QUALITY = "quality"; String SRC = "src"; + String SOURCE = "source"; String PLACE_HOLDER = "placeHolder"; String RESIZE_MODE = "resizeMode"; String AUTO_RECYCLE = "autoBitmapRecycle"; @@ -326,11 +327,8 @@ public class Constants { String DESTORY = "destroy"; } - String STOP_PROPAGATION = "stopPropagation"; - - - + String ONMESSAGE = "message"; } public interface PSEUDO { http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fbaffef2/android/sdk/src/main/java/com/taobao/weex/ui/component/WXWeb.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXWeb.java b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXWeb.java index d2fe72b..81c649a 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXWeb.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXWeb.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import android.view.View; import com.taobao.weex.WXSDKInstance; +import com.taobao.weex.WXSDKManager; import com.taobao.weex.annotation.Component; import com.taobao.weex.adapter.URIAdapter; import com.taobao.weex.common.Constants; @@ -42,6 +43,7 @@ public class WXWeb extends WXComponent { public static final String GO_BACK = "goBack"; public static final String GO_FORWARD = "goForward"; public static final String RELOAD = "reload"; + public static final String POST_MESSAGE = "postMessage"; protected IWebView mWebView; @Deprecated @@ -96,6 +98,14 @@ public class WXWeb extends WXComponent { } } }); + mWebView.setOnMessageListener(new IWebView.OnMessageListener() { + @Override + public void onMessage(Object msg) { + Map<String, Object> params = new HashMap<>(); + params.put("data", msg); + fireEvent(Constants.Event.ONMESSAGE, params); + } + }); return mWebView.getView(); } @@ -118,6 +128,11 @@ public class WXWeb extends WXComponent { if (src != null) setUrl(src); return true; + case Constants.Name.SOURCE: + String source = WXUtils.getString(param,null); + if (source != null) + setSource(source); + return true; } return super.setProperty(key,param); } @@ -137,7 +152,14 @@ public class WXWeb extends WXComponent { } } - public void setAction(String action) { + @WXComponentProp(name = Constants.Name.SOURCE) + public void setSource(String source) { + if (!TextUtils.isEmpty(source) && getHostView() != null) { + loadDataWithBaseURL(source); + } + } + + public void setAction(String action, Object data) { if (!TextUtils.isEmpty(action)) { if (action.equals(GO_BACK)) { goBack(); @@ -145,6 +167,8 @@ public class WXWeb extends WXComponent { goForward(); } else if (action.equals(RELOAD)) { reload(); + } else if (action.equals(POST_MESSAGE)) { + postMessage(data); } } } @@ -162,6 +186,18 @@ public class WXWeb extends WXComponent { getWebView().loadUrl(url); } + private void loadDataWithBaseURL(String source) { + String baseUrl = null; + try { + String bundleUrl = WXSDKManager.getInstance().getSDKInstance(getInstanceId()).getBundleUrl(); + Uri uri = Uri.parse(bundleUrl); + baseUrl = uri.getScheme() + "://" + uri.getAuthority(); + } catch (Exception e) { + // do noting + } + getWebView().loadDataWithBaseURL(source, baseUrl); + } + private void reload() { getWebView().reload(); } @@ -174,6 +210,10 @@ public class WXWeb extends WXComponent { getWebView().goBack(); } + private void postMessage(Object msg) { + getWebView().postMessage(msg); + } + private IWebView getWebView() { return mWebView; } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fbaffef2/android/sdk/src/main/java/com/taobao/weex/ui/module/WXWebViewModule.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXWebViewModule.java b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXWebViewModule.java index 5e058b9..9878c85 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXWebViewModule.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXWebViewModule.java @@ -29,7 +29,8 @@ public class WXWebViewModule extends WXModule{ private enum Action { reload, goBack, - goForward + goForward, + postMessage } @JSMethod(uiThread = true) @@ -47,15 +48,23 @@ public class WXWebViewModule extends WXModule{ action(Action.reload, ref); } - private void action(Action action, String ref) { + @JSMethod(uiThread = true) + public void postMessage(String ref, Object msg) { + action(Action.postMessage, ref, msg); + } + private void action(Action action, String ref, Object data) { WXComponent webComponent = - WXSDKManager.getInstance() - .getWXRenderManager() - .getWXComponent(mWXSDKInstance.getInstanceId(), ref); + WXSDKManager.getInstance() + .getWXRenderManager() + .getWXComponent(mWXSDKInstance.getInstanceId(), ref); if(webComponent instanceof WXWeb) { - ((WXWeb) webComponent).setAction(action.name()); + ((WXWeb) webComponent).setAction(action.name(), data); } } + private void action(Action action, String ref) { + action(action, ref, null); + } + } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fbaffef2/android/sdk/src/main/java/com/taobao/weex/ui/view/IWebView.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/view/IWebView.java b/android/sdk/src/main/java/com/taobao/weex/ui/view/IWebView.java index 9b3f0c7..5b0d332 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/view/IWebView.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/view/IWebView.java @@ -24,12 +24,15 @@ public interface IWebView { public View getView(); public void destroy(); public void loadUrl(String url); + public void loadDataWithBaseURL(String source, String baseUrl); public void reload(); public void goBack(); public void goForward(); + public void postMessage(Object msg); public void setShowLoading(boolean shown); public void setOnErrorListener(OnErrorListener listener); public void setOnPageListener(OnPageListener listener); + public void setOnMessageListener(OnMessageListener listener); public interface OnErrorListener { public void onError(String type, Object message); @@ -40,4 +43,8 @@ public interface IWebView { public void onPageStart(String url); public void onPageFinish(String url, boolean canGoBack, boolean canGoForward); } + + public interface OnMessageListener { + public void onMessage(Object msg); + } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fbaffef2/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java index 68fac88..14a0ace 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXWebView.java @@ -21,11 +21,19 @@ package com.taobao.weex.ui.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; +import android.net.Uri; import android.net.http.SslError; +import android.os.Build; +import android.os.Handler; +import android.os.Message; import android.support.annotation.Nullable; +import android.text.TextUtils; import android.view.Gravity; import android.view.View; +import android.webkit.JavascriptInterface; +import android.webkit.JsPromptResult; import android.webkit.SslErrorHandler; +import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; @@ -36,18 +44,29 @@ import android.webkit.WebViewClient; import android.widget.FrameLayout; import android.widget.ProgressBar; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; import com.taobao.weex.utils.WXLogUtils; +import java.lang.ref.WeakReference; + public class WXWebView implements IWebView { private Context mContext; private WebView mWebView; private ProgressBar mProgressBar; private boolean mShowLoading = true; + private Handler mMessageHandler; + private static final int POST_MESSAGE = 1; + private static final String BRIDGE_NAME = "__WEEX_WEB_VIEW_BRIDGE"; + private static final int SDK_VERSION = Build.VERSION.SDK_INT; + // downgraded by CVE-2012-6636(https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6636) + private static final boolean DOWNGRADE_JS_INTERFACE = SDK_VERSION < 17; private OnErrorListener mOnErrorListener; private OnPageListener mOnPageListener; - + private OnMessageListener mOnMessageListener; public WXWebView(Context context) { mContext = context; @@ -75,6 +94,9 @@ public class WXWebView implements IWebView { mProgressBar.setLayoutParams(pLayoutParams); pLayoutParams.gravity = Gravity.CENTER; root.addView(mProgressBar); + + mMessageHandler = new MessageHandler(this); + return root; } @@ -95,6 +117,12 @@ public class WXWebView implements IWebView { } @Override + public void loadDataWithBaseURL(String source, String baseUrl) { + if (mWebView == null) return; + mWebView.loadDataWithBaseURL(baseUrl, source, "text/html", "utf-8", null); + } + + @Override public void reload() { if(getWebView() == null) return; @@ -115,6 +143,30 @@ public class WXWebView implements IWebView { getWebView().goForward(); } + @Override + public void postMessage(Object msg) { + if(getWebView() == null) + return; + + try { + JSONObject jsonMsg = new JSONObject(); + jsonMsg.put("data", msg); + evaluateJS("javascript:(function () {" + + "var event;" + + "var data = " + jsonMsg.toString() + ";" + + "try {" + + "event = new MessageEvent('message', data);" + + "} catch (e) {" + + "event = document.createEvent('MessageEvent');" + + "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" + + "}" + + "document.dispatchEvent(event);" + + "})();"); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + /*@Override public void setVisibility(int visibility) { if (mRootView != null) { @@ -137,6 +189,11 @@ public class WXWebView implements IWebView { mOnPageListener = listener; } + @Override + public void setOnMessageListener(OnMessageListener listener) { + mOnMessageListener = listener; + } + private void showProgressBar(boolean shown) { if (mShowLoading) { mProgressBar.setVisibility(shown ? View.VISIBLE : View.GONE); @@ -185,6 +242,13 @@ public class WXWebView implements IWebView { if (mOnPageListener != null) { mOnPageListener.onPageFinish(url, view.canGoBack(), view.canGoForward()); } + if (mOnMessageListener != null) { + evaluateJS("javascript:(window.postMessage = function(data) {" + + (DOWNGRADE_JS_INTERFACE + ? "prompt('" + BRIDGE_NAME + "://postMessage?data=' + JSON.stringify(data))" + : BRIDGE_NAME + ".postMessage(JSON.stringify(data));") + + "})"); + } } @Override @@ -211,7 +275,6 @@ public class WXWebView implements IWebView { mOnErrorListener.onError("error", "ssl error"); } } - }); wv.setWebChromeClient(new WebChromeClient() { @Override @@ -230,7 +293,78 @@ public class WXWebView implements IWebView { } } + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + Uri uri = Uri.parse(message); + String scheme = uri.getScheme(); + if (TextUtils.equals(scheme, BRIDGE_NAME)) { + if (TextUtils.equals(uri.getAuthority(), "postMessage")) { + String data = uri.getQueryParameter("data"); + onMessage(data); + result.confirm("success"); + } else { + result.confirm("fail"); + } + return true; + } + return super.onJsPrompt(view, url, message, defaultValue, result); + } }); + if (!DOWNGRADE_JS_INTERFACE) { + wv.addJavascriptInterface(new Object() { + @JavascriptInterface + public void postMessage(String data) { + onMessage(data); + } + }, BRIDGE_NAME); + } + } + + private void onMessage(String data) { + if (data != null && mOnMessageListener != null) { + try { + Message message = new Message(); + message.what = POST_MESSAGE; + message.obj = JSON.parse(data); + mMessageHandler.sendMessage(message); + } catch (JSONException e) { + throw new RuntimeException(e); + } + + } } + private void evaluateJS(String jsStr) { + if (SDK_VERSION < 19) { + mWebView.loadUrl(jsStr); + } else { + mWebView.evaluateJavascript(jsStr, new ValueCallback<String>() { + @Override + public void onReceiveValue(String value) { + } + }); + } + } + + private static class MessageHandler extends Handler { + private final WeakReference<WXWebView> mWv; + + private MessageHandler(WXWebView wv) { + mWv = new WeakReference<>(wv); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case POST_MESSAGE: + if (mWv.get() != null && mWv.get().mOnMessageListener != null) { + mWv.get().mOnMessageListener.onMessage(msg.obj); + } + break; + default: + break; + } + } + } }