diff --git a/Android/SQLitePlugin/README.md b/Android/SQLitePlugin/README.md new file mode 100755 index 0000000..8325ff9 --- /dev/null +++ b/Android/SQLitePlugin/README.md @@ -0,0 +1,4 @@ +Phonegap SQLitePlugin +===================== + +Do not use yet, this is not ready!! \ No newline at end of file diff --git a/Android/SQLitePlugin/assets/www/SQLitePlugin.js b/Android/SQLitePlugin/assets/www/SQLitePlugin.js new file mode 100755 index 0000000..35f02a5 --- /dev/null +++ b/Android/SQLitePlugin/assets/www/SQLitePlugin.js @@ -0,0 +1,245 @@ +(function() { + var callbacks, cbref, counter, getOptions, root; + + root = this; + + callbacks = {}; + + counter = 0; + + cbref = function(hash) { + var f; + f = "cb" + (counter += 1); + callbacks[f] = hash; + return f; + }; + + getOptions = function(opts, success, error) { + var cb, has_cbs; + cb = {}; + has_cbs = false; + if (typeof success === "function") { + has_cbs = true; + cb.success = success; + } + if (typeof error === "function") { + has_cbs = true; + cb.error = error; + } + if (has_cbs) opts.callback = cbref(cb); + return opts; + }; + root.SQLitePlugin = (function() { + console.log("root.SQLitePlugin"); + SQLitePlugin.prototype.openDBs = {}; + + function SQLitePlugin(dbPath, openSuccess, openError) { + console.log("SQLitePlugin"); + this.dbPath = dbPath; + this.openSuccess = openSuccess; + this.openError = openError; + if (!dbPath) { + throw new Error("Cannot create a SQLitePlugin instance without a dbPath"); + } + this.openSuccess || (this.openSuccess = function() { + console.log("DB opened: " + dbPath); + }); + this.openError || (this.openError = function(e) { + console.log(e.message); + }); + this.open(this.openSuccess, this.openError); + } + + SQLitePlugin.handleCallback = function(ref, type, obj) { + console.log("SQLitePlugin.prototype.handleCallback"); + var _ref; + if ((_ref = callbacks[ref]) != null) { + if (typeof _ref[type] === "function") _ref[type](obj); + } + callbacks[ref] = null; + delete callbacks[ref]; + }; + SQLitePlugin.prototype.executeSql = function(sql, values, success, error) { + console.log("SQLitePlugin.prototype.executeSql"); + var opts; + if (!sql) throw new Error("Cannot executeSql without a query"); + opts = getOptions({ + query: [sql].concat(values || []), + path: this.dbPath + }, success, error); + // PhoneGap.exec("SQLitePlugin.backgroundExecuteSql", opts); + PhoneGap.exec(null, null, "SQLitePlugin", "backgroundExecuteSql", opts); + }; + SQLitePlugin.prototype.transaction = function(fn, error, success) { + console.log("SQLitePlugin.prototype.transaction"); + var t; + t = new root.SQLitePluginTransaction(this.dbPath); + fn(t); + return t.complete(success, error); + }; + + SQLitePlugin.prototype.open = function(success, error) { + console.log("SQLitePlugin.prototype.open"); + var opts; + if (!(this.dbPath in this.openDBs)) { + this.openDBs[this.dbPath] = true; + opts = getOptions({ + path: this.dbPath + }, success, error); + //PhoneGap.exec("SQLitePlugin.open", opts); + /* + * db The name of the database + version The version + display_name The display name + size The size in bytes + * + */ + PhoneGap.exec(success, error, "SQLitePlugin", "open", [this.dbPath]); + } + }; + + SQLitePlugin.prototype.close = function(success, error) { + console.log("SQLitePlugin.prototype.close"); + var opts; + if (this.dbPath in this.openDBs) { + delete this.openDBs[this.dbPath]; + opts = getOptions({ + path: this.dbPath + }, success, error); + //PhoneGap.exec("SQLitePlugin.close", opts); + PhoneGap.exec(null, null, "SQLitePlugin", "close", opts); + } + }; + + return SQLitePlugin; + + })(); + get_unique_id = function() + { + var id = new Date().getTime(); + var id2 = new Date().getTime(); + while(id === id2) + { + id2 = new Date().getTime(); + } + return id2+'000'; + } + transaction_queue = []; + transaction_callback_queue = new Object(); + root.SQLitePluginTransaction = (function() { + console.log("root.SQLitePluginTransaction"); + function SQLitePluginTransaction(dbPath) { + console.log("root.SQLitePluginTransaction.SQLitePluginTransaction"); + this.dbPath = dbPath; + this.executes = []; + this.trans_id = get_unique_id(); + console.log("root.SQLitePluginTransaction - this.trans_id:"+this.trans_id); + transaction_queue[this.trans_id] = []; + } + SQLitePluginTransaction.queryCompleteCallback = function(transId, queryId, result) + { + var query = null; + for (var x in transaction_queue[transId]) + { + if(transaction_queue[transId][x]['query_id'] == queryId) + { + query = transaction_queue[transId][x]; + break; + } + } + if(query) + console.log("SQLitePluginTransaction.completeCallback---query:"+query['query']); + else + console.log("SQLitePluginTransaction.completeCallback---JSON.stringify(transaction_queue):"+JSON.stringify(transaction_queue[transId])); + if(query['callback']) + query['callback'](result) + } + SQLitePluginTransaction.txCompleteCallback = function(transId) + { + if(typeof transId != 'undefined') + { + console.log("SQLitePluginTransaction.txCompleteCallback---transId:"+transId); + if(transId && transaction_callback_queue[transId]) + transaction_callback_queue[transId](); + } + else + console.log("SQLitePluginTransaction.txCompleteCallback---transId = NULL"); + } + SQLitePluginTransaction.prototype.add_to_transaction = function(trans_id, query, params, callback, err_callback) + { + var new_query = new Object();; + new_query['trans_id'] = trans_id; + new_query['query_id'] = get_unique_id(); + new_query['query'] = query; + new_query['params'] = params; + new_query['callback'] = callback; + new_query['err_callback'] = err_callback; + if(!transaction_queue[trans_id]) + transaction_queue[trans_id] = []; + transaction_queue[trans_id].push(new_query); + } + + SQLitePluginTransaction.prototype.executeSql = function(sql, values, success, error) { + console.log("SQLitePluginTransaction.prototype.executeSql"); + var errorcb, successcb, txself; + txself = this; + successcb = null; + if (success) { + successcb = function(execres) { + console.log("executeSql callback:"+JSON.stringify(execres)); + var res, saveres; + saveres = execres; + res = { + rows: { + item: function(i) { + return saveres[i]; + }, + length: saveres.length + }, + rowsAffected: saveres.rowsAffected, + insertId: saveres.insertId || null + }; + return success(txself, res); + }; + } + errorcb = null; + if (error) { + errorcb = function(res) { + return error(txself, res); + }; + } + this.add_to_transaction(this.trans_id, sql, values, successcb, errorcb); + console.log("executeSql - add_to_transaction"+sql); + }; + + + + SQLitePluginTransaction.prototype.complete = function(success, error) { + console.log("SQLitePluginTransaction.prototype.complete"); + var begin_opts, commit_opts, errorcb, executes, opts, successcb, txself; + if (this.__completed) throw new Error("Transaction already run"); + this.__completed = true; + txself = this; + successcb = function() {}; + if (success) { + successcb = function() { + return success(txself); + }; + } + errorcb = function(res) { + }; + if (error) { + errorcb = function(res) { + return error(txself, res); + }; + } + console.log("complete - this.transaction_queue"+JSON.stringify(transaction_queue[this.trans_id])); + transaction_callback_queue[this.trans_id] = successcb; + PhoneGap.exec(null, null, "SQLitePlugin", "executeSqlBatch", transaction_queue[this.trans_id]); + }; + + return SQLitePluginTransaction; + + })(); + +}).call(this); \ No newline at end of file diff --git a/Android/SQLitePlugin/src/com/phonegap/plugins/sqlitePlugin/SQLitePlugin.java b/Android/SQLitePlugin/src/com/phonegap/plugins/sqlitePlugin/SQLitePlugin.java new file mode 100755 index 0000000..e1ec8eb --- /dev/null +++ b/Android/SQLitePlugin/src/com/phonegap/plugins/sqlitePlugin/SQLitePlugin.java @@ -0,0 +1,317 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010, IBM Corporation + */ +package com.phonegap.plugins.sqlitePlugin; + +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 SQLitePlugin extends Plugin { + + // Data Definition Language + private static final String ALTER = "alter"; + private static final String CREATE = "create"; + private static final String DROP = "drop"; + private static final String TRUNCATE = "truncate"; + + SQLiteDatabase myDb = null; // Database object + String path = null; // Database path + String dbName = null; // Database name + + /** + * Constructor. + */ + public SQLitePlugin() { + } + + /** + * 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 { + // 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)); + } else if (action.equals("open")) { + this.openDatabase(args.getString(0), "1", + "database", 5000000); + //this.openDatabase(args.getString(0), args.getString(1), + // args.getString(2), args.getLong(3)); + } + else if (action.equals("executeSqlBatch")) + { + String[] queries = null; + String[] queryIDs = null; + String[][] params = null; + String trans_id = null; + JSONObject a = null; + JSONArray jsonArr = null; + int paramLen = 0; + + if (args.isNull(0)) { + queries = new String[0]; + } else { + int len = args.length(); + queries = new String[len]; + queryIDs = new String[len]; + params = new String[len][1]; + for (int i = 0; i < len; i++) + { + a = args.getJSONObject(i); + queries[i] = a.getString("query"); + queryIDs[i] = a.getString("query_id"); + trans_id = a.getString("trans_id"); + jsonArr = a.getJSONArray("params"); + paramLen = jsonArr.length(); + params[i] = new String[paramLen]; + + for (int j = 0; j < paramLen; j++) { + params[i][j] = jsonArr.getString(j); + } + } + } + if(trans_id != null) + this.executeSqlBatch(queries, params, queryIDs, trans_id); + else + Log.v("error", "null trans_id"); + } + else if (action.equals("executeSql")) { + String[] s = null; + if (args.isNull(1)) { + s = new String[0]; + } else { + JSONArray a = args.getJSONArray(1); + int len = a.length(); + s = new String[len]; + for (int i = 0; i < len; i++) { + s[i] = a.getString(i); + } + } + this.executeSql(args.getString(0), s, args.getString(2)); + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + /** + * 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) { + return true; + } + + /** + * 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) { + 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 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 { + if (isDDL(query)) { + this.myDb.execSQL(query); + this.sendJavascript("dddb.completeQuery('" + tx_id + "', '');"); + } + else { + Cursor myCursor = this.myDb.rawQuery(query, params); + //this.processResults(myCursor, tx_id); + myCursor.close(); + } + } + catch (SQLiteException ex) { + ex.printStackTrace(); + System.out.println("SQLitePlugin.executeSql(): Error=" + ex.getMessage()); + + // Send error message back to JavaScript + this.sendJavascript("dddb.fail('" + ex.getMessage() + "','" + tx_id + "');"); + } + } + public void executeSqlBatch(String[] queryarr, String[][] paramsarr, String[] queryIDs, String tx_id) { + try { + this.myDb.beginTransaction(); + String query = ""; + String query_id = ""; + String[] params; + int len = queryarr.length; + for (int i = 0; i < len; i++) { + query = queryarr[i]; + params = paramsarr[i]; + query_id = queryIDs[i]; + Cursor myCursor = this.myDb.rawQuery(query, params); + this.processResults(myCursor, query_id, tx_id); + myCursor.close(); + } + this.myDb.setTransactionSuccessful(); + } + catch (SQLiteException ex) { + ex.printStackTrace(); + System.out.println("SQLitePlugin.executeSql(): Error=" + ex.getMessage()); + + // Send error message back to JavaScript + //this.sendJavascript("dddb.fail('" + ex.getMessage() + "','" + tx_id + "');"); + } + finally { + this.myDb.endTransaction(); + Log.v("executeSqlBatch", tx_id); + this.sendJavascript("SQLitePluginTransaction.txCompleteCallback('" + tx_id + "');"); + } + } + + /** + * Checks to see the the query is a Data Definintion command + * + * @param query to be executed + * @return true if it is a DDL command, false otherwise + */ + private boolean isDDL(String query) { + String cmd = query.toLowerCase(); + if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) { + return true; + } + return false; + } + + /** + * Process query results. + * + * @param cur + * Cursor into query results + * @param tx_id + * Transaction id + */ + public void processResults(Cursor cur, String query_id, String tx_id) { + + String result = "[]"; + // If query result has rows + + if (cur.moveToFirst()) { + JSONArray fullresult = new JSONArray(); + String key = ""; + String value = ""; + int colCount = cur.getColumnCount(); + + // Build up JSON result object for each row + do { + JSONObject row = new JSONObject(); + try { + for (int i = 0; i < colCount; ++i) { + key = cur.getColumnName(i); + value = cur.getString(i); + row.put(key, value); + } + fullresult.put(row); + + } catch (JSONException e) { + e.printStackTrace(); + } + + } while (cur.moveToNext()); + + result = fullresult.toString(); + } + + // Let JavaScript know that there are no more rows + this.sendJavascript(" SQLitePluginTransaction.queryCompleteCallback('" + tx_id + "','" + query_id + "', " + result + ");"); + + } + +}