1 /* 2 * libjingle 3 * Copyright 2013, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 package org.appspot.apprtc; 29 30 import android.annotation.SuppressLint; 31 import android.app.Activity; 32 import android.util.Log; 33 import android.webkit.ConsoleMessage; 34 import android.webkit.JavascriptInterface; 35 import android.webkit.WebChromeClient; 36 import android.webkit.WebView; 37 import android.webkit.WebViewClient; 38 39 /** 40 * Java-land version of Google AppEngine's JavaScript Channel API: 41 * https://developers.google.com/appengine/docs/python/channel/javascript 42 * 43 * Requires a hosted HTML page that opens the desired channel and dispatches JS 44 * on{Open,Message,Close,Error}() events to a global object named 45 * "androidMessageHandler". 46 */ 47 public class GAEChannelClient { 48 private static final String TAG = "GAEChannelClient"; 49 private WebView webView; 50 private final ProxyingMessageHandler proxyingMessageHandler; 51 52 /** 53 * Callback interface for messages delivered on the Google AppEngine channel. 54 * 55 * Methods are guaranteed to be invoked on the UI thread of |activity| passed 56 * to GAEChannelClient's constructor. 57 */ 58 public interface MessageHandler { onOpen()59 public void onOpen(); onMessage(String data)60 public void onMessage(String data); onClose()61 public void onClose(); onError(int code, String description)62 public void onError(int code, String description); 63 } 64 65 /** Asynchronously open an AppEngine channel. */ 66 @SuppressLint("SetJavaScriptEnabled") GAEChannelClient( Activity activity, String token, MessageHandler handler)67 public GAEChannelClient( 68 Activity activity, String token, MessageHandler handler) { 69 webView = new WebView(activity); 70 webView.getSettings().setJavaScriptEnabled(true); 71 webView.setWebChromeClient(new WebChromeClient() { // Purely for debugging. 72 public boolean onConsoleMessage (ConsoleMessage msg) { 73 Log.d(TAG, "console: " + msg.message() + " at " + 74 msg.sourceId() + ":" + msg.lineNumber()); 75 return false; 76 } 77 }); 78 webView.setWebViewClient(new WebViewClient() { // Purely for debugging. 79 public void onReceivedError( 80 WebView view, int errorCode, String description, 81 String failingUrl) { 82 Log.e(TAG, "JS error: " + errorCode + " in " + failingUrl + 83 ", desc: " + description); 84 } 85 }); 86 proxyingMessageHandler = 87 new ProxyingMessageHandler(activity, handler, token); 88 webView.addJavascriptInterface( 89 proxyingMessageHandler, "androidMessageHandler"); 90 webView.loadUrl("file:///android_asset/channel.html"); 91 } 92 93 /** Close the connection to the AppEngine channel. */ close()94 public void close() { 95 if (webView == null) { 96 return; 97 } 98 proxyingMessageHandler.disconnect(); 99 webView.removeJavascriptInterface("androidMessageHandler"); 100 webView.loadUrl("about:blank"); 101 webView = null; 102 } 103 104 // Helper class for proxying callbacks from the Java<->JS interaction 105 // (private, background) thread to the Activity's UI thread. 106 private static class ProxyingMessageHandler { 107 private final Activity activity; 108 private final MessageHandler handler; 109 private final boolean[] disconnected = { false }; 110 private final String token; 111 112 public ProxyingMessageHandler(Activity activity, MessageHandler handler, String token)113 ProxyingMessageHandler(Activity activity, MessageHandler handler, 114 String token) { 115 this.activity = activity; 116 this.handler = handler; 117 this.token = token; 118 } 119 disconnect()120 public void disconnect() { 121 disconnected[0] = true; 122 } 123 disconnected()124 private boolean disconnected() { 125 return disconnected[0]; 126 } 127 getToken()128 @JavascriptInterface public String getToken() { 129 return token; 130 } 131 onOpen()132 @JavascriptInterface public void onOpen() { 133 activity.runOnUiThread(new Runnable() { 134 public void run() { 135 if (!disconnected()) { 136 handler.onOpen(); 137 } 138 } 139 }); 140 } 141 onMessage(final String data)142 @JavascriptInterface public void onMessage(final String data) { 143 activity.runOnUiThread(new Runnable() { 144 public void run() { 145 if (!disconnected()) { 146 handler.onMessage(data); 147 } 148 } 149 }); 150 } 151 onClose()152 @JavascriptInterface public void onClose() { 153 activity.runOnUiThread(new Runnable() { 154 public void run() { 155 if (!disconnected()) { 156 handler.onClose(); 157 } 158 } 159 }); 160 } 161 onError( final int code, final String description)162 @JavascriptInterface public void onError( 163 final int code, final String description) { 164 activity.runOnUiThread(new Runnable() { 165 public void run() { 166 if (!disconnected()) { 167 handler.onError(code, description); 168 } 169 } 170 }); 171 } 172 } 173 } 174