Slide 1

Slide 1 text

Good bye, Javascript Interface @Potatotips 11/29/2016 Tomoaki Imai

Slide 2

Slide 2 text

http://mercan.mercari.com/entry/2016/11/18/110000 twitter: @tomoaki_imai github: tomoima525 ࠓҪ ஐষ Android Engineer @Mercari https://github.com/tomoima525/kotoha-slack

Slide 3

Slide 3 text

WebView in Mercari

Slide 4

Slide 4 text

Javascript Interface

Slide 5

Slide 5 text

WebSettings settings = webView.getSettings();
 settings.setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavaScriptInterface(activity), “android”); public class JavaScriptInterface { public JavascriptInterface(BaseActivity activity){ … this.activity = activity; } @JavascriptInterface
 public void showMessage(String text) { //Call Native method BaseActivity.showMessage(title, text); } } Javascript Interface basic function showMessage(title, text) { android.showMessage(title, text); }

Slide 6

Slide 6 text

WebSettings settings = webView.getSettings();
 settings.setJavaScriptEnabled(true); webView.addJavascriptInterface(new JavaScriptInterface(activity), “android”); public class JavaScriptInterface { public JavascriptInterface(BaseActivity activity){ … this.activity = activity; } @JavascriptInterface
 public void showMessage(String text) { //Call Native method BaseActivity.showMessage(title, text); } } Javascript Interface basic function showMessage(title, text) { android.showMessage(title, text); }

Slide 7

Slide 7 text

• Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript Problems with Javascript Interface

Slide 8

Slide 8 text

• Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript • Thus, hard to figure out crashes Problems with Javascript Interface

Slide 9

Slide 9 text

• Architecture is a black box https://developer.android.com/guide/webapps/webview.html#UsingJavaScript • Thus, hard to figure out crashes Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@42249680 is not valid; is your activity running? at com.kouzoh.mercari.util.WebViewUtil$DefaultJavaScriptInterface $2.run(WebViewUtil.java:484) Problems with Javascript Interface

Slide 10

Slide 10 text

Problems with Javascript Interface • Code complexity on Front end function showMessage(title, text) { if(client.android){ android.showMessage(title, text); } else if(client.ios){ showMessageForIOS(title, text) } } function showMessageForIOS(title, text){…}

Slide 11

Slide 11 text

What can we do?

Slide 12

Slide 12 text

class ViewController: UIViewController, UIWebViewDelegate { //. . . func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { if(request.URL!.scheme == "scheme"){ // … pass to native method return false } return true } how iOS do (using UIWebView)

Slide 13

Slide 13 text

Evaluate url?

Slide 14

Slide 14 text

Evaluating Url scheme://app/showMessage?title=Potato&text=This%20is%20%Message! Method: showMessage() Argument: title => Potato text => This is Message!

Slide 15

Slide 15 text

Evaluating Url • No black box ( just parse Url!) • Has consistency with iOS Url evaluation

Slide 16

Slide 16 text

Basic Architecture 6SJ.PEFM 8FC4DIFNF)BOEMFS $VTUPN8FC$MJFOU /BUJWFNFUIPE scheme://app/showMessage?title=Potato&text=This%20is%20%Message! method: showMessage arguments: title=Potato text=This%20is%20%Message! activity.showMessage("Potato", “This is Message!”); shouldOverrideUrloading() parse call

Slide 17

Slide 17 text

public class UriModel { private final static String SCHEME = “scheme://app“
 String method;
 Map params = new HashMap<>();
 
 public UriModel(Uri uri) {
 try { 
 String urlData = URLDecoder.decode( uri.toString().substring(SCHEME.length()), "UTF-8");
 urlData = urlData.replaceFirst("\\?", "¥¥");
 String[] splitUrl = urlData.split("¥¥");
 String method = splitUrl[0];
 this.setMethod(method);
 if (splitUrl.length == 1) return;
 
 Set parameterNames = uri.getQueryParameterNames();
 Iterator parameterIterator = parameterNames.iterator();
 
 while (parameterIterator.hasNext()){
 String key = parameterIterator.next();
 this.getParams().put(key, uri.getQueryParameter(key));
 }
 } catch (UnsupportedEncodingException e) {
 }
 } Uri Model Class QBSTFNFUIPE QBSTFQBSBNT

Slide 18

Slide 18 text

public class CustomWebViewClient extends WebViewClient {
 
 protected WebActivity activity;
 
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient

Slide 19

Slide 19 text

public class CustomWebViewClient extends WebViewClient {
 private final static String APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient

Slide 20

Slide 20 text

public class CustomWebViewClient extends WebViewClient {
 private final static String APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 WebSchemeHandler schemeHandler;
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 schemeHandler = new WebSchemeHandler(activity);
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
 
 
 
 return false;
 } } Custom WebViewClient

Slide 21

Slide 21 text

public class CustomWebViewClient extends WebViewClient {
 private final static String APP_SCHEME = "scheme.*";
 protected WebActivity activity;
 WebSchemeHandler schemeHandler;
 
 public CustomWebViewClient(WebActivity activity) {
 super();
 this.activity = activity;
 schemeHandler = new WebSchemeHandler(activity);
 }
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 if (request.getUri().getScheme().matches(APP_SCHEME)){
 schemeHandler.handleUri(view, request.getUri());
 return true;
 }
 return false;
 } } Custom WebViewClient

Slide 22

Slide 22 text

public class WebSchemeHandler { WebActivity activity; public WebSchemeHandler(WebActivity activity) {
 this.activity = activity;
 } public void handleUri(WebView webView, Uri uri) {
 UriModel uriModel = new UriModel(uri);
 switch (uriModel.getMethod()) {
 case “showMessage": activity.showMessage( uriModel.getParams().get(“title”), uriModel.getParams().get(“text”)); . . . Handle Uri

Slide 23

Slide 23 text

public class WebSchemeHandler { WebActivity activity; public WebSchemeHandler(WebActivity activity) {
 this.activity = activity;
 } void getDeviceInfo(WebView webView, final String funcName) {
 activity.runOnUiThread(() -> {
 if (webView != null) {
 String deviceInfo = activity.getDeviceInfo(); webView.evaluateJavascript(funcName + "('" + deviceInfo + "')", null);
 }
 });
 } Callbacks to Web

Slide 24

Slide 24 text

Things to keep in mind • Only allow schemes when the web page is secure ‣ Only allow secured(≒ owned) websites ‣ Do not redirect or access to external unknown sites

Slide 25

Slide 25 text

WebView 2.0?? • From iOS 8.0 ‣ UIWebView -> WKWebView • From Android 7.0 ‣ Compatibility issue https://developer.android.com/reference/android/webkit/WebView.html #evaluateJavascript(java.lang.String, android.webkit.ValueCallback)