Add XHR-based callbacks from Java to JavaScript.

This commit is contained in:
Bryce Curtis 2010-08-18 13:12:53 -05:00
parent 4b3255e4fd
commit 5bdad8c0ca
3 changed files with 167 additions and 20 deletions

48
framework/assets/js/phonegap.js.base Normal file → Executable file
View File

@ -67,6 +67,9 @@ PhoneGap.addConstructor = function(func) {
} }
} }
} }
// Start listening for callbacks from Java
PhoneGap.JSCallback();
// all constructors run, now fire the deviceready event // all constructors run, now fire the deviceready event
var e = document.createEvent('Events'); var e = document.createEvent('Events');
e.initEvent('deviceready'); e.initEvent('deviceready');
@ -133,3 +136,48 @@ PhoneGap.run_command = function() {
document.location = url; document.location = url;
}; };
/**
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
* any JavaScript code that needs to be run. This is used for callbacks from
* Java to JavaScript.
*/
PhoneGap.JSCallback = function() {
var xmlhttp = new XMLHttpRequest();
// Callback function when XMLHttpRequest is ready
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState == 4){
// If callback has JavaScript statement to execute
if (xmlhttp.status == 200) {
var msg = xmlhttp.responseText;
setTimeout(function() {
try {
var t = eval(msg);
}
catch (e) {
console.log("JSCallback Error: "+e);
}
}, 1);
setTimeout(PhoneGap.JSCallback, 1);
}
// If callback ping (used to keep XHR request from timing out)
else if (xmlhttp.status == 404) {
setTimeout(PhoneGap.JSCallback, 10);
}
// If error, restart callback server
else {
console.log("JSCallback Error: Request failed.");
CallbackServer.restartServer();
setTimeout(PhoneGap.JSCallback, 100);
}
}
}
xmlhttp.open("GET", "http://127.0.0.1:"+CallbackServer.getPort()+"/" , true);
xmlhttp.send();
}

View File

@ -9,46 +9,97 @@ import android.hardware.SensorManager;
import android.content.Context; import android.content.Context;
import android.webkit.WebView; import android.webkit.WebView;
/**
* This class listens to the compass sensor and calls navigator.compass.setHeading(heading)
* method in Javascript every sensor change event it receives.
*/
public class CompassListener implements SensorEventListener{ public class CompassListener implements SensorEventListener{
WebView mAppView;
Context mCtx; public static int STOPPED = 0;
Sensor mSensor; public static int STARTING = 1;
public static int RUNNING = 2;
public static int ERROR_FAILED_TO_START = 3;
private SensorManager sensorManager; WebView mAppView; // WebView object
DroidGap mCtx; // Activity (DroidGap) object
int status; // status of listener
private SensorManager sensorManager;// Sensor manager
Sensor mSensor; // Compass sensor returned by sensor manager
/**
* Constructor.
*
* @param appView
* @param ctx The Activity (DroidGap) object
*/
CompassListener(WebView appView, Context ctx) CompassListener(WebView appView, Context ctx)
{ {
mCtx = ctx; this.mCtx = (DroidGap)ctx;
mAppView = appView; this.mAppView = appView;
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE); this.sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
} }
/**
* Start listening for compass sensor.
*
* @return status of listener
*/
public int start() {
public void start() // Get accelerometer from sensor manager
{
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION); List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if (list.size() > 0)
{ // If found, then register as listener
if (list.size() > 0) {
this.mSensor = list.get(0); this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL); this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
this.status = CompassListener.STARTING;
} }
// If error, then set status to error
else {
this.status = CompassListener.ERROR_FAILED_TO_START;
}
return this.status;
} }
public void stop() /**
{ * Stop listening to compass sensor.
this.sensorManager.unregisterListener(this); */
public void stop() {
if (this.status != CompassListener.STOPPED) {
this.sensorManager.unregisterListener(this);
}
this.status = CompassListener.STOPPED;
}
/**
* Called when listener is to be shut down and object is being destroyed.
*/
public void destroy() {
this.stop();
} }
public void onAccuracyChanged(Sensor sensor, int accuracy) { public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
} }
/**
* Sensor listener event.
*
* @param SensorEvent event
*/
public void onSensorChanged(SensorEvent event) { public void onSensorChanged(SensorEvent event) {
// We only care about the orientation as far as it refers to Magnetic North // We only care about the orientation as far as it refers to Magnetic North
float heading = event.values[0]; float heading = event.values[0];
mAppView.loadUrl("javascript:navigator.compass.setHeading(" + heading + ")");
this.status = CompassListener.RUNNING;
// TODO This is very expensive to process every event. Should this use polling from JS instead?
mCtx.sendJavascript("navigator.compass.setHeading(" + heading + ");");
} }
} }

View File

@ -72,6 +72,7 @@ public class DroidGap extends Activity {
private CryptoHandler crypto; private CryptoHandler crypto;
private BrowserKey mKey; private BrowserKey mKey;
private AudioHandler audio; private AudioHandler audio;
private CallbackServer callbackServer;
private Uri imageUri; private Uri imageUri;
@ -185,10 +186,38 @@ public class DroidGap extends Activity {
if (accel != null) { if (accel != null) {
accel.destroy(); accel.destroy();
} }
if (launcher != null) {
}
if (mContacts != null) {
}
if (fs != null) {
}
if (netMan != null) {
}
if (mCompass != null) {
mCompass.destroy();
}
if (crypto != null) {
}
if (mKey != null) {
}
if (audio != null) {
}
if (callbackServer != null) {
callbackServer.destroy();
}
} }
private void bindBrowser(WebView appView) private void bindBrowser(WebView appView)
{ {
callbackServer = new CallbackServer();
gap = new Device(appView, this); gap = new Device(appView, this);
accel = new AccelBroker(appView, this); accel = new AccelBroker(appView, this);
launcher = new CameraLauncher(appView, this); launcher = new CameraLauncher(appView, this);
@ -211,7 +240,7 @@ public class DroidGap extends Activity {
appView.addJavascriptInterface(crypto, "GapCrypto"); appView.addJavascriptInterface(crypto, "GapCrypto");
appView.addJavascriptInterface(mKey, "BackButton"); appView.addJavascriptInterface(mKey, "BackButton");
appView.addJavascriptInterface(audio, "GapAudio"); appView.addJavascriptInterface(audio, "GapAudio");
appView.addJavascriptInterface(callbackServer, "CallbackServer");
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) if (android.os.Build.VERSION.RELEASE.startsWith("1."))
{ {
@ -227,6 +256,25 @@ public class DroidGap extends Activity {
{ {
appView.loadUrl(url); appView.loadUrl(url);
} }
/**
* Send JavaScript statement back to JavaScript.
*
* @param message
*/
public void sendJavascript(String statement) {
System.out.println("DroidGap.sendResponse("+statement+")");
this.callbackServer.sendJavascript(statement);
}
/**
* Get the port that the callback server is listening on.
*
* @return
*/
public int getPort() {
return this.callbackServer.getPort();
}
/** /**