You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2013/07/30 17:12:24 UTC
git commit: [CB-4428] Add android-storage plugin.
Updated Branches:
refs/heads/plugins [created] d3c19693e
[CB-4428] Add android-storage plugin.
Project: http://git-wip-us.apache.org/repos/asf/cordova-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-labs/commit/d3c19693
Tree: http://git-wip-us.apache.org/repos/asf/cordova-labs/tree/d3c19693
Diff: http://git-wip-us.apache.org/repos/asf/cordova-labs/diff/d3c19693
Branch: refs/heads/plugins
Commit: d3c19693e126a7199b865b6d7aed835166479a55
Parents: 8b8d4f6
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Jul 30 11:11:25 2013 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Tue Jul 30 11:11:25 2013 -0400
----------------------------------------------------------------------
README.md | 25 +--
android-storage/README.md | 3 +
android-storage/Storage.java | 244 +++++++++++++++++++++++++++
android-storage/openDatabase.js | 47 ++++++
android-storage/plugin.xml | 25 +++
android-storage/storage.js | 311 +++++++++++++++++++++++++++++++++++
6 files changed, 633 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index a994618..c098977 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,3 @@
-# Cordova Laboratory
-
-> Caution: Safety Goggles are Recommended!
-
-## Purpose
-
-The purpose of this repo is for experimental code. Examples include demo apps,
-native api explorations, or anything really that does not fit in an existing Cordova platform.
-
-## Project Organization
-
-> Everyone works on a branch
-
-`master` branch should *never* have content.
-
-Each project should create a separate branch to work on. There are major benefits
-to this practice:
-
-- Each project has an isolate git history, which allows for easy migration to
- a new git repository;
-- Working directory is not polluted with the files of other projects.
-- Projects will not step on each others toes.
\ No newline at end of file
+cordova-labs plugins branch
+-------------------------------
+This branch stores plugins that are built by the Cordova team, but are not supported as "core" plugins.
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/android-storage/README.md
----------------------------------------------------------------------
diff --git a/android-storage/README.md b/android-storage/README.md
new file mode 100644
index 0000000..4851e98
--- /dev/null
+++ b/android-storage/README.md
@@ -0,0 +1,3 @@
+cordova-plugin-android-storage
+-------------------------------
+This plugin enables WebSQL support on Android.
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/android-storage/Storage.java
----------------------------------------------------------------------
diff --git a/android-storage/Storage.java b/android-storage/Storage.java
new file mode 100644
index 0000000..193febc
--- /dev/null
+++ b/android-storage/Storage.java
@@ -0,0 +1,244 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+package org.apache.cordova.labs.storage;
+
+import java.io.File;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.*;
+
+/**
+ * This class implements the HTML5 database support to work around a bug for
+ * Android 3.0 devices. It is not used for other versions of Android, since
+ * HTML5 database is built in to the browser.
+ */
+public class Storage extends CordovaPlugin {
+
+ // 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 Storage() {
+ }
+
+ /**
+ * Executes the request and returns PluginResult.
+ *
+ * @param action
+ * The action to execute.
+ * @param args
+ * JSONArry of arguments for the plugin.
+ * @param callbackContext
+ * The callback context used when calling back into JavaScript.
+ * @return True if the action was valid, false otherwise.
+ */
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ if (action.equals("openDatabase")) {
+ this.openDatabase(args.getString(0), args.getString(1),
+ args.getString(2), args.getLong(3));
+ } 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));
+ }
+ else {
+ return false;
+ }
+ callbackContext.success();
+ return true;
+ }
+
+ /**
+ * Clean up and close database.
+ */
+ @Override
+ public void onDestroy() {
+ if (this.myDb != null) {
+ this.myDb.close();
+ this.myDb = null;
+ }
+ }
+
+ /**
+ * Clean up on navigation/refresh.
+ */
+ public void onReset() {
+ this.onDestroy();
+ }
+
+ // --------------------------------------------------------------------------
+ // LOCAL METHODS
+ // --------------------------------------------------------------------------
+
+ /**
+ * 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) {
+ this.path = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
+ }
+
+ this.dbName = this.path + File.separator + db + ".db";
+
+ /*
+ * What is all this nonsense? Well the separator was incorrect so the db was showing up in the wrong
+ * directory. This bit of code fixes that issue and moves the db to the correct directory.
+ */
+ File oldDbFile = new File(this.path + File.pathSeparator + db + ".db");
+ if (oldDbFile.exists()) {
+ File dbPath = new File(this.path);
+ File dbFile = new File(dbName);
+ dbPath.mkdirs();
+ oldDbFile.renameTo(dbFile);
+ }
+
+ 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.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').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("Storage.executeSql(): Error=" + ex.getMessage());
+
+ // Send error message back to JavaScript
+ this.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').failQuery('" + ex.getMessage() + "','" + tx_id + "');");
+ }
+ }
+
+ /**
+ * Checks to see the the query is a Data Definition 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 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.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/android-storage/openDatabase.js
----------------------------------------------------------------------
diff --git a/android-storage/openDatabase.js b/android-storage/openDatabase.js
new file mode 100644
index 0000000..a67aea6
--- /dev/null
+++ b/android-storage/openDatabase.js
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+
+var modulemapper = require('cordova/modulemapper'),
+ storage = require('./storage');
+
+var originalOpenDatabase = modulemapper.getOriginalSymbol(window, 'openDatabase');
+
+module.exports = function(name, version, desc, size) {
+ // First patch WebSQL if necessary
+ if (!originalOpenDatabase) {
+ // Not defined, create an openDatabase function for all to use!
+ return storage.openDatabase.apply(this, arguments);
+ }
+
+ // Defined, but some Android devices will throw a SECURITY_ERR -
+ // so we wrap the whole thing in a try-catch and shim in our own
+ // if the device has Android bug 16175.
+ try {
+ return originalOpenDatabase(name, version, desc, size);
+ } catch (ex) {
+ if (ex.code !== 18) {
+ throw ex;
+ }
+ }
+ return storage.openDatabase(name, version, desc, size);
+};
+
+
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/android-storage/plugin.xml
----------------------------------------------------------------------
diff --git a/android-storage/plugin.xml b/android-storage/plugin.xml
new file mode 100644
index 0000000..00fed84
--- /dev/null
+++ b/android-storage/plugin.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+ id="org.apache.cordova.labs.androidstorage"
+ version="0.1.0">
+
+ <name>Android Storage</name>
+
+ <!-- android -->
+ <platform name="android">
+ <js-module src="storage.js" name="storage" />
+
+ <js-module src="openDatabase.js" name="openDatabase">
+ <clobbers target="openDatabase" />
+ </js-module>
+
+ <config-file target="res/xml/config.xml" parent="/*">
+ <feature name="Storage">
+ <param name="android-package" value="org.apache.cordova.labs.storage.Storage"/>
+ </feature>
+ </config-file>
+
+ <source-file src="Storage.java" target-dir="src/org/apache/cordova/labs/storage" />
+ </platform>
+</plugin>
http://git-wip-us.apache.org/repos/asf/cordova-labs/blob/d3c19693/android-storage/storage.js
----------------------------------------------------------------------
diff --git a/android-storage/storage.js b/android-storage/storage.js
new file mode 100644
index 0000000..e615ebc
--- /dev/null
+++ b/android-storage/storage.js
@@ -0,0 +1,311 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ channel = require('cordova/channel');
+
+var queryQueue = {};
+
+/**
+ * SQL result set object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Rows = function() {
+ this.resultSet = []; // results array
+ this.length = 0; // number of rows
+};
+
+/**
+ * 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];
+};
+
+/**
+ * SQL result set that is returned to user.
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Result = function() {
+ this.rows = new DroidDB_Rows();
+};
+
+/**
+ * Callback from native code when query is complete.
+ * PRIVATE METHOD
+ *
+ * @param id Query id
+ */
+function completeQuery(id, data) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete 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 = data;
+ r.rows.length = data.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);
+ }
+ }
+}
+
+/**
+ * Callback from native code when query fails
+ * PRIVATE METHOD
+ *
+ * @param reason Error message
+ * @param id Query id
+ */
+function failQuery(reason, id) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete 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);
+ }
+ }
+}
+
+/**
+ * SQL query object
+ * PRIVATE METHOD
+ *
+ * @constructor
+ * @param tx The transaction object that this query belongs to
+ */
+var DroidDB_Query = function(tx) {
+
+ // Set the id of the query
+ this.id = utils.createUUID();
+
+ // Add this query to the queue
+ 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;
+
+};
+
+/**
+ * Transaction object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Tx = function() {
+
+ // Set the id of the transaction
+ this.id = utils.createUUID();
+
+ // 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;
+ var i;
+ for (i in this.queryList) {
+ if (this.queryList.hasOwnProperty(i)) {
+ 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);
+ }
+ }
+};
+
+/**
+ * Execute SQL statement
+ *
+ * @param sql SQL statement to execute
+ * @param params Statement parameters
+ * @param successCallback Success callback
+ * @param errorCallback Error callback
+ */
+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);
+ queryQueue[query.id] = query;
+
+ // Save callbacks
+ query.successCallback = successCallback;
+ query.errorCallback = errorCallback;
+
+ // Call native code
+ exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
+};
+
+var DatabaseShell = function() {
+};
+
+/**
+ * Start a transaction.
+ * Does not support rollback in event of failure.
+ *
+ * @param process {Function} The transaction function
+ * @param successCallback {Function}
+ * @param errorCallback {Function}
+ */
+DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
+ var tx = new DroidDB_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);
+ }
+ }
+ }
+};
+
+/**
+ * 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
+ */
+var DroidDB_openDatabase = function(name, version, display_name, size) {
+ exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
+ var db = new DatabaseShell();
+ return db;
+};
+
+
+module.exports = {
+ openDatabase:DroidDB_openDatabase,
+ failQuery:failQuery,
+ completeQuery:completeQuery
+};