diff --git a/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java b/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java index 0a824e19b62e863b7d3534a6b6179d385deec3f3..7ca659e2cb12451e12a85d76af243d3a2124cc2c 100644 --- a/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java +++ b/ring-android/app/src/main/java/cx/ring/history/DatabaseHelper.java @@ -18,28 +18,36 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - package cx.ring.history; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.util.Log; + import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; import java.sql.SQLException; +import java.util.ArrayList; + +/** + * Database History Version + * 7 : changing columns names. See https://gerrit-ring.savoirfairelinux.com/#/c/4297 + */ /** * Database helper class used to manage the creation and upgrading of your database. This class also usually provides * the DAOs used by the other classes. */ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { - + private static final String TAG = DatabaseHelper.class.getSimpleName(); private static final String DATABASE_NAME = "history.db"; // any time you make changes to your database objects, you may have to increase the database version - private static final int DATABASE_VERSION = 6; + private static final int DATABASE_VERSION = 7; private Dao<HistoryCall, Integer> historyDao = null; private Dao<HistoryText, Integer> historyTextDao = null; @@ -55,12 +63,11 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @Override public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) { try { - //TableUtils.dropTable(connectionSource, HistoryCall.class, true); - Log.i(DatabaseHelper.class.getName(), "onCreate"); + Log.d(TAG, "onCreate"); TableUtils.createTable(connectionSource, HistoryCall.class); TableUtils.createTable(connectionSource, HistoryText.class); } catch (SQLException e) { - Log.e(DatabaseHelper.class.getName(), "Can't create database", e); + Log.e(TAG, "Can't create database", e); throw new RuntimeException(e); } } @@ -71,20 +78,13 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { */ @Override public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { + Log.i(TAG, "onUpgrade " + oldVersion + " -> " + newVersion); try { - Log.i(DatabaseHelper.class.getName(), "onUpgrade " + oldVersion + " -> " + newVersion); - if (oldVersion == 4 && newVersion == 5) { - getTextHistoryDao().executeRaw("ALTER TABLE `historytext` ADD COLUMN read INTEGER;"); - } else { - //TableUtils. - TableUtils.dropTable(connectionSource, HistoryCall.class, true); - TableUtils.dropTable(connectionSource, HistoryText.class, true); - // after we drop the old databases, we create the new ones - onCreate(db, connectionSource); - } - } catch (SQLException e) { - Log.e(DatabaseHelper.class.getName(), "Can't update databases", e); - throw new RuntimeException(e); + updateDatabase(oldVersion, db); + } catch (SQLiteException exc) { + exc.printStackTrace(); + clearDatabase(db); + onCreate(db, connectionSource); } } @@ -98,6 +98,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } return historyDao; } + public Dao<HistoryText, Integer> getTextHistoryDao() throws SQLException { if (historyTextDao == null) { historyTextDao = getDao(HistoryText.class); @@ -114,4 +115,122 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { historyDao = null; historyTextDao = null; } + + /** + * Main method to update the database from an old version to the last + * + * @param fromDatabaseVersion the old version of the database + * @param db the SQLiteDatabase to work with + * @throws SQLiteException + */ + private void updateDatabase(int fromDatabaseVersion, SQLiteDatabase db) throws SQLiteException { + try { + while (fromDatabaseVersion < DATABASE_VERSION) { + switch (fromDatabaseVersion) { + case 6: + updateDatabaseFrom6(db); + break; + } + fromDatabaseVersion++; + } + Log.d(TAG, "Database has been updated to the last version."); + } catch (SQLiteException exc) { + Log.e(TAG, "Database has failed to update to the last version."); + throw exc; + } + } + + /** + * Executes the migration from the database version 6 to the next + * + * @param db the SQLiteDatabase to work with + * @throws SQLiteException + */ + private void updateDatabaseFrom6(SQLiteDatabase db) throws SQLiteException { + if (db != null && db.isOpen()) { + try { + Log.d(TAG, "Will begin migration from database version 6 to next."); + db.beginTransaction(); + //~ Create the new historyCall table and int index + db.execSQL("CREATE TABLE IF NOT EXISTS `historycall` (`accountID` VARCHAR , `callID` VARCHAR , " + + "`call_end` BIGINT , `TIMESTAMP_START` BIGINT , `contactID` BIGINT , " + + "`contactKey` VARCHAR , `direction` INTEGER , `missed` SMALLINT , " + + "`number` VARCHAR , `recordPath` VARCHAR ) ;"); + db.execSQL("CREATE INDEX IF NOT EXISTS `historycall_TIMESTAMP_START_idx` ON `historycall` " + + "( `TIMESTAMP_START` );"); + //~ Create the new historyText table and int indexes + db.execSQL("CREATE TABLE IF NOT EXISTS `historytext` (`accountID` VARCHAR , `callID` VARCHAR , " + + "`contactID` BIGINT , `contactKey` VARCHAR , `direction` INTEGER , " + + "`id` BIGINT , `message` VARCHAR , `number` VARCHAR , `read` SMALLINT , " + + "`TIMESTAMP` BIGINT , PRIMARY KEY (`id`) );"); + db.execSQL("CREATE INDEX IF NOT EXISTS `historytext_TIMESTAMP_idx` ON `historytext` ( `TIMESTAMP` );"); + db.execSQL("CREATE INDEX IF NOT EXISTS `historytext_id_idx` ON `historytext` ( `id` );"); + + Cursor hasATable = db.rawQuery("SELECT name FROM sqlite_master WHERE type=? AND name=?;", + new String[]{"table", "a"}); + if (hasATable.getCount() > 0) { + //~ Copying data from the old table "a" + db.execSQL("INSERT INTO `historycall` (TIMESTAMP_START, call_end, number, missed," + + "direction, recordPath, accountID, contactID, contactKey, callID) " + + "SELECT TIMESTAMP_START,b,c,d,e,f,g,h,i,j FROM a;"); + db.execSQL("DROP TABLE IF EXISTS a_TIMESTAMP_START_idx;"); + db.execSQL("DROP TABLE a;"); + } + hasATable.close(); + + Cursor hasETable = db.rawQuery("SELECT name FROM sqlite_master WHERE type=? AND name=?;", + new String[]{"table", "e"}); + if (hasETable.getCount() > 0) { + //~ Copying data from the old table "e" + db.execSQL("INSERT INTO historytext (id, TIMESTAMP, number, direction, accountID," + + "contactID, contactKey, callID, message, read) " + + "SELECT id,TIMESTAMP,c,d,e,f,g,h,i,j FROM e;"); + //~ Remove old tables "a" and "e" + db.execSQL("DROP TABLE IF EXISTS e_TIMESTAMP_idx;"); + db.execSQL("DROP TABLE IF EXISTS e_id_idx;"); + db.execSQL("DROP TABLE e;"); + } + hasETable.close(); + + db.setTransactionSuccessful(); + db.endTransaction(); + Log.d(TAG, "Migration from database version 6 to next, done."); + } catch (SQLiteException exception) { + Log.e(TAG, "Migration from database version 6 to next, failed."); + throw exception; + } + } + } + + /** + * Removes all the data from the database, ie all the tables. + * + * @param db the SQLiteDatabase to work with + */ + private void clearDatabase(SQLiteDatabase db) { + if (db != null && db.isOpen()) { + Log.d(TAG, "Will clear database."); + ArrayList<String> tableNames = new ArrayList<>(); + Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null); + if (c.moveToFirst()) { + while (!c.isAfterLast()) { + tableNames.add(c.getString(0)); + c.moveToNext(); + } + } + c.close(); + + try { + db.beginTransaction(); + for (String tableName : tableNames) { + db.execSQL("DROP TABLE " + tableName + ";"); + } + db.setTransactionSuccessful(); + db.endTransaction(); + Log.d(TAG, "Database is cleared"); + } catch (SQLiteException exc) { + exc.printStackTrace(); + } + } + } } diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java b/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java index f5f88f14b41c724a3faad55c2eab1b1276bc27b9..c2563fffd9e4663fcba5ec9044a0952cb151b1fd 100644 --- a/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java +++ b/ring-android/app/src/main/java/cx/ring/history/HistoryCall.java @@ -27,6 +27,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; import cx.ring.R; import cx.ring.model.SipCall; @@ -37,27 +38,40 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +@DatabaseTable(tableName = HistoryCall.TABLE_NAME) public class HistoryCall implements Parcelable { - @DatabaseField(index = true, columnName = "TIMESTAMP_START") + public static final String TABLE_NAME = "historycall"; + public static final String COLUMN_TIMESTAMP_START_NAME = "TIMESTAMP_START"; + public static final String COLUMN_TIMESTAMP_END_NAME = "call_end"; + public static final String COLUMN_NUMBER_NAME = "number"; + public static final String COLUMN_MISSED_NAME = "missed"; + public static final String COLUMN_DIRECTION_NAME = "direction"; + public static final String COLUMN_RECORD_PATH_NAME = "recordPath"; + public static final String COLUMN_ACCOUNT_ID_NAME = "accountID"; + public static final String COLUMN_CONTACT_ID_NAME = "contactID"; + public static final String COLUMN_CONTACT_KEY_NAME = "contactKey"; + public static final String COLUMN_CALL_ID_NAME = "callID"; + + @DatabaseField(index = true, columnName = COLUMN_TIMESTAMP_START_NAME) public long call_start; - @DatabaseField + @DatabaseField(columnName = COLUMN_TIMESTAMP_END_NAME) public long call_end; - @DatabaseField + @DatabaseField(columnName = COLUMN_NUMBER_NAME) public String number; - @DatabaseField + @DatabaseField(columnName = COLUMN_MISSED_NAME) boolean missed; - @DatabaseField + @DatabaseField(columnName = COLUMN_DIRECTION_NAME) int direction; - @DatabaseField + @DatabaseField(columnName = COLUMN_RECORD_PATH_NAME) String recordPath; - @DatabaseField + @DatabaseField(columnName = COLUMN_ACCOUNT_ID_NAME) String accountID; - @DatabaseField + @DatabaseField(columnName = COLUMN_CONTACT_ID_NAME) long contactID; - @DatabaseField + @DatabaseField(columnName = COLUMN_CONTACT_KEY_NAME) String contactKey; - @DatabaseField + @DatabaseField(columnName = COLUMN_CALL_ID_NAME) String callID; public String getAccountID() { diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java b/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java index fafe1dd1eb43108c2c08587cadf1198ca56a53f4..c0d6ffefb8f1f85a5f1876becc35fc44e6c5329a 100644 --- a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java +++ b/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java @@ -140,13 +140,13 @@ public class HistoryManager { public List<HistoryCall> getAll() throws SQLException { QueryBuilder<HistoryCall, Integer> qb = getHelper().getHistoryDao().queryBuilder(); - qb.orderBy("TIMESTAMP_START", true); + qb.orderBy(HistoryCall.COLUMN_TIMESTAMP_START_NAME, true); return getHelper().getHistoryDao().query(qb.prepare()); } public List<HistoryText> getAllTextMessages() throws SQLException { QueryBuilder<HistoryText, Integer> qb = getHelper().getTextHistoryDao().queryBuilder(); - qb.orderBy("TIMESTAMP", true); + qb.orderBy(HistoryText.COLUMN_TIMESTAMP_NAME, true); return getHelper().getTextHistoryDao().query(qb.prepare()); } @@ -171,7 +171,7 @@ public class HistoryManager { DeleteBuilder<HistoryText, Integer> deleteTextHistoryBuilder = getHelper() .getTextHistoryDao() .deleteBuilder(); - deleteTextHistoryBuilder.where().in("id", textMessagesIds); + deleteTextHistoryBuilder.where().in(HistoryText.COLUMN_ID_NAME, textMessagesIds); deleteTextHistoryBuilder.delete(); //~ Deleting calls ArrayList<String> callIds = new ArrayList<>(entry.getValue().getCalls().size()); @@ -181,7 +181,7 @@ public class HistoryManager { DeleteBuilder<HistoryCall, Integer> deleteCallsHistoryBuilder = getHelper() .getHistoryDao() .deleteBuilder(); - deleteCallsHistoryBuilder.where().in("callId", callIds); + deleteCallsHistoryBuilder.where().in(HistoryCall.COLUMN_CALL_ID_NAME, callIds); deleteCallsHistoryBuilder.delete(); } } catch (SQLException e) { diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryText.java b/ring-android/app/src/main/java/cx/ring/history/HistoryText.java index 71f5b2b301df9caeb94f5ca0df13058593a98c8e..1d1bbbe5ae267bd8da375d35a8b344f5c2b51222 100644 --- a/ring-android/app/src/main/java/cx/ring/history/HistoryText.java +++ b/ring-android/app/src/main/java/cx/ring/history/HistoryText.java @@ -22,33 +22,47 @@ package cx.ring.history; import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + import java.util.Date; import java.util.Random; import cx.ring.model.TextMessage; +@DatabaseTable(tableName = HistoryText.TABLE_NAME) public class HistoryText { - - @DatabaseField(index = true, columnName="id", id = true) + public static final String TABLE_NAME = "historytext"; + public static final String COLUMN_ID_NAME = "id"; + public static final String COLUMN_TIMESTAMP_NAME = "TIMESTAMP"; + public static final String COLUMN_NUMBER_NAME = "number"; + public static final String COLUMN_DIRECTION_NAME = "direction"; + public static final String COLUMN_ACCOUNT_ID_NAME = "accountID"; + public static final String COLUMN_CONTACT_ID_NAME = "contactID"; + public static final String COLUMN_CONTACT_KEY_NAME = "contactKey"; + public static final String COLUMN_CALL_ID_NAME = "callID"; + public static final String COLUMN_MESSAGE_NAME = "message"; + public static final String COLUMN_READ_NAME = "read"; + + @DatabaseField(index = true, columnName=COLUMN_ID_NAME, id = true) public long id; - @DatabaseField(index = true, columnName="TIMESTAMP") + @DatabaseField(index = true, columnName=COLUMN_TIMESTAMP_NAME) public long time; - @DatabaseField + @DatabaseField(columnName=COLUMN_NUMBER_NAME) public String number; - @DatabaseField + @DatabaseField(columnName=COLUMN_DIRECTION_NAME) public int direction; - @DatabaseField + @DatabaseField(columnName=COLUMN_ACCOUNT_ID_NAME) String accountID; - @DatabaseField + @DatabaseField(columnName=COLUMN_CONTACT_ID_NAME) long contactID; - @DatabaseField + @DatabaseField(columnName=COLUMN_CONTACT_KEY_NAME) String contactKey; - @DatabaseField + @DatabaseField(columnName=COLUMN_CALL_ID_NAME) String callID; - @DatabaseField + @DatabaseField(columnName=COLUMN_MESSAGE_NAME) String message; - @DatabaseField + @DatabaseField(columnName=COLUMN_READ_NAME) boolean read; static private final Random R = new Random();