/* * Copyright 2011 David Brazdil * * Licensed 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 uk.ac.cam.db538.cryptosms.data; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.joda.time.format.ISODateTimeFormat; import uk.ac.cam.db538.cryptosms.data.Message.MessageType; import uk.ac.cam.db538.cryptosms.utils.PhoneNumber; import android.content.ContentValues; import android.content.Context; import android.database.*; import android.database.sqlite.*; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; /* * Class communicating with the database */ public class DbPendingAdapter { private static final String DATABASE_NAME = "pending.db"; private static final String DATABASE_TABLE = "sms"; private static final int DATABASE_VERSION = 1; // The index (key) column name for use in where clauses public static final String KEY_ID = "_id"; public static final int COLUMN_ID = 0; // The name and column index of each column in your database public static final String KEY_SENDER = "sender"; public static final int COLUMN_SENDER = 1; public static final String KEY_TIMESTAMP = "timeStamp"; public static final int COLUMN_TIMESTAMP = 2; public static final String KEY_DATA = "data"; public static final int COLUMN_DATA = 3; // SQL Statement to create a new database private static final String DATABASE_CREATE_TABLE = "create table " + DATABASE_TABLE + " (" + KEY_ID + " integer primary key autoincrement, " + KEY_SENDER + " text not null, " + KEY_TIMESTAMP + " datetime not null, " + KEY_DATA + " blob not null );"; // Variable to hold the database instance private SQLiteDatabase mDatabase; // Context of the application using the database. private final Context mContext; // Database open/upgrade helper private DbPendingHelper mHelper; /** * Instantiates a database adapter * * @param context the context */ public DbPendingAdapter(Context context) { mContext = context; mHelper = new DbPendingHelper(mContext, DATABASE_NAME, null, DATABASE_VERSION); } /** * Opens the database * * @return the db pending adapter * @throws SQLException the sQL exception */ public DbPendingAdapter open() throws SQLException { mDatabase = mHelper.getWritableDatabase(); return this; } /** * Closes the database */ public void close() { mDatabase.close(); } private ContentValues getValues(Pending pending) { ContentValues values = new ContentValues(); values.put(KEY_SENDER, pending.getSender()); values.put(KEY_TIMESTAMP, ISODateTimeFormat.dateTime().print(pending.getTimeStamp())); values.put(KEY_DATA, pending.getData()); return values; } private ArrayList<Pending> getPending(Cursor cursor) { ArrayList<Pending> list = new ArrayList<Pending>(cursor.getCount()); if (cursor.moveToFirst()) { do { Pending pending = new Pending( cursor.getString(COLUMN_SENDER), ISODateTimeFormat.dateTimeParser().parseDateTime(cursor.getString(COLUMN_TIMESTAMP)), cursor.getBlob(COLUMN_DATA)); pending.setRowIndex(cursor.getLong(COLUMN_ID)); list.add(pending); } while (cursor.moveToNext()); } return list; } /** * Inserts entry into the database * * @param pending the pending * @return the long */ public long insertEntry(Pending pending) { pending.setRowIndex(mDatabase.insert(DATABASE_TABLE, null, getValues(pending))); return pending.getRowIndex(); } /** * Removes an entry from the database * * @param pending the pending * @return true, if successful */ public boolean removeEntry(Pending pending) { return mDatabase.delete(DATABASE_TABLE, KEY_ID + "=" + pending.getRowIndex(), null) > 0; } /** * Gets an entry from the database * * @param rowIndex the row index * @return the entry */ public Pending getEntry(long rowIndex) { Cursor cursor = mDatabase.query(DATABASE_TABLE, null, KEY_ID + "=" + rowIndex, null, null, null, null); ArrayList<Pending> result = getPending(cursor); if (result.size() > 0) return result.get(0); else return null; } private ArrayList<Pending> getAllMatchingEntries(String where) { Cursor cursor = mDatabase.query(DATABASE_TABLE, null, where, null, null, null, null); ArrayList<Pending> result = getPending(cursor); cursor.close(); return result; } public ArrayList<Pending> getAllEntries() { return getAllMatchingEntries(null); } /** * Gets all entries with given sender * * @param sender the sender * @return the all sender entries */ public ArrayList<Pending> getAllSenderEntries(String sender) { return getAllMatchingEntries(KEY_SENDER + "='" + sender + "'"); } public ArrayList<ArrayList<Pending>> getAllIdGroups() { ArrayList<Pending> allEntries = getAllEntries(); Comparator<Pending> comparatorMain = new Comparator<Pending>() { @Override public int compare(Pending object1, Pending object2) { // level 1 - senders if (PhoneNumber.compare(object1.getSender(), object2.getSender())) { // level 2 - types MessageType type1 = object1.getType(); MessageType type2 = object2.getType(); if (type1.equals(type2)) { // level 3 - ids/timestamps if (type1 == MessageType.TEXT) { return TextMessage.getMessageId(object1.getData()) - TextMessage.getMessageId(object2.getData()); } else if (type1 == MessageType.HANDSHAKE || type1 == MessageType.CONFIRM) { Long timeStamp1 = Long.valueOf(KeysMessage.getMessageTimeStamp(object1.getData())); Long timeStamp2 = Long.valueOf(KeysMessage.getMessageTimeStamp(object2.getData())); return timeStamp1.compareTo(timeStamp2); } else // unknown type return 0; } else return type1.compareTo(type2); } else return object1.getSender().compareToIgnoreCase(object2.getSender()); } }; // Comparator<Pending> comparatorIndex = new Comparator<Pending>() { // @Override // public int compare(Pending object1, Pending object2) { // return object1.getIndex() - object2.getIndex(); // } // }; // sort the entries according to their // 1) sender // 2) type // 3) ID // 4) index Collections.sort(allEntries, comparatorMain); // now divide into groups ArrayList<ArrayList<Pending>> idGroups = new ArrayList<ArrayList<Pending>>(); ArrayList<Pending> thisIdGroup = null; Pending lastItem = null; for (Pending p : allEntries) { // do we need to create a new id group? if (lastItem == null || comparatorMain.compare(lastItem, p) != 0) { thisIdGroup = new ArrayList<Pending>(); idGroups.add(thisIdGroup); } // add item into id group thisIdGroup.add(p); lastItem = p; } return idGroups; } /** * Updates an entry in the database * * @param pending the pending * @return true, if successful */ public boolean updateEntry(Pending pending) { return mDatabase.update(DATABASE_TABLE, getValues(pending), KEY_ID + "=" + pending.getRowIndex(), null) > 0; } /** * Drops all data from the database */ public void clear() { mDatabase.execSQL("DELETE FROM '" + DATABASE_TABLE + "'; VACUUM;"); } private static class DbPendingHelper extends SQLiteOpenHelper { public DbPendingHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } // Called when no database exists in disk and the helper class needs // to create a new one. @Override public void onCreate(SQLiteDatabase _db) { _db.execSQL(DATABASE_CREATE_TABLE); } // Called when there is a database version mismatch meaning that the version // of the database on disk needs to be upgraded to the current version. @Override public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) { // Log the version upgrade. Log.w("TaskDBAdapter", "Upgrading from version " + _oldVersion + " to " + _newVersion + ", which will destroy all old data"); // Upgrade the existing database to conform to the new version. Multiple // previous versions can be handled by comparing _oldVersion and _newVersion // values. // The simplest case is to drop the old table and create a new one. _db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE); // Create a new one. onCreate(_db); } } }