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
+};