package org.bitseal.database; import java.util.ArrayList; import org.bitseal.data.Payload; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.util.Base64; import android.util.Log; /** * A singleton class which controls the creation, reading, updating, and * deletion of stored Payload objects. * * @author Jonathan Coe */ public class PayloadProvider { private static final String TAG = "PAYLOAD_PROVIDER"; private static PayloadProvider sPayloadProvider; private Context mAppContext; private static ContentResolver mContentResolver; private PayloadProvider(Context appContext) { mAppContext = appContext; mContentResolver = mAppContext.getContentResolver(); } /** * Returns an instance of this singleton class. * * @param c - A Context object for the currently running application */ public static PayloadProvider get(Context c) { if (sPayloadProvider == null) { Context appContext = c.getApplicationContext(); sPayloadProvider = new PayloadProvider(appContext); } return sPayloadProvider; } /** * Takes an Payload object and adds it to the app's * SQLite database as a new record, returning the ID of the * newly created record. * * @param p - The Payload object to be added * * @return id - A long value representing the ID of the newly * created record */ public long addPayload(Payload p) { int belongsToMe = 0; if (p.belongsToMe()) { belongsToMe = 1; } int processingComplete = 0; if (p.processingComplete()) { processingComplete = 1; } int powDone = 0; if (p.powDone()) { powDone = 1; } ContentValues values = new ContentValues(); values.put(PayloadsTable.COLUMN_RELATED_ADDRESS_ID, p.getRelatedAddressId()); values.put(PayloadsTable.COLUMN_BELONGS_TO_ME, belongsToMe); values.put(PayloadsTable.COLUMN_PROCESSING_COMPLETE, processingComplete); values.put(PayloadsTable.COLUMN_TIME, p.getTime()); values.put(PayloadsTable.COLUMN_TYPE, p.getType()); values.put(PayloadsTable.COLUMN_ACK, p.isAck()); values.put(PayloadsTable.COLUMN_POW_DONE, powDone); values.put(PayloadsTable.COLUMN_PAYLOAD, Base64.encodeToString(p.getPayload(), Base64.DEFAULT)); Uri insertionUri = mContentResolver.insert(DatabaseContentProvider.CONTENT_URI_PAYLOADS, values); Log.i(TAG, "Payload with type " + p.getType() + " and time value " + p.getTime() + " saved to database"); // Parse the ID of the newly created record from the insertion URI String uriString = insertionUri.toString(); String idString = uriString.substring(uriString.indexOf("/") + 1); long id = Long.parseLong(idString); return id; } /** * Finds all Payloads in the application's database that match the given field * * @param columnName - A String specifying the name of the column in the database that * should be used to find matching records. See the PayloadsTable class to find * the relevant column name. * @param searchString - A String specifying the value to search for. There are 4 use cases * for this:<br> * 1) The value to search for is a String (e.g. A label from the UI). In this case the value * can be passed in directly.<br> * 2) The value to search for is an int or long. In this case you should use String.valueOf(x) * and pass in the resulting String.<br> * 3) The value to search for is a boolean. In this case you should pass in the String "0" for * false or the String "1" for true. <br> * 4) The value to search for is a byte[]. In this case you should encode the byte[] into a * Base64 encoded String using the class android.util.Base64 and pass in the resulting String.<br><br> * * <b>NOTE:</b> The above String conversion is very clumsy, but seems to be necessary. See * https://stackoverflow.com/questions/20911760/android-how-to-query-sqlitedatabase-with-non-string-selection-args * * @return An ArrayList containing Payload objects populated with the data from * the database search */ public ArrayList<Payload> searchPayloads(String columnName, String searchString) { ArrayList<Payload> matchingRecords = new ArrayList<Payload>(); // Specify which columns from the table we are interested in String[] projection = { PayloadsTable.COLUMN_ID, PayloadsTable.COLUMN_RELATED_ADDRESS_ID, PayloadsTable.COLUMN_BELONGS_TO_ME, PayloadsTable.COLUMN_PROCESSING_COMPLETE, PayloadsTable.COLUMN_TIME, PayloadsTable.COLUMN_TYPE, PayloadsTable.COLUMN_ACK, PayloadsTable.COLUMN_POW_DONE, PayloadsTable.COLUMN_PAYLOAD}; // Query the database via the ContentProvider Cursor cursor = mContentResolver.query( DatabaseContentProvider.CONTENT_URI_PAYLOADS, projection, PayloadsTable.TABLE_PAYLOADS + "." + columnName + " = ? ", new String[]{searchString}, null); if (cursor.moveToFirst()) { do { long id = cursor.getLong(0); long relatedAddressId = cursor.getLong(1); int belongsToMeValue = cursor.getInt(2); boolean belongsToMe = false; if (belongsToMeValue == 1) { belongsToMe = true; } int processingCompleteValue = cursor.getInt(3); boolean processingComplete = false; if (processingCompleteValue == 1) { processingComplete = true; } long time = cursor.getLong(4); String type = cursor.getString(5); int isAckValue = cursor.getInt(6); boolean isAck = false; if (isAckValue == 1) { isAck = true; } int powDoneValue = cursor.getInt(7); boolean powDone = false; if (powDoneValue == 1) { powDone = true; } byte[] payload = Base64.decode(cursor.getString(8), Base64.DEFAULT); Payload p = new Payload(); p.setId(id); p.setRelatedAddressId(relatedAddressId); p.setBelongsToMe(belongsToMe); p.setProcessingComplete(processingComplete); p.setTime(time); p.setType(type); p.setAck(isAck); p.setPOWDone(powDone); p.setPayload(payload); matchingRecords.add(p); } while (cursor.moveToNext()); } cursor.close(); return matchingRecords; } /** * Finds all Payloads in the application's database that match the given criteria. This * method allows for multiple search terms. * * @param columnNames - The columns in the table to use in the query * @param selections - The selections for each column * * @return An ArrayList containing Payload objects populated with the data from * the database search */ public ArrayList<Payload> searchPayloads(String[] columnNames, String[] selections) { ArrayList<Payload> matchingRecords = new ArrayList<Payload>(); // Specify which columns from the table we are interested in String[] projection = { PayloadsTable.COLUMN_ID, PayloadsTable.COLUMN_RELATED_ADDRESS_ID, PayloadsTable.COLUMN_BELONGS_TO_ME, PayloadsTable.COLUMN_PROCESSING_COMPLETE, PayloadsTable.COLUMN_TIME, PayloadsTable.COLUMN_TYPE, PayloadsTable.COLUMN_ACK, PayloadsTable.COLUMN_POW_DONE, PayloadsTable.COLUMN_PAYLOAD}; // Build the selection String String selectionString = PayloadsTable.TABLE_PAYLOADS + "."; int counter = 0; for (String columnName : columnNames) { String stringToAppend = columnName + " = ? "; if ((counter + 1) != columnNames.length) // If this is not the last column name in the search data { stringToAppend = stringToAppend + "AND "; } selectionString = selectionString + stringToAppend; counter ++; } // Query the database via the ContentProvider Cursor cursor = mContentResolver.query( DatabaseContentProvider.CONTENT_URI_PAYLOADS, projection, selectionString, selections, null); if (cursor.moveToFirst()) { do { long id = cursor.getLong(0); long relatedAddressId = cursor.getLong(1); int belongsToMeValue = cursor.getInt(2); boolean belongsToMe = false; if (belongsToMeValue == 1) { belongsToMe = true; } int processingCompleteValue = cursor.getInt(3); boolean processingComplete = false; if (processingCompleteValue == 1) { processingComplete = true; } long time = cursor.getLong(4); String type = cursor.getString(5); int isAckValue = cursor.getInt(6); boolean isAck = false; if (isAckValue == 1) { isAck = true; } int powDoneValue = cursor.getInt(7); boolean powDone = false; if (powDoneValue == 1) { powDone = true; } byte[] payload = Base64.decode(cursor.getString(8), Base64.DEFAULT); Payload p = new Payload(); p.setId(id); p.setRelatedAddressId(relatedAddressId); p.setBelongsToMe(belongsToMe); p.setProcessingComplete(processingComplete); p.setTime(time); p.setType(type); p.setAck(isAck); p.setPOWDone(powDone); p.setPayload(payload); matchingRecords.add(p); } while (cursor.moveToNext()); } cursor.close(); return matchingRecords; } /** * Searches the database for the Payload with the given ID. * This method will return exactly one Payload object or throw * a RuntimeException. * * @param id - A long value representing the Payload's ID. * * @return The Payload object with the given ID. */ public Payload searchForSingleRecord(long id) { ArrayList<Payload> retrievedRecords = searchPayloads(PayloadsTable.COLUMN_ID, String.valueOf(id)); if (retrievedRecords.size() != 1) { throw new RuntimeException("There should be exactly 1 record found in this search. Instead " + retrievedRecords.size() + " records were found"); } else { return retrievedRecords.get(0); } } /** * Returns an ArrayList containing all the Payloads stored in the * application's database * * @return An ArrayList containing one Payload object for * each record in the Payloads table. */ public ArrayList<Payload> getAllPayloads() { ArrayList<Payload> payloads = new ArrayList<Payload>(); // Specify which columns from the table we are interested in String[] projection = { PayloadsTable.COLUMN_ID, PayloadsTable.COLUMN_RELATED_ADDRESS_ID, PayloadsTable.COLUMN_BELONGS_TO_ME, PayloadsTable.COLUMN_PROCESSING_COMPLETE, PayloadsTable.COLUMN_TIME, PayloadsTable.COLUMN_TYPE, PayloadsTable.COLUMN_ACK, PayloadsTable.COLUMN_POW_DONE, PayloadsTable.COLUMN_PAYLOAD}; // Query the database via the ContentProvider Cursor cursor = mContentResolver.query( DatabaseContentProvider.CONTENT_URI_PAYLOADS, projection, null, null, null); if (cursor.moveToFirst()) { do { long id = cursor.getLong(0); long relatedAddressId = cursor.getLong(1); int belongsToMeValue = cursor.getInt(2); boolean belongsToMe = false; if (belongsToMeValue == 1) { belongsToMe = true; } int processingCompleteValue = cursor.getInt(3); boolean processingComplete = false; if (processingCompleteValue == 1) { processingComplete = true; } long time = cursor.getLong(4); String type = cursor.getString(5); int isAckValue = cursor.getInt(6); boolean isAck = false; if (isAckValue == 1) { isAck = true; } int powDoneValue = cursor.getInt(7); boolean powDone = false; if (powDoneValue == 1) { powDone = true; } byte[] payload = Base64.decode(cursor.getString(8), Base64.DEFAULT); Payload p = new Payload(); p.setId(id); p.setRelatedAddressId(relatedAddressId); p.setBelongsToMe(belongsToMe); p.setProcessingComplete(processingComplete); p.setTime(time); p.setType(type); p.setAck(isAck); p.setPOWDone(powDone); p.setPayload(payload); payloads.add(p); } while (cursor.moveToNext()); } cursor.close(); return payloads; } /** * Updates the database record for a given Payload object<br><br> * * <b>NOTE:</b> This method uses the given Payload's ID field to determine * which record in the database to update * * @param p - The Payload object to be updated */ public void updatePayload(Payload p) { int belongsToMe = 0; if (p.belongsToMe()) { belongsToMe = 1; } int processingComplete = 0; if (p.processingComplete()) { processingComplete = 1; } int powDone = 0; if (p.powDone()) { powDone = 1; } ContentValues values = new ContentValues(); values.put(PayloadsTable.COLUMN_RELATED_ADDRESS_ID, p.getRelatedAddressId()); values.put(PayloadsTable.COLUMN_BELONGS_TO_ME, belongsToMe); values.put(PayloadsTable.COLUMN_PROCESSING_COMPLETE, processingComplete); values.put(PayloadsTable.COLUMN_TIME, p.getTime()); values.put(PayloadsTable.COLUMN_TYPE, p.getType()); values.put(PayloadsTable.COLUMN_ACK, p.isAck()); values.put(PayloadsTable.COLUMN_POW_DONE, powDone); values.put(PayloadsTable.COLUMN_PAYLOAD, Base64.encodeToString(p.getPayload(), Base64.DEFAULT)); long id = p.getId(); // Query the database via the ContentProvider and update the record with the matching ID mContentResolver.update(DatabaseContentProvider.CONTENT_URI_PAYLOADS, values, PayloadsTable.COLUMN_ID + " = ? ", new String[]{String.valueOf(id)}); Log.i(TAG, "Payload with ID " + id + " updated"); } /** * Deletes an Payload object from the application's SQLite database<br><br> * * <b>NOTE:</b> This method uses the given Payload's ID field to determine * which record in the database to delete * * @param p - The Payload object to be deleted */ public void deletePayload(Payload p) { long id = p.getId(); // Query the database via the ContentProvider and delete the record with the matching ID int recordsDeleted = mContentResolver.delete( DatabaseContentProvider.CONTENT_URI_PAYLOADS, PayloadsTable.COLUMN_ID + " = ? ", new String[]{String.valueOf(id)}); Log.i(TAG, recordsDeleted + " Payload(s) deleted from database"); } /** * Deletes all Payloads from the database */ public void deleteAllPayloads() { // Query the database via the ContentProvider and delete all the records int recordsDeleted = mContentResolver.delete( DatabaseContentProvider.CONTENT_URI_PAYLOADS, null, null); Log.i(TAG, recordsDeleted + " Payload(s) deleted from database"); } /** * Deletes any Payloads that have a time value earlier than the specified value. * * @param deletionTime - The specified time * * @return The number of payloads deleted from the database */ public void deletePayloadsCreatedBefore(long deletionTime) { int recordsDeleted = mContentResolver.delete( DatabaseContentProvider.CONTENT_URI_PAYLOADS, PayloadsTable.COLUMN_TIME + " < ? ", new String[]{String.valueOf(deletionTime)}); Log.i(TAG, recordsDeleted + " Payload(s) deleted from database"); } }