mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
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.
This commit is contained in:
parent
a9a5284a67
commit
5e3e9ddb8e
@ -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<String> 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<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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<String>();
|
||||
|
||||
// Determine if XHR or polling is to be used
|
||||
if ((url != null) && !url.startsWith("file://")) {
|
||||
@ -133,16 +126,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() {
|
||||
|
||||
public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
|
||||
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) {
|
||||
synchronized (this) {
|
||||
this.javascript.add(statement);
|
||||
this.empty = false;
|
||||
this.jsMessageQueue = queue;
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
163
framework/src/org/apache/cordova/NativeToJsMessageQueue.java
Executable file
163
framework/src/org/apache/cordova/NativeToJsMessageQueue.java
Executable file
@ -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<String> queue = new LinkedList<String>();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user