diff --git a/app/src/main/java/com/philliphsu/clock2/Alarm.java b/app/src/main/java/com/philliphsu/clock2/Alarm.java index 2044e01..c4c430a 100644 --- a/app/src/main/java/com/philliphsu/clock2/Alarm.java +++ b/app/src/main/java/com/philliphsu/clock2/Alarm.java @@ -75,7 +75,7 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa } /** ONLY CALL THIS WHEN CREATING AN ALARM INSTANCE FROM A CURSOR */ - // TODO: To be even more safe, create a ctor that takes the two Cursors and + // TODO: To be even more safe, create a ctor that takes a Cursor and // initialize the instance here instead of in AlarmDatabaseHelper. public void setSnoozing(long snoozingUntilMillis) { this.snoozingUntilMillis = snoozingUntilMillis; diff --git a/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java b/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java index afe7b15..a1aa651 100644 --- a/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java +++ b/app/src/main/java/com/philliphsu/clock2/OnBootUpAlarmScheduler.java @@ -4,8 +4,8 @@ import android.app.IntentService; import android.content.Context; import android.content.Intent; -import com.philliphsu.clock2.model.AlarmDatabaseHelper.AlarmCursor; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmCursor; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.util.AlarmController; /** @@ -61,9 +61,9 @@ public class OnBootUpAlarmScheduler extends IntentService { if (intent != null) { AlarmController controller = new AlarmController(this, null); // IntentService works in a background thread, so this won't hold us up. - AlarmCursor cursor = DatabaseManager.getInstance(this).queryEnabledAlarms(); + AlarmCursor cursor = new AlarmsTableManager(this).queryEnabledAlarms(); while (cursor.moveToNext()) { - Alarm alarm = cursor.getAlarm(); + Alarm alarm = cursor.getItem(); if (!alarm.isEnabled()) { throw new IllegalStateException( "queryEnabledAlarms() returned alarm(s) that aren't enabled"); diff --git a/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java index a754b49..c9740be 100644 --- a/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java +++ b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java @@ -4,7 +4,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmCursor; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.util.AlarmController; import static com.philliphsu.clock2.util.Preconditions.checkNotNull; @@ -41,8 +42,8 @@ public class PendingAlarmScheduler extends BroadcastReceiver { new Thread(new Runnable() { @Override public void run() { - Alarm alarm = checkNotNull(DatabaseManager - .getInstance(context).getAlarm(id)); + AlarmCursor cursor = new AlarmsTableManager(context).queryItem(id); + Alarm alarm = checkNotNull(cursor.getItem()); if (!alarm.isEnabled()) { throw new IllegalStateException("Alarm must be enabled!"); } diff --git a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java index 99fb353..39c2a20 100644 --- a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java +++ b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java @@ -9,7 +9,8 @@ import android.content.Intent; import android.os.AsyncTask; import android.support.v4.app.NotificationCompat; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmCursor; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.util.AlarmController; import static android.app.PendingIntent.FLAG_ONE_SHOT; @@ -41,7 +42,8 @@ public class UpcomingAlarmReceiver extends BroadcastReceiver { new AsyncTask() { @Override protected Alarm doInBackground(Void... params) { - return checkNotNull(DatabaseManager.getInstance(context).getAlarm(id)); + AlarmCursor cursor = new AlarmsTableManager(context).queryItem(id); + return checkNotNull(cursor.getItem()); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java deleted file mode 100644 index 5a2f6f8..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java +++ /dev/null @@ -1,313 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.CursorWrapper; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.util.LocalBroadcastHelper; - -import static com.philliphsu.clock2.DaysOfWeek.FRIDAY; -import static com.philliphsu.clock2.DaysOfWeek.MONDAY; -import static com.philliphsu.clock2.DaysOfWeek.SATURDAY; -import static com.philliphsu.clock2.DaysOfWeek.SUNDAY; -import static com.philliphsu.clock2.DaysOfWeek.THURSDAY; -import static com.philliphsu.clock2.DaysOfWeek.TUESDAY; -import static com.philliphsu.clock2.DaysOfWeek.WEDNESDAY; - -/** - * Created by Phillip Hsu on 6/24/2016. - * - * TODO: We can generalize this class to all data models, not just Alarms. - */ -@Deprecated -public class AlarmDatabaseHelper extends SQLiteOpenHelper { - private static final String TAG = "AlarmDatabaseHelper"; - private static final String DB_NAME = "alarms.db"; - private static final int VERSION_1 = 1; - - // TODO: Consider creating an inner class that implements BaseColumns - // and defines all the columns. - // TODO: Consider defining index constants for each column, - // and then removing all cursor getColumnIndex() calls. - // TODO: Consider making these public, so callers can customize their - // WHERE queries. - private static final String TABLE_ALARMS = "alarms"; - private static final String COLUMN_ID = "_id"; - private static final String COLUMN_HOUR = "hour"; - private static final String COLUMN_MINUTES = "minutes"; - private static final String COLUMN_LABEL = "label"; - private static final String COLUMN_RINGTONE = "ringtone"; - private static final String COLUMN_VIBRATES = "vibrates"; - private static final String COLUMN_ENABLED = "enabled"; - - // TODO: Delete this column, becuase new sort order does not consider it - @Deprecated - private static final String COLUMN_RING_TIME_MILLIS = "ring_time_millis"; - - private static final String COLUMN_SNOOZING_UNTIL_MILLIS = "snoozing_until_millis"; - private static final String COLUMN_SUNDAY = "sunday"; - private static final String COLUMN_MONDAY = "monday"; - private static final String COLUMN_TUESDAY = "tuesday"; - private static final String COLUMN_WEDNESDAY = "wednesday"; - private static final String COLUMN_THURSDAY = "thursday"; - private static final String COLUMN_FRIDAY = "friday"; - private static final String COLUMN_SATURDAY = "saturday"; - private static final String COLUMN_IGNORE_UPCOMING_RING_TIME = "ignore_upcoming_ring_time"; - - // https://www.sqlite.org/lang_select.html#orderby - // Rows are first sorted based on the results of evaluating the left-most expression in the - // ORDER BY list, then ties are broken by evaluating the second left-most expression and so on. - // The order in which two rows for which all ORDER BY expressions evaluate to equal values are - // returned is undefined. Each ORDER BY expression may be optionally followed by one of the keywords - // ASC (smaller values are returned first) or DESC (larger values are returned first). If neither - // ASC or DESC are specified, rows are sorted in ascending (smaller values first) order by default. - - // First sort by ring time in ascending order (smaller values first), - // then break ties by sorting by id in ascending order. - @Deprecated - private static final String SORT_ORDER = - COLUMN_RING_TIME_MILLIS + " ASC, " + COLUMN_ID + " ASC"; - - private static final String NEW_SORT_ORDER = COLUMN_HOUR + " ASC, " - + COLUMN_MINUTES + " ASC, " - // TOneverDO: Sort COLUMN_ENABLED or else alarms could be reordered - // if you toggle them on/off, which looks confusing. - // TODO: Figure out how to get the order to be: - // No recurring days -> - // Recurring earlier in user's weekday order -> - // Recurring everyday - // As written now, this is incorrect! For one, it assumes - // the standard week order (starting on Sunday). - // DESC gives us (Sunday -> Saturday -> No recurring days), - // ASC gives us the reverse (No recurring days -> Saturday -> Sunday). - // TODO: If assuming standard week order, try ASC for all days but - // write COLUMN_SATURDAY first, then COLUMN_FRIDAY, ... , COLUMN_SUNDAY. - // Check if that gives us (No recurring days -> Sunday -> Saturday). -// + COLUMN_SUNDAY + " DESC, " -// + COLUMN_MONDAY + " DESC, " -// + COLUMN_TUESDAY + " DESC, " -// + COLUMN_WEDNESDAY + " DESC, " -// + COLUMN_THURSDAY + " DESC, " -// + COLUMN_FRIDAY + " DESC, " -// + COLUMN_SATURDAY + " DESC, " - // All else equal, newer alarms first - + COLUMN_ID + " DESC"; // TODO: If duplicate alarm times disallowed, delete this - - private final Context mAppContext; - - public AlarmDatabaseHelper(Context context) { - super(context.getApplicationContext(), DB_NAME, null, VERSION_1); - mAppContext = context.getApplicationContext(); - // TODO: Here is where you could compute the sort expression - // for the recurring days order, based on the user's defined - // weekday order. For example, if we read the first day of - // the week as Sunday, then we build a String called RECURRENCE_ORDER: - // RECURRENCE_ORDER = - // COLUMN_SATURDAY + " ASC, " - // + ... - // + COLUMN_SUNDAY + " ASC"; - // Note how the weekday order is reversed when - // we refer to the columns. We should also include - // ordering by id as the last piece of this string: - // + COLUMN_ID + " DESC"; - // and remove that piece from the NEW_SORT_ORDER - // constant. This is so we can later concatenate - // NEW_SORT_ORDER and RECURRENCE_ORDER but maintain - // the original order of the sort expressions. - // We should also rename that constant - // to BASE_SORT_ORDER. Last, in the query() methods, - // we can pass in - // BASE_SORT_ORDER + RECURRENCE_ORDER - // to its orderBy parameter. - } - - @Override - public void onCreate(SQLiteDatabase db) { - // https://www.sqlite.org/datatype3.html - // INTEGER data type is stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude - // of the value. As soon as INTEGER values are read off of disk and into memory for processing, - // they are converted to the most general datatype (8-byte signed integer). - // 8 byte == 64 bits so this means they are read as longs...? - db.execSQL("CREATE TABLE " + TABLE_ALARMS + " (" - + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + COLUMN_HOUR + " INTEGER NOT NULL, " - + COLUMN_MINUTES + " INTEGER NOT NULL, " - + COLUMN_LABEL + " TEXT, " - + COLUMN_RINGTONE + " TEXT NOT NULL, " - + COLUMN_VIBRATES + " INTEGER NOT NULL, " - + COLUMN_ENABLED + " INTEGER NOT NULL, " - + COLUMN_RING_TIME_MILLIS + " INTEGER NOT NULL, " - + COLUMN_SNOOZING_UNTIL_MILLIS + " INTEGER, " - + COLUMN_SUNDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_MONDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_TUESDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_WEDNESDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_THURSDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_FRIDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_SATURDAY + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_IGNORE_UPCOMING_RING_TIME + " INTEGER NOT NULL);"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // I don't think we need to drop current tables unless you make structural changes - // to the schema in the new version. - } - - public long insertAlarm(Alarm alarm) { - long id = getWritableDatabase().insert(TABLE_ALARMS, - null, toContentValues(alarm)); - alarm.setId(id); - notifyContentChanged(); - return id; - } - - /** - * @deprecated Use {@link #updateAlarm(long, Alarm)} instead - */ - @Deprecated - public int updateAlarm(Alarm oldAlarm, Alarm newAlarm) { - newAlarm.setId(oldAlarm.id()); - SQLiteDatabase db = getWritableDatabase(); - int rowsUpdated = db.update(TABLE_ALARMS, - toContentValues(newAlarm), - COLUMN_ID + " = " + newAlarm.id(), - null); - notifyContentChanged(); - return rowsUpdated; - } - - public int updateAlarm(long id, Alarm newAlarm) { - newAlarm.setId(id); - SQLiteDatabase db = getWritableDatabase(); - int rowsUpdated = db.update(TABLE_ALARMS, - toContentValues(newAlarm), - COLUMN_ID + " = " + id, - null); - notifyContentChanged(); - return rowsUpdated; - } - - public int deleteAlarm(Alarm alarm) { - SQLiteDatabase db = getWritableDatabase(); - int rowsDeleted = db.delete(TABLE_ALARMS, - COLUMN_ID + " = " + alarm.id(), - null); - notifyContentChanged(); - return rowsDeleted; - } - - public AlarmCursor queryAlarm(long id) { - Cursor c = getReadableDatabase().query(TABLE_ALARMS, - null, // All columns - COLUMN_ID + " = " + id, // Selection for this alarm id - null, // selection args, none b/c id already specified in selection - null, // group by - null, // order/sort by - null, // having - "1"); // limit 1 row - return new AlarmCursor(c); - } - - public AlarmCursor queryAlarms() { - // Select all rows and columns - return queryAlarms(null); - } - - public AlarmCursor queryEnabledAlarms() { - return queryAlarms(COLUMN_ENABLED + " = 1"); - } - - private AlarmCursor queryAlarms(String where) { - Cursor c = getReadableDatabase().query(TABLE_ALARMS, - null, where, null, null, null, NEW_SORT_ORDER); - return new AlarmCursor(c); - } - - private ContentValues toContentValues(Alarm alarm) { - ContentValues values = new ContentValues(); - values.put(COLUMN_HOUR, alarm.hour()); - values.put(COLUMN_MINUTES, alarm.minutes()); - values.put(COLUMN_LABEL, alarm.label()); - values.put(COLUMN_RINGTONE, alarm.ringtone()); - values.put(COLUMN_VIBRATES, alarm.vibrates()); - values.put(COLUMN_ENABLED, alarm.isEnabled()); - values.put(COLUMN_RING_TIME_MILLIS, alarm.ringsAt()); - values.put(COLUMN_SNOOZING_UNTIL_MILLIS, alarm.snoozingUntil()); - values.put(COLUMN_SUNDAY, alarm.isRecurring(SUNDAY)); - values.put(COLUMN_MONDAY, alarm.isRecurring(MONDAY)); - values.put(COLUMN_TUESDAY, alarm.isRecurring(TUESDAY)); - values.put(COLUMN_WEDNESDAY, alarm.isRecurring(WEDNESDAY)); - values.put(COLUMN_THURSDAY, alarm.isRecurring(THURSDAY)); - values.put(COLUMN_FRIDAY, alarm.isRecurring(FRIDAY)); - values.put(COLUMN_SATURDAY, alarm.isRecurring(SATURDAY)); - values.put(COLUMN_IGNORE_UPCOMING_RING_TIME, alarm.isIgnoringUpcomingRingTime()); - return values; - } - - private void notifyContentChanged() { - Log.d(TAG, "notifyContentChanged()"); - LocalBroadcastHelper.sendBroadcast(mAppContext, - SQLiteCursorLoader.ACTION_CHANGE_CONTENT); - } - - // An alternative method to creating an Alarm from a cursor is to - // make an Alarm constructor that takes an Cursor param. However, - // this method has the advantage of keeping all the constants - // contained within this file. Another advantage is the contents of - // the Alarm class remain as pure Java, which can facilitate unit testing - // because it has no dependence on Cursor, which is part of the Android - // SDK. - public static class AlarmCursor extends CursorWrapper { - - public AlarmCursor(Cursor c) { - super(c); - } - - /** - * @return an Alarm instance configured for the current row, - * or null if the current row is invalid - */ - public Alarm getAlarm() { - if (isBeforeFirst() || isAfterLast()) - return null; - // TODO: Use getColumnIndexOrThrow() - Alarm alarm = Alarm.builder() - .hour(getInt(getColumnIndex(COLUMN_HOUR))) - .minutes(getInt(getColumnIndex(COLUMN_MINUTES))) - .vibrates(isTrue(COLUMN_VIBRATES)) - .ringtone(getString(getColumnIndex(COLUMN_RINGTONE))) - .label(getString(getColumnIndex(COLUMN_LABEL))) - .build(); - alarm.setId(getLong(getColumnIndex(COLUMN_ID))); - alarm.setEnabled(isTrue(COLUMN_ENABLED)); - alarm.setSnoozing(getLong(getColumnIndex(COLUMN_SNOOZING_UNTIL_MILLIS))); - alarm.setRecurring(SUNDAY, isTrue(COLUMN_SUNDAY)); - alarm.setRecurring(MONDAY, isTrue(COLUMN_MONDAY)); - alarm.setRecurring(TUESDAY, isTrue(COLUMN_TUESDAY)); - alarm.setRecurring(WEDNESDAY, isTrue(COLUMN_WEDNESDAY)); - alarm.setRecurring(THURSDAY, isTrue(COLUMN_THURSDAY)); - alarm.setRecurring(FRIDAY, isTrue(COLUMN_FRIDAY)); - alarm.setRecurring(SATURDAY, isTrue(COLUMN_SATURDAY)); - alarm.ignoreUpcomingRingTime(isTrue(COLUMN_IGNORE_UPCOMING_RING_TIME)); - return alarm; - } - - public long getId() { - if (isBeforeFirst() || isAfterLast()) { - Log.e(TAG, "Failed to retrieve id, cursor out of range"); - return -1; - } - return getLong(getColumnIndexOrThrow(COLUMN_ID)); - } - - private boolean isTrue(String columnName) { - return getInt(getColumnIndex(columnName)) == 1; - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmListLoader.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmListLoader.java deleted file mode 100644 index 7ed0b51..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmListLoader.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.model.AlarmDatabaseHelper.AlarmCursor; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Phillip Hsu on 7/2/2016. - */ -@Deprecated -public class AlarmListLoader extends DataListLoader { - - public AlarmListLoader(Context context) { - super(context); - } - - // Why not just have one method where we just call DatabaseManager.getAlarms()? - // I.e. why not load the cursor and extract the Alarms from it all in one? - // I figure if the loader is interrupted in the middle of loading, the underlying - // cursor won't be closed... - - // TODO: If we end up doing it this way, then delete the redundant methods in DatabaseManager. - - @Override - protected AlarmCursor loadCursor() { - return DatabaseManager.getInstance(getContext()).queryAlarms(); - } - - @Override - protected List loadItems(AlarmCursor cursor) { - ArrayList alarms = new ArrayList<>(); - if (cursor != null) { - while (cursor.moveToNext()) { - alarms.add(cursor.getAlarm()); - } - cursor.close(); - } - return alarms; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java index f321337..0066ae8 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java +++ b/app/src/main/java/com/philliphsu/clock2/model/AlarmLoader.java @@ -18,6 +18,6 @@ public class AlarmLoader extends DataLoader { @Override public Alarm loadInBackground() { - return DatabaseManager.getInstance(getContext()).getAlarm(mAlarmId); + return new AlarmsTableManager(getContext()).queryItem(mAlarmId).getItem(); } } diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmsListCursorLoader.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmsListCursorLoader.java index 460367a..f3b005f 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmsListCursorLoader.java +++ b/app/src/main/java/com/philliphsu/clock2/model/AlarmsListCursorLoader.java @@ -7,7 +7,7 @@ import com.philliphsu.clock2.Alarm; /** * Created by Phillip Hsu on 6/28/2016. */ -public class AlarmsListCursorLoader extends NewSQLiteCursorLoader { +public class AlarmsListCursorLoader extends SQLiteCursorLoader { public static final String ACTION_CHANGE_CONTENT = "com.philliphsu.clock2.model.AlarmsListCursorLoader.action.CHANGE_CONTENT"; diff --git a/app/src/main/java/com/philliphsu/clock2/model/BaseDatabaseHelper.java b/app/src/main/java/com/philliphsu/clock2/model/BaseDatabaseHelper.java deleted file mode 100644 index d420a8e..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/BaseDatabaseHelper.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -import com.philliphsu.clock2.util.LocalBroadcastHelper; - -/** - * Created by Phillip Hsu on 7/29/2016. - */ -@Deprecated -public abstract class BaseDatabaseHelper extends SQLiteOpenHelper { - - public static final String COLUMN_ID = "_id"; - - private final Context mAppContext; - - /** - * @param context the Context with which the application context will be retrieved - * @param name the name of the database file. Because this is required by the SQLiteOpenHelper - * constructor, we can't, for instance, have an abstract getDatabaseFileName() that - * subclasses implement and the base class can call on their behalf. - * @param version the version - */ - public BaseDatabaseHelper(Context context, String name, - /*SQLiteDatabase.CursorFactory factory,*/ - int version) { - super(context.getApplicationContext(), name, null, version); - mAppContext = context.getApplicationContext(); - } - - /** - * @return the table managed by this helper - */ - protected abstract String getTableName(); - - /** - * @return the ContentValues representing the item's properties. - * You do not need to put a mapping for {@link #COLUMN_ID}, since - * the database manages ids for us (if you created your table - * with {@link #COLUMN_ID} as an {@code INTEGER PRIMARY KEY}). - */ - protected abstract ContentValues toContentValues(T item); - - /** - * @return optional String specifying the sort order - * to use when querying the database. The default - * implementation returns null, which may return - * queries unordered. - */ - protected String getQuerySortOrder() { - return null; - } - - public long insertItem(T item) { - long id = getWritableDatabase().insert( - getTableName(), null, toContentValues(item)); - item.setId(id); - notifyContentChanged(); - return id; - } - - public int updateItem(long id, T newItem) { - newItem.setId(id); - SQLiteDatabase db = getWritableDatabase(); - int rowsUpdated = db.update(getTableName(), - toContentValues(newItem), - COLUMN_ID + " = " + id, - null); - notifyContentChanged(); - return rowsUpdated; - } - - public int deleteItem(T item) { - SQLiteDatabase db = getWritableDatabase(); - int rowsDeleted = db.delete(getTableName(), - COLUMN_ID + " = " + item.getId(), - null); - notifyContentChanged(); - return rowsDeleted; - } - - public Cursor queryItem(long id) { - return queryItems(COLUMN_ID + " = " + id, "1"); - } - - public Cursor queryItems() { - // Select all rows and columns - return queryItems(null, null); - } - - protected Cursor queryItems(String where, String limit) { - return getReadableDatabase().query(getTableName(), - null, // All columns - where, // Selection, i.e. where COLUMN_* = [value we're looking for] - null, // selection args, none b/c id already specified in selection - null, // group by - null, // having - getQuerySortOrder(), // order/sort by - limit); // limit - } - - /** - * Broadcasts to any registered receivers that the data backed - * by this helper has changed, and so they should requery and - * update themselves as necessary. - */ - private void notifyContentChanged() { - LocalBroadcastHelper.sendBroadcast(mAppContext, - SQLiteCursorLoader.ACTION_CHANGE_CONTENT); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/ClockAppDatabaseHelper.java b/app/src/main/java/com/philliphsu/clock2/model/ClockAppDatabaseHelper.java index 6f5b1a0..74d409a 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/ClockAppDatabaseHelper.java +++ b/app/src/main/java/com/philliphsu/clock2/model/ClockAppDatabaseHelper.java @@ -12,10 +12,18 @@ public class ClockAppDatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME = "clock_app.db"; private static final int VERSION_1 = 1; + private static ClockAppDatabaseHelper sDatabaseHelper; + + public static ClockAppDatabaseHelper getInstance(Context context) { + if (sDatabaseHelper == null) + sDatabaseHelper = new ClockAppDatabaseHelper(context); + return sDatabaseHelper; + } + /** * @param context the Context with which the application context will be retrieved */ - public ClockAppDatabaseHelper(Context context) { + private ClockAppDatabaseHelper(Context context) { super(context.getApplicationContext(), DB_NAME, null, VERSION_1); } diff --git a/app/src/main/java/com/philliphsu/clock2/model/DataListLoader.java b/app/src/main/java/com/philliphsu/clock2/model/DataListLoader.java deleted file mode 100644 index c7a71f9..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/DataListLoader.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; -import android.database.CursorWrapper; -import android.support.v4.content.AsyncTaskLoader; - -import java.util.List; - -/** - * Created by Phillip Hsu on 7/2/2016. - */ -@Deprecated -// TODO: Consider C extends MyTypeBoundedCursorWrapper -public abstract class DataListLoader extends AsyncTaskLoader> { - - private C mCursor; - private List mItems; - - public DataListLoader(Context context) { - super(context); - } - - protected abstract C loadCursor(); - protected abstract List loadItems(C cursor); - - @Override - public List loadInBackground() { - mCursor = loadCursor(); - if (mCursor != null) { - // Ensure that the content window is filled - // Ensure that the data is available in memory once it is - // passed to the main thread - mCursor.getCount(); - } - return loadItems(mCursor); - } - - @Override - public void deliverResult(List items) { - if (isReset()) { - // An async query came in while the loader is stopped - if (mCursor != null) { - mCursor.close(); - } - return; - } - - mItems = items; - if (isStarted()) { - super.deliverResult(items); - } - - // TODO: might not be necessary. The analogue of this was - // to close the *old* cursor after assigning the new cursor. - // This is closing the current cursor? But then again, we don't - // care about the cursor after we've extracted the items from it.. - // Close the cursor because it is no longer needed. - if (mCursor != null && !mCursor.isClosed()) { - mCursor.close(); - } - } - - @Override - protected void onStartLoading() { - if (mCursor != null && mItems != null) { - // Deliver any previously loaded data immediately. - deliverResult(mItems); - } - if (takeContentChanged() || mCursor == null || mItems == null) { - forceLoad(); - } - } - - @Override - protected void onStopLoading() { - // Attempt to cancel the current load task if possible. - cancelLoad(); - } - - @Override - public void onCanceled(List data) { - if (mCursor != null && !mCursor.isClosed()) { - mCursor.close(); - } - } - - @Override - protected void onReset() { - super.onReset(); - // Ensure the loader is stopped - onStopLoading(); - if (mCursor != null && !mCursor.isClosed()) { - mCursor.close(); - } - mCursor = null; - mItems = null; - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java b/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java deleted file mode 100644 index 4c6f7ab..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.Context; - -import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.model.AlarmDatabaseHelper.AlarmCursor; - -import java.util.ArrayList; - -/** - * Created by Phillip Hsu on 6/25/2016. - */ -@Deprecated -public class DatabaseManager { - - private static DatabaseManager sDatabaseManager; - private final Context mContext; - private final AlarmDatabaseHelper mHelper; // TODO: Call close() when *the app* is exiting. - - private DatabaseManager(Context context) { - // TODO: Do we need to hold onto this? - mContext = context.getApplicationContext(); - // This will internally get the application context - mHelper = new AlarmDatabaseHelper(context); - } - - public static DatabaseManager getInstance(Context context) { - if (sDatabaseManager == null) { - sDatabaseManager = new DatabaseManager(context); - } - return sDatabaseManager; - } - - public long insertAlarm(Alarm alarm) { - return mHelper.insertAlarm(alarm); - } - - /** - * @deprecated Use {@link #updateAlarm(long, Alarm)} instead, because all - * that is needed from the oldAlarm is its id. - */ - @Deprecated - public int updateAlarm(Alarm oldAlarm, Alarm newAlarm) { - return mHelper.updateAlarm(oldAlarm, newAlarm); - } - - public int updateAlarm(long id, Alarm newAlarm) { - return mHelper.updateAlarm(id, newAlarm); - } - - public int deleteAlarm(Alarm alarm) { - return mHelper.deleteAlarm(alarm); - } - - // Since the query returns at most one row, just return the Alarm the row represents. - public Alarm getAlarm(long id) { - Alarm alarm = null; - AlarmCursor cursor = mHelper.queryAlarm(id); - if (cursor != null && cursor.moveToFirst()) { - alarm = cursor.getAlarm(); - cursor.close(); - } - return alarm; - } - - /** @deprecated Use {@link #queryAlarms()} */ - // TODO: Possible redundant. See AlarmListLoader. - @Deprecated - public ArrayList getAlarms() { - return getAlarms(mHelper.queryAlarms()); - } - - // TODO: Possible redundant. See AlarmListLoader. - public ArrayList getEnabledAlarms() { - return getAlarms(mHelper.queryEnabledAlarms()); - } - - // TODO: Possible redundant. See AlarmListLoader. - private ArrayList getAlarms(AlarmCursor cursor) { - ArrayList alarms = new ArrayList<>(); - if (cursor != null) { - while (cursor.moveToNext()) { - alarms.add(cursor.getAlarm()); - } - cursor.close(); - } - return alarms; - } - - public AlarmCursor queryAlarms() { - return mHelper.queryAlarms(); - } - - public AlarmCursor queryEnabledAlarms() { - return mHelper.queryEnabledAlarms(); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java b/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java index c5dfd87..bcb49bc 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java +++ b/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java @@ -23,7 +23,7 @@ public abstract class DatabaseTableManager { public DatabaseTableManager(Context context) { // Internally uses the app context - mDbHelper = new ClockAppDatabaseHelper(context); + mDbHelper = ClockAppDatabaseHelper.getInstance(context); mAppContext = context.getApplicationContext(); } @@ -42,7 +42,7 @@ public abstract class DatabaseTableManager { /** * @return the Intent action that will be used to send broadcasts - * to our designated {@link NewSQLiteCursorLoader} whenever an + * to our designated {@link SQLiteCursorLoader} whenever an * underlying change to our data is detected. The Loader should * receive the broadcast and reload its data. */ @@ -87,7 +87,15 @@ public abstract class DatabaseTableManager { } public Cursor queryItem(long id) { - return queryItems(COLUMN_ID + " = " + id, "1"); + Cursor c = queryItems(COLUMN_ID + " = " + id, "1"); + // Since the query returns at most one row, move the cursor to that row. + // Most callers of this method will not know they have to move the cursor. + // How come we don't need to do this for queries that can potentially return + // multiple rows? Because those returned cursors will almost always be + // displayed by a BaseCursorAdapter, which moves cursors to the appropriate + // positions as it binds VHs. + c.moveToFirst(); + return c; } public Cursor queryItems() { diff --git a/app/src/main/java/com/philliphsu/clock2/model/NewSQLiteCursorLoader.java b/app/src/main/java/com/philliphsu/clock2/model/NewSQLiteCursorLoader.java deleted file mode 100644 index 6c5048e..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/NewSQLiteCursorLoader.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.support.v4.content.AsyncTaskLoader; -import android.util.Log; - -import com.philliphsu.clock2.util.LocalBroadcastHelper; - -/** - * Created by Phillip Hsu on 6/28/2016. - * - * Efficiently loads and holds a Cursor. - */ -public abstract class NewSQLiteCursorLoader< - T extends ObjectWithId, - C extends BaseItemCursor> - extends AsyncTaskLoader { - - private static final String TAG = "SQLiteCursorLoader"; - - private C mCursor; - private OnContentChangeReceiver mOnContentChangeReceiver; - - public NewSQLiteCursorLoader(Context context) { - super(context); - } - - protected abstract C loadCursor(); - - /** - * @return the Intent action that will be registered to this Loader - * for receiving broadcasts about underlying data changes to our - * designated database table - */ - protected abstract String getOnContentChangeAction(); - - /* Runs on a worker thread */ - @Override - public C loadInBackground() { - C cursor = loadCursor(); - if (cursor != null) { - // Ensure that the content window is filled - // Ensure that the data is available in memory once it is - // passed to the main thread - cursor.getCount(); - } - return cursor; - } - - /* Runs on the UI thread */ - @Override - public void deliverResult(C cursor) { - if (isReset()) { - // An async query came in while the loader is stopped - if (cursor != null) { - cursor.close(); - } - return; - } - Cursor oldCursor = mCursor; - mCursor = cursor; - - if (isStarted()) { - super.deliverResult(cursor); - } - - // Close the old cursor because it is no longer needed. - // Because an existing cursor may be cached and redelivered, it is important - // to make sure that the old cursor and the new cursor are not the - // same before the old cursor is closed. - if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { - oldCursor.close(); - } - } - - // Refer to the docs if you wish to understand the rest of the API as used below. - - @Override - protected void onStartLoading() { - if (mCursor != null) { - deliverResult(mCursor); - } - - if (mOnContentChangeReceiver == null) { - mOnContentChangeReceiver = new OnContentChangeReceiver(); - LocalBroadcastHelper.registerReceiver(getContext(), - mOnContentChangeReceiver, getOnContentChangeAction()); - } - - if (takeContentChanged() || mCursor == null) { - forceLoad(); - } - } - - @Override - protected void onStopLoading() { - // Attempt to cancel the current load task if possible. - cancelLoad(); - } - - @Override - public void onCanceled(C cursor) { - if (cursor != null && !cursor.isClosed()) { - cursor.close(); - } - } - - @Override - protected void onReset() { - super.onReset(); - // Ensure the loader is stopped - onStopLoading(); - - if (mCursor != null && !mCursor.isClosed()) { - mCursor.close(); - } - mCursor = null; - - if (mOnContentChangeReceiver != null) { - LocalBroadcastHelper.unregisterReceiver(getContext(), - mOnContentChangeReceiver); - mOnContentChangeReceiver = null; - } - } - - private final class OnContentChangeReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "Received content change event"); - onContentChanged(); - } - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/SQLiteCursorLoader.java b/app/src/main/java/com/philliphsu/clock2/model/SQLiteCursorLoader.java index 63f53df..725ab87 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/SQLiteCursorLoader.java +++ b/app/src/main/java/com/philliphsu/clock2/model/SQLiteCursorLoader.java @@ -14,24 +14,33 @@ import com.philliphsu.clock2.util.LocalBroadcastHelper; * * Efficiently loads and holds a Cursor. */ -public abstract class SQLiteCursorLoader extends AsyncTaskLoader { +public abstract class SQLiteCursorLoader< + T extends ObjectWithId, + C extends BaseItemCursor> + extends AsyncTaskLoader { + private static final String TAG = "SQLiteCursorLoader"; - public static final String ACTION_CHANGE_CONTENT = "com.philliphsu.clock2.model.action.CHANGE_CONTENT"; - - private Cursor mCursor; + private C mCursor; private OnContentChangeReceiver mOnContentChangeReceiver; public SQLiteCursorLoader(Context context) { super(context); } - protected abstract Cursor loadCursor(); + protected abstract C loadCursor(); + + /** + * @return the Intent action that will be registered to this Loader + * for receiving broadcasts about underlying data changes to our + * designated database table + */ + protected abstract String getOnContentChangeAction(); /* Runs on a worker thread */ @Override - public Cursor loadInBackground() { - Cursor cursor = loadCursor(); + public C loadInBackground() { + C cursor = loadCursor(); if (cursor != null) { // Ensure that the content window is filled // Ensure that the data is available in memory once it is @@ -43,7 +52,7 @@ public abstract class SQLiteCursorLoader extends AsyncTaskLoader { /* Runs on the UI thread */ @Override - public void deliverResult(Cursor cursor) { + public void deliverResult(C cursor) { if (isReset()) { // An async query came in while the loader is stopped if (cursor != null) { @@ -78,7 +87,7 @@ public abstract class SQLiteCursorLoader extends AsyncTaskLoader { if (mOnContentChangeReceiver == null) { mOnContentChangeReceiver = new OnContentChangeReceiver(); LocalBroadcastHelper.registerReceiver(getContext(), - mOnContentChangeReceiver, ACTION_CHANGE_CONTENT); + mOnContentChangeReceiver, getOnContentChangeAction()); } if (takeContentChanged() || mCursor == null) { @@ -93,7 +102,7 @@ public abstract class SQLiteCursorLoader extends AsyncTaskLoader { } @Override - public void onCanceled(Cursor cursor) { + public void onCanceled(C cursor) { if (cursor != null && !cursor.isClosed()) { cursor.close(); } diff --git a/app/src/main/java/com/philliphsu/clock2/model/TimerDatabaseHelper.java b/app/src/main/java/com/philliphsu/clock2/model/TimerDatabaseHelper.java deleted file mode 100644 index c137565..0000000 --- a/app/src/main/java/com/philliphsu/clock2/model/TimerDatabaseHelper.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.philliphsu.clock2.model; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; - -import com.philliphsu.clock2.Timer; - -/** - * Created by Phillip Hsu on 7/29/2016. - */ -@Deprecated -public class TimerDatabaseHelper extends BaseDatabaseHelper { - private static final String TAG = "TimerDatabaseHelper"; - private static final String DB_NAME = "timers.db"; - private static final int VERSION_1 = 1; - - private static final String TABLE_TIMERS = "timers"; - // TODO: Consider making these public, so we can move TimerCursor to its own top-level class. - private static final String COLUMN_HOUR = "hour"; - private static final String COLUMN_MINUTE = "minute"; - private static final String COLUMN_SECOND = "second"; - private static final String COLUMN_LABEL = "label"; - - // http://stackoverflow.com/q/24183958/5055032 - // https://www.sqlite.org/lang_keywords.html - // GROUP is a reserved keyword, so your CREATE TABLE statement - // will not compile if you include this! -// private static final String COLUMN_GROUP = "group"; - - private static final String COLUMN_END_TIME = "end_time"; - private static final String COLUMN_PAUSE_TIME = "pause_time"; - - private static final String SORT_ORDER = - COLUMN_HOUR + " ASC, " - + COLUMN_MINUTE + " ASC, " - + COLUMN_SECOND + " ASC, " - // All else equal, newer timers first - + COLUMN_ID + " DESC"; - - public TimerDatabaseHelper(Context context) { - super(context, DB_NAME, VERSION_1); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_TIMERS + " (" - + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + COLUMN_HOUR + " INTEGER NOT NULL, " - + COLUMN_MINUTE + " INTEGER NOT NULL, " - + COLUMN_SECOND + " INTEGER NOT NULL, " - + COLUMN_LABEL + " TEXT NOT NULL, " -// + COLUMN_GROUP + " TEXT NOT NULL, " - + COLUMN_END_TIME + " INTEGER NOT NULL, " - + COLUMN_PAUSE_TIME + " INTEGER NOT NULL);"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // I don't think we need to drop current tables unless you make structural changes - // to the schema in the new version. - } - - // ============================================================================================= - // Overridden methods can have a more specific return type, as long as that type - // is a subtype of the original return type. - - @Override - public TimerCursor queryItem(long id) { - return wrapInTimerCursor(queryItem(id)); - } - - @Override - public TimerCursor queryItems() { - return wrapInTimerCursor(super.queryItems()); - } - - @Override - protected TimerCursor queryItems(String where, String limit) { - return wrapInTimerCursor(super.queryItems(where, limit)); - } - // ============================================================================================= - - @Override - protected String getTableName() { - return TABLE_TIMERS; - } - - @Override - protected ContentValues toContentValues(Timer timer) { - ContentValues cv = new ContentValues(); - cv.put(COLUMN_HOUR, timer.hour()); - cv.put(COLUMN_MINUTE, timer.minute()); - cv.put(COLUMN_SECOND, timer.second()); - cv.put(COLUMN_LABEL, timer.label()); -// cv.put(COLUMN_GROUP, timer.group()); - cv.put(COLUMN_END_TIME, timer.endTime()); - cv.put(COLUMN_PAUSE_TIME, timer.pauseTime()); - return cv; - } - - @Override - protected String getQuerySortOrder() { - return SORT_ORDER; - } - - private TimerCursor wrapInTimerCursor(Cursor c) { - return new TimerCursor(c); - } - -} diff --git a/app/src/main/java/com/philliphsu/clock2/model/TimersListCursorLoader.java b/app/src/main/java/com/philliphsu/clock2/model/TimersListCursorLoader.java index 22d0755..bf6c02a 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/TimersListCursorLoader.java +++ b/app/src/main/java/com/philliphsu/clock2/model/TimersListCursorLoader.java @@ -7,7 +7,7 @@ import com.philliphsu.clock2.Timer; /** * Created by Phillip Hsu on 7/29/2016. */ -public class TimersListCursorLoader extends NewSQLiteCursorLoader { +public class TimersListCursorLoader extends SQLiteCursorLoader { public static final String ACTION_CHANGE_CONTENT = "com.philliphsu.clock2.model.TimersListCursorLoader.action.CHANGE_CONTENT"; diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java index 9030004..a236628 100644 --- a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java +++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java @@ -21,7 +21,8 @@ import android.util.Log; import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.R; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmCursor; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.AlarmUtils; import com.philliphsu.clock2.util.LocalBroadcastHelper; @@ -96,8 +97,8 @@ public class RingtoneService extends Service { // TODO: abstract this, make subc new Thread(new Runnable() { @Override public void run() { - mAlarm = checkNotNull(DatabaseManager - .getInstance(RingtoneService.this).getAlarm(id)); + AlarmCursor cursor = new AlarmsTableManager(RingtoneService.this).queryItem(id); + mAlarm = checkNotNull(cursor.getItem()); playRingtone(); } }).start(); diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java index e465a5f..468ec1c 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmController.java @@ -12,7 +12,7 @@ import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.PendingAlarmScheduler; import com.philliphsu.clock2.R; import com.philliphsu.clock2.UpcomingAlarmReceiver; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.ringtone.RingtoneActivity; import com.philliphsu.clock2.ringtone.RingtoneService; @@ -34,6 +34,7 @@ public final class AlarmController { private final Context mAppContext; private final View mSnackbarAnchor; + private final AlarmsTableManager mTableManager; /** * @@ -43,6 +44,7 @@ public final class AlarmController { public AlarmController(Context context, View snackbarAnchor) { mAppContext = context.getApplicationContext(); mSnackbarAnchor = snackbarAnchor; + mTableManager = new AlarmsTableManager(context); } /** @@ -174,7 +176,7 @@ public final class AlarmController { new Thread(new Runnable() { @Override public void run() { - DatabaseManager.getInstance(mAppContext).updateAlarm(alarm.id(), alarm); + mTableManager.updateItem(alarm.id(), alarm); } }).start(); } diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java index 32da1fb..3a51b3c 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java @@ -13,7 +13,7 @@ import com.philliphsu.clock2.Alarm; import com.philliphsu.clock2.PendingAlarmScheduler; import com.philliphsu.clock2.R; import com.philliphsu.clock2.UpcomingAlarmReceiver; -import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.model.AlarmsTableManager; import com.philliphsu.clock2.ringtone.RingtoneActivity; import com.philliphsu.clock2.ringtone.RingtoneService; @@ -30,6 +30,7 @@ import static java.util.concurrent.TimeUnit.HOURS; * managing the upcoming alarm notification. * * TODO: Adapt this to Timers too... + * TODO: Keep only utility methods. Not the scheduling and cancelling methods. */ public final class AlarmUtils { private static final String TAG = "AlarmUtils"; @@ -219,7 +220,7 @@ public final class AlarmUtils { new Thread(new Runnable() { @Override public void run() { - DatabaseManager.getInstance(c).updateAlarm(alarm.id(), alarm); + new AlarmsTableManager(c).updateItem(alarm.id(), alarm); } }).start(); }