Merge branch 'master' of github.com:phonegap/phonegap-android

This commit is contained in:
brianleroux 2010-10-14 17:04:19 -07:00
commit f15bdf9445
7 changed files with 382 additions and 97 deletions

View File

@ -316,7 +316,9 @@ PhoneGap.stringify = function(args) {
s = s + '}'; s = s + '}';
} }
else { else {
s = s + '"' + args[i] + '"'; var a = args[i].replace(/\\/g, '\\\\');
a = a.replace(/"/g, '\\"');
s = s + '"' + a + '"';
} }
} }
s = s + "]"; s = s + "]";

308
framework/assets/js/storage.js Normal file → Executable file
View File

@ -5,71 +5,305 @@
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required * 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() { var DroidDB = function() {
this.txQueue = []; this.queryQueue = {};
}; };
DroidDB.prototype.addResult = function(rawdata, tx_id) { /**
eval("var data = " + rawdata); * Callback from native code when result from a query is available.
var tx = this.txQueue[tx_id]; * PRIVATE METHOD
tx.resultSet.push(data); *
* @param rawdata JSON string of the row data
* @param id Query id
*/
DroidDB.prototype.addResult = function(rawdata, id) {
try {
eval("var data = " + rawdata + ";");
var query = this.queryQueue[id];
query.resultSet.push(data);
} catch (e) {
console.log("DroidDB.addResult(): Error="+e);
}
}; };
DroidDB.prototype.completeQuery = function(tx_id) { /**
var tx = this.txQueue[tx_id]; * Callback from native code when query is complete.
var r = new result(); * PRIVATE METHOD
r.rows.resultSet = tx.resultSet; *
r.rows.length = tx.resultSet.length; * @param id Query id
tx.win(r); */
DroidDB.prototype.completeQuery = function(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);
}
}
}; };
DroidDB.prototype.fail = function(reason, tx_id) { /**
var tx = this.txQueue[tx_id]; * Callback from native code when query fails
tx.fail(reason); * PRIVATE METHOD
*
* @param reason Error message
* @param id Query id
*/
DroidDB.prototype.fail = function(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);
}
}
}; };
var DatabaseShell = function() { var DatabaseShell = function() {
}; };
DatabaseShell.prototype.transaction = function(process) { /**
tx = new Tx(); * Start a transaction.
process(tx); * 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, successCallback, errorCallback) {
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);
}
}
}
}; };
var Tx = function() { /**
droiddb.txQueue.push(this); * Transaction object
this.id = droiddb.txQueue.length - 1; * PRIVATE METHOD
*/
var DroidDB_Tx = function() {
// Set the id of the transaction
this.id = PhoneGap.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;
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 = []; 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 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);
droiddb.queryQueue[query.id] = query;
// Save callbacks
query.successCallback = successCallback;
query.errorCallback = errorCallback;
// Call native code
PhoneGap.execAsync(null, null, "Storage", "executeSql", [sql, params, query.id]);
}; };
Tx.prototype.executeSql = function(query, params, win, fail) { /**
PhoneGap.execAsync(null, null, "Storage", "executeSql", [query, params, this.id]); * SQL result set that is returned to user.
tx.win = win; * PRIVATE METHOD
tx.fail = fail; */
DroidDB_Result = function() {
this.rows = new DroidDB_Rows();
}; };
var result = function() { /**
this.rows = new Rows(); * SQL result set object
* PRIVATE METHOD
*/
DroidDB_Rows = function() {
this.resultSet = []; // results array
this.length = 0; // number of rows
}; };
var Rows = function() { /**
this.resultSet = []; * Get item from SQL result set
this.length = 0; *
* @param row The row number to return
* @return The row object
*/
DroidDB_Rows.prototype.item = function(row) {
return this.resultSet[row];
}; };
Rows.prototype.item = function(row_id) { /**
return this.resultSet[id]; * Open database
}; *
* @param name Database name
var dbSetup = function(name, version, display_name, size) { * @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]); PhoneGap.execAsync(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
db_object = new DatabaseShell(); var db = new DatabaseShell();
return db_object; return db;
}; };
PhoneGap.addConstructor(function() { PhoneGap.addConstructor(function() {
if (typeof window.openDatabase == "undefined") { if (typeof window.openDatabase == "undefined") {
navigator.openDatabase = window.openDatabase = dbSetup; navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
window.droiddb = new DroidDB(); window.droiddb = new DroidDB();
} }
}); });

View File

@ -182,6 +182,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
if (this.isStreaming(file)) { if (this.isStreaming(file)) {
this.mPlayer.setDataSource(file); this.mPlayer.setDataSource(file);
this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
this.setState(MEDIA_STARTING);
this.mPlayer.setOnPreparedListener(this);
this.mPlayer.prepareAsync(); this.mPlayer.prepareAsync();
} }
@ -195,13 +197,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
else { else {
this.mPlayer.setDataSource("/sdcard/" + file); this.mPlayer.setDataSource("/sdcard/" + file);
} }
this.setState(MEDIA_STARTING);
this.mPlayer.setOnPreparedListener(this);
this.mPlayer.prepare(); this.mPlayer.prepare();
// Get duration // Get duration
this.duration = this.mPlayer.getDuration(); this.duration = this.mPlayer.getDuration();
} }
this.mPlayer.setOnPreparedListener(this);
this.setState(MEDIA_STARTING);
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -31,6 +31,7 @@ import android.app.Activity;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.ContactMethods; import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.ContactMethodsColumns; import android.provider.Contacts.ContactMethodsColumns;
import android.provider.Contacts.Organizations; import android.provider.Contacts.Organizations;
@ -117,9 +118,8 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
contact.put("id", contactId); contact.put("id", contactId);
// Do query for name and note // Do query for name and note
// Right now we are just querying the displayName
Cursor cur = cr.query(People.CONTENT_URI, Cursor cur = cr.query(People.CONTENT_URI,
null, new String[] {People.DISPLAY_NAME, People.NOTES},
"people._id = ?", "people._id = ?",
new String[] {contactId}, new String[] {contactId},
null); null);

View File

@ -291,15 +291,6 @@ public class DroidGap extends Activity {
this.addService("Storage", "com.phonegap.Storage"); this.addService("Storage", "com.phonegap.Storage");
this.addService("Temperature", "com.phonegap.TempListener"); 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);
}
} }
/** /**

View File

@ -2,21 +2,21 @@ package com.phonegap;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject;
import com.phonegap.api.Plugin; import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult; import com.phonegap.api.PluginResult;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.*; 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 { public class Storage extends Plugin {
private static final String LOG_TAG = "SQLite Storage:"; SQLiteDatabase myDb = null; // Database object
String path = null; // Database path
SQLiteDatabase myDb; String dbName = null; // Database name
String path;
String txid = "";
/** /**
* Constructor. * Constructor.
@ -37,6 +37,7 @@ public class Storage extends Plugin {
String result = ""; String result = "";
try { try {
// TODO: Do we want to allow a user to do this, since they could get to other app databases?
if (action.equals("setStorage")) { if (action.equals("setStorage")) {
this.setStorage(args.getString(0)); this.setStorage(args.getString(0));
} }
@ -67,57 +68,120 @@ public class Storage extends Plugin {
public boolean isSynch(String action) { public boolean isSynch(String action) {
return false; return false;
} }
/**
* Clean up and close database.
*/
@Override
public void onDestroy() {
if (this.myDb != null) {
this.myDb.close();
this.myDb = null;
}
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// LOCAL METHODS // 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) { 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) { public void openDatabase(String db, String version, String display_name, long size) {
if (path != null) {
path += db + ".db"; // If database is open, then close it
myDb = SQLiteDatabase.openOrCreateDatabase(path, null); 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) { public void executeSql(String query, String[] params, String tx_id) {
try { try {
txid = tx_id; Cursor myCursor = this.myDb.rawQuery(query, params);
Cursor myCursor = myDb.rawQuery(query, params); this.processResults(myCursor, tx_id);
processResults(myCursor); myCursor.close();
} catch (SQLiteException ex) { }
Log.d(LOG_TAG, ex.getMessage()); catch (SQLiteException ex) {
txid = ""; ex.printStackTrace();
this.sendJavascript("droiddb.fail(" + ex.getMessage() + "," + txid + ");"); 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 = ""; * Process query results.
String value = ""; *
String resultString = ""; * @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()) { if (cur.moveToFirst()) {
String key = "";
String value = "";
int colCount = cur.getColumnCount(); int colCount = cur.getColumnCount();
// Build up JSON result object for each row
do { do {
resultString = "{"; JSONObject result = new JSONObject();
for (int i = 0; i < colCount; ++i) { try {
key = cur.getColumnName(i); for (int i = 0; i < colCount; ++i) {
value = cur.getString(i); key = cur.getColumnName(i);
resultString += " \"" + key + "\" : \"" + value + "\""; value = cur.getString(i).replace("\"", "\\\""); // must escape " with \" for JavaScript
if (i != (colCount - 1)) { result.put(key, value);
resultString += ",";
} }
// 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());
} while (cur.moveToNext());
this.sendJavascript("droiddb.completeQuery(" + txid + ");"); }
txid = ""; // Let JavaScript know that there are no more rows
myDb.close(); this.sendJavascript("droiddb.completeQuery('" + tx_id + "');");
}
} }
} }

View File

@ -140,15 +140,7 @@ public final class PluginManager {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private boolean isPhoneGapPlugin(Class c) { private boolean isPhoneGapPlugin(Class c) {
if (c != null) { if (c != null) {
if (c.getSuperclass().getName().equals("com.phonegap.api.Plugin")) { return com.phonegap.api.Plugin.class.isAssignableFrom(c) || com.phonegap.api.IPlugin.class.isAssignableFrom(c);
return true;
}
Class[] interfaces = c.getInterfaces();
for (int j=0; j<interfaces.length; j++) {
if (interfaces[j].getName().equals("com.phonegap.api.IPlugin")) {
return true;
}
}
} }
return false; return false;
} }