From 3a9c106aba5481df843f1349bb315e65fef2af9b Mon Sep 17 00:00:00 2001 From: macdonst Date: Sat, 9 Oct 2010 02:37:34 +0800 Subject: [PATCH 01/11] Small changes for older Android SDK --- .../src/com/phonegap/ContactAccessorSdk3_4.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessorSdk3_4.java b/framework/src/com/phonegap/ContactAccessorSdk3_4.java index ae64b109..1e3d049b 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk3_4.java +++ b/framework/src/com/phonegap/ContactAccessorSdk3_4.java @@ -31,6 +31,7 @@ import android.app.Activity; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; +import android.provider.Contacts; import android.provider.Contacts.ContactMethods; import android.provider.Contacts.ContactMethodsColumns; import android.provider.Contacts.Organizations; @@ -117,9 +118,8 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { contact.put("id", contactId); // Do query for name and note - // Right now we are just querying the displayName Cursor cur = cr.query(People.CONTENT_URI, - null, + new String[] {People.DISPLAY_NAME, People.NOTES}, "people._id = ?", new String[] {contactId}, null); @@ -128,22 +128,22 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { if (isRequired("displayName",populate)) { contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME))); } - if (isRequired("phoneNumbers",populate)) { + else if (isRequired("phoneNumbers",populate)) { contact.put("phoneNumbers", phoneQuery(cr, contactId)); } - if (isRequired("emails",populate)) { + else if (isRequired("emails",populate)) { contact.put("emails", emailQuery(cr, contactId)); } - if (isRequired("addresses",populate)) { + else if (isRequired("addresses",populate)) { contact.put("addresses", addressQuery(cr, contactId)); } - if (isRequired("organizations",populate)) { + else if (isRequired("organizations",populate)) { contact.put("organizations", organizationQuery(cr, contactId)); } - if (isRequired("ims",populate)) { + else if (isRequired("ims",populate)) { contact.put("ims", imQuery(cr, contactId)); } - if (isRequired("note",populate)) { + else if (isRequired("note",populate)) { contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES))); } // nickname From b125f4e74be872a4c55ebb6cc1e92ae55d78544a Mon Sep 17 00:00:00 2001 From: macdonst Date: Sat, 9 Oct 2010 02:52:16 +0800 Subject: [PATCH 02/11] Fixing if/else condition --- .../src/com/phonegap/ContactAccessorSdk3_4.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessorSdk3_4.java b/framework/src/com/phonegap/ContactAccessorSdk3_4.java index 1e3d049b..de3c42e1 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk3_4.java +++ b/framework/src/com/phonegap/ContactAccessorSdk3_4.java @@ -128,22 +128,23 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { if (isRequired("displayName",populate)) { contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME))); } - else if (isRequired("phoneNumbers",populate)) { + if (isRequired("phoneNumbers",populate)) { + Log.d(LOG_TAG, "Populating phone numbers"); contact.put("phoneNumbers", phoneQuery(cr, contactId)); } - else if (isRequired("emails",populate)) { + if (isRequired("emails",populate)) { contact.put("emails", emailQuery(cr, contactId)); } - else if (isRequired("addresses",populate)) { + if (isRequired("addresses",populate)) { contact.put("addresses", addressQuery(cr, contactId)); } - else if (isRequired("organizations",populate)) { + if (isRequired("organizations",populate)) { contact.put("organizations", organizationQuery(cr, contactId)); } - else if (isRequired("ims",populate)) { + if (isRequired("ims",populate)) { contact.put("ims", imQuery(cr, contactId)); } - else if (isRequired("note",populate)) { + if (isRequired("note",populate)) { contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES))); } // nickname @@ -334,6 +335,8 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { while (cursor.moveToNext()) { phone = new JSONObject(); try{ + Log.d(LOG_TAG, "Found = " + cursor.getString(cursor.getColumnIndex(Phones.NUMBER))); + phone.put("primary", false); phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER))); phone.put("type", cursor.getString(cursor.getColumnIndex(Phones.TYPE))); From 3fd372f9d131e6430bf32efca45905f8c7338124 Mon Sep 17 00:00:00 2001 From: macdonst Date: Sat, 9 Oct 2010 02:55:05 +0800 Subject: [PATCH 03/11] Remove logs --- framework/src/com/phonegap/ContactAccessorSdk3_4.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessorSdk3_4.java b/framework/src/com/phonegap/ContactAccessorSdk3_4.java index de3c42e1..879902c3 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk3_4.java +++ b/framework/src/com/phonegap/ContactAccessorSdk3_4.java @@ -129,7 +129,6 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME))); } if (isRequired("phoneNumbers",populate)) { - Log.d(LOG_TAG, "Populating phone numbers"); contact.put("phoneNumbers", phoneQuery(cr, contactId)); } if (isRequired("emails",populate)) { @@ -335,8 +334,6 @@ public class ContactAccessorSdk3_4 extends ContactAccessor { while (cursor.moveToNext()) { phone = new JSONObject(); try{ - Log.d(LOG_TAG, "Found = " + cursor.getString(cursor.getColumnIndex(Phones.NUMBER))); - phone.put("primary", false); phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER))); phone.put("type", cursor.getString(cursor.getColumnIndex(Phones.TYPE))); From 5164464d28a760b86e4f933e53b39b00d2e0d51f Mon Sep 17 00:00:00 2001 From: davejohnson Date: Mon, 11 Oct 2010 20:30:54 +0800 Subject: [PATCH 04/11] Change droidgap create to not use 'grep' and have better error messages --- lib/classic.rb | 12 ++++++++++-- lib/create.rb | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/classic.rb b/lib/classic.rb index 77d31581..f28e4e72 100644 --- a/lib/classic.rb +++ b/lib/classic.rb @@ -49,8 +49,16 @@ class Classic # runs android create project # TODO need to allow more flexible SDK targetting via config.xml def create_android - target_id = `android list targets | grep id:`.split("\n").last.match(/\d+/).to_a.first - `android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }` + IO.popen("android list targets") { |f| + targets = f.readlines(nil)[0].scan(/id\:.*$/) + if (targets.length > 0) + target_id = targets.last.match(/\d+/).to_a.first + `android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }` + else + puts "No Android targets found. Please run 'android' and install at least one SDK package." + puts "If that makes no sense then you need to go read the Android SDK documentation." + end + } end # copies the project/www folder into tmp/android/www diff --git a/lib/create.rb b/lib/create.rb index 00b793c6..eec2c154 100644 --- a/lib/create.rb +++ b/lib/create.rb @@ -29,8 +29,8 @@ class Create < Classic @content = 'index.html' # stop executation on errors - raise 'No index.html found!' unless File.exists? File.join(path, 'index.html') - raise 'Could not find android in your path!' if @android_sdk_path.empty? + raise 'Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path.' unless File.exists? File.join(path, 'index.html') + raise 'Could not find android in your PATH!' if @android_sdk_path.empty? end # reads in a config.xml file From 032db387f8a9d8e4dafb2d9e15931f2b31c5b000 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 12 Oct 2010 15:53:57 -0500 Subject: [PATCH 05/11] Fix database for Android 1.x devices. It now behaves like HTML5 database API. --- framework/assets/js/phonegap.js.base | 4 +- framework/assets/js/storage.js | 162 +++++++++++++++++++---- framework/src/com/phonegap/DroidGap.java | 9 -- framework/src/com/phonegap/Storage.java | 137 +++++++++++++------ 4 files changed, 236 insertions(+), 76 deletions(-) mode change 100644 => 100755 framework/assets/js/storage.js diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 59401348..acef0497 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -316,7 +316,9 @@ PhoneGap.stringify = function(args) { s = s + '}'; } else { - s = s + '"' + args[i] + '"'; + var a = args[i].replace(/\\/g, '\\\\'); + a = a.replace(/"/g, '\\"'); + s = s + '"' + a + '"'; } } s = s + "]"; diff --git a/framework/assets/js/storage.js b/framework/assets/js/storage.js old mode 100644 new mode 100755 index ba234035..fe299a87 --- a/framework/assets/js/storage.js +++ b/framework/assets/js/storage.js @@ -5,71 +5,175 @@ * most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required */ +/** + * Storage object that is called by native code when performing queries. + * PRIVATE METHOD + */ var DroidDB = function() { - this.txQueue = []; + this.txQueue = {}; }; +/** + * Callback from native code when result from a query is available. + * PRIVATE METHOD + * + * @param rawdata JSON string of the row data + * @param tx_id Transaction id + */ DroidDB.prototype.addResult = function(rawdata, tx_id) { - eval("var data = " + rawdata); - var tx = this.txQueue[tx_id]; - tx.resultSet.push(data); + try { + eval("var data = " + rawdata + ";"); + var tx = this.txQueue[tx_id]; + tx.resultSet.push(data); + } catch (e) { + console.log("DroidDB.addResult(): Error="+e); + } }; +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param tx_id + */ DroidDB.prototype.completeQuery = function(tx_id) { - var tx = this.txQueue[tx_id]; - var r = new result(); - r.rows.resultSet = tx.resultSet; - r.rows.length = tx.resultSet.length; - tx.win(r); + var tx = null; + try { + tx = this.txQueue[tx_id]; + var r = new DroidDB_Result(); + r.rows.resultSet = tx.resultSet; + r.rows.length = tx.resultSet.length; + delete this.txQueue[tx_id]; + } catch (e) { + console.log("DroidDB.completeQuery(): Error="+e); + } + try { + tx.successCallback(tx, r); + } catch (e) { + console.log("DroidDB.completeQuery(): Error calling user success callback="+e); + } }; +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason + * @param tx_id + */ DroidDB.prototype.fail = function(reason, tx_id) { - var tx = this.txQueue[tx_id]; - tx.fail(reason); + var tx = null; + try { + tx = this.txQueue[tx_id]; + delete this.txQueue[tx_id]; + } catch (e) { + console.log("DroidDB.fail(): Error="+e); + } + try { + tx.errorCallback(reason); + } catch (e) { + console.log("DroidDB.fail(): Error calling user error callback="+e); + } }; var DatabaseShell = function() { }; +/** + * Start a transaction. + * + * @param process {Function} The transaction function + */ DatabaseShell.prototype.transaction = function(process) { - tx = new Tx(); + var tx = new DroidDB_Tx(); process(tx); }; -var Tx = function() { - droiddb.txQueue.push(this); - this.id = droiddb.txQueue.length - 1; +/** + * Transaction object + * PRIVATE METHOD + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = PhoneGap.createUUID(); + + // Add this transaction to the queue + droiddb.txQueue[this.id] = this; + + // Init result this.resultSet = []; }; -Tx.prototype.executeSql = function(query, params, win, fail) { +/** + * Execute SQL statement + * + * @param query + * @param params + * @param successCallback + * @param errorCallback + */ +DroidDB_Tx.prototype.executeSql = function(query, params, successCallback, errorCallback) { + + // Init params array + if (typeof params == 'undefined') { + params = []; + } + + // Save callbacks + var tx = droiddb.txQueue[this.id]; + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + + // Call native code PhoneGap.execAsync(null, null, "Storage", "executeSql", [query, params, this.id]); - tx.win = win; - tx.fail = fail; }; -var result = function() { - this.rows = new Rows(); +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + */ +DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); }; -var Rows = function() { - this.resultSet = []; - this.length = 0; +/** + * SQL result set object + * PRIVATE METHOD + */ +DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows }; -Rows.prototype.item = function(row_id) { - return this.resultSet[id]; +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; }; -var dbSetup = function(name, version, display_name, size) { +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +DroidDB_openDatabase = function(name, version, display_name, size) { PhoneGap.execAsync(null, null, "Storage", "openDatabase", [name, version, display_name, size]); - db_object = new DatabaseShell(); - return db_object; + var db = new DatabaseShell(); + return db; }; PhoneGap.addConstructor(function() { if (typeof window.openDatabase == "undefined") { - navigator.openDatabase = window.openDatabase = dbSetup; + navigator.openDatabase = window.openDatabase = DroidDB_openDatabase; window.droiddb = new DroidDB(); } }); diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 46697229..e091cb10 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -291,15 +291,6 @@ public class DroidGap extends Activity { this.addService("Storage", "com.phonegap.Storage"); this.addService("Temperature", "com.phonegap.TempListener"); - // Add in support for storage for Android 1.X devices - if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { - System.out.println("Android 1.X device"); - - Package pack = this.getClass().getPackage(); - String appPackage = pack.getName(); - this.pluginManager.exec("Storage", "setStorage", null, "["+appPackage+"]", false); - } - } /** diff --git a/framework/src/com/phonegap/Storage.java b/framework/src/com/phonegap/Storage.java index a3339306..924b5b55 100755 --- a/framework/src/com/phonegap/Storage.java +++ b/framework/src/com/phonegap/Storage.java @@ -2,21 +2,21 @@ package com.phonegap; import org.json.JSONArray; import org.json.JSONException; - +import org.json.JSONObject; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; - import android.database.Cursor; import android.database.sqlite.*; -import android.util.Log; +/** + * This class implements the HTML5 database support for Android 1.X devices. + * It is not used for Android 2.X, since HTML5 database is built in to the browser. + */ public class Storage extends Plugin { - private static final String LOG_TAG = "SQLite Storage:"; - - SQLiteDatabase myDb; - String path; - String txid = ""; + SQLiteDatabase myDb = null; // Database object + String path = null; // Database path + String dbName = null; // Database name /** * Constructor. @@ -37,6 +37,7 @@ public class Storage extends Plugin { String result = ""; try { + // TODO: Do we want to allow a user to do this, since they could get to other app databases? if (action.equals("setStorage")) { this.setStorage(args.getString(0)); } @@ -67,57 +68,119 @@ public class Storage extends Plugin { public boolean isSynch(String action) { return false; } + + /** + * Clean up and close database. + */ + @Override + public void onDestroy() { + if (this.myDb != null) { + this.myDb.close(); + this.myDb = null; + } + } //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- + /** + * Set the application package for the database. Each application saves its + * database files in a directory with the application package as part of the file name. + * + * For example, application "com.phonegap.demo.Demo" would save its database + * files in "/data/data/com.phonegap.demo/databases/" directory. + * + * @param appPackage The application package. + */ public void setStorage(String appPackage) { - path = "/data/data/" + appPackage + "/databases/"; + this.path = "/data/data/" + appPackage + "/databases/"; } + /** + * Open database. + * + * @param db The name of the database + * @param version The version + * @param display_name The display name + * @param size The size in bytes + */ public void openDatabase(String db, String version, String display_name, long size) { - if (path != null) { - path += db + ".db"; - myDb = SQLiteDatabase.openOrCreateDatabase(path, null); + + // If database is open, then close it + if (this.myDb != null) { + this.myDb.close(); } + + // If no database path, generate from application package + if (this.path == null) { + Package pack = this.ctx.getClass().getPackage(); + String appPackage = pack.getName(); + this.setStorage(appPackage); + } + + this.dbName = this.path + db + ".db"; + this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null); } + /** + * Execute SQL statement. + * + * @param query The SQL query + * @param params Parameters for the query + * @param tx_id Transaction id + */ public void executeSql(String query, String[] params, String tx_id) { try { - txid = tx_id; - Cursor myCursor = myDb.rawQuery(query, params); - processResults(myCursor); - } catch (SQLiteException ex) { - Log.d(LOG_TAG, ex.getMessage()); - txid = ""; - this.sendJavascript("droiddb.fail(" + ex.getMessage() + "," + txid + ");"); + Cursor myCursor = this.myDb.rawQuery(query, params); + this.processResults(myCursor, tx_id); + myCursor.close(); + } + catch (SQLiteException ex) { + ex.printStackTrace(); + System.out.println("Storage.executeSql(): Error=" + ex.getMessage()); + + // Send error message back to JavaScript + this.sendJavascript("droiddb.fail('" + ex.getMessage() + "','" + tx_id + "');"); } } - public void processResults(Cursor cur) { - String key = ""; - String value = ""; - String resultString = ""; + /** + * Process query results. + * + * @param cur Cursor into query results + * @param tx_id Transaction id + */ + public void processResults(Cursor cur, String tx_id) { + + // If query result has rows if (cur.moveToFirst()) { + String key = ""; + String value = ""; int colCount = cur.getColumnCount(); + + // Build up JSON result object for each row do { - resultString = "{"; - for (int i = 0; i < colCount; ++i) { - key = cur.getColumnName(i); - value = cur.getString(i); - resultString += " \"" + key + "\" : \"" + value + "\""; - if (i != (colCount - 1)) { - resultString += ","; + JSONObject result = new JSONObject(); + try { + for (int i = 0; i < colCount; ++i) { + key = cur.getColumnName(i); + value = cur.getString(i).replace("\"", "\\\""); // must escape " with \" for JavaScript + result.put(key, value); } + + // Send row back to JavaScript + this.sendJavascript("droiddb.addResult('" + result.toString() + "','" + tx_id + "');"); + + } catch (JSONException e) { + e.printStackTrace(); } - resultString += "}"; - this.sendJavascript("droiddb.addResult('" + resultString + "', " + txid + ");"); - } while (cur.moveToNext()); - this.sendJavascript("droiddb.completeQuery(" + txid + ");"); - txid = ""; - myDb.close(); - } + + } while (cur.moveToNext()); + + // Let JavaScript know that there are no more rows + this.sendJavascript("droiddb.completeQuery('" + tx_id + "');"); + } } } \ No newline at end of file From 522a7225dba89885c846cb5aedc6ef965f0b8235 Mon Sep 17 00:00:00 2001 From: Dave Johnson Date: Tue, 12 Oct 2010 23:18:11 +0100 Subject: [PATCH 06/11] Change PluginManager.isPhoneGapPlugin() to be much shorter like on the BlackBerry --- framework/src/com/phonegap/api/PluginManager.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index 2addf3af..fd666a56 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -140,15 +140,7 @@ public final class PluginManager { @SuppressWarnings("unchecked") private boolean isPhoneGapPlugin(Class c) { if (c != null) { - if (c.getSuperclass().getName().equals("com.phonegap.api.Plugin")) { - return true; - } - Class[] interfaces = c.getInterfaces(); - for (int j=0; j Date: Tue, 12 Oct 2010 23:36:08 +0100 Subject: [PATCH 07/11] Add IPlugin to isPhoneGapPlugin check --- framework/src/com/phonegap/api/PluginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index fd666a56..8ad13abe 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -140,7 +140,7 @@ public final class PluginManager { @SuppressWarnings("unchecked") private boolean isPhoneGapPlugin(Class c) { if (c != null) { - return com.phonegap.api.Plugin.class.isAssignableFrom(c); + return com.phonegap.api.Plugin.class.isAssignableFrom(c) || com.phonegap.api.IPlugin.class.isAssignableFrom(c); } return false; } From 5bdc81e84d570cabd11db22b16c47730b7dcd1b4 Mon Sep 17 00:00:00 2001 From: Jos Shepherd Date: Wed, 13 Oct 2010 14:22:56 +0800 Subject: [PATCH 08/11] Fix to new Android 1.6 storage code - callback wasn't being called for empty result sets. --- framework/src/com/phonegap/Storage.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/src/com/phonegap/Storage.java b/framework/src/com/phonegap/Storage.java index 924b5b55..0d764f9c 100755 --- a/framework/src/com/phonegap/Storage.java +++ b/framework/src/com/phonegap/Storage.java @@ -178,9 +178,10 @@ public class Storage extends Plugin { } while (cur.moveToNext()); - // Let JavaScript know that there are no more rows - this.sendJavascript("droiddb.completeQuery('" + tx_id + "');"); } + // Let JavaScript know that there are no more rows + this.sendJavascript("droiddb.completeQuery('" + tx_id + "');"); + } } \ No newline at end of file From 1f3bd9f51c00c049017145ac185f458df4016731 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 13 Oct 2010 13:43:52 -0500 Subject: [PATCH 09/11] Add support for multiple executeSql statements in transaction that returns results. If a transaction had more than one "SELECT * FROM TABLE" statement, then only one of the executeSql callbacks would be called. The others would not be called, even though they were successful. This is because a transaction object had only one result set. Changed code so that each executeSql has a result set and unique "query id" so that the query results can be sent back to the correct statement's callback. --- framework/assets/js/storage.js | 233 ++++++++++++++++++++++++++------- 1 file changed, 183 insertions(+), 50 deletions(-) diff --git a/framework/assets/js/storage.js b/framework/assets/js/storage.js index fe299a87..5f81db1a 100755 --- a/framework/assets/js/storage.js +++ b/framework/assets/js/storage.js @@ -10,7 +10,7 @@ * PRIVATE METHOD */ var DroidDB = function() { - this.txQueue = {}; + this.queryQueue = {}; }; /** @@ -18,13 +18,14 @@ var DroidDB = function() { * PRIVATE METHOD * * @param rawdata JSON string of the row data - * @param tx_id Transaction id + * @param id Query id */ -DroidDB.prototype.addResult = function(rawdata, tx_id) { +DroidDB.prototype.addResult = function(rawdata, id) { + console.log("DroidDB.addResult("+rawdata+", "+id+")"); try { eval("var data = " + rawdata + ";"); - var tx = this.txQueue[tx_id]; - tx.resultSet.push(data); + var query = this.queryQueue[id]; + query.resultSet.push(data); } catch (e) { console.log("DroidDB.addResult(): Error="+e); } @@ -34,23 +35,40 @@ DroidDB.prototype.addResult = function(rawdata, tx_id) { * Callback from native code when query is complete. * PRIVATE METHOD * - * @param tx_id + * @param id Query id */ -DroidDB.prototype.completeQuery = function(tx_id) { - var tx = null; - try { - tx = this.txQueue[tx_id]; - var r = new DroidDB_Result(); - r.rows.resultSet = tx.resultSet; - r.rows.length = tx.resultSet.length; - delete this.txQueue[tx_id]; - } catch (e) { - console.log("DroidDB.completeQuery(): Error="+e); - } - try { - tx.successCallback(tx, r); - } catch (e) { - console.log("DroidDB.completeQuery(): Error calling user success callback="+e); +DroidDB.prototype.completeQuery = function(id) { + console.log("DroidDB.completeQuery("+id+")"); + var query = this.queryQueue[id]; + if (query) { + try { + delete this.queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = query.resultSet; + r.rows.length = query.resultSet.length; + try { + if (typeof query.successCallback == 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } } }; @@ -58,21 +76,39 @@ DroidDB.prototype.completeQuery = function(tx_id) { * Callback from native code when query fails * PRIVATE METHOD * - * @param reason - * @param tx_id + * @param reason Error message + * @param id Query id */ -DroidDB.prototype.fail = function(reason, tx_id) { - var tx = null; - try { - tx = this.txQueue[tx_id]; - delete this.txQueue[tx_id]; - } catch (e) { - console.log("DroidDB.fail(): Error="+e); - } - try { - tx.errorCallback(reason); - } catch (e) { - console.log("DroidDB.fail(): Error calling user error callback="+e); +DroidDB.prototype.fail = function(reason, id) { + console.log("DroidDB.fail("+reason+", "+id+")"); + var query = this.queryQueue[id]; + if (query) { + try { + delete this.queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback == 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } } }; @@ -81,12 +117,28 @@ var DatabaseShell = function() { /** * Start a transaction. + * Does not support rollback in event of failure. * - * @param process {Function} The transaction function + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} */ -DatabaseShell.prototype.transaction = function(process) { +DatabaseShell.prototype.transaction = function(process, successCallback, errorCallback) { var tx = new DroidDB_Tx(); - process(tx); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } }; /** @@ -98,35 +150,116 @@ var DroidDB_Tx = function() { // Set the id of the transaction this.id = PhoneGap.createUUID(); - // Add this transaction to the queue - droiddb.txQueue[this.id] = this; + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + for (var i in this.queryList) { + count++; + } + if (count == 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * SQL query object + * PRIVATE METHOD + * + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = PhoneGap.createUUID(); + + // Add this query to the queue + droiddb.queryQueue[this.id] = this; // Init result this.resultSet = []; -}; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +} /** * Execute SQL statement * - * @param query - * @param params - * @param successCallback - * @param errorCallback + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback */ -DroidDB_Tx.prototype.executeSql = function(query, params, successCallback, errorCallback) { +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { // Init params array if (typeof params == 'undefined') { params = []; } + // Create query and add to queue + var query = new DroidDB_Query(this); + droiddb.queryQueue[query.id] = query; + // Save callbacks - var tx = droiddb.txQueue[this.id]; - tx.successCallback = successCallback; - tx.errorCallback = errorCallback; + query.successCallback = successCallback; + query.errorCallback = errorCallback; // Call native code - PhoneGap.execAsync(null, null, "Storage", "executeSql", [query, params, this.id]); + PhoneGap.execAsync(null, null, "Storage", "executeSql", [sql, params, query.id]); }; /** From be16eebf55be1955abd6f64749f541de0918411a Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 13 Oct 2010 13:47:43 -0500 Subject: [PATCH 10/11] Remove logging from storage.js. --- framework/assets/js/storage.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/assets/js/storage.js b/framework/assets/js/storage.js index 5f81db1a..42499161 100755 --- a/framework/assets/js/storage.js +++ b/framework/assets/js/storage.js @@ -21,7 +21,6 @@ var DroidDB = function() { * @param id Query id */ DroidDB.prototype.addResult = function(rawdata, id) { - console.log("DroidDB.addResult("+rawdata+", "+id+")"); try { eval("var data = " + rawdata + ";"); var query = this.queryQueue[id]; @@ -38,7 +37,6 @@ DroidDB.prototype.addResult = function(rawdata, id) { * @param id Query id */ DroidDB.prototype.completeQuery = function(id) { - console.log("DroidDB.completeQuery("+id+")"); var query = this.queryQueue[id]; if (query) { try { @@ -80,7 +78,6 @@ DroidDB.prototype.completeQuery = function(id) { * @param id Query id */ DroidDB.prototype.fail = function(reason, id) { - console.log("DroidDB.fail("+reason+", "+id+")"); var query = this.queryQueue[id]; if (query) { try { From 6d605c1cbf8eb5f43543d819afec77a04abe6ec2 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 14 Oct 2010 09:58:37 -0500 Subject: [PATCH 11/11] Set prepareListener before calling prepare. --- framework/src/com/phonegap/AudioPlayer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/com/phonegap/AudioPlayer.java b/framework/src/com/phonegap/AudioPlayer.java index 48a58004..44c71776 100755 --- a/framework/src/com/phonegap/AudioPlayer.java +++ b/framework/src/com/phonegap/AudioPlayer.java @@ -182,6 +182,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On if (this.isStreaming(file)) { this.mPlayer.setDataSource(file); this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + this.setState(MEDIA_STARTING); + this.mPlayer.setOnPreparedListener(this); this.mPlayer.prepareAsync(); } @@ -195,13 +197,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On else { this.mPlayer.setDataSource("/sdcard/" + file); } + this.setState(MEDIA_STARTING); + this.mPlayer.setOnPreparedListener(this); this.mPlayer.prepare(); // Get duration this.duration = this.mPlayer.getDuration(); } - this.mPlayer.setOnPreparedListener(this); - this.setState(MEDIA_STARTING); } catch (Exception e) { e.printStackTrace();