Hey Andrew, There is something not quite right with this commit. When I run Mob Spec I can reproduce this problem:
E/Web Console( 1420): TypeError: Result of expression 'exec.nativeToJsModes' [undefined] is not an object. at file:///android_asset/www/cordova.android.js:3741 All I needed to do to reproduce was to run mobile spec and navigate to the Audio Play/Record section. Can you take a look at it? Simon Mac Donald http://hi.im/simonmacdonald On Fri, Aug 17, 2012 at 11:09 AM, <[email protected]> wrote: > Make how exec() sends & receives messages configurable. > > This also deletes the cordova.shuttingDown and cordova.UsePolling flags. > > > Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/repo > Commit: > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/commit/5fecf16a > Tree: > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/tree/5fecf16a > Diff: > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/diff/5fecf16a > > Branch: refs/heads/master > Commit: 5fecf16a62155e74dfb8d9f666e24fcf21a22ae9 > Parents: 03a0aca > Author: Andrew Grieve <[email protected]> > Authored: Thu Aug 16 17:11:53 2012 -0400 > Committer: Andrew Grieve <[email protected]> > Committed: Fri Aug 17 11:08:15 2012 -0400 > > ---------------------------------------------------------------------- > lib/android/exec.js | 180 +++++++++++++++++++-------- > lib/android/platform.js | 23 +--- > lib/android/plugin/android/callback.js | 138 ++++++++++---------- > lib/android/plugin/android/polling.js | 55 ++++---- > lib/cordova.js | 4 - > 5 files changed, 225 insertions(+), 175 deletions(-) > ---------------------------------------------------------------------- > > > > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/5fecf16a/lib/android/exec.js > ---------------------------------------------------------------------- > diff --git a/lib/android/exec.js b/lib/android/exec.js > index 60403be..1bd12e8 100644 > --- a/lib/android/exec.js > +++ b/lib/android/exec.js > @@ -12,71 +12,141 @@ > * @param {String} action Action to be run in cordova > * @param {String[]} [args] Zero or more arguments to pass to the > method > */ > -var cordova = require('cordova'); > +var cordova = require('cordova'), > + callback = require('cordova/plugin/android/callback'), > + polling = require('cordova/plugin/android/polling'), > + jsToNativeBridgeMode, > + nativeToJsBridgeMode, > + jsToNativeModes = { > + PROMPT: 0, > + JS_OBJECT: 1, > + LOCATION_CHANGE: 2 // Not yet implemented > + }, > + nativeToJsModes = { > + POLLING: 0, > + HANGING_GET: 1, > + LOAD_URL: 2, // Not yet implemented > + ONLINE_EVENT: 3, // Not yet implemented > + PRIVATE_API: 4 // Not yet implemented > + }; > > -module.exports = function(success, fail, service, action, args) { > - try { > - var callbackId = service + cordova.callbackId++; > - if (success || fail) { > - cordova.callbacks[callbackId] = {success:success, fail:fail}; > - } > +function androidExec(success, fail, service, action, args) { > + try { > + var callbackId = service + cordova.callbackId++, > + argsJson = JSON.stringify(args), > + result; > + if (success || fail) { > + cordova.callbacks[callbackId] = {success:success, fail:fail}; > + } > > - var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, > action, callbackId, true])); > + if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT) { > + // Explicit cast to string is required on Android 2.1 to > convert from > + // a Java string to a JS string. > + result = '' + _cordovaExec.exec(service, action, callbackId, > argsJson); > + } else { > + result = prompt(argsJson, "gap:"+JSON.stringify([service, > action, callbackId, true])); > + } > > - // If a result was returned > - if (r.length > 0) { > - var v = JSON.parse(r); > + // If a result was returned > + if (result.length > 0) { > + var v = JSON.parse(result); > > - // If status is OK, then return value back to caller > - if (v.status === cordova.callbackStatus.OK) { > + // If status is OK, then return value back to caller > + if (v.status === cordova.callbackStatus.OK) { > > - // If there is a success callback, then call it now with > - // returned value > - if (success) { > - try { > - success(v.message); > - } catch (e) { > - console.log("Error in success callback: " + > callbackId + " = " + e); > - } > + // If there is a success callback, then call it now with > + // returned value > + if (success) { > + try { > + success(v.message); > + } catch (e) { > + console.log("Error in success callback: " + > callbackId + " = " + e); > + } > > - // Clear callback if not expecting any more results > - if (!v.keepCallback) { > - delete cordova.callbacks[callbackId]; > - } > - } > - return v.message; > - } > + // Clear callback if not expecting any more results > + if (!v.keepCallback) { > + delete cordova.callbacks[callbackId]; > + } > + } > + return v.message; > + } > > - // If no result > - else if (v.status === cordova.callbackStatus.NO_RESULT) { > - // Clear callback if not expecting any more results > - if (!v.keepCallback) { > - delete cordova.callbacks[callbackId]; > - } > - } > + // If no result > + else if (v.status === cordova.callbackStatus.NO_RESULT) { > + // Clear callback if not expecting any more results > + if (!v.keepCallback) { > + delete cordova.callbacks[callbackId]; > + } > + } > > - // If error, then display error > - else { > - console.log("Error: Status="+v.status+" Message="+v.message); > + // If error, then display error > + else { > + console.log("Error: Status="+v.status+" > Message="+v.message); > > - // If there is a fail callback, then call it now with > returned value > - if (fail) { > - try { > - fail(v.message); > - } > - catch (e1) { > - console.log("Error in error callback: "+callbackId+" > = "+e1); > - } > + // If there is a fail callback, then call it now with > returned value > + if (fail) { > + try { > + fail(v.message); > + } > + catch (e1) { > + console.log("Error in error callback: > "+callbackId+" = "+e1); > + } > > - // Clear callback if not expecting any more results > - if (!v.keepCallback) { > - delete cordova.callbacks[callbackId]; > - } > - } > - return null; > + // Clear callback if not expecting any more results > + if (!v.keepCallback) { > + delete cordova.callbacks[callbackId]; > + } > + } > + return null; > + } > + } > + } catch (e2) { > + console.log("Error: "+e2); > + } > +}; > + > +androidExec.jsToNativeModes = jsToNativeModes; > +androidExec.nativeToJsModes = nativeToJsModes; > + > +androidExec.setJsToNativeBridgeMode = function(mode) { > + if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaExec) { > + console.log('Falling back on PROMPT mode since _cordovaExec is > missing.'); > + mode = jsToNativeModes.PROMPT; > + } > + jsToNativeBridgeMode = mode; > +}; > + > +androidExec.setNativeToJsBridgeMode = function(mode) { > + if (mode == nativeToJsBridgeMode) { > + return; > + } > + if (nativeToJsBridgeMode == 0) { > + polling.stop(); > + } else if (nativeToJsBridgeMode == 1) { > + callback.stop(); > + } > + nativeToJsBridgeMode = mode; > + if (mode == 0) { > + polling.start(); > + } else if (mode == 1) { > + callback.start(); > + } > +}; > + > +// Start listening for XHR callbacks > +// Figure out which bridge approach will work on this Android > +// device: polling or XHR-based callbacks > +androidExec.initialize = function() { > + if (jsToNativeBridgeMode === undefined) { > + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); > + } > + if (nativeToJsBridgeMode === undefined) { > + if (callback.isAvailable()) { > + > androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET); > + } else { > + androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING); > } > } > - } catch (e2) { > - console.log("Error: "+e2); > - } > }; > + > +module.exports = androidExec; > > > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/5fecf16a/lib/android/platform.js > ---------------------------------------------------------------------- > diff --git a/lib/android/platform.js b/lib/android/platform.js > index 4439d30..cbd21bf 100644 > --- a/lib/android/platform.js > +++ b/lib/android/platform.js > @@ -3,32 +3,15 @@ module.exports = { > initialize:function() { > var channel = require("cordova/channel"), > cordova = require('cordova'), > - callback = require('cordova/plugin/android/callback'), > - polling = require('cordova/plugin/android/polling'), > exec = require('cordova/exec'); > > channel.onDestroy.subscribe(function() { > - cordova.shuttingDown = true; > + exec.setNativeToJsBridgeMode(-1); > }); > > - // Start listening for XHR callbacks > - // Figure out which bridge approach will work on this Android > - // device: polling or XHR-based callbacks > + // Use a setTimeout here to give apps a chance to set the bridge > mode. > setTimeout(function() { > - if (cordova.UsePolling) { > - polling(); > - } > - else { > - var isPolling = prompt("usePolling", > "gap_callbackServer:"); > - cordova.UsePolling = isPolling; > - if (isPolling == "true") { > - cordova.UsePolling = true; > - polling(); > - } else { > - cordova.UsePolling = false; > - callback(); > - } > - } > + exec.initialize(); > }, 1); > > // Inject a listener for the backbutton on the document. > > > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/5fecf16a/lib/android/plugin/android/callback.js > ---------------------------------------------------------------------- > diff --git a/lib/android/plugin/android/callback.js > b/lib/android/plugin/android/callback.js > index d41a164..4729678 100644 > --- a/lib/android/plugin/android/callback.js > +++ b/lib/android/plugin/android/callback.js > @@ -1,85 +1,85 @@ > var port = null, > token = null, > - cordova = require('cordova'), > - polling = require('cordova/plugin/android/polling'), > - callback = function() { > - // Exit if shutting down app > - if (cordova.shuttingDown) { > - return; > - } > + exec = require('cordova/exec'), > + xmlhttp; > > - // If polling flag was changed, start using polling from now on > - if (cordova.UsePolling) { > - polling(); > - return; > - } > +module.exports = { > + start: function callback() { > + xmlhttp = new XMLHttpRequest(); > > - var xmlhttp = new XMLHttpRequest(); > + // Callback function when XMLHttpRequest is ready > + xmlhttp.onreadystatechange=function(){ > + if (!xmlhttp) { > + return; > + } > + if(xmlhttp.readyState === 4){ > + // If callback has JavaScript statement to execute > + if (xmlhttp.status === 200) { > > - // Callback function when XMLHttpRequest is ready > - xmlhttp.onreadystatechange=function(){ > - if(xmlhttp.readyState === 4){ > + // Need to url decode the response > + var msg = decodeURIComponent(xmlhttp.responseText); > + setTimeout(function() { > + try { > + var t = eval(msg); > + } > + catch (e) { > + // If we're getting an error here, seeing the > message will help in debugging > + console.log("JSCallback: Message from Server: > " + msg); > + console.log("JSCallback Error: "+e); > + } > + }, 1); > + setTimeout(callback, 1); > + } > > - // Exit if shutting down app > - if (cordova.shuttingDown) { > - return; > - } > + // If callback ping (used to keep XHR request from timing > out) > + else if (xmlhttp.status === 404) { > + setTimeout(callback, 10); > + } > > - // If callback has JavaScript statement to execute > - if (xmlhttp.status === 200) { > + // If security error > + else if (xmlhttp.status === 403) { > + console.log("JSCallback Error: Invalid token. > Stopping callbacks."); > + } > > - // Need to url decode the response > - var msg = decodeURIComponent(xmlhttp.responseText); > - setTimeout(function() { > - try { > - var t = eval(msg); > - } > - catch (e) { > - // If we're getting an error here, seeing the > message will help in debugging > - console.log("JSCallback: Message from Server: " > + msg); > - console.log("JSCallback Error: "+e); > - } > - }, 1); > - setTimeout(callback, 1); > - } > + // If server is stopping > + else if (xmlhttp.status === 503) { > + console.log("JSCallback Server Closed: Stopping > callbacks."); > + } > > - // If callback ping (used to keep XHR request from timing > out) > - else if (xmlhttp.status === 404) { > - setTimeout(callback, 10); > - } > + // If request wasn't GET > + else if (xmlhttp.status === 400) { > + console.log("JSCallback Error: Bad request. Stopping > callbacks."); > + } > > - // If security error > - else if (xmlhttp.status === 403) { > - console.log("JSCallback Error: Invalid token. Stopping > callbacks."); > - } > + // If error, revert to polling > + else { > + console.log("JSCallback Error: Request failed."); > + > exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING); > + } > + } > + }; > > - // If server is stopping > - else if (xmlhttp.status === 503) { > - console.log("JSCallback Server Closed: Stopping > callbacks."); > - } > + if (port === null) { > + port = prompt("getPort", "gap_callbackServer:"); > + } > + if (token === null) { > + token = prompt("getToken", "gap_callbackServer:"); > + } > + xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); > + xmlhttp.send(); > + }, > > - // If request wasn't GET > - else if (xmlhttp.status === 400) { > - console.log("JSCallback Error: Bad request. Stopping > callbacks."); > - } > + stop: function() { > + if (xmlhttp) { > + var tmp = xmlhttp; > + xmlhttp = null; > + tmp.abort(); > + } > + }, > > - // If error, revert to polling > - else { > - console.log("JSCallback Error: Request failed."); > - cordova.UsePolling = true; > - polling(); > - } > - } > - }; > + isAvailable: function() { > + return ("true" != prompt("usePolling", "gap_callbackServer:")); > + } > > - if (port === null) { > - port = prompt("getPort", "gap_callbackServer:"); > - } > - if (token === null) { > - token = prompt("getToken", "gap_callbackServer:"); > - } > - xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true); > - xmlhttp.send(); > }; > > -module.exports = callback; > \ No newline at end of file > > > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/5fecf16a/lib/android/plugin/android/polling.js > ---------------------------------------------------------------------- > diff --git a/lib/android/plugin/android/polling.js > b/lib/android/plugin/android/polling.js > index 0d6e87e..406dc62 100644 > --- a/lib/android/plugin/android/polling.js > +++ b/lib/android/plugin/android/polling.js > @@ -1,33 +1,34 @@ > var cordova = require('cordova'), > period = 50, > - polling = function() { > - // Exit if shutting down app > - if (cordova.shuttingDown) { > - return; > - } > + enabled = false; > > - // If polling flag was changed, stop using polling from now on and > switch to XHR server / callback > - if (!cordova.UsePolling) { > - require('cordova/plugin/android/callback')(); > - return; > - } > > - var msg = prompt("", "gap_poll:"); > - if (msg) { > - setTimeout(function() { > - try { > - var t = eval(""+msg); > - } > - catch (e) { > - console.log("JSCallbackPolling: Message from Server: " > + msg); > - console.log("JSCallbackPolling Error: "+e); > - } > - }, 1); > - setTimeout(polling, 1); > - } > - else { > - setTimeout(polling, period); > - } > +function doPoll() { > + if (!enabled) { > + return; > + } > + var msg = prompt("", "gap_poll:"); > + if (msg) { > + try { > + eval(""+msg); > + } > + catch (e) { > + console.log("JSCallbackPolling: Message from Server: " + msg); > + console.log("JSCallbackPolling Error: "+e); > + } > + setTimeout(doPoll, 1); > + } else { > + setTimeout(doPoll, period); > + } > +} > + > +module.exports = { > + start: function() { > + enabled = true; > + setTimeout(doPoll, 1); > + }, > + stop: function() { > + enabled = false; > + } > }; > > -module.exports = polling; > \ No newline at end of file > > > http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/5fecf16a/lib/cordova.js > ---------------------------------------------------------------------- > diff --git a/lib/cordova.js b/lib/cordova.js > index dbaf478..ea5f7cd 100644 > --- a/lib/cordova.js > +++ b/lib/cordova.js > @@ -136,10 +136,6 @@ var cordova = { > window.dispatchEvent(evt); > } > }, > - // TODO: this is Android only; think about how to do this better > - shuttingDown:false, > - UsePolling:false, > - // END TODO > > // TODO: iOS only > // This queue holds the currently executing command and all pending > >
