diff options
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/persistance')
3 files changed, 309 insertions, 1 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java new file mode 100644 index 00000000..7e3fdab0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java @@ -0,0 +1,203 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.database.Cursor; + +/** + * Created by steckbrief on 15.04.2016. + */ +public abstract class CursorHelper { + + static double getDouble(Cursor cursor, String columnName) { + if (null == cursor) { + return Double.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Double.MIN_VALUE; + } + return getDouble(cursor, columnIndex); + } + + static String getString(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getString(cursor, columnIndex); + } + + static float getFloat(Cursor cursor, String columnName) { + if (null == cursor) { + return Float.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Float.MIN_VALUE; + } + return getFloat(cursor, columnIndex); + } + + static int getInt(Cursor cursor, String columnName) { + if (null == cursor) { + return Integer.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Integer.MIN_VALUE; + } + return getInt(cursor, columnIndex); + } + + static long getLong(Cursor cursor, String columnName) { + if (null == cursor) { + return Long.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Long.MIN_VALUE; + } + return getLong(cursor, columnIndex); + } + + static int getShort(Cursor cursor, String columnName) { + if (null == cursor) { + return Short.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Short.MIN_VALUE; + } + return getShort(cursor, columnIndex); + } + + static byte[] getBlob(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getBlob(cursor, columnIndex); + } + + /** + * Returns the zero-based index for the given column name, or -1 if the column doesn't exist. + * If you expect the column to exist use {@link Cursor#getColumnIndexOrThrow(String)} instead, which + * will make the error more clear. + * + * @param columnName the name of the target column. + * @return the zero-based column index for the given column name, or -1 if + * the column name does not exist. + * @see Cursor#getColumnIndexOrThrow(String) + */ + static int getColumnIndex(Cursor cursor, String columnName) { + return cursor.getColumnIndex(columnName); + } + + /** + * Returns the value of the requested column as a byte array. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a blob type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a byte array. + */ + static byte[] getBlob(Cursor cursor, int columnIndex) { + return cursor.getBlob(columnIndex); + } + + /** + * Returns the value of the requested column as a String. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a string type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a String. + */ + static String getString(Cursor cursor, int columnIndex) { + return cursor.getString(columnIndex); + } + + /** + * Returns the value of the requested column as a short. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Short.MIN_VALUE</code>, + * <code>Short.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a short. + */ + static short getShort(Cursor cursor, int columnIndex) { + return cursor.getShort(columnIndex); + } + + /** + * Returns the value of the requested column as an int. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Integer.MIN_VALUE</code>, + * <code>Integer.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as an int. + */ + static int getInt(Cursor cursor, int columnIndex) { + return cursor.getInt(columnIndex); + } + + /** + * Returns the value of the requested column as a long. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Long.MIN_VALUE</code>, + * <code>Long.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a long. + */ + static long getLong(Cursor cursor, int columnIndex) { + return cursor.getLong(columnIndex); + } + + /** + * Returns the value of the requested column as a float. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>float</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a float. + */ + static float getFloat(Cursor cursor, int columnIndex) { + return cursor.getFloat(columnIndex); + } + + /** + * Returns the value of the requested column as a double. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>double</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a double. + */ + static double getDouble(Cursor cursor, int columnIndex) { + return cursor.getDouble(columnIndex); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java index 4157d6f8..768f16c5 100644 --- a/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/DatabaseBackend.java @@ -34,6 +34,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.json.JSONException; import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.BuildConfig; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlServiceImpl; import de.thedevstack.conversationsplus.crypto.axolotl.SQLiteAxolotlStore; @@ -53,6 +54,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; private static final int DATABASE_VERSION = 25; + private static final int C_TO_CPLUS_VERSION_OFFSET = 1000; + private static final int CPLUS_DATABASE_VERSION = 2; + private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100; + private static final int PHYSICAL_DATABASE_VERSION = DATABASE_VERSION + C_TO_CPLUS_VERSION_OFFSET + (CPLUS_DATABASE_VERSION * CPLUS_DATABASE_VERSION_MULTIPLIER); private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -129,8 +134,17 @@ public class DatabaseBackend extends SQLiteOpenHelper { + ") ON CONFLICT IGNORE" + ");"; + private static int calculateCDatabaseVersion(int physicalDatabaseVersion) { + return physicalDatabaseVersion % CPLUS_DATABASE_VERSION_MULTIPLIER; + } + + private static int calculateCPLusDatabaseVersion(int physicalDatabaseVersion) { + int cPlusDatabaseVersion = (physicalDatabaseVersion - C_TO_CPLUS_VERSION_OFFSET) / CPLUS_DATABASE_VERSION_MULTIPLIER; + return cPlusDatabaseVersion < 0 ? 0 : cPlusDatabaseVersion; + } + private DatabaseBackend(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); + super(context, DATABASE_NAME, null, PHYSICAL_DATABASE_VERSION); } @Override @@ -176,10 +190,34 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_PREKEYS_STATEMENT); db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); db.execSQL(CREATE_IDENTITIES_STATEMENT); + + // Create Conversations+ related tables + db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); } + protected void onUpgradeCPlusDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { + Logging.d("db.upgrade.cplus", "Updating Conversations+ database from version '" + oldVersion + "' to '" + newVersion + "'"); + if (oldVersion != newVersion) { + if ((oldVersion == 0 || oldVersion == 1) && newVersion == 2) { + Logging.d("db.upgrade.cplus", "Creating additional parameters table for messages."); + db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); + db.execSQL("INSERT INTO " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + "(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") " + + " SELECT " + Message.UUID + " FROM " + Message.TABLENAME); + } + } + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgradeConversationsDatabase(db, calculateCDatabaseVersion(oldVersion), calculateCDatabaseVersion(newVersion)); + onUpgradeCPlusDatabase(db, calculateCPLusDatabaseVersion(oldVersion), calculateCPLusDatabaseVersion(newVersion)); + } + + protected void onUpgradeConversationsDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { + Logging.d("db.upgrade.conversations", "Updating Conversations database from version '" + oldVersion + "' to '" + newVersion + "'"); + if (oldVersion == newVersion) { + return; + } if (oldVersion < 2 && newVersion >= 2) { db.execSQL("update " + Account.TABLENAME + " set " + Account.OPTIONS + " = " + Account.OPTIONS + " | 8"); @@ -396,8 +434,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void createMessage(Message message) { + Logging.d("db.msg.insert", "Inserting new message with uuid '" + message.getUuid() + "', isRead: " + message.isRead()); + SQLiteDatabase db = this.getWritableDatabase(); + db.beginTransaction(); db.insert(Message.TABLENAME, null, message.getContentValues()); + db.insert(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, null, MessageDatabaseAccess.getAdditionalParametersContentValues(message)); + db.setTransactionSuccessful(); + db.endTransaction(); } public void createAccount(Account account) { @@ -471,6 +515,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.moveToLast(); do { Message message = Message.fromCursor(cursor); + MessageDatabaseAccess.populateMessageParameters(db, message); message.setConversation(conversation); list.add(message); } while (cursor.moveToPrevious()); @@ -502,6 +547,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { @Override public Message next() { Message message = Message.fromCursor(cursor); + MessageDatabaseAccess.populateMessageParameters(db, message); cursor.moveToNext(); return message; } @@ -598,17 +644,28 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void updateMessage(Message message) { + Logging.d("db.msg.update", "Updating message with uuid '" + message.getUuid() + "', isRead: " + message.isRead()); SQLiteDatabase db = this.getWritableDatabase(); String[] args = {message.getUuid()}; + db.beginTransaction(); db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); + db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); + db.setTransactionSuccessful(); + db.endTransaction(); } public void updateMessage(Message message, String uuid) { + Logging.d("db.msg.update", "Updating message with uuid '" + uuid + "', isRead: " + message.isRead()); + SQLiteDatabase db = this.getWritableDatabase(); String[] args = {uuid}; + db.beginTransaction(); db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); + db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); + db.setTransactionSuccessful(); + db.endTransaction(); } public void readRoster(Roster roster) { @@ -642,6 +699,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void deleteMessagesInConversation(Conversation conversation) { + Logging.d("db.msg.delete", "Deleting messages in conversation with uuid '" + conversation.getUuid()); + SQLiteDatabase db = this.getWritableDatabase(); String[] args = {conversation.getUuid()}; db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java new file mode 100644 index 00000000..737c16ff --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java @@ -0,0 +1,46 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import de.thedevstack.conversationsplus.entities.Message; +import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException; +import de.thedevstack.conversationsplus.xmpp.jid.Jid; + +/** + * Created by steckbrief on 15.04.2016. + */ +public class MessageDatabaseAccess { + static final String TABLE_NAME_ADDITIONAL_PARAMETERS = "message_parameters"; + static final String COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD = "httpupload"; + static final String COLUMN_NAME_MSG_PARAMS_MSGUUID = "message_uuid"; + + static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V0 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " (" + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, " + + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)"; + + static ContentValues getAdditionalParametersContentValues(Message message) { + ContentValues additionalParameters = new ContentValues(); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID, message.getUuid()); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD, message.isHttpUploaded() ? 1 : 0); + + return additionalParameters; + } + + static void populateMessageParametersFromCursor(Cursor cursor, Message message) { + boolean isHttpUploaded = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD) == 1; + message.setHttpUploaded(isHttpUploaded); + } + + static void populateMessageParameters(SQLiteDatabase db, Message message) { + Cursor paramsCursor = db.query(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, + null, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", + new String[] {message.getUuid()}, + null, null, null, null); + paramsCursor.moveToNext(); + MessageDatabaseAccess.populateMessageParametersFromCursor(paramsCursor, message); + paramsCursor.close(); + } +} |