Begin conversion to Room

pull/618/head
Alex Baker 7 years ago
parent 6a440cd91e
commit c2255296d7

@ -1,12 +1,12 @@
{ {
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 39,
"identityHash": "eab7679fcfaa5fd45ac7da7a4b205348", "identityHash": "7e082aef9f43061a29426ac5c9489db0",
"entities": [ "entities": [
{ {
"tableName": "notification", "tableName": "notification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)",
"fields": [ "fields": [
{ {
"fieldPath": "uid", "fieldPath": "uid",
@ -18,7 +18,7 @@
"fieldPath": "taskId", "fieldPath": "taskId",
"columnName": "task", "columnName": "task",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": false "notNull": true
}, },
{ {
"fieldPath": "timestamp", "fieldPath": "timestamp",
@ -54,7 +54,7 @@
], ],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"eab7679fcfaa5fd45ac7da7a4b205348\")" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"7e082aef9f43061a29426ac5c9489db0\")"
] ]
} }
} }

@ -6,7 +6,6 @@ import android.content.Context;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
import org.tasks.db.AppDatabase;
import org.tasks.notifications.NotificationDao; import org.tasks.notifications.NotificationDao;
import org.tasks.preferences.PermissionChecker; import org.tasks.preferences.PermissionChecker;
import org.tasks.preferences.PermissivePermissionChecker; import org.tasks.preferences.PermissivePermissionChecker;

@ -173,8 +173,7 @@ public class DatabaseDao<TYPE extends AbstractModel> {
DatabaseChangeOp insert = new DatabaseChangeOp() { DatabaseChangeOp insert = new DatabaseChangeOp() {
@Override @Override
public boolean makeChange() { public boolean makeChange() {
long newRow = database.insert(table.name, long newRow = database.insert(table.name, item.getMergedValues());
AbstractModel.ID_PROPERTY.name, item.getMergedValues());
boolean result = newRow >= 0; boolean result = newRow >= 0;
if (result) { if (result) {
item.setId(newRow); item.setId(newRow);

@ -5,18 +5,14 @@
*/ */
package com.todoroo.astrid.dao; package com.todoroo.astrid.dao;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.RoomDatabase;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.SqlConstructorVisitor;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
@ -26,13 +22,11 @@ import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskListMetadata; import com.todoroo.astrid.data.TaskListMetadata;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.provider.Astrid2TaskProvider;
import org.tasks.analytics.Tracker; import org.tasks.notifications.Notification;
import org.tasks.injection.ApplicationScope; import org.tasks.notifications.NotificationDao;
import org.tasks.injection.ForApplication;
import javax.inject.Inject; import java.io.IOException;
import timber.log.Timber; import timber.log.Timber;
@ -42,12 +36,14 @@ import timber.log.Timber;
* @author Tim Su <tim@todoroo.com> * @author Tim Su <tim@todoroo.com>
* *
*/ */
@ApplicationScope @android.arch.persistence.room.Database(entities = {Notification.class}, version = 39)
public class Database { public abstract class Database extends RoomDatabase {
private static final int VERSION = 38; public abstract NotificationDao notificationDao();
private static final String NAME = "database";
private static final Table[] TABLES = new Table[] { public static final String NAME = "database";
public static final Table[] TABLES = new Table[] {
Task.TABLE, Task.TABLE,
Metadata.TABLE, Metadata.TABLE,
StoreObject.TABLE, StoreObject.TABLE,
@ -57,19 +53,8 @@ public class Database {
TaskListMetadata.TABLE, TaskListMetadata.TABLE,
}; };
private final SQLiteOpenHelper helper; private SupportSQLiteDatabase database;
private final Context context; private Runnable onDatabaseUpdated;
private final Tracker tracker;
private SQLiteDatabase database;
// --- listeners
@Inject
public Database(@ForApplication Context context, Tracker tracker) {
this.context = context;
this.tracker = tracker;
helper = new DatabaseHelper(context, getName(), VERSION);
}
// --- implementation // --- implementation
@ -77,109 +62,17 @@ public class Database {
return NAME; return NAME;
} }
/**
* Create indices
*/
private void onCreateTables() {
StringBuilder sql = new StringBuilder();
sql.append("CREATE INDEX IF NOT EXISTS md_tid ON ").
append(Metadata.TABLE).append('(').
append(Metadata.TASK.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS md_tkid ON ").
append(Metadata.TABLE).append('(').
append(Metadata.TASK.name).append(',').
append(Metadata.KEY.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS so_id ON ").
append(StoreObject.TABLE).append('(').
append(StoreObject.TYPE.name).append(',').
append(StoreObject.ITEM.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS t_rid ON ").
append(Task.TABLE).append('(').
append(Task.UUID.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
}
private boolean onUpgrade(int oldVersion, int newVersion) {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
switch(oldVersion) {
case 35:
tryExecSQL(addColumnSql(TagData.TABLE, TagData.COLOR, visitor, "-1"));
case 36:
tryExecSQL(addColumnSql(StoreObject.TABLE, StoreObject.DELETION_DATE, visitor, "0"));
case 37:
tryExecSQL(addColumnSql(StoreObject.TABLE, StoreObject.VALUE4, visitor, "-1"));
return true;
}
return false;
}
private void tryExecSQL(String sql) {
try {
database.execSQL(sql);
} catch (SQLiteException e) {
tracker.reportException(e);
}
}
private static String addColumnSql(Table table, Property<?> property, SqlConstructorVisitor visitor, String defaultValue) {
StringBuilder builder = new StringBuilder();
builder.append("ALTER TABLE ")
.append(table.name)
.append(" ADD ")
.append(property.accept(visitor, null));
if (!TextUtils.isEmpty(defaultValue)) {
builder.append(" DEFAULT ").append(defaultValue);
}
return builder.toString();
}
private void tryAddColumn(Table table, Property<?> column, String defaultValue) {
try {
SqlConstructorVisitor visitor = new SqlConstructorVisitor();
String sql = "ALTER TABLE " + table.name + " ADD " + //$NON-NLS-1$//$NON-NLS-2$
column.accept(visitor, null);
if (!TextUtils.isEmpty(defaultValue)) {
sql += " DEFAULT " + defaultValue;
}
database.execSQL(sql);
} catch (SQLiteException e) {
// ignored, column already exists
Timber.e(e, e.getMessage());
}
}
private String createTableSql(SqlConstructorVisitor visitor, public Database setOnDatabaseUpdated(Runnable onDatabaseUpdated) {
String tableName, Property<?>[] properties) { this.onDatabaseUpdated = onDatabaseUpdated;
StringBuilder sql = new StringBuilder(); return this;
sql.append("CREATE TABLE IF NOT EXISTS ").append(tableName).append('(').
append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
for(Property<?> property : properties) {
if(AbstractModel.ID_PROPERTY.name.equals(property.name)) {
continue;
}
sql.append(',').append(property.accept(visitor, null));
}
sql.append(')');
return sql.toString();
} }
private void onDatabaseUpdated() { private void onDatabaseUpdated() {
Astrid2TaskProvider.notifyDatabaseModification(context); if (onDatabaseUpdated != null) {
onDatabaseUpdated.run();
}
} }
/** /**
@ -204,7 +97,7 @@ public class Database {
} }
try { try {
database = helper.getWritableDatabase(); database = getOpenHelper().getWritableDatabase();
} catch (NullPointerException e) { } catch (NullPointerException e) {
Timber.e(e, e.getMessage()); Timber.e(e, e.getMessage());
throw new IllegalStateException(e); throw new IllegalStateException(e);
@ -228,7 +121,7 @@ public class Database {
if(database != null && database.isOpen()) { if(database != null && database.isOpen()) {
return; return;
} }
database = helper.getReadableDatabase(); database = getOpenHelper().getReadableDatabase();
} }
/** /**
@ -236,7 +129,11 @@ public class Database {
*/ */
public synchronized final void close() { public synchronized final void close() {
if(database != null) { if(database != null) {
database.close(); try {
database.close();
} catch (IOException e) {
Timber.e(e, e.getMessage());
}
} }
database = null; database = null;
} }
@ -244,7 +141,7 @@ public class Database {
/** /**
* @return sql database. opens database if not yet open * @return sql database. opens database if not yet open
*/ */
public synchronized final SQLiteDatabase getDatabase() { public synchronized final SupportSQLiteDatabase getDatabase() {
if(database == null) { if(database == null) {
AndroidUtilities.sleepDeep(300L); AndroidUtilities.sleepDeep(300L);
openForWriting(); openForWriting();
@ -263,13 +160,13 @@ public class Database {
// --- database wrapper // --- database wrapper
public Cursor rawQuery(String sql) { public Cursor rawQuery(String sql) {
return getDatabase().rawQuery(sql, null); return getDatabase().query(sql, null);
} }
public long insert(String table, String nullColumnHack, ContentValues values) { public long insert(String table, ContentValues values) {
long result; long result;
try { try {
result = getDatabase().insertOrThrow(table, nullColumnHack, values); result = getDatabase().insert(table, SQLiteDatabase.CONFLICT_REPLACE, values);
} catch (SQLiteConstraintException e) { // Throw these exceptions } catch (SQLiteConstraintException e) { // Throw these exceptions
throw e; throw e;
} catch (Exception e) { // Suppress others } catch (Exception e) { // Suppress others
@ -287,71 +184,9 @@ public class Database {
} }
public int update(String table, ContentValues values, String whereClause) { public int update(String table, ContentValues values, String whereClause) {
int result = getDatabase().update(table, values, whereClause, null); int result = getDatabase().update(table, SQLiteDatabase.CONFLICT_REPLACE, values, whereClause, null);
onDatabaseUpdated(); onDatabaseUpdated();
return result; return result;
} }
// --- helper classes
/**
* Default implementation of Astrid database helper
*/
private class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String name, int version) {
super(context, name, null, version);
}
/**
* Called to create the database tables
*/
@Override
public void onCreate(SQLiteDatabase db) {
StringBuilder sql = new StringBuilder();
SqlConstructorVisitor sqlVisitor = new SqlConstructorVisitor();
// create tables
for(Table table : TABLES) {
sql.append("CREATE TABLE IF NOT EXISTS ").append(table.name).append('(').
append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
for(Property<?> property : table.getProperties()) {
if(AbstractModel.ID_PROPERTY.name.equals(property.name)) {
continue;
}
sql.append(',').append(property.accept(sqlVisitor, null));
}
sql.append(')');
db.execSQL(sql.toString());
sql.setLength(0);
}
// post-table-creation
database = db;
onCreateTables();
}
/**
* Called to upgrade the database to a new version
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Timber.i("Upgrading database from version %s to %s", oldVersion, newVersion);
database = db;
try {
if(!Database.this.onUpgrade(oldVersion, newVersion)) {
// We don't know how to handle this case because someone forgot to
// implement the upgrade. We can't drop tables, we can only
// throw a nasty exception at this time
throw new IllegalStateException("Missing database migration " +
"from " + oldVersion + " to " + newVersion);
}
} catch (Exception e) {
Timber.e(e, "database-upgrade-%s-%s-%s", getName(), oldVersion, newVersion);
}
}
}
} }

@ -1,12 +0,0 @@
package org.tasks.db;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;
import org.tasks.notifications.Notification;
import org.tasks.notifications.NotificationDao;
@Database(entities = {Notification.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract NotificationDao notificationDao();
}

@ -0,0 +1,107 @@
package org.tasks.db;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.RoomDatabase;
import android.arch.persistence.room.migration.Migration;
import android.support.annotation.NonNull;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.SqlConstructorVisitor;
import com.todoroo.andlib.data.Table;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.StoreObject;
import com.todoroo.astrid.data.Task;
public class Migrations {
private static final Migration MIGRATION_35_36 = new Migration(35, 36) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `tagdata` ADD COLUMN `color` INTEGER DEFAULT -1");
}
};
private static final Migration MIGRATION_36_37 = new Migration(36, 37) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `store` ADD COLUMN `deleted` INTEGER DEFAULT 0");
}
};
private static final Migration MIGRATION_37_38 = new Migration(37, 38) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `store` ADD COLUMN `value4` TEXT DEFAULT -1");
}
};
private static final Migration MIGRATION_38_39 = new Migration(38, 39) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS `notification` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)");
database.execSQL("CREATE UNIQUE INDEX `index_notification_task` ON `notification` (`task`)");
}
};
public static Migration[] MIGRATIONS = new Migration[] {
MIGRATION_35_36,
MIGRATION_36_37,
MIGRATION_37_38,
MIGRATION_38_39
};
public static RoomDatabase.Callback ON_CREATE = new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
StringBuilder sql = new StringBuilder();
SqlConstructorVisitor sqlVisitor = new SqlConstructorVisitor();
// create tables
for(Table table : Database.TABLES) {
sql.append("CREATE TABLE IF NOT EXISTS ").append(table.name).append('(').
append(AbstractModel.ID_PROPERTY).append(" INTEGER PRIMARY KEY AUTOINCREMENT");
for(Property<?> property : table.getProperties()) {
if(AbstractModel.ID_PROPERTY.name.equals(property.name)) {
continue;
}
sql.append(',').append(property.accept(sqlVisitor, null));
}
sql.append(')');
db.execSQL(sql.toString());
sql.setLength(0);
}
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS md_tid ON ").
append(Metadata.TABLE).append('(').
append(Metadata.TASK.name).
append(')');
db.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS md_tkid ON ").
append(Metadata.TABLE).append('(').
append(Metadata.TASK.name).append(',').
append(Metadata.KEY.name).
append(')');
db.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS so_id ON ").
append(StoreObject.TABLE).append('(').
append(StoreObject.TYPE.name).append(',').
append(StoreObject.ITEM.name).
append(')');
db.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS t_rid ON ").
append(Task.TABLE).append('(').
append(Task.UUID.name).
append(')');
db.execSQL(sql.toString());
sql.setLength(0);
}
};
}

@ -3,9 +3,12 @@ package org.tasks.injection;
import android.arch.persistence.room.Room; import android.arch.persistence.room.Room;
import android.content.Context; import android.content.Context;
import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.provider.Astrid2TaskProvider;
import org.tasks.ErrorReportingSingleThreadExecutor; import org.tasks.ErrorReportingSingleThreadExecutor;
import org.tasks.analytics.Tracker; import org.tasks.analytics.Tracker;
import org.tasks.db.AppDatabase; import org.tasks.db.Migrations;
import org.tasks.locale.Locale; import org.tasks.locale.Locale;
import org.tasks.notifications.NotificationDao; import org.tasks.notifications.NotificationDao;
@ -44,12 +47,17 @@ public class ApplicationModule {
@Provides @Provides
@ApplicationScope @ApplicationScope
public AppDatabase getAppDatabase() { public Database getAppDatabase() {
return Room.databaseBuilder(context, AppDatabase.class, "app-database").build(); return Room
.databaseBuilder(context, Database.class, Database.NAME)
.addMigrations(Migrations.MIGRATIONS)
.addCallback(Migrations.ON_CREATE)
.build()
.setOnDatabaseUpdated(() -> Astrid2TaskProvider.notifyDatabaseModification(context));
} }
@Provides @Provides
public NotificationDao getNotificationDao(AppDatabase appDatabase) { public NotificationDao getNotificationDao(Database database) {
return appDatabase.notificationDao(); return database.notificationDao();
} }
} }

@ -14,7 +14,7 @@ public class Notification {
public int uid; public int uid;
@ColumnInfo(name = "task") @ColumnInfo(name = "task")
public Long taskId; public long taskId;
@ColumnInfo(name = "timestamp") @ColumnInfo(name = "timestamp")
public long timestamp; public long timestamp;

@ -3,7 +3,6 @@ package org.tasks.notifications;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.tasks.db.AppDatabase;
import org.tasks.injection.BroadcastComponent; import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver; import org.tasks.injection.InjectingBroadcastReceiver;
@ -14,7 +13,6 @@ import timber.log.Timber;
public class NotificationClearedReceiver extends InjectingBroadcastReceiver { public class NotificationClearedReceiver extends InjectingBroadcastReceiver {
@Inject NotificationManager notificationManager; @Inject NotificationManager notificationManager;
@Inject AppDatabase appDatabase;
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {

@ -139,7 +139,7 @@ public class NotificationManager {
List<org.tasks.notifications.Notification> notifications = notificationDao.getAllOrdered(); List<org.tasks.notifications.Notification> notifications = notificationDao.getAllOrdered();
if (cancelExisting) { if (cancelExisting) {
for (org.tasks.notifications.Notification notification : notifications) { for (org.tasks.notifications.Notification notification : notifications) {
notificationManagerCompat.cancel(notification.taskId.intValue()); notificationManagerCompat.cancel((int) notification.taskId);
} }
} }
@ -185,10 +185,10 @@ public class NotificationManager {
for (org.tasks.notifications.Notification notification : notifications) { for (org.tasks.notifications.Notification notification : notifications) {
NotificationCompat.Builder builder = getTaskNotification(notification); NotificationCompat.Builder builder = getTaskNotification(notification);
if (builder == null) { if (builder == null) {
notificationManagerCompat.cancel(notification.taskId.intValue()); notificationManagerCompat.cancel((int) notification.taskId);
notificationDao.delete(notification.taskId); notificationDao.delete(notification.taskId);
} else { } else {
builder.setGroup(useGroupKey ? GROUP_KEY : (atLeastNougat() ? notification.taskId.toString() : null)) builder.setGroup(useGroupKey ? GROUP_KEY : (atLeastNougat() ? Long.toString(notification.taskId) : null))
.setGroupAlertBehavior(alert ? NotificationCompat.GROUP_ALERT_CHILDREN : NotificationCompat.GROUP_ALERT_SUMMARY); .setGroupAlertBehavior(alert ? NotificationCompat.GROUP_ALERT_CHILDREN : NotificationCompat.GROUP_ALERT_SUMMARY);
notify(notification.taskId, builder, alert, nonstop, fiveTimes); notify(notification.taskId, builder, alert, nonstop, fiveTimes);
alert = false; alert = false;

Loading…
Cancel
Save