Return-Path: X-Original-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-callback-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 0B4FFD832 for ; Wed, 22 Aug 2012 13:57:12 +0000 (UTC) Received: (qmail 1537 invoked by uid 500); 22 Aug 2012 13:57:11 -0000 Delivered-To: apmail-incubator-callback-commits-archive@incubator.apache.org Received: (qmail 1474 invoked by uid 500); 22 Aug 2012 13:57:11 -0000 Mailing-List: contact callback-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: callback-dev@incubator.apache.org Delivered-To: mailing list callback-commits@incubator.apache.org Received: (qmail 1245 invoked by uid 99); 22 Aug 2012 13:57:11 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 22 Aug 2012 13:57:11 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 2DC391EE6F; Wed, 22 Aug 2012 13:57:11 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: agrieve@apache.org To: callback-commits@incubator.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [4/6] android commit: Refactor Native->JS messaging logic into its own class. Message-Id: <20120822135711.2DC391EE6F@tyr.zones.apache.org> Date: Wed, 22 Aug 2012 13:57:11 +0000 (UTC) Refactor Native->JS messaging logic into its own class. This will make it easy to add more modes. This also adds logic to set the move via a prompt() from JS. Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/commit/5e3e9ddb Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/tree/5e3e9ddb Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/diff/5e3e9ddb Branch: refs/heads/master Commit: 5e3e9ddb8e8aadbb4b864b9b6946a8fea9b50144 Parents: a9a5284 Author: Andrew Grieve Authored: Fri Aug 10 12:55:29 2012 -0400 Committer: Andrew Grieve Committed: Wed Aug 22 09:46:30 2012 -0400 ---------------------------------------------------------------------- .../src/org/apache/cordova/CallbackServer.java | 82 ++------ .../org/apache/cordova/CordovaChromeClient.java | 12 +- .../src/org/apache/cordova/CordovaWebView.java | 12 +- .../org/apache/cordova/CordovaWebViewClient.java | 10 +- framework/src/org/apache/cordova/DroidGap.java | 4 +- .../org/apache/cordova/NativeToJsMessageQueue.java | 163 +++++++++++++++ 6 files changed, 201 insertions(+), 82 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/CallbackServer.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/CallbackServer.java b/framework/src/org/apache/cordova/CallbackServer.java index b8075b5..d0296a3 100755 --- a/framework/src/org/apache/cordova/CallbackServer.java +++ b/framework/src/org/apache/cordova/CallbackServer.java @@ -59,8 +59,9 @@ public class CallbackServer implements Runnable { private ServerSocket waitSocket; /** * The list of JavaScript statements to be sent to JavaScript. + * This can be null when there are no messages available. */ - private LinkedList javascript; + private NativeToJsMessageQueue jsMessageQueue; /** * The port to listen on. @@ -77,10 +78,6 @@ public class CallbackServer implements Runnable { */ private boolean active; - /** - * Indicates that the JavaScript statements list is empty - */ - private boolean empty; /** * Indicates that polling should be used instead of XHR. @@ -98,9 +95,7 @@ public class CallbackServer implements Runnable { public CallbackServer() { //Log.d(LOG_TAG, "CallbackServer()"); this.active = false; - this.empty = true; this.port = 0; - this.javascript = new LinkedList(); } /** @@ -113,10 +108,8 @@ public class CallbackServer implements Runnable { */ public void init(String url) { //System.out.println("CallbackServer.start("+url+")"); - this.active = false; - this.empty = true; + this.stopServer(); this.port = 0; - this.javascript = new LinkedList(); // Determine if XHR or polling is to be used if ((url != null) && !url.startsWith("file://")) { @@ -134,16 +127,6 @@ public class CallbackServer implements Runnable { } /** - * Re-init when loading a new HTML page into webview. - * - * @param url The URL of the Cordova app being loaded - */ - public void reinit(String url) { - this.stopServer(); - this.init(url); - } - - /** * Return if polling is being used instead of XHR. * @return */ @@ -224,11 +207,19 @@ public class CallbackServer implements Runnable { // Must have security token if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) { //Log.d(LOG_TAG, "CallbackServer -- Processing GET request"); + String js = null; // Wait until there is some data to send, or send empty data every 10 sec // to prevent XHR timeout on the client synchronized (this) { - while (this.empty) { + while (this.active) { + if (jsMessageQueue != null) { + // TODO(agrieve): Should this use popAll() instead? + js = jsMessageQueue.pop(); + if (js != null) { + break; + } + } try { this.wait(10000); // prevent timeout from happening //Log.d(LOG_TAG, "CallbackServer>>> break <<<"); @@ -242,17 +233,14 @@ public class CallbackServer implements Runnable { if (this.active) { // If no data, then send 404 back to client before it times out - if (this.empty) { + if (js == null) { //Log.d(LOG_TAG, "CallbackServer -- sending data 0"); response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space } else { //Log.d(LOG_TAG, "CallbackServer -- sending item"); response = "HTTP/1.1 200 OK\r\n\r\n"; - String js = this.getJavascript(); - if (js != null) { - response += encode(js, "UTF-8"); - } + response += encode(js, "UTF-8"); } } else { @@ -305,46 +293,10 @@ public class CallbackServer implements Runnable { public void destroy() { this.stopServer(); } - - /** - * Get the number of JavaScript statements. - * - * @return int - */ - public int getSize() { - synchronized (this) { - int size = this.javascript.size(); - return size; - } - } - - /** - * Get the next JavaScript statement and remove from list. - * - * @return String - */ - public String getJavascript() { - synchronized (this) { - if (this.javascript.size() == 0) { - return null; - } - String statement = this.javascript.remove(0); - if (this.javascript.size() == 0) { - this.empty = true; - } - return statement; - } - } - - /** - * Add a JavaScript statement to the list. - * - * @param statement - */ - public void sendJavascript(String statement) { + + public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) { synchronized (this) { - this.javascript.add(statement); - this.empty = false; + this.jsMessageQueue = queue; this.notify(); } } http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/CordovaChromeClient.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java index cdfc9d1..7feee19 100755 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ b/framework/src/org/apache/cordova/CordovaChromeClient.java @@ -23,12 +23,9 @@ import org.apache.cordova.api.LOG; import org.json.JSONArray; import org.json.JSONException; -//import android.app.Activity; import android.app.AlertDialog; -//import android.content.Context; import android.content.DialogInterface; import android.view.KeyEvent; -//import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.JsPromptResult; import android.webkit.JsResult; @@ -211,9 +208,16 @@ public class CordovaChromeClient extends WebChromeClient { } } + // Sets the native->JS bridge mode. + else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) { + this.appView.jsMessageQueue.setBridgeMode(Integer.parseInt(message)); + result.confirm(""); + } + // Polling for JavaScript messages else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) { - String r = this.appView.callbackServer.getJavascript(); + // TODO(agrieve): Use popAll() here. + String r = this.appView.jsMessageQueue.pop(); result.confirm(r); } http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/CordovaWebView.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 85c3cd8..131f754 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -88,6 +88,8 @@ public class CordovaWebView extends WebView { private boolean handleButton = false; + NativeToJsMessageQueue jsMessageQueue; + /** * Constructor. * @@ -190,14 +192,14 @@ public class CordovaWebView extends WebView { } } - /** * Initialize webview. */ @SuppressWarnings("deprecation") @SuppressLint("NewApi") private void setup() { - + jsMessageQueue = new NativeToJsMessageQueue(this); + this.setInitialScale(0); this.setVerticalScrollBarEnabled(false); this.requestFocusFromTouch(); @@ -456,7 +458,7 @@ public class CordovaWebView extends WebView { * * @param url */ - private void loadUrlNow(String url) { + void loadUrlNow(String url) { LOG.d(TAG, ">>> loadUrlNow()"); super.loadUrl(url); } @@ -495,9 +497,7 @@ public class CordovaWebView extends WebView { * @param message */ public void sendJavascript(String statement) { - if (this.callbackServer != null) { - this.callbackServer.sendJavascript(statement); - } + this.jsMessageQueue.add(statement); } /** http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/CordovaWebViewClient.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 6e4ff7f..037f200 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -230,14 +230,14 @@ public class CordovaWebViewClient extends WebViewClient { this.doClearHistory = true; } - // Create callback server and plugin manager + // Flush stale messages. + this.appView.jsMessageQueue.reset(); + + // Create callback server if (this.appView.callbackServer == null) { this.appView.callbackServer = new CallbackServer(); - this.appView.callbackServer.init(url); - } - else { - this.appView.callbackServer.reinit(url); } + this.appView.callbackServer.init(url); // Broadcast message that page has loaded this.appView.postMessage("onPageStarted", url); http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/DroidGap.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 68dbe08..03ca2ad 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -714,8 +714,8 @@ public class DroidGap extends Activity implements CordovaInterface { * @param message */ public void sendJavascript(String statement) { - if (this.appView != null && this.appView.callbackServer != null) { - this.appView.callbackServer.sendJavascript(statement); + if (this.appView != null) { + this.appView.jsMessageQueue.add(statement); } } http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/5e3e9ddb/framework/src/org/apache/cordova/NativeToJsMessageQueue.java ---------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java new file mode 100755 index 0000000..037cc9c --- /dev/null +++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java @@ -0,0 +1,163 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova; + +import java.util.LinkedList; + +import android.util.Log; + +/** + * Holds the list of messages to be sent to the WebView. + */ +public class NativeToJsMessageQueue { + private static final String LOG_TAG = "JsMessageQueue"; + + // This must match the default value in incubator-cordova-js/lib/android/exec.js + private static final int DEFAULT_BRIDGE_MODE = 1; + + /** + * The list of JavaScript statements to be sent to JavaScript. + */ + private LinkedList queue = new LinkedList(); + + /** + * The index into registeredListeners to treat as active. + */ + private int activeListenerIndex; + + /** + * The array of listeners that can be used to send messages to JS. + */ + private BridgeMode[] registeredListeners; + + public NativeToJsMessageQueue(CordovaWebView webView) { + registeredListeners = new BridgeMode[2]; + registeredListeners[0] = null; + registeredListeners[1] = new CallbackBridgeMode(webView); + reset(); +// POLLING: 0, +// HANGING_GET: 1, +// LOAD_URL: 2, +// ONLINE_EVENT: 3, +// PRIVATE_API: 4 + } + + /** + * Changes the bridge mode. + */ + public void setBridgeMode(int value) { + if (value < 0 || value >= registeredListeners.length) { + Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value); + } else { + if (value != activeListenerIndex) { + Log.d(LOG_TAG, "Set native->JS mode to " + value); + synchronized (this) { + activeListenerIndex = value; + BridgeMode activeListener = registeredListeners[value]; + if (!queue.isEmpty() && activeListener != null) { + activeListener.onNativeToJsMessageAvailable(this); + } + } + } + } + } + + /** + * Clears all messages and resets to the default bridge mode. + */ + public void reset() { + synchronized (this) { + queue.clear(); + setBridgeMode(DEFAULT_BRIDGE_MODE); + } + } + + /** + * Removes and returns the last statement in the queue. + * Returns null if the queue is empty. + */ + public String pop() { + synchronized (this) { + if (queue.isEmpty()) { + return null; + } + return queue.remove(0); + } + } + + /** + * Combines and returns all statements. Clears the queue. + * Returns null if the queue is empty. + */ + public String popAll() { + synchronized (this) { + int length = queue.size(); + if (length == 0) { + return null; + } + StringBuffer sb = new StringBuffer(); + // Wrap each statement in a try/finally so that if one throws it does + // not affect the next. + int i = 0; + for (String message : queue) { + if (++i == length) { + sb.append(message); + } else { + sb.append("try{") + .append(message) + .append("}finally{"); + } + } + for ( i = 1; i < length; ++i) { + sb.append('}'); + } + queue.clear(); + return sb.toString(); + } + } + + /** + * Add a JavaScript statement to the list. + */ + public void add(String statement) { + synchronized (this) { + queue.add(statement); + if (registeredListeners[activeListenerIndex] != null) { + registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable(this); + } + } + } + + private interface BridgeMode { + void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue); + } + + private static class CallbackBridgeMode implements BridgeMode { + private CordovaWebView webView; + public CallbackBridgeMode(CordovaWebView webView) { + this.webView = webView; + } + public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) { + if (webView.callbackServer != null) { + webView.callbackServer.onNativeToJsMessageAvailable(queue); + } + } + } + +}