Replaced all usages of DatabaseManager with AlarmsTableManager
This commit is contained in:
parent
5ac5b34640
commit
992e091db7
@ -75,7 +75,7 @@ public abstract class Alarm extends ObjectWithId implements JsonSerializable, Pa
|
||||
}
|
||||
|
||||
/** <b>ONLY CALL THIS WHEN CREATING AN ALARM INSTANCE FROM A CURSOR</b> */
|
||||
// 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;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
|
||||
@ -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<Void, Void, Alarm>() {
|
||||
@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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Alarm, AlarmCursor> {
|
||||
|
||||
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<Alarm> loadItems(AlarmCursor cursor) {
|
||||
ArrayList<Alarm> alarms = new ArrayList<>();
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
alarms.add(cursor.getAlarm());
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return alarms;
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,6 @@ public class AlarmLoader extends DataLoader<Alarm> {
|
||||
|
||||
@Override
|
||||
public Alarm loadInBackground() {
|
||||
return DatabaseManager.getInstance(getContext()).getAlarm(mAlarmId);
|
||||
return new AlarmsTableManager(getContext()).queryItem(mAlarmId).getItem();
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import com.philliphsu.clock2.Alarm;
|
||||
/**
|
||||
* Created by Phillip Hsu on 6/28/2016.
|
||||
*/
|
||||
public class AlarmsListCursorLoader extends NewSQLiteCursorLoader<Alarm, AlarmCursor> {
|
||||
public class AlarmsListCursorLoader extends SQLiteCursorLoader<Alarm, AlarmCursor> {
|
||||
public static final String ACTION_CHANGE_CONTENT
|
||||
= "com.philliphsu.clock2.model.AlarmsListCursorLoader.action.CHANGE_CONTENT";
|
||||
|
||||
|
||||
@ -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<T extends ObjectWithId> 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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<D>
|
||||
public abstract class DataListLoader<D, C extends CursorWrapper> extends AsyncTaskLoader<List<D>> {
|
||||
|
||||
private C mCursor;
|
||||
private List<D> mItems;
|
||||
|
||||
public DataListLoader(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected abstract C loadCursor();
|
||||
protected abstract List<D> loadItems(C cursor);
|
||||
|
||||
@Override
|
||||
public List<D> 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<D> 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<D> 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;
|
||||
}
|
||||
}
|
||||
@ -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<Alarm> getAlarms() {
|
||||
return getAlarms(mHelper.queryAlarms());
|
||||
}
|
||||
|
||||
// TODO: Possible redundant. See AlarmListLoader.
|
||||
public ArrayList<Alarm> getEnabledAlarms() {
|
||||
return getAlarms(mHelper.queryEnabledAlarms());
|
||||
}
|
||||
|
||||
// TODO: Possible redundant. See AlarmListLoader.
|
||||
private ArrayList<Alarm> getAlarms(AlarmCursor cursor) {
|
||||
ArrayList<Alarm> 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();
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ public abstract class DatabaseTableManager<T extends ObjectWithId> {
|
||||
|
||||
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<T extends ObjectWithId> {
|
||||
|
||||
/**
|
||||
* @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<T extends ObjectWithId> {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@ -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<T>>
|
||||
extends AsyncTaskLoader<C> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,24 +14,33 @@ import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||
*
|
||||
* Efficiently loads and holds a Cursor.
|
||||
*/
|
||||
public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
|
||||
public abstract class SQLiteCursorLoader<
|
||||
T extends ObjectWithId,
|
||||
C extends BaseItemCursor<T>>
|
||||
extends AsyncTaskLoader<C> {
|
||||
|
||||
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<Cursor> {
|
||||
|
||||
/* 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<Cursor> {
|
||||
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<Cursor> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(Cursor cursor) {
|
||||
public void onCanceled(C cursor) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@ -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<Timer> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@ import com.philliphsu.clock2.Timer;
|
||||
/**
|
||||
* Created by Phillip Hsu on 7/29/2016.
|
||||
*/
|
||||
public class TimersListCursorLoader extends NewSQLiteCursorLoader<Timer, TimerCursor> {
|
||||
public class TimersListCursorLoader extends SQLiteCursorLoader<Timer, TimerCursor> {
|
||||
public static final String ACTION_CHANGE_CONTENT
|
||||
= "com.philliphsu.clock2.model.TimersListCursorLoader.action.CHANGE_CONTENT";
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user