diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base old mode 100644 new mode 100755 index d971b939..a574e8d7 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -67,6 +67,9 @@ PhoneGap.addConstructor = function(func) { } } } + // Start listening for callbacks from Java + PhoneGap.JSCallback(); + // all constructors run, now fire the deviceready event var e = document.createEvent('Events'); e.initEvent('deviceready'); @@ -133,3 +136,48 @@ PhoneGap.run_command = function() { 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(); +} diff --git a/framework/src/com/phonegap/CompassListener.java b/framework/src/com/phonegap/CompassListener.java index ea1a73b2..b2d07eff 100644 --- a/framework/src/com/phonegap/CompassListener.java +++ b/framework/src/com/phonegap/CompassListener.java @@ -9,46 +9,97 @@ import android.hardware.SensorManager; import android.content.Context; 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{ - WebView mAppView; - Context mCtx; - Sensor mSensor; + + public static int STOPPED = 0; + 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) { - mCtx = ctx; - mAppView = appView; - sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE); + this.mCtx = (DroidGap)ctx; + this.mAppView = appView; + 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 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.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() - { - this.sensorManager.unregisterListener(this); + /** + * Stop listening to compass sensor. + */ + 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) { - // TODO Auto-generated method stub - + // TODO Auto-generated method stub } - + /** + * Sensor listener event. + * + * @param SensorEvent event + */ public void onSensorChanged(SensorEvent event) { + // We only care about the orientation as far as it refers to Magnetic North 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 + ");"); } } diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 84f3715f..782e11e0 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -72,6 +72,7 @@ public class DroidGap extends Activity { private CryptoHandler crypto; private BrowserKey mKey; private AudioHandler audio; + private CallbackServer callbackServer; private Uri imageUri; @@ -185,10 +186,38 @@ public class DroidGap extends Activity { if (accel != null) { 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) { + callbackServer = new CallbackServer(); gap = new Device(appView, this); accel = new AccelBroker(appView, this); launcher = new CameraLauncher(appView, this); @@ -211,7 +240,7 @@ public class DroidGap extends Activity { appView.addJavascriptInterface(crypto, "GapCrypto"); appView.addJavascriptInterface(mKey, "BackButton"); appView.addJavascriptInterface(audio, "GapAudio"); - + appView.addJavascriptInterface(callbackServer, "CallbackServer"); if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { @@ -227,6 +256,25 @@ public class DroidGap extends Activity { { 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(); + } /**