From 7eb3e5d13955725f44f72cb816c2fc2b2f376e18 Mon Sep 17 00:00:00 2001
From: filmaj <maj.fil@gmail.com>
Date: Wed, 16 May 2012 19:38:11 -0700
Subject: [PATCH 01/13] [CB-659] create script should work on android

---
 bin/create.bat                            |  2 +-
 bin/create.js                             | 61 ++++++++++++++------
 bin/templates/project/cordova/create.bat  |  2 +
 bin/templates/project/cordova/create.js   | 70 +++++++++++++++++++++++
 bin/templates/project/cordova/debug.bat   |  0
 bin/templates/project/cordova/emulate.bat |  0
 bin/templates/project/cordova/log.bat     |  0
 7 files changed, 117 insertions(+), 18 deletions(-)
 create mode 100644 bin/templates/project/cordova/create.bat
 create mode 100644 bin/templates/project/cordova/create.js
 create mode 100644 bin/templates/project/cordova/debug.bat
 create mode 100644 bin/templates/project/cordova/emulate.bat
 create mode 100644 bin/templates/project/cordova/log.bat

diff --git a/bin/create.bat b/bin/create.bat
index b9c9392b..7182d21e 100644
--- a/bin/create.bat
+++ b/bin/create.bat
@@ -1 +1 @@
-cscript create.js
\ No newline at end of file
+cscript bin\create.js %*
\ No newline at end of file
diff --git a/bin/create.js b/bin/create.js
index 24e70325..96e61eca 100644
--- a/bin/create.js
+++ b/bin/create.js
@@ -25,8 +25,9 @@
  */
 
 function read(filename) {
+    WScript.Echo('Reading in ' + filename);
     var fso=WScript.CreateObject("Scripting.FileSystemObject");
-    var f=fso.OpenTextFile(filename, 1, true);
+    var f=fso.OpenTextFile(filename, 1);
     var s=f.ReadAll();
     f.Close();
     return s;
@@ -40,8 +41,24 @@ function write(filename, contents) {
 function replaceInFile(filename, regexp, replacement) {
     write(filename, read(filename).replace(regexp, replacement));
 }
-function exec(s) {
+function exec(s, output) {
+    WScript.Echo('Executing ' + s);
     var o=shell.Exec(s);
+    while (o.Status == 0) {
+        WScript.Sleep(100);
+    }
+    WScript.Echo("Command exited with code " + o.Status);
+}
+
+function fork(s) {
+    WScript.Echo('Executing ' + s);
+    var o=shell.Exec(s);
+    while (o.Status != 1) {
+        WScript.Sleep(100);
+    }
+    WScript.Echo(o.StdOut.ReadAll());
+    WScript.Echo(o.StdErr.ReadAll());
+    WScript.Echo("Command exited with code " + o.Status);
 }
 
 var args = WScript.Arguments, PROJECT_PATH="example", 
@@ -61,6 +78,14 @@ var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
 var TARGET=shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s([0-9]).*/)[1];
 var VERSION=read('VERSION').replace(/\r\n/,'').replace(/\n/,'');
 
+WScript.Echo("Project path: " + PROJECT_PATH);
+WScript.Echo("Package: " + PACKAGE);
+WScript.Echo("Activity: " + ACTIVITY);
+WScript.Echo("Package as path: " + PACKAGE_AS_PATH);
+WScript.Echo("Activity path: " + ACTIVITY_PATH);
+WScript.Echo("Manifest path: " + MANIFEST_PATH);
+WScript.Echo("Cordova version: " + VERSION);
+
 // clobber any existing example
 
 /*
@@ -84,24 +109,26 @@ exec('ant.bat -f framework\\build.xml jar');
 // copy in the project template
 exec('cmd /c xcopy bin\\templates\\project '+PROJECT_PATH+' /S /Y');
 
+// copy example www assets
+exec('cmd /c xcopy ' + PROJECT_PATH + '\\cordova\\assets ' + PROJECT_PATH + ' /S /Y');
+
 // copy in cordova.js
-exec('cmd /c copy framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
+exec('cmd /c copy framework\\assets\\js\\cordova.android.js '+PROJECT_PATH+'\\.cordova\\android\\cordova-'+VERSION+'.js /Y');
 
 // copy in cordova.jar
-exec('cmd /c copy framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
+exec('cmd /c copy framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\.cordova\\android\\cordova-'+VERSION+'.jar /Y');
 
-// copy in default activity
-exec('cmd /c copy bin\\templates\\Activity.java '+ACTIVITY_PATH+' /Y');
+// copy in xml
+exec('cmd /c copy framework\\res\\xml\\cordova.xml ' + PROJECT_PATH + '\\.cordova\\android\\cordova.xml /Y');
+exec('cmd /c copy framework\\res\\xml\\plugins.xml ' + PROJECT_PATH + '\\.cordova\\android\\plugins.xml /Y');
 
-// interpolate the activity name and package
-replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
-replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
+// write out config file
+write(PROJECT_PATH + '\\.cordova\\config',
+  'VERSION=' + VERSION + '\r\n' +
+  'PROJECT_PATH=' + PROJECT_PATH + '\r\n' +
+  'PACKAGE=' + PACKAGE + '\r\n' +
+  'ACTIVITY=' + ACTIVITY + '\r\n' +
+  'TARGET=' + TARGET);
 
-replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
-replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
-
-/*
-# leave the id for launching
-touch $PROJECT_PATH/package-activity
-echo $PACKAGE/$PACKAGE.$ACTIVITY >  $PROJECT_PATH/package-activity 
-*/
+// run project-specific create process
+fork('cscript.exe ' + PROJECT_PATH + '\\cordova\\create.js');
\ No newline at end of file
diff --git a/bin/templates/project/cordova/create.bat b/bin/templates/project/cordova/create.bat
new file mode 100644
index 00000000..cfde65a5
--- /dev/null
+++ b/bin/templates/project/cordova/create.bat
@@ -0,0 +1,2 @@
+echo "BALLS"
+cscript cordova\create.js
\ No newline at end of file
diff --git a/bin/templates/project/cordova/create.js b/bin/templates/project/cordova/create.js
new file mode 100644
index 00000000..b87f0a10
--- /dev/null
+++ b/bin/templates/project/cordova/create.js
@@ -0,0 +1,70 @@
+var shell=WScript.CreateObject("WScript.Shell");
+
+function exec(s, output) {
+    WScript.Echo('Executing ' + s);
+    var o=shell.Exec(s);
+    while (o.Status == 0) {
+        WScript.Sleep(100);
+    }
+    WScript.Echo("Command exited with code " + o.Status);
+}
+function read(filename) {
+    WScript.Echo('Reading in ' + filename);
+    var fso=WScript.CreateObject("Scripting.FileSystemObject");
+    var f=fso.OpenTextFile(filename, 1);
+    var s=f.ReadAll();
+    f.Close();
+    return s;
+}
+function write(filename, contents) {
+    var fso=WScript.CreateObject("Scripting.FileSystemObject");
+    var f=fso.OpenTextFile(filename, 2, true);
+    f.Write(contents);
+    f.Close();
+}
+function replaceInFile(filename, regexp, replacement) {
+    write(filename, read(filename).replace(regexp, replacement));
+}
+
+// working dir
+var PWD = WScript.ScriptFullName.split('\\cordova\\create.js').join('');
+
+var fso=WScript.CreateObject("Scripting.FileSystemObject");
+var f=fso.OpenTextFile(PWD + '\\.cordova\\config', 1);
+while (!f.AtEndOfStream) {
+  var prop = f.ReadLine().split('=');
+  var line = 'var ' + prop[0] + '=' + "'" + prop[1] + "';";
+  eval(line); // hacky shit to load config but whatevs
+}
+
+var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
+var ACTIVITY_PATH=PWD+'\\src\\'+PACKAGE_AS_PATH+'\\'+ACTIVITY+'.java';
+var MANIFEST_PATH=PWD+'\\AndroidManifest.xml';
+
+exec('android.bat create project --target ' + TARGET + ' --path ' + PWD + ' --package ' + PACKAGE + ' --activity ' + ACTIVITY);
+
+// copy in activity and other android assets
+exec('cmd /c xcopy ' + PWD + '\\cordova\\templates\project\* ' + PWD +' /Y /S');
+
+// copy in cordova.js
+exec('cmd /c copy ' + PWD + '\\.cordova\\android\\cordova-' + VERSION + '.js ' + PWD + '\\assets\\www /Y');
+
+// copy in cordova.jar
+exec('cmd /c copy ' + PWD + '\\.cordova\\android\\cordova-' + VERSION + '.jar ' + PWD + '\\libs /Y');
+
+// copy in res/xml
+exec('cmd /c md ' + PWD + '\\res\\xml');
+exec('cmd /c copy ' + PWD + '\\.cordova\\android\\cordova.xml ' + PWD + '\\res\\xml /Y');
+exec('cmd /c copy ' + PWD + '\\.cordova\\android\\plugins.xml ' + PWD + '\\res\\xml /Y');
+
+// copy in default activity
+exec('cmd /c copy ' + PWD + '\\cordova\\templates\\Activity.java ' + ACTIVITY_PATH + ' /Y');
+
+// interpolate the activity name and package
+replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
+replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
+
+replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
+replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
+
+WScript.Echo('DONE!');
\ No newline at end of file
diff --git a/bin/templates/project/cordova/debug.bat b/bin/templates/project/cordova/debug.bat
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/templates/project/cordova/emulate.bat b/bin/templates/project/cordova/emulate.bat
new file mode 100644
index 00000000..e69de29b
diff --git a/bin/templates/project/cordova/log.bat b/bin/templates/project/cordova/log.bat
new file mode 100644
index 00000000..e69de29b

From 1f45503e2f313e9d13aefc73660c225c0edde3ff Mon Sep 17 00:00:00 2001
From: filmaj <maj.fil@gmail.com>
Date: Thu, 17 May 2012 10:59:38 -0700
Subject: [PATCH 02/13] [CB-659] create script for android on windows now works
 fully. also pulls down commons-codec jar appropriately

---
 bin/create.js                           | 44 +++++++++++++++++++++++--
 bin/templates/project/cordova/create.js |  5 ++-
 2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/bin/create.js b/bin/create.js
index 96e61eca..a15db7a2 100644
--- a/bin/create.js
+++ b/bin/create.js
@@ -64,6 +64,9 @@ function fork(s) {
 var args = WScript.Arguments, PROJECT_PATH="example", 
     PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
     shell=WScript.CreateObject("WScript.Shell");
+    
+// working dir
+var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
 
 if (args.Count() == 3) {
     WScript.Echo('Found expected arguments');
@@ -86,7 +89,7 @@ WScript.Echo("Activity path: " + ACTIVITY_PATH);
 WScript.Echo("Manifest path: " + MANIFEST_PATH);
 WScript.Echo("Cordova version: " + VERSION);
 
-// clobber any existing example
+// TODO: clobber any existing example
 
 /*
 if [ $# -eq 0 ]
@@ -101,13 +104,50 @@ exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --p
 // update the cordova framework project to a target that exists on this machine
 exec('android.bat update project --target '+TARGET+' --path framework');
 
+// pull down commons codec if necessary
+var fso = WScript.CreateObject('Scripting.FileSystemObject');
+if (!fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.6.jar')) {
+  // We need the .jar
+  var url = 'http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip';
+  var savePath = ROOT + '\\framework\\libs\\commons-codec-1.6-bin.zip';
+  if (!fso.FileExists(savePath)) {
+    // We need the zip to get the jar
+    var xhr = WScript.CreateObject('MSXML2.XMLHTTP');
+    xhr.open('GET', url, false);
+    xhr.send();
+    if (xhr.status == 200) {
+      var stream = WScript.CreateObject('ADODB.Stream');
+      stream.Open();
+      stream.Type = 1;
+      stream.Write(xhr.ResponseBody);
+      stream.Position = 0;
+      stream.SaveToFile(savePath);
+      stream.Close();
+    } else {
+      WScript.Echo('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
+    }
+  }
+  var app = WScript.CreateObject('Shell.Application');
+  var source = app.NameSpace(savePath).Items();
+  var target = app.NameSpace(ROOT + '\\framework\\libs');
+  target.CopyHere(source, 256);
+  
+  // Move the jar into libs
+  fso.MoveFile(ROOT + '\\framework\\libs\\commons-codec-1.6\\commons-codec-1.6.jar', ROOT + '\\framework\\libs\\commons-codec-1.6.jar');
+  
+  // Clean up
+  fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.6-bin.zip');
+  fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.6', true);
+}
+
+
 // compile cordova.js and cordova.jar
 // if you see an error about "Unable to resolve target" then you may need to 
 // update your android tools or install an additional Android platform version
 exec('ant.bat -f framework\\build.xml jar');
 
 // copy in the project template
-exec('cmd /c xcopy bin\\templates\\project '+PROJECT_PATH+' /S /Y');
+exec('cmd /c xcopy bin\\templates\\project\\* '+PROJECT_PATH+' /S /Y');
 
 // copy example www assets
 exec('cmd /c xcopy ' + PROJECT_PATH + '\\cordova\\assets ' + PROJECT_PATH + ' /S /Y');
diff --git a/bin/templates/project/cordova/create.js b/bin/templates/project/cordova/create.js
index b87f0a10..2bf56031 100644
--- a/bin/templates/project/cordova/create.js
+++ b/bin/templates/project/cordova/create.js
@@ -9,7 +9,6 @@ function exec(s, output) {
     WScript.Echo("Command exited with code " + o.Status);
 }
 function read(filename) {
-    WScript.Echo('Reading in ' + filename);
     var fso=WScript.CreateObject("Scripting.FileSystemObject");
     var f=fso.OpenTextFile(filename, 1);
     var s=f.ReadAll();
@@ -44,7 +43,7 @@ var MANIFEST_PATH=PWD+'\\AndroidManifest.xml';
 exec('android.bat create project --target ' + TARGET + ' --path ' + PWD + ' --package ' + PACKAGE + ' --activity ' + ACTIVITY);
 
 // copy in activity and other android assets
-exec('cmd /c xcopy ' + PWD + '\\cordova\\templates\project\* ' + PWD +' /Y /S');
+exec('cmd /c xcopy ' + PWD + '\\cordova\\templates\\project\\* ' + PWD +' /Y /S');
 
 // copy in cordova.js
 exec('cmd /c copy ' + PWD + '\\.cordova\\android\\cordova-' + VERSION + '.js ' + PWD + '\\assets\\www /Y');
@@ -67,4 +66,4 @@ replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
 replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
 replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
 
-WScript.Echo('DONE!');
\ No newline at end of file
+WScript.Echo('Create completed successfully.');
\ No newline at end of file

From 0850229c9ff9e81021141ae8edcd0ccd655ffe74 Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Thu, 17 May 2012 11:14:23 -0700
Subject: [PATCH 03/13] [CB-804] ADded proper cordova icon sizes for the create
 script

---
 .../project/res/drawable-hdpi/icon.png        | Bin 0 -> 6080 bytes
 .../project/res/drawable-ldpi/icon.png        | Bin 0 -> 3096 bytes
 .../project/res/drawable-mdpi/icon.png        | Bin 0 -> 4090 bytes
 .../project/res/drawable-xhdpi/icon.png       | Bin 0 -> 7685 bytes
 .../templates/project/res/drawable/icon.png   | Bin 5800 -> 7685 bytes
 5 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bin/templates/project/cordova/templates/project/res/drawable-hdpi/icon.png
 create mode 100644 bin/templates/project/cordova/templates/project/res/drawable-ldpi/icon.png
 create mode 100644 bin/templates/project/cordova/templates/project/res/drawable-mdpi/icon.png
 create mode 100644 bin/templates/project/cordova/templates/project/res/drawable-xhdpi/icon.png
 mode change 100755 => 100644 bin/templates/project/cordova/templates/project/res/drawable/icon.png

diff --git a/bin/templates/project/cordova/templates/project/res/drawable-hdpi/icon.png b/bin/templates/project/cordova/templates/project/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d27634485b3a20d8a0188107e84812f4f71084b
GIT binary patch
literal 6080
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

literal 0
HcmV?d00001

diff --git a/bin/templates/project/cordova/templates/project/res/drawable-ldpi/icon.png b/bin/templates/project/cordova/templates/project/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd5032a4f2e0b734d4bdece13564c4b55de963a6
GIT binary patch
literal 3096
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
NTPp`rk)>zke*nlqMCSki

literal 0
HcmV?d00001

diff --git a/bin/templates/project/cordova/templates/project/res/drawable-mdpi/icon.png b/bin/templates/project/cordova/templates/project/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..e79c6062c1dcd9e98f092dbd7e51b5b76dd897d2
GIT binary patch
literal 4090
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

literal 0
HcmV?d00001

diff --git a/bin/templates/project/cordova/templates/project/res/drawable-xhdpi/icon.png b/bin/templates/project/cordova/templates/project/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ec7ffbfbdafc58df28e6e89041ee21acd0f6177e
GIT binary patch
literal 7685
zcmbVxWmr`I+Af`gbl1=-H9_~#sYrJZCBqN{L&uOJC5VKifRvPkbcrC{(k&n$C7?)o
zaPR$p&-t*g_rqD&wbrkm`+j<TSnHYYBQ-J-CK4<xEHVvsWxc!6=I>2JaQEEEKug>W
zj3^Zols?iCg|_vAW5FDdc5t8u!qy3{2e)<b_Z)!BU}51pyBL_DOmwuR?2!l|+rKhG
zeh7~{G!~YOyq|}y{Zlv!Xa{$4ahK&hYVG6%x;V&k8jI?HbUc*c&MxW!UU2<@M+Wu*
zPwgcgIOXMlGJaBb1PD0F7U+j?bN80=ljZyuuhiZA@3t@}@LwXRr?Q;?Hp)at7pR2v
zf&)c`K!WxlF)&a}L<lSbk`$2?075`ukT4h`3=$UvgQP^or9fcde=g2DZC(zJQhLg&
z|Iu}~lI3(pp**C7h0$oV5E?3k^l}mgOG-)#gCN2Xh~S-sptrv}%GOWN-J9zl24%Rn
zy_bsz$_42T{L5%-hx9?oa^6|`Zx;|AIy(PD?C$-aK;4B**w5BO7%T)5Mj-zB^)G2}
zlpg&5n(<$yy$$?5;KF)vZ={cx{arpBx&8s)#qNJs^jGlC8mY%#E_X?>byG&#`yk-%
zC=F#<&bt#K2Nwq^Wtb{hQd~(=LJ<NMlK_KZA~2}3q$F4z27-yJh=cy&_-|NIkf@lb
zvWSG3m<SjQRsl<jN`fR{FtDhEsyGw^6Z;3N;qHyHb+?EAqu1q5?|-o3|0`BX$qR0a
zLV6h>k#7G~fUYwVh4gktdH|L5MTNjX9b0=B_rE)T6ZCI?mEm44zHkRsFC+r^uLw)I
z{1*!#%Bm2U5=aax3W5B0&;N~e`2UHT@SQQjf6L?lDVKk??rQMw^nZ%~Zt*|u19!jc
z8LzwMpz=!4#=>HPYAC}D{1(1j`p0w6n*Oo!`L$G62MwHGAq_;gcU#5ad2|_3X}=Ex
ztJ_3G#zt~{1_DDBl4Ij(qB?Ot53)xBlNHqSf!xe)5sDc5evTC%pqvwV^(=HVCv&;k
zX(xAgx3FdJB)EE|BILZK{K^nLcF3t$@u9lBW&bj96>UVmnf>us`s)u8t9;!fmhUy#
z4IZX7TH-Z)CzV((s~^c8IS*J&=rBHNb7^QdczDA%xz5;UV*bsepA|g^%}*FYT&#6&
zUEOv|*iA2%jEMnUdgtl^U4bh}y`kH0ofVji|2zmvR`TrChKk!6a78E6%-TCFVftk-
zQ`MUn{W)*QwOYb`6)5Do#sYGGkvS+V61(9#iDlUo(2`1osE;CIfF<`%%vjqmJgki-
zOGYzA_9dQ%tbChOU2U*@Y;teSZ|~0=`ZN5O*VH2vLeC@*954^u$X@k(UJsK+0r8o2
zgyiG<L&?=f`?|z>P8{8$$-WoW5;nz8dar+8r66*(9ay{I56ZA$A}aeZHXUsw!nx~B
zs?)^Wzk_7JYY4l^gAIfNcR85nv6wgNBZxJwm_X~~o<YaaAceA!!HI;;0mmX)vL{na
z+x3kF_o$^jg0_vmgpG0Hx_>zLOOb=B6<*cC6$^u3tx$f#XJ7Q>T2f9q<U7vz0~vE+
z-W6l!tEXbGr{L3Gzo1A=Gv~SYtw*V&r)X|&!`*BbLEAXVv@4b6!O%~S<Y7LRl=<2b
zpjtQIRZe;M!4=ocrU$usaghkqjoY$fK_+9ntL7lq`E;(wwKu9Lmw1-`;Ipt7^zM$t
z;i`+=QmK8Y<bH+-qFDVEW?$3a%$~Um1>@3RF?iIj0C#BQr*Jny@7=0-s}6U!WX*JF
z_l3LlX;ct@O$j-4bnT*+xE&Rv&l_;C32-E^x*V-o^Y6&70Lp-sK`I*32c(G)(ln2c
zcVpIa$(P8syyi784YyA#9?KKj8}kBFM>zG*PcJvj<j6C$^3n!{mV&<%u06B9qCF0h
zPvE8TxW1o=MxtcHMt?eaPL{gsTBBLi)Nfwztb`BQrz-B@t#5stRJ!UA&Riqi@XICq
zq(~fcnMzJfTuIMe_O$E>_0<a`+KN@(AqU7|&FOA#wr<ZgZl^=E(vq*V9J2+Tg(r2D
zk+a0um1SQDTuQlgDu?H}mX;%`2!n|GR|0xL#86pTLN4Pf7~0rLJ%|%04(L~ZfFHTj
z;eZ86V3=8)5-c*1H^u>%|E8F3Ea+YypPwhTWM$R+805+JiKy7KemQ)RXErAE_kxzk
z(ZD9~#pEmEyxx=761TUiaHUYT{_2Ib_bDM}D;<7}Bt#EfrE7={?G^vjdMIfU^{5#e
z8{ao;+$A+zm996Jmc*MSsqW{TEtshqz2}Fx;IzohRwUa~-)E&{S_<J}NPZTPtEz2c
zX&J$9T*8q)f`M%&LjWe~EIi??tdif3_Gz`b937s#|CDX$U6Y%--)sSL@>uO&B+q~0
z!|8Odx*zqHaF);B^8Tx}RwUs0X^}Sk=;MjBI1c6(tG|rK5DX@__9Oi98v~?E#lxyn
zddW?IAl)CvlRMfwim|H=Ys|+Afa_duhGQG<cU9z3DoMSFT`rZSt;UHf+?ccoZ?GdG
zH*QT{AL9hbo~_+@d|f-x^<6fRm`SK5Q2R<5%-X*%y@k)pXSVT8%X|FC56JaY-5+f8
z-*1v3A1tT{Ua5JQ&f{4_dph#9$QQ6KJA_ZngRDblr;<+2y**0vZQDEElURL9Rc*TT
zcmAHnsz4YH*A4W_scBA&vA%4cI#%oa<?@-H#AS*Ox#mZq2It!d6E5MA6Wm#8jwf$O
zh<>sWM*yP~aHIg^go}lF$PZ{0NcnuKTnkWfYyJF^<er@YKH&%()ikvgEhX(Z?%wz)
zzp?0|{->UYy{N+Vklo=wU30w+YZoZW(;JD9P(HV=5)YY_R~Otj>8gEE##H_WBbV<f
zUYW-J$)CsV@$^z+<>PV`eQ#G9%;@3l%W`l<<A9(0QZeA;h4QyCMe@P$^8`V^pZmf_
z98gy3TfF{SUx-aLN?Kp|BmHXB$aNzyj#05L_AFB}Z!h9Rp|dy_+HA?pRTpc8-^eYI
zR0y`rT#^thB|v|rY(E?PWpg%I<bkILDPS+@bmqgoZDXW=>f%Byl$1#NATC)m)N6!`
zn5_00BW1$+`mi529(nu+2S(%U!j}=<*Y`Vh$OS;3xk0bZ&5x_C1D`mXQWLjDSG5S@
z%5g-*^A@q!hPE;&Di~N)I5?UCDX4n#L(>`sS`ZsCzAwkIh`eBiymhhCgLZc0pJXR7
z$O1uzX2=tJQi0549+tonyT?ofx*0cVOhGj!Ro0#NjuLe8r8BTo?F9UJz7kEbMSE+Y
zcZK?>;`@uIx0E1!cHc7xphi)`LXZ34Sk2*V6VN}mBPQlH-FRMsxAnAS7xpZ_M7VBt
zbT0>@3S*Ku>L?iu!I_yq6~IcA^Mi=(8CX9x9$2R{!$r70aAQIrRY=zsZ|32DMuPJ7
zHJN?Y;G=1B>i`8@yl~X7^gccH@E14^3bGmzxP%;04I4%n>%3ICL0Pw?((WEj<)4pl
z(L^kblZ0&J@6+yP8C{R76faRdA5v3h38(OP05Sl0>upGHfB!~Cx}?3#>T+l}b}m<+
z^H~n<Dyw58uxJy(Is4qcY*{dFG}S5t=y6p;@FX&D$=r`4u7CNSC|`l+l*az#?4ilW
z^Y`X`P2JS-tx~cP*fxx!&kUEXVg|U`sb)^#L1FIv{wD7pinLx{t}T}0cOD<}t$i#B
zp@=KiD_0fHK2izLBUrEd0x&T*-*<j1od{r)^>~afIb$;chy{Z44VE`Z5{lJR{#5JH
zuit`LDc@aX@%2WKQNE)r*rBiDij1ED%^8;g%8Ca$)yh5En2M1FvL>dcbY*+>2{?Xj
z-d{hzFlYw4erlEWi6Z2QjXkpK&BB{q`n6XmPToduJwug24^f!q9zAc<)pai0jwokc
zF?ji4^Tt70N0BZp$Z{&kUvL_WuRH<xl-81gmHgl^hRXGYR5iliph~z112pKT%=CR^
zS;%eA^Ft&zy$egemNI{@-GF$V$S*7wb)`u<jpeRjIlkJ`*g;G>n~vX>(K~yfe}AN0
z?`=+MZeeFNVL)I2M$^i>VYj#*z<?n5vzsEj3=7V6&z|8xwc|9$h2Sbm8ul?zS=)=}
zXVDgKG1-MSe<#@-fO95k1yb6w9oWcBE2LQ#69W6lzVFd{Vs8~|+nLk+<d>)_)=^91
zF?`Kbo1dzP#>UPCp&9_kTm>*4Lgm;Us>H}X5ZUHa9xHhx*qm5PxA*CD2=xJJe^}}|
z4_B2W4#`eb#nAP!%%C6-Yym0(^itM*uBS+$@yE}MKzq>lxj`-kWz2C34*d^0mc!|A
zCIqkJ^Oxb&6!ex_?`f&BB9<Oy_cBKs;}!QVEnWLiKBxHUU9Zzt&A!x5s0*>MIj!qD
zHJ|WiqHHwdd2__dMDr_Onm2(VzfzxX*}{xqc*s*k5O2#%k`g<Wz*%G5HMg(LGsU@l
z@!nz7b%D~js5s&1hKxtqVH|cyJQ~MVdtD7Uc*1I8W?J@zWt`_{Vou};pB$MR0yp5#
zb~QHdO8P=kHz#hamh1OLC`0|Eh%-`q9K$bxcnep+=J>|M79>*1nx6usU3t0+opRSE
zkXa|&(Um%q#lMpG0~qx=hP8VU0Q1yzj=5QC7|N0$*fiAYK128>t8;*CK1_CQ$bI=^
z;Pv4$q0NLe)*s)o2j8+<NLU)e)uswW(b~FgPR<{QzGt~meDp6WOX0Ti1L*nUJilc(
z@W&L87-n9({t#&6sEdrX_Cy~<<Eq{35#HDlUh!m(Kb$9>-%{b$<?%XXI^ugnOcio`
zN%~ytO@|h5Z2riyES^&T5hmv3*mQaj$tDMIHzt>eLMZJ$WyN##WL%I1;yuLw{>^&|
zAc53E)!oyMLTQppXnKiOtB##KuacO~m|O$0?CoQeBK>6<o9fZ*$F%^DSuzIN4oBBH
z>`LBuqdK&8rXn3UoeyYVs2st^<RscciS8+mf0eHZWf%DQGor2$P|%9oP+dED5_};L
zfqTP&>(*MgCB>M4el0lIX*@=)xp&U3LE}O+xHzuEKMOhw%b@#~kKS<TK01i#c1zA?
z;x@CnID*j3Xc`{eWW9{CPVE8iTAP`LV~mD)$c``3-MP7szI6w;ey(Gf!w)e=Q`}ry
zNScavbNMT@US35$Juqx@3@|h<rgV?PHimmjK2^HVbxj5ts2fj~(rfqhylOGkY%7Qi
zaE=W8dLrT-2}*~Q^)N#$9&N>`Vrm{1J>ihspnX9Yi8JxIQed)jpBtO<+)j<erH$-Z
z88FVbU_gRDJNQfdrw%oB7Q9QK_@|-h@J}>jtON~N>Z-*XI{6rtH5`MXO1ex)kV4w`
z;>^Y}>gj?+0@~m`aM)+5QU{BCR)dTAxh2fOb>pk46Wpdk0^v+O&i;21do-EafZJ0h
zG9dl5r-@ll#kptc)cd(u?AKq33z*TZWb1{ZGmkh#r;Rs1+XxWZInbpTi-z@F3DB-K
zurw$27EMFB8q#xh3xClNJu)&fjv_E>f182Rrb+wS@F5#}@e$*apmfVk)=s@mpSoqz
zL{Cje(q_Lvpq-+kzCF#kLyNm<Q!%X5$p$wgbt3*gB<wrhJSmS4p5?Tw;tP>rRauzk
z-X@?H#r;65S);1rMLqT5gWM9DuX~u&&F1dYcQVp(*alYo33T<nSO`Xq`3!DzX^3-E
zi!$AN*ZYS2EzE9Lu-aa4FJ;;LgZ2-uN7F6uyCVx<GoX4Yr_N%?s2$A~Y{+gxe$14S
zsA(5>X?Ro`M3Shs?;9{Huy8u_)5NVIb_y^PQscj<TpPdYk(1r~+y{~;Tc5eSs&wsA
zkLyuV{&W=(2--tO5K!K;H8$sFe@xCxWI&|!NsyDm-8c^4#!p7JDRsTUS0MtxNwKw0
zd!}GAL#6S+W}msnLO`jY(>%K~b^=t$pa6O>KUCQE1sI@RMTu+DF<+<iyUR$_W@&rJ
zX5;4*l5}o}NNk9RvmQn7(EXeJ{hDut1Yr<k|D{oi@L24ZX32uQ8&{Np6cUi>N9xL6
z3>B5f`Fv@n=Hbl^Iws4rWHcOZRh1b1VZ!)Hs%R<U&$xRk9GtL8OI^~ttou73^Ye=_
zDX>`dA7GpJ@aMHxe=zP!+}C__^#j^oYcw6IwI(FgGlL;FZ@;Jg=7X_?Nu*FDuz=%@
z7^!VUCA$WE&0;;d|L6<qRjI5)s?jT(+XX<jbggxh@9VnL<mJ(Rkto9m>`9rsbeR<s
z6O=|eY5{=Z*<A<ZqMR*HFx29Y{j^Od)W1Nd3FITGeSL6Nax<c<bER;qM+I=1St*+i
zh6qbE(~ftzG(OlAoa*BX=~0kBEz+LDZfwCzOYbDh>->f1S4y1uVHJ<Cp!%&1EQ&c(
zh~C8+1&b0dX=h90Bx;=lAsw+{DDfkHf@$xMXFe_d0d>W=$PjyPeUKtgBTkP}1E)&O
zdkONV!Xc!$ujKK9SA$tw-tu+}(kWq}NzYMu3{mu)Hny!4?3K_;YMcX&!)x=J^G_wf
zh~DpQVw48kCWxV;4FOqPe}(ca$Jfzcwuf|}U4Hpb%zKRWaR5@|WCx1p#c?nFa9?Cl
z_=+nZge7U)eMEMx6+iEK0$Hxqc_I>Cl-=pT>!X!E9t~n#g1Kc{>rzWBgtN5~@ce1=
z;7_$`2^&w==ZL=2fh(vysjvhvJjrlI;9jrs5JPMXK5w7(Jxfj4MqTeQr+h7;UN?+n
z@r3$clmB``Tp`f*PFZ+zd@HQLwopi`%B`Y&d8L5k1QVk};W!M>9ISN;ODupwp_gh$
zix$%sXb@fgyHc$Qkm!J;No2;0w}Pv&t{+iKGv0CLy;>{+D(UU`B?dT<^pS3NLDGfK
zhm5BfQ+(*^JB>o@-T|4z{e~GV(NuEFvL?=aN?(P?TtmLey##`S19VzJ>nT5YrHq+D
zVVW6!hohsf7!+1EeOrOCR9@aqM@?kw6;<~G6k9JNQPj0vr%y4pv4Z0a`5%h8i4o&I
z6OMa?<@_?_u1W>y;WmP_nn?fQZM{a}-L!!cLqLtF`pZw;WI!CfBtD|Nl_Uq%VEKn9
zHXzu{J-dfPw(U*Jq^Xr>trT((2=0?01<sGA6Y6`zG8&bCL_R7x!BgM6(v6d%(9*IR
zA7bP?xAlM*(Qjx*KEa;D-qVtXy$$gAr7)-CM157ChV7YP<}&CR_sn3la6_9dBJbT<
zVhYIv3(_iqy?kkvkl#{Ibz{Z(F|QJd5ZZrrbZy%$sO!$)UxGM0Afm#U_v4>2;lGIy
z94=vnIcjO34vCu3P`p3-ARA9U28Fa7s>35~eOgZ9%Y?@_kav~HfpV&_mEXe^cdvz~
zQdA;DaoTO8I}@W1?=>tNZOgGuy--;Qyja7YuuR7syYg0hwpWVTP^QZU{v6iN)6FWP
zNoY-Ep?cxb`w_bo++-0b+2i&&{`*uXeN}&U;oEuf?Q0Dj*rd_J=K%hQ=}vL<r<Rz(
zZLR)9*22L1@rj96Dt#<EFUVe!>tXD?Z$zGTW~cpZdMV_9BDi8FPG2lclDoVgc>8<D
ze~1Egec|b-+{=#Kf(f=hdo6`53>E})d}0PWiz^?-XDFO{kIANgj%|@&aH7bwaiOt`
z+w3^%uX??zaXGAQFD_*~*}$CBnnsCE<PPutEQte4lKsh`Rf)~_D7)0Ztkyy(X}8SO
zT*IA?ZQ*AAkw)7|&WK_G2!BD#kcqm94A*41k%5_+ITaTdmjUY8&&!iTCuJ7iQ1mlH
z<R_=jK*K27$=Yu{iWX$;Uon|78<DTPF@+Yk?$G|7KNFy<OYXW6a`2E?b(o%G5^x%{
z-RS?Q55xYne3U}+?TZ=)LCMs|@;CMG-21PIqHA;D@urk{eW{P-nooWV36E{y|0)7U
zPQQ6y(?rBoXm9H*FD@+|bhvcv-_~(@v6&S5{o6z#`fb{R5!vp1oztJI^I0qAiu{?j
zKmGET>S*K`;w4Ur_2T7m@Gs}8x?pjlTvv<9q1o+N-p|$y+fsNlTp^#swzlBW1kvBS
zf?XER?&H_Cyk}~AAIV;jWficK^ZX@zBwH>x;P>(RM|9(Iup5Y+oO~>JC(Za9+e6Ft
zR@J1uwjD8RUqxLlsp1I)hq~Y=yOWe?0lD65lBj!Xk64-WsjWvCML=9q!x`RG0u#7T
zJYQd`ym)55xoKP~oPFD)>h9yy@D9_-eNlVjI~gG2JoRvo?d{ttow6+a_IUbPQnRBm
z*D^Q)aS+T?LvXXPrtXYYXM<tNJU>4tKH-mkkPj0vdS-kbv3gGhBkYDmPQ^?gZohl?
z?qwF)`>b<kiibhmMk*_~+SWN1p36VPydkm%#81DTb2lAEot&IVYM_Lighwx;8}bPJ
z%ZczNE{BJQ4cP>Y<eC@yzdk0Kf=EN5l{t4^R8l0X0U9s%6&Dxh-(&mnTM%s@91Lrt
zoO)5QZ#8S%)sqQzdR6ZPjcO)1%#lh#LKNTSNu%8<i=7r<9m{-wXw=C}7Iq9RS$1%A
zaVcARgjrM|1Y?OR{4&b^6!qJsr8^9JDeKr3=A0uLdK)yKk6ac(q!4&SP_A3%$34mM
zo{BzxVk}(R@aDw=Qyzo$1Y=lV4h!Xq(7<Ca%kvxOA&W?u{F`O{S;ueY4%Ny^9)q16
zyDf))n2CnO$mRlkQKW!H3#u%vcW^(7gt?7QuJPNdwbgw$WZ>k*#5vR%5Zv~gWZ{`b
zE2v{aimLOSZ0AKzT3Oiu{DZ|P+81>ucKUkz0#FCW487Lq<6$I|la<x!m~R=$<BQBt
zEzfkBI7)faTf&$C5l+*j6*+9q;cYYf@jJ+r(Pj(czH@rG-PebsIBq#|T`tqJP!2=U
z6%uz4!(<4KnhVIbqpC!4NokTfZpZkK)JDz}S#zhfNWqPiu1iu(YwvmS*4qUHN5@K6
z)0|VVZHV_pnYsWBnqH3?d0t;$UFjFUf>Tv{_;`64hT1S0{8ZYm5uwWano|^(AIG+;
zjs`<zeGreX>tWOsk-9C@Sf|azM`HMySz&;KuFO=v$85?Kd9aOV;TP#DI8JvTx(^Cr
zSI%!^0{2J6jmL?mBGBrFXAaq2W4W@@U=fjNKH^?L4|Gv|x<-2GTJE6moRnT@cwj&9
zhgI+G{BBwCy7$MAfib>Q%t36f5ivcmp9A_4jh^2YMOwdcBr<n&?v8rbcu2E;{Dl@7
z9@W?=gCT=_2PU|7+$GVj`+HZCBNuyn)z;7nC->qp4o4$pV?e2M#M(1A)=zvYh;d(7
zBI4jQg#~^vrwND%o+2nph~#j5-7k|I2eM2kUBB`q?sf9X7uF%g`(P6-DPwQ&HTVmc
zqlG))gY-VTHNG#TkOQnF+gSh84qh01lZC_)6_Gv>JLYThW6Bv*y<A=5qNHITJwRbC
z-H|(%_bt&;5V<gGabM-T#O@XWRH`U0i|UB`>yW8=R`B<F44-q&Xl&eDvZ?}Fo&-i-
zr648s%1tabTtO+^hkYYg(R+^e(GO0}flmEd%L4V?NfWNZs$AO92Jz>b04_azyG!g4
z+{#i0KNm%9mQiW2<lEQH#{^94JYA_y>YcZl?Dj$Sq8L*;2NOV02A<fgskTw_0dTmU
zG*?^kH~6@SdQNMOUF{<4sc4V&E8TogDvK`?Z$<4ymK4pB9VGk#u;Ayxx=9y}c(!B=
zDfRM|#vk3W-z@e}bF2GK`ffCsYTALGw_tZh@wBrZ)1>ok6MHMY{h$Sz7v!7~S2FkG
t?-y4BI_f)Lgf<=%xLo&ch2k?{RXJtgd)#~Q?eC9S4V6dARf;y4{{z$z*Z%+j

literal 0
HcmV?d00001

diff --git a/bin/templates/project/cordova/templates/project/res/drawable/icon.png b/bin/templates/project/cordova/templates/project/res/drawable/icon.png
old mode 100755
new mode 100644
index 697df7f37de3bc418dcee9409240eab4891e496d..ec7ffbfbdafc58df28e6e89041ee21acd0f6177e
GIT binary patch
literal 7685
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

literal 5800
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


From 5dacb8d2d5bb1e8a9806f07185f510a608586f15 Mon Sep 17 00:00:00 2001
From: Don Coleman <dcoleman@chariotsolutions.com>
Date: Thu, 17 May 2012 14:51:41 -0400
Subject: [PATCH 04/13] [CB-792] Add HTTP status code to FileTransferError

---
 .../src/org/apache/cordova/FileTransfer.java  | 553 ++++++++++--------
 1 file changed, 305 insertions(+), 248 deletions(-)

diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java
index a631a055..368d97ad 100644
--- a/framework/src/org/apache/cordova/FileTransfer.java
+++ b/framework/src/org/apache/cordova/FileTransfer.java
@@ -27,6 +27,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
@@ -35,7 +36,6 @@ import java.util.Iterator;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
@@ -57,7 +57,7 @@ public class FileTransfer extends Plugin {
     private static final String LOG_TAG = "FileTransfer";
     private static final String LINE_START = "--";
     private static final String LINE_END = "\r\n";
-    private static final String BOUNDRY =  "*****";
+    private static final String BOUNDARY =  "*****";
 
     public static int FILE_NOT_FOUND_ERR = 1;
     public static int INVALID_URL_ERR = 2;
@@ -82,49 +82,251 @@ public class FileTransfer extends Plugin {
             return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
         }
 
-        try {
-            if (action.equals("upload")) {
-                // Setup the options
-                String fileKey = null;
-                String fileName = null;
-                String mimeType = null;
+        if (action.equals("upload")) {
+            return upload(source, target, args);
+        } else if (action.equals("download")) {
+            return download(source, target);
+        } else {
+            return new PluginResult(PluginResult.Status.INVALID_ACTION);
+        }
+    }
 
-                fileKey = getArgument(args, 2, "file");
-                fileName = getArgument(args, 3, "image.jpg");
-                mimeType = getArgument(args, 4, "image/jpeg");
-                JSONObject params = args.optJSONObject(5);
-                boolean trustEveryone = args.optBoolean(6);
-                boolean chunkedMode = args.optBoolean(7) || args.isNull(7); //Always use chunked mode unless set to false as per API
-                FileUploadResult r = upload(source, target, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode);
-                Log.d(LOG_TAG, "****** About to return a result from upload");
-                return new PluginResult(PluginResult.Status.OK, r.toJSONObject());
-            } else if (action.equals("download")) {
-                JSONObject r = download(source, target);
-                Log.d(LOG_TAG, "****** About to return a result from download");
-                return new PluginResult(PluginResult.Status.OK, r);
-            } else {
-                return new PluginResult(PluginResult.Status.INVALID_ACTION);
+    /**
+     * Uploads the specified file to the server URL provided using an HTTP multipart request.
+     * @param source        Full path of the file on the file system
+     * @param target        URL of the server to receive the file
+     * @param args          JSON Array of args
+     *
+     * args[2] fileKey       Name of file request parameter
+     * args[3] fileName      File name to be used on server
+     * args[4] mimeType      Describes file content type
+     * args[5] params        key:value pairs of user-defined parameters
+     * @return FileUploadResult containing result of upload request
+     */
+    private PluginResult upload(String source, String target, JSONArray args) {
+        Log.d(LOG_TAG, "upload " + source + " to " +  target);
+
+        HttpURLConnection conn = null;
+        try {
+            // Setup the options
+            String fileKey = getArgument(args, 2, "file");
+            String fileName = getArgument(args, 3, "image.jpg");
+            String mimeType = getArgument(args, 4, "image/jpeg");
+            JSONObject params = args.optJSONObject(5);
+            if (params == null) params = new JSONObject();
+            boolean trustEveryone = args.optBoolean(6);
+            boolean chunkedMode = args.optBoolean(7) || args.isNull(7); //Always use chunked mode unless set to false as per API
+
+            Log.d(LOG_TAG, "fileKey: " + fileKey);
+            Log.d(LOG_TAG, "fileName: " + fileName);
+            Log.d(LOG_TAG, "mimeType: " + mimeType);
+            Log.d(LOG_TAG, "params: " + params);
+            Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
+            Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
+
+            // Create return object
+            FileUploadResult result = new FileUploadResult();
+
+            // Get a input stream of the file on the phone
+            FileInputStream fileInputStream = (FileInputStream) getPathFromUri(source);
+
+            DataOutputStream dos = null;
+
+            int bytesRead, bytesAvailable, bufferSize;
+            long totalBytes;
+            byte[] buffer;
+            int maxBufferSize = 8096;
+
+            //------------------ CLIENT REQUEST
+            // open a URL connection to the server
+            URL url = new URL(target);
+
+            // Open a HTTP connection to the URL based on protocol
+            if (url.getProtocol().toLowerCase().equals("https")) {
+                // Using standard HTTPS connection. Will not allow self signed certificate
+                if (!trustEveryone) {
+                    conn = (HttpsURLConnection) url.openConnection();
+                }
+                // Use our HTTPS connection that blindly trusts everyone.
+                // This should only be used in debug environments
+                else {
+                    // Setup the HTTPS connection class to trust everyone
+                    trustAllHosts();
+                    HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
+                    // Save the current hostnameVerifier
+                    defaultHostnameVerifier = https.getHostnameVerifier();
+                    // Setup the connection not to verify hostnames
+                    https.setHostnameVerifier(DO_NOT_VERIFY);
+                    conn = https;
+                }
             }
+            // Return a standard HTTP connection
+            else {
+                conn = (HttpURLConnection) url.openConnection();
+            }
+
+            // Allow Inputs
+            conn.setDoInput(true);
+
+            // Allow Outputs
+            conn.setDoOutput(true);
+
+            // Don't use a cached copy.
+            conn.setUseCaches(false);
+
+            // Use a post method.
+            conn.setRequestMethod("POST");
+            conn.setRequestProperty("Connection", "Keep-Alive");
+            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
+
+            // Handle the other headers
+            try {
+              JSONObject headers = params.getJSONObject("headers");
+              for (Iterator iter = headers.keys(); iter.hasNext();)
+              {
+                String headerKey = iter.next().toString();
+                conn.setRequestProperty(headerKey, headers.getString(headerKey));
+              }
+            } catch (JSONException e1) {
+              // No headers to be manipulated!
+            }
+
+            // Set the cookies on the response
+            String cookie = CookieManager.getInstance().getCookie(target);
+            if (cookie != null) {
+                conn.setRequestProperty("Cookie", cookie);
+            }
+
+
+            /*
+                * Store the non-file portions of the multipart data as a string, so that we can add it
+                * to the contentSize, since it is part of the body of the HTTP request.
+                */
+            String extraParams = "";
+            try {
+                for (Iterator iter = params.keys(); iter.hasNext();) {
+                    Object key = iter.next();
+                    if(!String.valueOf(key).equals("headers"))
+                    {
+                      extraParams += LINE_START + BOUNDARY + LINE_END;
+                      extraParams += "Content-Disposition: form-data; name=\"" +  key.toString() + "\";";
+                      extraParams += LINE_END + LINE_END;
+                      extraParams += params.getString(key.toString());
+                      extraParams += LINE_END;
+                    }
+                }
+            } catch (JSONException e) {
+                Log.e(LOG_TAG, e.getMessage(), e);
+            }
+
+            extraParams += LINE_START + BOUNDARY + LINE_END;
+            extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"";
+
+            String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END;
+            String tailParams = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END;
+
+            // Should set this up as an option
+            if (chunkedMode) {
+                conn.setChunkedStreamingMode(maxBufferSize);
+            }
+            else
+            {
+              int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
+              Log.d(LOG_TAG, "String Length: " + stringLength);
+              int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
+              Log.d(LOG_TAG, "Content Length: " + fixedLength);
+              conn.setFixedLengthStreamingMode(fixedLength);
+            }
+
+
+            dos = new DataOutputStream( conn.getOutputStream() );
+            dos.writeBytes(extraParams);
+            //We don't want to chagne encoding, we just want this to write for all Unicode.
+            dos.write(fileName.getBytes("UTF-8"));
+            dos.writeBytes(midParams);
+
+            // create a buffer of maximum size
+            bytesAvailable = fileInputStream.available();
+            bufferSize = Math.min(bytesAvailable, maxBufferSize);
+            buffer = new byte[bufferSize];
+
+            // read file and write it into form...
+            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+            totalBytes = 0;
+
+            while (bytesRead > 0) {
+                totalBytes += bytesRead;
+                result.setBytesSent(totalBytes);
+                dos.write(buffer, 0, bufferSize);
+                bytesAvailable = fileInputStream.available();
+                bufferSize = Math.min(bytesAvailable, maxBufferSize);
+                bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+            }
+
+            // send multipart form data necesssary after file data...
+            dos.writeBytes(tailParams);
+
+            // close streams
+            fileInputStream.close();
+            dos.flush();
+            dos.close();
+
+            //------------------ read the SERVER RESPONSE
+            StringBuffer responseString = new StringBuffer("");
+            DataInputStream inStream;
+            try {
+                inStream = new DataInputStream ( conn.getInputStream() );
+            } catch(FileNotFoundException e) {
+                Log.e(LOG_TAG, e.toString(), e);
+                throw new IOException("Received error from server");
+            }
+
+            String line;
+            while (( line = inStream.readLine()) != null) {
+                responseString.append(line);
+            }
+            Log.d(LOG_TAG, "got response from server");
+            Log.d(LOG_TAG, responseString.toString());
+
+            // send request and retrieve response
+            result.setResponseCode(conn.getResponseCode());
+            result.setResponse(responseString.toString());
+
+            inStream.close();
+
+            // Revert back to the proper verifier and socket factories
+            if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
+                ((HttpsURLConnection) conn).setHostnameVerifier(defaultHostnameVerifier);
+                HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
+            }
+
+            Log.d(LOG_TAG, "****** About to return a result from upload");
+            return new PluginResult(PluginResult.Status.OK, result.toJSONObject());
+
         } catch (FileNotFoundException e) {
-            Log.e(LOG_TAG, e.getMessage(), e);
-            JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target);
+            JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn);
+            Log.e(LOG_TAG, error.toString(), e);
             return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
-        } catch (IllegalArgumentException e) {
-            Log.e(LOG_TAG, e.getMessage(), e);
-            JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target);
-            return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
-        } catch (SSLException e) {
-            Log.e(LOG_TAG, e.getMessage(), e);
-            Log.d(LOG_TAG, "Got my ssl exception!!!");
-            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target);
+        } catch (MalformedURLException e) {
+            JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, conn);
+            Log.e(LOG_TAG, error.toString(), e);
             return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
         } catch (IOException e) {
-            Log.e(LOG_TAG, e.getMessage(), e);
-            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target);
+            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
+            Log.e(LOG_TAG, error.toString(), e);
             return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
         } catch (JSONException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
             return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+        } catch (Throwable t) {
+            // Shouldn't happen, but will
+            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
+            Log.wtf(LOG_TAG, error.toString(), t);
+            return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+        } finally {
+            if (conn != null) {
+                conn.disconnect();
+            }
         }
     }
 
@@ -172,18 +374,36 @@ public class FileTransfer extends Plugin {
         }
     }
 
-    /**
-     * Create an error object based on the passed in errorCode
-     * @param errorCode 	the error
-     * @return JSONObject containing the error
-     */
-    private JSONObject createFileTransferError(int errorCode, String source, String target) {
+    private JSONObject createFileTransferError(int errorCode, String source, String target, HttpURLConnection connection) {
+
+        Integer httpStatus = null;
+
+        if (connection != null) {
+            try {
+                httpStatus = connection.getResponseCode();
+            } catch (IOException e) {
+                Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
+            }
+        }
+
+        return createFileTransferError(errorCode, source, target, httpStatus);
+    }
+
+        /**
+        * Create an error object based on the passed in errorCode
+        * @param errorCode 	the error
+        * @return JSONObject containing the error
+        */
+    private JSONObject createFileTransferError(int errorCode, String source, String target, Integer httpStatus) {
         JSONObject error = null;
         try {
             error = new JSONObject();
             error.put("code", errorCode);
             error.put("source", source);
             error.put("target", target);
+            if (httpStatus != null) {
+                error.put("http_status", httpStatus);
+            }
         } catch (JSONException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
         }
@@ -208,198 +428,6 @@ public class FileTransfer extends Plugin {
         return arg;
     }
 
-    /**
-     * Uploads the specified file to the server URL provided using an HTTP
-     * multipart request.
-     * @param file      Full path of the file on the file system
-     * @param server        URL of the server to receive the file
-     * @param fileKey       Name of file request parameter
-     * @param fileName      File name to be used on server
-     * @param mimeType      Describes file content type
-     * @param params        key:value pairs of user-defined parameters
-     * @return FileUploadResult containing result of upload request
-     */
-    public FileUploadResult upload(String file, String server, final String fileKey, final String fileName,
-            final String mimeType, JSONObject params, boolean trustEveryone, boolean chunkedMode) throws IOException, SSLException {
-        // Create return object
-        FileUploadResult result = new FileUploadResult();
-
-        // Get a input stream of the file on the phone
-        FileInputStream fileInputStream = (FileInputStream) getPathFromUri(file);
-
-        HttpURLConnection conn = null;
-        DataOutputStream dos = null;
-
-        int bytesRead, bytesAvailable, bufferSize;
-        long totalBytes;
-        byte[] buffer;
-        int maxBufferSize = 8096;
-
-        //------------------ CLIENT REQUEST
-        // open a URL connection to the server
-        URL url = new URL(server);
-
-        // Open a HTTP connection to the URL based on protocol
-        if (url.getProtocol().toLowerCase().equals("https")) {
-            // Using standard HTTPS connection. Will not allow self signed certificate
-            if (!trustEveryone) {
-                conn = (HttpsURLConnection) url.openConnection();
-            }
-            // Use our HTTPS connection that blindly trusts everyone.
-            // This should only be used in debug environments
-            else {
-                // Setup the HTTPS connection class to trust everyone
-                trustAllHosts();
-                HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
-                // Save the current hostnameVerifier
-                defaultHostnameVerifier = https.getHostnameVerifier();
-                // Setup the connection not to verify hostnames
-                https.setHostnameVerifier(DO_NOT_VERIFY);
-                conn = https;
-            }
-        }
-        // Return a standard HTTP connection
-        else {
-            conn = (HttpURLConnection) url.openConnection();
-        }
-
-        // Allow Inputs
-        conn.setDoInput(true);
-
-        // Allow Outputs
-        conn.setDoOutput(true);
-
-        // Don't use a cached copy.
-        conn.setUseCaches(false);
-
-        // Use a post method.
-        conn.setRequestMethod("POST");
-        conn.setRequestProperty("Connection", "Keep-Alive");
-        conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY);
-
-        // Handle the other headers
-        try {
-          JSONObject headers = params.getJSONObject("headers");
-          for (Iterator iter = headers.keys(); iter.hasNext();)
-          {
-            String headerKey = iter.next().toString();
-            conn.setRequestProperty(headerKey, headers.getString(headerKey));
-          }
-        } catch (JSONException e1) {
-          // No headers to be manipulated!
-        }
-        
-        // Set the cookies on the response
-        String cookie = CookieManager.getInstance().getCookie(server);
-        if (cookie != null) {
-            conn.setRequestProperty("Cookie", cookie);
-        }
-        
-
-        /*
-         * Store the non-file portions of the multipart data as a string, so that we can add it 
-         * to the contentSize, since it is part of the body of the HTTP request.
-         */
-        String extraParams = "";
-        try {
-            for (Iterator iter = params.keys(); iter.hasNext();) {
-                Object key = iter.next();
-                if(key.toString() != "headers")
-                {
-                  extraParams += LINE_START + BOUNDRY + LINE_END;
-                  extraParams += "Content-Disposition: form-data; name=\"" +  key.toString() + "\";";
-                  extraParams += LINE_END + LINE_END;
-                  extraParams += params.getString(key.toString());
-                  extraParams += LINE_END;
-                }
-            }
-        } catch (JSONException e) {
-            Log.e(LOG_TAG, e.getMessage(), e);
-        }
-        
-        extraParams += LINE_START + BOUNDRY + LINE_END;
-        extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"";
-        
-        String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END;
-        String tailParams = LINE_END + LINE_START + BOUNDRY + LINE_START + LINE_END;
-        
-        // Should set this up as an option
-        if (chunkedMode) {
-            conn.setChunkedStreamingMode(maxBufferSize);
-        }
-        else
-        {
-          int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
-          Log.d(LOG_TAG, "String Length: " + stringLength);
-          int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
-          Log.d(LOG_TAG, "Content Length: " + fixedLength);
-          conn.setFixedLengthStreamingMode(fixedLength);
-        }
-        
-
-        dos = new DataOutputStream( conn.getOutputStream() );
-        dos.writeBytes(extraParams);
-        //We don't want to chagne encoding, we just want this to write for all Unicode.
-        dos.write(fileName.getBytes("UTF-8"));
-        dos.writeBytes(midParams);
-
-        // create a buffer of maximum size
-        bytesAvailable = fileInputStream.available();
-        bufferSize = Math.min(bytesAvailable, maxBufferSize);
-        buffer = new byte[bufferSize];
-
-        // read file and write it into form...
-        bytesRead = fileInputStream.read(buffer, 0, bufferSize);
-        totalBytes = 0;
-
-        while (bytesRead > 0) {
-            totalBytes += bytesRead;
-            result.setBytesSent(totalBytes);
-            dos.write(buffer, 0, bufferSize);
-            bytesAvailable = fileInputStream.available();
-            bufferSize = Math.min(bytesAvailable, maxBufferSize);
-            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
-        }
-
-        // send multipart form data necesssary after file data...
-        dos.writeBytes(tailParams);
-
-        // close streams
-        fileInputStream.close();
-        dos.flush();
-        dos.close();
-
-        //------------------ read the SERVER RESPONSE
-        StringBuffer responseString = new StringBuffer("");
-        DataInputStream inStream;
-        try {
-            inStream = new DataInputStream ( conn.getInputStream() );
-        } catch(FileNotFoundException e) {
-            throw new IOException("Received error from server");
-        }
-
-        String line;
-        while (( line = inStream.readLine()) != null) {
-            responseString.append(line);
-        }
-        Log.d(LOG_TAG, "got response from server");
-        Log.d(LOG_TAG, responseString.toString());
-
-        // send request and retrieve response
-        result.setResponseCode(conn.getResponseCode());
-        result.setResponse(responseString.toString());
-
-        inStream.close();
-        conn.disconnect();
-
-        // Revert back to the proper verifier and socket factories
-        if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
-            ((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier);
-            HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
-        }
-
-        return result;
-    }
 
     /**
      * Downloads a file form a given URL and saves it to the specified directory.
@@ -408,7 +436,10 @@ public class FileTransfer extends Plugin {
      * @param target      	Full path of the file on the file system
      * @return JSONObject 	the downloaded file
      */
-    public JSONObject download(String source, String target) throws IOException {
+    private PluginResult download(String source, String target) {
+        Log.d(LOG_TAG, "download " + source + " to " +  target);
+
+        HttpURLConnection connection = null;
         try {
             File file = getFileFromPath(target);
 
@@ -419,7 +450,7 @@ public class FileTransfer extends Plugin {
             if(this.ctx.isUrlWhiteListed(source))
             {
               URL url = new URL(source);
-              HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+              connection = (HttpURLConnection) url.openConnection();
               connection.setRequestMethod("GET");
               
               //Add cookie support
@@ -431,7 +462,7 @@ public class FileTransfer extends Plugin {
               
               connection.connect();
 
-              Log.d(LOG_TAG, "Download file:" + url);
+              Log.d(LOG_TAG, "Download file: " + url);
 
               InputStream inputStream = connection.getInputStream();
               byte[] buffer = new byte[1024];
@@ -450,23 +481,40 @@ public class FileTransfer extends Plugin {
 
               // create FileEntry object
               FileUtils fileUtil = new FileUtils();
+              JSONObject fileEntry = fileUtil.getEntry(file);
 
-              return fileUtil.getEntry(file);
+              return new PluginResult(PluginResult.Status.OK, fileEntry);
             }
             else
             {
-              throw new IOException("Error: Unable to connect to domain");
+                Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
+                JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, 401);
+                return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+            }
+
+        } catch (FileNotFoundException e) {
+            JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
+            Log.e(LOG_TAG, error.toString(), e);
+            return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+        } catch (MalformedURLException e) {
+            JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, connection);
+            Log.e(LOG_TAG, error.toString(), e);
+            return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+        } catch (Exception e) {  // IOException, JSONException, NullPointer
+            JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
+            Log.e(LOG_TAG, error.toString(), e);
+            return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
             }
-        } catch (Exception e) {
-            Log.d(LOG_TAG, e.getMessage(), e);
-            throw new IOException("Error while downloading");
         }
     }
 
     /**
      * Get an input stream based on file path or content:// uri
      *
-     * @param path
+     * @param path foo
      * @return an input stream
      * @throws FileNotFoundException
      */
@@ -491,14 +539,23 @@ public class FileTransfer extends Plugin {
     /**
      * Get a File object from the passed in path
      * 
-     * @param path
-     * @return
+     * @param path file path
+     * @return file object
      */
-    private File getFileFromPath(String path) {
-        if (path.startsWith("file://")) {
-            return new File(path.substring(7));
+    private File getFileFromPath(String path) throws FileNotFoundException {
+        File file;
+        String prefix = "file://";
+
+        if (path.startsWith(prefix)) {
+            file = new File(path.substring(prefix.length()));
         } else {
-            return new File(path);
+            file = new File(path);
         }
+
+        if (file.getParent() == null) {
+            throw new FileNotFoundException();
+        }
+
+        return file;
     }
 }

From 71e47aa77286298ddf03dc1fa8c9c42046877aad Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 13:00:11 -0700
Subject: [PATCH 05/13] [CB-463] rewrite of accel plugin

---
 .../src/org/apache/cordova/AccelListener.java | 396 +++++++++---------
 1 file changed, 205 insertions(+), 191 deletions(-)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index f751e4e2..40ddec49 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -18,7 +18,11 @@
 */
 package org.apache.cordova;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.cordova.api.CordovaInterface;
 import org.apache.cordova.api.Plugin;
@@ -32,6 +36,8 @@ import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.location.Location;
+import android.util.Log;
 import android.content.Context;
 
 /**
@@ -40,20 +46,20 @@ import android.content.Context;
  */
 public class AccelListener extends Plugin implements SensorEventListener {
 
-	public static int STOPPED = 0;
-	public static int STARTING = 1;
+    public static int STOPPED = 0;
+    public static int STARTING = 1;
     public static int RUNNING = 2;
     public static int ERROR_FAILED_TO_START = 3;
     
-    public float TIMEOUT = 30000;		// Timeout in msec to shut off listener
-    
-    float x,y,z;						// most recent acceleration values
-    long timestamp;						// time of most recent value
-    int status;							// status of listener
-    long lastAccessTime;				// time the value was last retrieved
+    private float x,y,z;						        // most recent acceleration values
+    private long timestamp;					        // time of most recent value
+    private int status;							        // status of listener
 
-    private SensorManager sensorManager;// Sensor manager
-    Sensor mSensor;						// Acceleration sensor returned by sensor manager
+    private SensorManager sensorManager;    // Sensor manager
+    private Sensor mSensor;						      // Acceleration sensor returned by sensor manager
+    
+    private HashMap<String, String> watches = new HashMap<String, String>();
+    private List<String> callbacks = new ArrayList<String>();
 
     /**
      * Create an accelerometer listener.
@@ -66,169 +72,149 @@ public class AccelListener extends Plugin implements SensorEventListener {
         this.setStatus(AccelListener.STOPPED);
      }
     
-	/**
-	 * Sets the context of the Command. This can then be used to do things like
-	 * get file paths associated with the Activity.
-	 * 
-	 * @param ctx The context of the main Activity.
-	 */
-	public void setContext(CordovaInterface ctx) {
-		super.setContext(ctx);
+    /**
+     * Sets the context of the Command. This can then be used to do things like
+     * get file paths associated with the Activity.
+     * 
+     * @param ctx The context of the main Activity.
+     */
+    public void setContext(CordovaInterface ctx) {
+        super.setContext(ctx);
         this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
-	}
+    }
 
-	/**
-	 * Executes the request and returns PluginResult.
-	 * 
-	 * @param action 		The action to execute.
-	 * @param args 			JSONArry of arguments for the plugin.
-	 * @param callbackId	The callback id used when calling back into JavaScript.
-	 * @return 				A PluginResult object with a status and message.
-	 */
-	public PluginResult execute(String action, JSONArray args, String callbackId) {
-		PluginResult.Status status = PluginResult.Status.OK;
-		String result = "";		
-		
-		try {
-			if (action.equals("getStatus")) {
-				int i = this.getStatus();
-				return new PluginResult(status, i);
-			}
-			else if (action.equals("start")) {
-				int i = this.start();
-				return new PluginResult(status, i);
-			}
-			else if (action.equals("stop")) {
-				this.stop();
-				return new PluginResult(status, 0);
-			}
-			else if (action.equals("getAcceleration")) {
-				// If not running, then this is an async call, so don't worry about waiting
-				if (this.status != AccelListener.RUNNING) {
-					int r = this.start();
-					if (r == AccelListener.ERROR_FAILED_TO_START) {
-						return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START);
-					}
-					// Wait until running
-					long timeout = 2000;
-					while ((this.status == STARTING) && (timeout > 0)) {
-						timeout = timeout - 100;
-						try {
-							Thread.sleep(100);
-						} catch (InterruptedException e) {
-							e.printStackTrace();
-						}
-					}
-					if (timeout == 0) {
-						return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START);						
-					}
-				}
-	            this.lastAccessTime = System.currentTimeMillis();
-				JSONObject r = new JSONObject();
-				r.put("x", this.x);
-				r.put("y", this.y);
-				r.put("z", this.z);
-				// TODO: Should timestamp be sent?
-				r.put("timestamp", this.timestamp);
-				return new PluginResult(status, r);
-			}
-			else if (action.equals("setTimeout")) {
-				try {
-					float timeout = Float.parseFloat(args.getString(0));
-					this.setTimeout(timeout);
-					return new PluginResult(status, 0);
-				} catch (NumberFormatException e) {
-					status = PluginResult.Status.INVALID_ACTION;
-					e.printStackTrace();
-				} catch (JSONException e) {
-					status = PluginResult.Status.JSON_EXCEPTION;
-					e.printStackTrace();
-				}
-			}
-			else if (action.equals("getTimeout")) {
-				float f = this.getTimeout();
-				return new PluginResult(status, f);
-			} else {
-        // Unsupported action
-        return new PluginResult(PluginResult.Status.INVALID_ACTION);
-      }
-			return new PluginResult(status, result);
-		} catch (JSONException e) {
-			return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
-		}
-	}
+    /**
+     * Executes the request and returns PluginResult.
+     * 
+     * @param action 		The action to execute.
+     * @param args 			JSONArry of arguments for the plugin.
+     * @param callbackId	The callback id used when calling back into JavaScript.
+     * @return 				A PluginResult object with a status and message.
+     */
+    public PluginResult execute(String action, JSONArray args, String callbackId) {
+    	PluginResult.Status status = PluginResult.Status.NO_RESULT;
+    	String message = "";
+    	PluginResult result = new PluginResult(status, message);
+    	result.setKeepCallback(true);	
+    	try {
+    		if (action.equals("getAcceleration")) {
+    			if (this.status != AccelListener.RUNNING) {
+    				// If not running, then this is an async call, so don't worry about waiting
+    				// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
+    				this.callbacks.add(callbackId);
+    				this.start();
+    			} else {
+    				return new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
+    			}
+    		}
+    		else if (action.equals("addWatch")) {
+    			String watchId = args.getString(0);
+    			this.watches.put(watchId, callbackId);
+    			if (this.status != AccelListener.RUNNING) {
+    				this.start();
+    			}
+    		}
+    		else if (action.equals("clearWatch")) {
+    			String watchId = args.getString(0);
+    			if (this.watches.containsKey(watchId)) {
+    				this.watches.remove(watchId);
+    				if (this.size() == 0) {
+    					this.stop();
+    				}
+    			}
+    			return new PluginResult(PluginResult.Status.OK);
+    		} else {
+    			// Unsupported action
+    			return new PluginResult(PluginResult.Status.INVALID_ACTION);
+    		}
+    	} catch (JSONException e) {
+    		return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+    	}
+    	return result;
+    }
 
-	/**
-	 * Identifies if action to be executed returns a value and should be run synchronously.
-	 * 
-	 * @param action	The action to execute
-	 * @return			T=returns value
-	 */
-	public boolean isSynch(String action) {
-		if (action.equals("getStatus")) {
-			return true;
-		}
-		else if (action.equals("getAcceleration")) {
-			// Can only return value if RUNNING
-			if (this.status == AccelListener.RUNNING) {
-				return true;
-			}
-		}
-		else if (action.equals("getTimeout")) {
-			return true;
-		}
-		return false;
-	}
+    /**
+     * Identifies if action to be executed returns a value and should be run synchronously.
+     * 
+     * @param action	The action to execute
+     * @return			T=returns value
+     */
+    public boolean isSynch(String action) {
+    	if (action.equals("getAcceleration") && this.status == AccelListener.RUNNING) {
+    		return true;
+    	} else if (action.equals("addWatch") && this.status == AccelListener.RUNNING) {
+        	return true;
+        } else if (action.equals("clearWatch")) {
+            return true;
+        }
+        return false;
+    }
     
     /**
      * Called by AccelBroker when listener is to be shut down.
      * Stop listener.
      */
     public void onDestroy() {
-    	this.stop();    	
+        this.stop();
     }
 
     //--------------------------------------------------------------------------
     // LOCAL METHODS
     //--------------------------------------------------------------------------
 
+    private int size() {
+    	return this.watches.size() + this.callbacks.size();
+    }
     /**
      * Start listening for acceleration sensor.
      * 
      * @return 			status of listener
      */
-    public int start() {
+    private int start() {
+    	// If already starting or running, then just return
+    	if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
+    		return this.status;
+    	}
+    	
+    	this.setStatus(AccelListener.STARTING);
+    	
+    	// Get accelerometer from sensor manager
+    	List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
 
-		// If already starting or running, then just return
-        if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
-        	return this.status;
-        }
-
-        // Get accelerometer from sensor manager
-        List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
-        
-        // If found, then register as listener
-        if ((list != null) && (list.size() > 0)) {
-            this.mSensor = list.get(0);
-            this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
-            this.setStatus(AccelListener.STARTING);
-            this.lastAccessTime = System.currentTimeMillis();
-        }
-        
-        // If error, then set status to error
-        else {
-            this.setStatus(AccelListener.ERROR_FAILED_TO_START);
-        }
-        
-        return this.status;
+    	// If found, then register as listener
+    	if ((list != null) && (list.size() > 0)) {
+    		this.mSensor = list.get(0);
+    		this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
+    		this.setStatus(AccelListener.STARTING);
+    	} else {
+    		this.setStatus(AccelListener.ERROR_FAILED_TO_START);
+    		this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
+    		return this.status;
+    	}
+    	
+    	// Wait until running
+    	long timeout = 2000;
+    	while ((this.status == STARTING) && (timeout > 0)) {
+    		timeout = timeout - 100;
+    		try {
+    			Thread.sleep(100);
+    		} catch (InterruptedException e) {
+    			e.printStackTrace();
+    		}
+    	}
+    	if (timeout == 0) {
+    		this.setStatus(AccelListener.ERROR_FAILED_TO_START);
+    		this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
+    	}
+    	return this.status;
     }
 
     /**
      * Stop listening to acceleration sensor.
      */
-    public void stop() {
+    private void stop() {
         if (this.status != AccelListener.STOPPED) {
-        	this.sensorManager.unregisterListener(this);
+            this.sensorManager.unregisterListener(this);
         }
         this.setStatus(AccelListener.STOPPED);
     }
@@ -248,64 +234,92 @@ public class AccelListener extends Plugin implements SensorEventListener {
      * @param SensorEvent event
      */
     public void onSensorChanged(SensorEvent event) {
-    	
-    	// Only look at accelerometer events
+        // Only look at accelerometer events
         if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
             return;
         }
         
         // If not running, then just return
         if (this.status == AccelListener.STOPPED) {
-        	return;
+            return;
         }
         
+        this.setStatus(AccelListener.RUNNING);
+        
         // Save time that event was received
         this.timestamp = System.currentTimeMillis();
         this.x = event.values[0];
         this.y = event.values[1];
-        this.z = event.values[2];            
+        this.z = event.values[2];
 
-        this.setStatus(AccelListener.RUNNING);
-
-        // If values haven't been read for TIMEOUT time, then turn off accelerometer sensor to save power
-		if ((this.timestamp - this.lastAccessTime) > this.TIMEOUT) {
-			this.stop();
-		}		
+        this.win();
+        
+        if (this.size() == 0) {
+        	this.stop();
+        }
     }
 
-    /**
-     * Get status of accelerometer sensor.
-     * 
-     * @return			status
-     */
-	public int getStatus() {
-		return this.status;
-	}
-		
-	/**
-	 * Set the timeout to turn off accelerometer sensor if getX() hasn't been called.
-	 * 
-	 * @param timeout		Timeout in msec.
-	 */
-	public void setTimeout(float timeout) {
-		this.TIMEOUT = timeout;
-	}
-	
-	/**
-	 * Get the timeout to turn off accelerometer sensor if getX() hasn't been called.
-	 * 
-	 * @return timeout in msec
-	 */
-	public float getTimeout() {
-		return this.TIMEOUT;
-	}
-	
-	/**
-	 * Set the status and send it to JavaScript.
-	 * @param status
-	 */
-	private void setStatus(int status) {
-		this.status = status;
-	}
+    private void fail(int code, String message) {
+    	// Error object
+    	JSONObject errorObj = new JSONObject();
+    	try {
+			errorObj.put("code", code);
+	    	errorObj.put("message", message);
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+    	PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
+    	
+        for (String callbackId: this.callbacks)
+        {
+        	this.error(err, callbackId);
+        }
+        this.callbacks.clear();
+        
+        err.setKeepCallback(true);
+        
+        Iterator it = this.watches.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry pairs = (Map.Entry)it.next();
+            this.error(err, (String)pairs.getValue());
+        }
+    }
     
-}
+    private void win() {
+    	// Success return object
+    	PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
+    	
+    	for (String callbackId: this.callbacks)
+        {
+        	this.success(result, callbackId);
+        }
+        this.callbacks.clear();
+        
+        result.setKeepCallback(true);
+        
+        Iterator it = this.watches.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry pairs = (Map.Entry)it.next();
+            this.success(result, (String)pairs.getValue());
+        }
+    }
+
+	private void setStatus(int status) {
+        this.status = status;
+    }
+	
+	private JSONObject getAccelerationJSON() {
+		JSONObject r = new JSONObject();
+		try {
+			r.put("x", this.x);
+			r.put("y", this.y);
+			r.put("z", this.z);
+			r.put("timestamp", this.timestamp);
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return r;
+	}
+}
\ No newline at end of file

From 24adc6d00cea0c603077bb3919a678071a0d32c6 Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 13:04:07 -0700
Subject: [PATCH 06/13] [CB-463] added the JS updates for accel refactor

---
 framework/assets/js/cordova.android.js | 87 +++++++++++++-------------
 1 file changed, 45 insertions(+), 42 deletions(-)

diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js
index 7bb06c2f..957be777 100644
--- a/framework/assets/js/cordova.android.js
+++ b/framework/assets/js/cordova.android.js
@@ -1,6 +1,6 @@
-// commit facaa38a0bd924aa15c14c372537c00382f1e593
+// commit 7b6ae77e5030060e8e99fe0b79ddcf9d698bf375
 
-// File generated at :: Thu May 10 2012 16:39:13 GMT-0700 (PDT)
+// File generated at :: Mon May 14 2012 13:03:22 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -1144,13 +1144,14 @@ module.exports = {
 // file: lib/common/plugin/Acceleration.js
 define("cordova/plugin/Acceleration", function(require, exports, module) {
 var Acceleration = function(x, y, z, timestamp) {
-  this.x = x;
-  this.y = y;
-  this.z = z;
-  this.timestamp = timestamp || (new Date()).getTime();
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
 };
 
 module.exports = Acceleration;
+
 });
 
 // file: lib/common/plugin/Camera.js
@@ -3369,11 +3370,16 @@ define("cordova/plugin/accelerometer", function(require, exports, module) {
  * @constructor
  */
 var utils = require("cordova/utils"),
-    exec = require("cordova/exec");
+    exec = require("cordova/exec"),
+    Acceleration = require('cordova/plugin/Acceleration');
 
-// Local singleton variables.
+
+// Keeps reference to watchAcceleration calls.
 var timers = {};
 
+// Last returned acceleration object from native
+var accel = null;
+
 var accelerometer = {
     /**
      * Asynchronously aquires the current acceleration.
@@ -3383,21 +3389,18 @@ var accelerometer = {
      * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
      */
     getCurrentAcceleration: function(successCallback, errorCallback, options) {
-
         // successCallback required
         if (typeof successCallback !== "function") {
-            console.log("Accelerometer Error: successCallback is not a function");
-            return;
+            throw "getCurrentAcceleration must be called with at least a success callback function as first parameter.";
         }
 
-        // errorCallback optional
-        if (errorCallback && (typeof errorCallback !== "function")) {
-            console.log("Accelerometer Error: errorCallback is not a function");
-            return;
-        }
+        var win = function(a) {
+            accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+            successCallback(accel);
+        };
 
         // Get acceleration
-        exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
+        exec(win, errorCallback, "Accelerometer", "getAcceleration", []);
     },
 
     /**
@@ -3409,36 +3412,34 @@ var accelerometer = {
      * @return String                       The watch id that must be passed to #clearWatch to stop watching.
      */
     watchAcceleration: function(successCallback, errorCallback, options) {
-
         // Default interval (10 sec)
-        var frequency = (options !== undefined && options.frequency !== undefined)? options.frequency : 10000;
+        var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000;
 
         // successCallback required
         if (typeof successCallback !== "function") {
-            console.log("Accelerometer Error: successCallback is not a function");
-            return;
+            throw "watchAcceleration must be called with at least a success callback function as first parameter.";
         }
 
-        // errorCallback optional
-        if (errorCallback && (typeof errorCallback !== "function")) {
-            console.log("Accelerometer Error: errorCallback is not a function");
-            return;
-        }
-
-        // Make sure accelerometer timeout > frequency + 10 sec
-        exec(
-            function(timeout) {
-                if (timeout < (frequency + 10000)) {
-                    exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]);
-                }
-            },
-            function(e) { }, "Accelerometer", "getTimeout", []);
-
-        // Start watch timer
+        // Keep reference to watch id, and report accel readings as often as defined in frequency
         var id = utils.createUUID();
         timers[id] = window.setInterval(function() {
-            exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
-        }, (frequency ? frequency : 1));
+            if (accel) {
+                successCallback(accel);
+            }
+        }, frequency);
+
+        // Success callback from native just updates the accel object.
+        var win = function(a) {
+            accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+        };
+
+        // Fail callback clears the watch and sends an error back.
+        var fail = function(err) {
+            accelerometer.clearWatch(id);
+            errorCallback(err);
+        };
+
+        exec(win, fail, "Accelerometer", "addWatch", [id, frequency]);
 
         return id;
     },
@@ -3449,16 +3450,17 @@ var accelerometer = {
      * @param {String} id       The id of the watch returned from #watchAcceleration.
      */
     clearWatch: function(id) {
-
         // Stop javascript timer & remove from timer list
-        if (id && timers[id] !== undefined) {
+        if (id && timers[id]) {
             window.clearInterval(timers[id]);
             delete timers[id];
+            exec(null, null, "Accelerometer", "clearWatch", [id]);
         }
     }
 };
 
 module.exports = accelerometer;
+
 });
 
 // file: lib/android/plugin/android/app.js
@@ -5172,7 +5174,7 @@ window.cordova = require('cordova');
                     // Fire onDeviceReady event once all constructors have run and
                     // cordova info has been received from native side.
                     channel.join(function() {
-                        channel.onDeviceReady.fire();
+                        require('cordova').fireDocumentEvent('deviceready');
                     }, channel.deviceReadyChannelsArray);
 
                 }, [ channel.onDOMContentLoaded, channel.onNativeReady ]);
@@ -5191,4 +5193,5 @@ window.cordova = require('cordova');
 
 }(window));
 
+
 })();
\ No newline at end of file

From cb98bbce1f9a99fa5e8af44886979f8614cb390f Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 15:21:41 -0700
Subject: [PATCH 07/13] [CB-463] added accuracy checking to native accel
 implementation, this way getCurrentAcceleration returns fairly accurate
 results

---
 .../src/org/apache/cordova/AccelListener.java | 36 +++++++++++++------
 1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index 40ddec49..7596045c 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -54,6 +54,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
     private float x,y,z;						        // most recent acceleration values
     private long timestamp;					        // time of most recent value
     private int status;							        // status of listener
+    private int accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
 
     private SensorManager sensorManager;    // Sensor manager
     private Sensor mSensor;						      // Acceleration sensor returned by sensor manager
@@ -184,7 +185,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
     	// If found, then register as listener
     	if ((list != null) && (list.size() > 0)) {
     		this.mSensor = list.get(0);
-    		this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
+    		this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
     		this.setStatus(AccelListener.STARTING);
     	} else {
     		this.setStatus(AccelListener.ERROR_FAILED_TO_START);
@@ -217,6 +218,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
             this.sensorManager.unregisterListener(this);
         }
         this.setStatus(AccelListener.STOPPED);
+        this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
     }
 
     /**
@@ -226,6 +228,17 @@ public class AccelListener extends Plugin implements SensorEventListener {
      * @param accuracy
      */
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    	// Only look at accelerometer events
+        if (sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
+            return;
+        }
+        
+        // If not running, then just return
+        if (this.status == AccelListener.STOPPED) {
+            return;
+        }
+        Log.d("ACCEL", "accuracy is now " + accuracy);
+        this.accuracy = accuracy;
     }
 
     /**
@@ -246,16 +259,19 @@ public class AccelListener extends Plugin implements SensorEventListener {
         
         this.setStatus(AccelListener.RUNNING);
         
-        // Save time that event was received
-        this.timestamp = System.currentTimeMillis();
-        this.x = event.values[0];
-        this.y = event.values[1];
-        this.z = event.values[2];
+        if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
 
-        this.win();
-        
-        if (this.size() == 0) {
-        	this.stop();
+        	// Save time that event was received
+        	this.timestamp = System.currentTimeMillis();
+        	this.x = event.values[0];
+        	this.y = event.values[1];
+        	this.z = event.values[2];
+
+        	this.win();
+
+        	if (this.size() == 0) {
+        		this.stop();
+        	}
         }
     }
 

From df89d33fab102caed576ac01bf20c9b96ee30580 Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 16:04:36 -0700
Subject: [PATCH 08/13] removed a trailing log

---
 framework/src/org/apache/cordova/AccelListener.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index 7596045c..bc8bf170 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -237,7 +237,6 @@ public class AccelListener extends Plugin implements SensorEventListener {
         if (this.status == AccelListener.STOPPED) {
             return;
         }
-        Log.d("ACCEL", "accuracy is now " + accuracy);
         this.accuracy = accuracy;
     }
 
@@ -338,4 +337,4 @@ public class AccelListener extends Plugin implements SensorEventListener {
 		}
 		return r;
 	}
-}
\ No newline at end of file
+}

From 531efe1e30f86d33a6a4980b03c68835f803c400 Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 15:21:41 -0700
Subject: [PATCH 09/13] [CB-463] added accuracy checking to native accel
 implementation, this way getCurrentAcceleration returns fairly accurate
 results

---
 framework/src/org/apache/cordova/AccelListener.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index bc8bf170..6ccbecac 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -237,6 +237,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
         if (this.status == AccelListener.STOPPED) {
             return;
         }
+        Log.d("ACCEL", "accuracy is now " + accuracy);
         this.accuracy = accuracy;
     }
 

From 15ddef26f4e2966f951106d0c8881f1693f2ad5e Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Mon, 14 May 2012 16:04:36 -0700
Subject: [PATCH 10/13] removed a trailing log

---
 framework/src/org/apache/cordova/AccelListener.java | 1 -
 1 file changed, 1 deletion(-)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index 6ccbecac..bc8bf170 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -237,7 +237,6 @@ public class AccelListener extends Plugin implements SensorEventListener {
         if (this.status == AccelListener.STOPPED) {
             return;
         }
-        Log.d("ACCEL", "accuracy is now " + accuracy);
         this.accuracy = accuracy;
     }
 

From 2d5dcf24da18c210e08ccfcd473e1881a60bc429 Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Fri, 18 May 2012 13:50:45 -0700
Subject: [PATCH 11/13] [CB-463] updated js and rewrote accel plugin again to
 support the start/stop approach. optimized. single callback used for message
 passing

---
 framework/assets/js/cordova.android.js        | 103 ++++++++---
 .../src/org/apache/cordova/AccelListener.java | 167 ++++++------------
 2 files changed, 131 insertions(+), 139 deletions(-)

diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js
index 957be777..cdf619df 100644
--- a/framework/assets/js/cordova.android.js
+++ b/framework/assets/js/cordova.android.js
@@ -1,6 +1,6 @@
-// commit 7b6ae77e5030060e8e99fe0b79ddcf9d698bf375
+// commit 4a4ba9985c920850fe3f229abc60de984e196ab5
 
-// File generated at :: Mon May 14 2012 13:03:22 GMT-0700 (PDT)
+// File generated at :: Fri May 18 2012 13:43:11 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -3373,13 +3373,57 @@ var utils = require("cordova/utils"),
     exec = require("cordova/exec"),
     Acceleration = require('cordova/plugin/Acceleration');
 
+// Is the accel sensor running?
+var running = false;
 
 // Keeps reference to watchAcceleration calls.
 var timers = {};
 
+// Array of listeners; used to keep track of when we should call start and stop.
+var listeners = [];
+
 // Last returned acceleration object from native
 var accel = null;
 
+// Tells native to start.
+function start() {
+    exec(function(a) {
+        var tempListeners = listeners.slice(0);
+        accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+        for (var i = 0, l = tempListeners.length; i < l; i++) {
+            tempListeners[i].win(accel);
+        }
+    }, function(e) {
+        var tempListeners = listeners.slice(0);
+        for (var i = 0, l = tempListeners.length; i < l; i++) {
+            tempListeners[i].fail(e);
+        }
+    }, "Accelerometer", "start", []);
+    running = true;
+}
+
+// Tells native to stop.
+function stop() {
+    exec(null, null, "Accelerometer", "stop", []);
+    running = false;
+}
+
+// Adds a callback pair to the listeners array
+function createCallbackPair(win, fail) {
+    return {win:win, fail:fail};
+}
+
+// Removes a win/fail listener pair from the listeners array
+function removeListeners(l) {
+    var idx = listeners.indexOf(l);
+    if (idx > -1) {
+        listeners.splice(idx, 1);
+        if (listeners.length === 0) {
+            stop();
+        }
+    }
+}
+
 var accelerometer = {
     /**
      * Asynchronously aquires the current acceleration.
@@ -3394,13 +3438,22 @@ var accelerometer = {
             throw "getCurrentAcceleration must be called with at least a success callback function as first parameter.";
         }
 
+        var p;
         var win = function(a) {
-            accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
-            successCallback(accel);
+            successCallback(a);
+            removeListeners(p);
+        };
+        var fail = function(e) {
+            errorCallback(e);
+            removeListeners(p);
         };
 
-        // Get acceleration
-        exec(win, errorCallback, "Accelerometer", "getAcceleration", []);
+        p = createCallbackPair(win, fail);
+        listeners.push(p);
+
+        if (!running) {
+            start();
+        }
     },
 
     /**
@@ -3422,24 +3475,28 @@ var accelerometer = {
 
         // Keep reference to watch id, and report accel readings as often as defined in frequency
         var id = utils.createUUID();
-        timers[id] = window.setInterval(function() {
-            if (accel) {
-                successCallback(accel);
-            }
-        }, frequency);
 
-        // Success callback from native just updates the accel object.
-        var win = function(a) {
-            accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+        var p = createCallbackPair(function(){}, function(e) {
+            errorCallback(e);
+            removeListeners(p);
+        });
+        listeners.push(p);
+
+        timers[id] = {
+            timer:window.setInterval(function() {
+                if (accel) {
+                    successCallback(accel);
+                }
+            }, frequency),
+            listeners:p
         };
 
-        // Fail callback clears the watch and sends an error back.
-        var fail = function(err) {
-            accelerometer.clearWatch(id);
-            errorCallback(err);
-        };
-
-        exec(win, fail, "Accelerometer", "addWatch", [id, frequency]);
+        if (running) {
+            // If we're already running then immediately invoke the success callback
+            successCallback(accel);
+        } else {
+            start();
+        }
 
         return id;
     },
@@ -3452,9 +3509,9 @@ var accelerometer = {
     clearWatch: function(id) {
         // Stop javascript timer & remove from timer list
         if (id && timers[id]) {
-            window.clearInterval(timers[id]);
+            window.clearInterval(timers[id].timer);
+            removeListeners(timers[id].listeners);
             delete timers[id];
-            exec(null, null, "Accelerometer", "clearWatch", [id]);
         }
     }
 };
diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index bc8bf170..551e8bcb 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -58,9 +58,8 @@ public class AccelListener extends Plugin implements SensorEventListener {
 
     private SensorManager sensorManager;    // Sensor manager
     private Sensor mSensor;						      // Acceleration sensor returned by sensor manager
-    
-    private HashMap<String, String> watches = new HashMap<String, String>();
-    private List<String> callbacks = new ArrayList<String>();
+
+    private String callbackId;              // Keeps track of the single "start" callback ID passed in from JS
 
     /**
      * Create an accelerometer listener.
@@ -93,62 +92,28 @@ public class AccelListener extends Plugin implements SensorEventListener {
      * @return 				A PluginResult object with a status and message.
      */
     public PluginResult execute(String action, JSONArray args, String callbackId) {
-    	PluginResult.Status status = PluginResult.Status.NO_RESULT;
-    	String message = "";
-    	PluginResult result = new PluginResult(status, message);
-    	result.setKeepCallback(true);	
-    	try {
-    		if (action.equals("getAcceleration")) {
-    			if (this.status != AccelListener.RUNNING) {
-    				// If not running, then this is an async call, so don't worry about waiting
-    				// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
-    				this.callbacks.add(callbackId);
-    				this.start();
-    			} else {
-    				return new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
-    			}
-    		}
-    		else if (action.equals("addWatch")) {
-    			String watchId = args.getString(0);
-    			this.watches.put(watchId, callbackId);
-    			if (this.status != AccelListener.RUNNING) {
-    				this.start();
-    			}
-    		}
-    		else if (action.equals("clearWatch")) {
-    			String watchId = args.getString(0);
-    			if (this.watches.containsKey(watchId)) {
-    				this.watches.remove(watchId);
-    				if (this.size() == 0) {
-    					this.stop();
-    				}
-    			}
-    			return new PluginResult(PluginResult.Status.OK);
-    		} else {
-    			// Unsupported action
-    			return new PluginResult(PluginResult.Status.INVALID_ACTION);
-    		}
-    	} catch (JSONException e) {
-    		return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
-    	}
-    	return result;
-    }
+        PluginResult.Status status = PluginResult.Status.NO_RESULT;
+        String message = "";
+        PluginResult result = new PluginResult(status, message);
+        result.setKeepCallback(true);	
 
-    /**
-     * Identifies if action to be executed returns a value and should be run synchronously.
-     * 
-     * @param action	The action to execute
-     * @return			T=returns value
-     */
-    public boolean isSynch(String action) {
-    	if (action.equals("getAcceleration") && this.status == AccelListener.RUNNING) {
-    		return true;
-    	} else if (action.equals("addWatch") && this.status == AccelListener.RUNNING) {
-        	return true;
-        } else if (action.equals("clearWatch")) {
-            return true;
+        if (action.equals("start")) {
+            this.callbackId = callbackId;
+            if (this.status != AccelListener.RUNNING) {
+                // If not running, then this is an async call, so don't worry about waiting
+                // We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
+                this.start();
+            }
         }
-        return false;
+        else if (action.equals("stop")) {
+            if (this.status == AccelListener.RUNNING) {
+                this.stop();
+            }
+        } else {
+          // Unsupported action
+            return new PluginResult(PluginResult.Status.INVALID_ACTION);
+        }
+        return result;
     }
     
     /**
@@ -162,10 +127,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
     //--------------------------------------------------------------------------
     // LOCAL METHODS
     //--------------------------------------------------------------------------
-
-    private int size() {
-    	return this.watches.size() + this.callbacks.size();
-    }
+    //
     /**
      * Start listening for acceleration sensor.
      * 
@@ -267,74 +229,47 @@ public class AccelListener extends Plugin implements SensorEventListener {
         	this.z = event.values[2];
 
         	this.win();
-
-        	if (this.size() == 0) {
-        		this.stop();
-        	}
         }
     }
 
+    // Sends an error back to JS
     private void fail(int code, String message) {
-    	// Error object
-    	JSONObject errorObj = new JSONObject();
-    	try {
-			errorObj.put("code", code);
-	    	errorObj.put("message", message);
-		} catch (JSONException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-    	PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
-    	
-        for (String callbackId: this.callbacks)
-        {
-        	this.error(err, callbackId);
+        // Error object
+        JSONObject errorObj = new JSONObject();
+        try {
+            errorObj.put("code", code);
+            errorObj.put("message", message);
+        } catch (JSONException e) {
+            e.printStackTrace();
         }
-        this.callbacks.clear();
-        
+        PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
         err.setKeepCallback(true);
-        
-        Iterator it = this.watches.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry pairs = (Map.Entry)it.next();
-            this.error(err, (String)pairs.getValue());
-        }
+
+        this.error(err, this.callbackId);
     }
     
     private void win() {
-    	// Success return object
-    	PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
-    	
-    	for (String callbackId: this.callbacks)
-        {
-        	this.success(result, callbackId);
-        }
-        this.callbacks.clear();
-        
+        // Success return object
+        PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
         result.setKeepCallback(true);
-        
-        Iterator it = this.watches.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry pairs = (Map.Entry)it.next();
-            this.success(result, (String)pairs.getValue());
-        }
+
+        this.success(result, this.callbackId);
     }
 
-	private void setStatus(int status) {
+    private void setStatus(int status) {
         this.status = status;
     }
 	
-	private JSONObject getAccelerationJSON() {
-		JSONObject r = new JSONObject();
-		try {
-			r.put("x", this.x);
-			r.put("y", this.y);
-			r.put("z", this.z);
-			r.put("timestamp", this.timestamp);
-		} catch (JSONException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		return r;
-	}
+    private JSONObject getAccelerationJSON() {
+        JSONObject r = new JSONObject();
+        try {
+            r.put("x", this.x);
+            r.put("y", this.y);
+            r.put("z", this.z);
+            r.put("timestamp", this.timestamp);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return r;
+    }
 }

From 6b24f2d547695db060a860abe0cd1780abdbef5b Mon Sep 17 00:00:00 2001
From: Fil Maj <maj.fil@gmail.com>
Date: Fri, 18 May 2012 15:23:57 -0700
Subject: [PATCH 12/13] Small spacing fixes

---
 .../src/org/apache/cordova/AccelListener.java | 42 +++++++++----------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index 551e8bcb..9e19d753 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -136,7 +136,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
     private int start() {
     	// If already starting or running, then just return
     	if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
-    		return this.status;
+          return this.status;
     	}
     	
     	this.setStatus(AccelListener.STARTING);
@@ -146,28 +146,28 @@ public class AccelListener extends Plugin implements SensorEventListener {
 
     	// If found, then register as listener
     	if ((list != null) && (list.size() > 0)) {
-    		this.mSensor = list.get(0);
-    		this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
-    		this.setStatus(AccelListener.STARTING);
+          this.mSensor = list.get(0);
+          this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
+          this.setStatus(AccelListener.STARTING);
     	} else {
-    		this.setStatus(AccelListener.ERROR_FAILED_TO_START);
-    		this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
-    		return this.status;
+          this.setStatus(AccelListener.ERROR_FAILED_TO_START);
+          this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
+          return this.status;
     	}
     	
     	// Wait until running
     	long timeout = 2000;
     	while ((this.status == STARTING) && (timeout > 0)) {
-    		timeout = timeout - 100;
-    		try {
-    			Thread.sleep(100);
-    		} catch (InterruptedException e) {
-    			e.printStackTrace();
-    		}
+          timeout = timeout - 100;
+          try {
+              Thread.sleep(100);
+          } catch (InterruptedException e) {
+              e.printStackTrace();
+          }
     	}
     	if (timeout == 0) {
-    		this.setStatus(AccelListener.ERROR_FAILED_TO_START);
-    		this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
+          this.setStatus(AccelListener.ERROR_FAILED_TO_START);
+          this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
     	}
     	return this.status;
     }
@@ -222,13 +222,13 @@ public class AccelListener extends Plugin implements SensorEventListener {
         
         if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
 
-        	// Save time that event was received
-        	this.timestamp = System.currentTimeMillis();
-        	this.x = event.values[0];
-        	this.y = event.values[1];
-        	this.z = event.values[2];
+            // Save time that event was received
+            this.timestamp = System.currentTimeMillis();
+            this.x = event.values[0];
+            this.y = event.values[1];
+            this.z = event.values[2];
 
-        	this.win();
+            this.win();
         }
     }
 

From fae0c3dcfd5a73e5a4cbd47f218e52e03ad73eed Mon Sep 17 00:00:00 2001
From: macdonst <simon.macdonald@gmail.com>
Date: Tue, 22 May 2012 14:48:02 -0400
Subject: [PATCH 13/13] Fix problem in Android template example getPicture

---
 .../project/cordova/templates/project/assets/www/main.js        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bin/templates/project/cordova/templates/project/assets/www/main.js b/bin/templates/project/cordova/templates/project/assets/www/main.js
index 739a02e3..3a8b04a1 100644
--- a/bin/templates/project/cordova/templates/project/assets/www/main.js
+++ b/bin/templates/project/cordova/templates/project/assets/www/main.js
@@ -88,7 +88,7 @@ function dump_pic(data) {
     viewport.style.position = "absolute";
     viewport.style.top = "10px";
     viewport.style.left = "10px";
-    document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
+    document.getElementById("test_img").src = data;
 }
 
 function fail(msg) {