You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2012/08/17 17:09:17 UTC

[2/2] js commit: Make how exec() sends & receives messages configurable.

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 <ag...@chromium.org>
Authored: Thu Aug 16 17:11:53 2012 -0400
Committer: Andrew Grieve <ag...@chromium.org>
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


Re: [2/2] js commit: Make how exec() sends & receives messages configurable.

Posted by Andrew Grieve <ag...@chromium.org>.
Hmm, this definitely happens to me too. It looks like the error doesn't
happen until you leave a page, and it's caused because there is a circular
reference between exec and callback.js.

Instead of throwing an error, require() is returning an empty object.

I'll fix it tomorrow, but probably we should make require() throw an error
in this case.




On Fri, Aug 17, 2012 at 12:03 PM, Simon MacDonald <simon.macdonald@gmail.com
> wrote:

> 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, <ag...@apache.org> 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 <ag...@chromium.org>
> > Authored: Thu Aug 16 17:11:53 2012 -0400
> > Committer: Andrew Grieve <ag...@chromium.org>
> > 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
> >
> >
>

Re: [2/2] js commit: Make how exec() sends & receives messages configurable.

Posted by Simon MacDonald <si...@gmail.com>.
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, <ag...@apache.org> 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 <ag...@chromium.org>
> Authored: Thu Aug 16 17:11:53 2012 -0400
> Committer: Andrew Grieve <ag...@chromium.org>
> 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
>
>