RecyclerViewFragment and other abstract classes in use for alarms and timers
This commit is contained in:
parent
b43ff03662
commit
066ac67325
@ -6,6 +6,7 @@ import android.support.annotation.NonNull;
|
|||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import com.philliphsu.clock2.model.JsonSerializable;
|
import com.philliphsu.clock2.model.JsonSerializable;
|
||||||
|
import com.philliphsu.clock2.model.ObjectWithId;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@ -21,11 +22,10 @@ import static com.philliphsu.clock2.DaysOfWeek.SUNDAY;
|
|||||||
* Created by Phillip Hsu on 5/26/2016.
|
* Created by Phillip Hsu on 5/26/2016.
|
||||||
*/
|
*/
|
||||||
@AutoValue
|
@AutoValue
|
||||||
public abstract class Alarm implements JsonSerializable, Parcelable {
|
public abstract class Alarm extends ObjectWithId implements JsonSerializable, Parcelable {
|
||||||
private static final int MAX_MINUTES_CAN_SNOOZE = 30;
|
private static final int MAX_MINUTES_CAN_SNOOZE = 30;
|
||||||
|
|
||||||
// =================== MUTABLE =======================
|
// =================== MUTABLE =======================
|
||||||
private long id;
|
|
||||||
private long snoozingUntilMillis;
|
private long snoozingUntilMillis;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private final boolean[] recurringDays = new boolean[NUM_DAYS];
|
private final boolean[] recurringDays = new boolean[NUM_DAYS];
|
||||||
@ -204,18 +204,15 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int intId() {
|
public int intId() {
|
||||||
return (int) id;
|
return (int) getId();
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove method signature from JsonSerializable interface.
|
// TODO: Remove method signature from JsonSerializable interface.
|
||||||
// TODO: Remove final modifier.
|
// TODO: Remove final modifier.
|
||||||
|
// TODO: Rename to getId() so usages refer to ObjectWithId#getId(), then delete this method.
|
||||||
@Override
|
@Override
|
||||||
public final long id() {
|
public final long id() {
|
||||||
return id;
|
return getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -246,7 +243,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
// because when we recreate the object, we can't initialize
|
// because when we recreate the object, we can't initialize
|
||||||
// those mutable fields until after we call build(). Values
|
// those mutable fields until after we call build(). Values
|
||||||
// in the parcel are read in the order they were written.
|
// in the parcel are read in the order they were written.
|
||||||
dest.writeLong(id);
|
dest.writeLong(getId());
|
||||||
dest.writeLong(snoozingUntilMillis);
|
dest.writeLong(snoozingUntilMillis);
|
||||||
dest.writeInt(enabled ? 1 : 0);
|
dest.writeInt(enabled ? 1 : 0);
|
||||||
dest.writeBooleanArray(recurringDays);
|
dest.writeBooleanArray(recurringDays);
|
||||||
@ -261,7 +258,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
.ringtone(in.readString())
|
.ringtone(in.readString())
|
||||||
.vibrates(in.readInt() != 0)
|
.vibrates(in.readInt() != 0)
|
||||||
.build();
|
.build();
|
||||||
alarm.id = in.readLong();
|
alarm.setId(in.readLong());
|
||||||
alarm.snoozingUntilMillis = in.readLong();
|
alarm.snoozingUntilMillis = in.readLong();
|
||||||
alarm.enabled = in.readInt() != 0;
|
alarm.enabled = in.readInt() != 0;
|
||||||
in.readBooleanArray(alarm.recurringDays);
|
in.readBooleanArray(alarm.recurringDays);
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
package com.philliphsu.clock2.alarms;
|
package com.philliphsu.clock2.alarms;
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.philliphsu.clock2.Alarm;
|
import com.philliphsu.clock2.Alarm;
|
||||||
|
import com.philliphsu.clock2.BaseCursorAdapter;
|
||||||
import com.philliphsu.clock2.OnListItemInteractionListener;
|
import com.philliphsu.clock2.OnListItemInteractionListener;
|
||||||
import com.philliphsu.clock2.model.AlarmDatabaseHelper.AlarmCursor;
|
|
||||||
import com.philliphsu.clock2.util.AlarmController;
|
import com.philliphsu.clock2.util.AlarmController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,63 +12,19 @@ import com.philliphsu.clock2.util.AlarmController;
|
|||||||
*
|
*
|
||||||
* TODO: Extend from BaseCursorAdapter
|
* TODO: Extend from BaseCursorAdapter
|
||||||
*/
|
*/
|
||||||
public class AlarmsCursorAdapter extends RecyclerView.Adapter<AlarmViewHolder> {
|
public class AlarmsCursorAdapter extends BaseCursorAdapter<Alarm, AlarmViewHolder, com.philliphsu.clock2.model.AlarmCursor> {
|
||||||
private static final String TAG = "AlarmsCursorAdapter";
|
private static final String TAG = "AlarmsCursorAdapter";
|
||||||
|
|
||||||
private final OnListItemInteractionListener<Alarm> mListener;
|
|
||||||
private final AlarmController mAlarmController;
|
private final AlarmController mAlarmController;
|
||||||
private AlarmCursor mCursor;
|
|
||||||
|
|
||||||
public AlarmsCursorAdapter(OnListItemInteractionListener<Alarm> listener,
|
public AlarmsCursorAdapter(OnListItemInteractionListener<Alarm> listener,
|
||||||
AlarmController alarmController) {
|
AlarmController alarmController) {
|
||||||
mListener = listener;
|
super(listener);
|
||||||
mAlarmController = alarmController;
|
mAlarmController = alarmController;
|
||||||
// Excerpt from docs of notifyDataSetChanged():
|
|
||||||
// "RecyclerView will attempt to synthesize [artificially create?]
|
|
||||||
// visible structural change events [when items are inserted, removed or
|
|
||||||
// moved] for adapters that report that they have stable IDs when
|
|
||||||
// [notifyDataSetChanged()] is used. This can help for the purposes of
|
|
||||||
// animation and visual object persistence [?] but individual item views
|
|
||||||
// will still need to be rebound and relaid out."
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlarmViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
protected AlarmViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener<Alarm> listener) {
|
||||||
return new AlarmViewHolder(parent, mListener, mAlarmController);
|
return new AlarmViewHolder(parent, listener, mAlarmController);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(AlarmViewHolder holder, int position) {
|
|
||||||
if (!mCursor.moveToPosition(position)) {
|
|
||||||
Log.e(TAG, "Failed to bind alarm " + position);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
holder.onBind(mCursor.getAlarm());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mCursor == null ? 0 : mCursor.getCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
if (mCursor == null || !mCursor.moveToPosition(position)) {
|
|
||||||
return super.getItemId(position); // -1
|
|
||||||
}
|
|
||||||
return mCursor.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Cursor param should be the appropriate subclass?
|
|
||||||
public void swapCursor(Cursor cursor) {
|
|
||||||
if (mCursor == cursor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mCursor != null) {
|
|
||||||
mCursor.close();
|
|
||||||
}
|
|
||||||
mCursor = (AlarmCursor) cursor;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,20 @@
|
|||||||
package com.philliphsu.clock2.alarms;
|
package com.philliphsu.clock2.alarms;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import com.philliphsu.clock2.Alarm;
|
import com.philliphsu.clock2.Alarm;
|
||||||
import com.philliphsu.clock2.AsyncItemChangeHandler;
|
import com.philliphsu.clock2.AsyncItemChangeHandler;
|
||||||
import com.philliphsu.clock2.OnListItemInteractionListener;
|
|
||||||
import com.philliphsu.clock2.R;
|
import com.philliphsu.clock2.R;
|
||||||
|
import com.philliphsu.clock2.RecyclerViewFragment;
|
||||||
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
||||||
|
import com.philliphsu.clock2.model.AlarmCursor;
|
||||||
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
|
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
|
||||||
import com.philliphsu.clock2.util.AlarmController;
|
import com.philliphsu.clock2.util.AlarmController;
|
||||||
import com.philliphsu.clock2.util.DelayedSnackbarHandler;
|
import com.philliphsu.clock2.util.DelayedSnackbarHandler;
|
||||||
@ -28,10 +22,11 @@ import com.philliphsu.clock2.util.DelayedSnackbarHandler;
|
|||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
// TODO: Use native fragments since we're targeting API >=19?
|
public class AlarmsFragment extends RecyclerViewFragment<
|
||||||
// TODO: Use native LoaderCallbacks.
|
Alarm,
|
||||||
public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
AlarmViewHolder,
|
||||||
OnListItemInteractionListener<Alarm>, ScrollHandler {
|
AlarmCursor,
|
||||||
|
AlarmsCursorAdapter> implements ScrollHandler {
|
||||||
private static final String TAG = "AlarmsFragment";
|
private static final String TAG = "AlarmsFragment";
|
||||||
private static final int REQUEST_EDIT_ALARM = 0;
|
private static final int REQUEST_EDIT_ALARM = 0;
|
||||||
// Public because MainActivity needs to use it.
|
// Public because MainActivity needs to use it.
|
||||||
@ -76,23 +71,6 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
|||||||
mAlarmController = new AlarmController(getActivity(), mSnackbarAnchor);
|
mAlarmController = new AlarmController(getActivity(), mSnackbarAnchor);
|
||||||
mAsyncItemChangeHandler = new AsyncItemChangeHandler(getActivity(),
|
mAsyncItemChangeHandler = new AsyncItemChangeHandler(getActivity(),
|
||||||
mSnackbarAnchor, this, mAlarmController);
|
mSnackbarAnchor, this, mAlarmController);
|
||||||
|
|
||||||
getLoaderManager().initLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
||||||
Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.fragment_alarms, container, false);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
|
|
||||||
// Set the adapter
|
|
||||||
Context context = view.getContext();
|
|
||||||
mList.setLayoutManager(new LinearLayoutManager(context));
|
|
||||||
mAdapter = new AlarmsCursorAdapter(this, mAlarmController);
|
|
||||||
mList.setAdapter(mAdapter);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -110,20 +88,29 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<AlarmCursor> onCreateLoader(int id, Bundle args) {
|
||||||
return new AlarmsListCursorLoader(getActivity());
|
return new AlarmsListCursorLoader(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
public void onLoadFinished(Loader<AlarmCursor> loader, AlarmCursor data) {
|
||||||
mAdapter.swapCursor(data);
|
super.onLoadFinished(loader, data);
|
||||||
// Scroll to the last modified alarm
|
// Scroll to the last modified alarm
|
||||||
performScrollToStableId();
|
performScrollToStableId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
public void onFabClick() {
|
||||||
mAdapter.swapCursor(null);
|
Intent intent = new Intent(getActivity(), EditAlarmActivity.class);
|
||||||
|
startActivityForResult(intent, REQUEST_CREATE_ALARM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AlarmsCursorAdapter getAdapter() {
|
||||||
|
if (mAdapter == null) {
|
||||||
|
mAdapter = new AlarmsCursorAdapter(this, mAlarmController);
|
||||||
|
}
|
||||||
|
return mAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.Alarm;
|
||||||
|
|
||||||
|
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 7/30/2016.
|
||||||
|
*/
|
||||||
|
// 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 the contents of
|
||||||
|
// the Alarm class as pure Java, which can facilitate unit testing
|
||||||
|
// because it has no dependence on Cursor, which is part of the Android SDK.
|
||||||
|
public class AlarmCursor extends BaseItemCursor<Alarm> {
|
||||||
|
private static final String TAG = "AlarmCursor";
|
||||||
|
|
||||||
|
public AlarmCursor(Cursor c) {
|
||||||
|
super(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an Alarm instance configured for the current row,
|
||||||
|
* or null if the current row is invalid
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Alarm getItem() {
|
||||||
|
if (isBeforeFirst() || isAfterLast())
|
||||||
|
return null;
|
||||||
|
Alarm alarm = Alarm.builder()
|
||||||
|
.hour(getInt(getColumnIndexOrThrow(AlarmsTable.COLUMN_HOUR)))
|
||||||
|
.minutes(getInt(getColumnIndexOrThrow(AlarmsTable.COLUMN_MINUTES)))
|
||||||
|
.vibrates(isTrue(AlarmsTable.COLUMN_VIBRATES))
|
||||||
|
.ringtone(getString(getColumnIndexOrThrow(AlarmsTable.COLUMN_RINGTONE)))
|
||||||
|
.label(getString(getColumnIndexOrThrow(AlarmsTable.COLUMN_LABEL)))
|
||||||
|
.build();
|
||||||
|
alarm.setId(getLong(getColumnIndexOrThrow(AlarmsTable.COLUMN_ID)));
|
||||||
|
alarm.setEnabled(isTrue(AlarmsTable.COLUMN_ENABLED));
|
||||||
|
alarm.setSnoozing(getLong(getColumnIndexOrThrow(AlarmsTable.COLUMN_SNOOZING_UNTIL_MILLIS)));
|
||||||
|
alarm.setRecurring(SUNDAY, isTrue(AlarmsTable.COLUMN_SUNDAY));
|
||||||
|
alarm.setRecurring(MONDAY, isTrue(AlarmsTable.COLUMN_MONDAY));
|
||||||
|
alarm.setRecurring(TUESDAY, isTrue(AlarmsTable.COLUMN_TUESDAY));
|
||||||
|
alarm.setRecurring(WEDNESDAY, isTrue(AlarmsTable.COLUMN_WEDNESDAY));
|
||||||
|
alarm.setRecurring(THURSDAY, isTrue(AlarmsTable.COLUMN_THURSDAY));
|
||||||
|
alarm.setRecurring(FRIDAY, isTrue(AlarmsTable.COLUMN_FRIDAY));
|
||||||
|
alarm.setRecurring(SATURDAY, isTrue(AlarmsTable.COLUMN_SATURDAY));
|
||||||
|
alarm.ignoreUpcomingRingTime(isTrue(AlarmsTable.COLUMN_IGNORE_UPCOMING_RING_TIME));
|
||||||
|
return alarm;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,6 +24,7 @@ import static com.philliphsu.clock2.DaysOfWeek.WEDNESDAY;
|
|||||||
*
|
*
|
||||||
* TODO: We can generalize this class to all data models, not just Alarms.
|
* TODO: We can generalize this class to all data models, not just Alarms.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||||
private static final String TAG = "AlarmDatabaseHelper";
|
private static final String TAG = "AlarmDatabaseHelper";
|
||||||
private static final String DB_NAME = "alarms.db";
|
private static final String DB_NAME = "alarms.db";
|
||||||
|
|||||||
@ -1,19 +1,27 @@
|
|||||||
package com.philliphsu.clock2.model;
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
|
||||||
|
import com.philliphsu.clock2.Alarm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 6/28/2016.
|
* Created by Phillip Hsu on 6/28/2016.
|
||||||
*/
|
*/
|
||||||
public class AlarmsListCursorLoader extends SQLiteCursorLoader {
|
public class AlarmsListCursorLoader extends NewSQLiteCursorLoader<Alarm, AlarmCursor> {
|
||||||
|
public static final String ACTION_CHANGE_CONTENT
|
||||||
|
= "com.philliphsu.clock2.model.AlarmsListCursorLoader.action.CHANGE_CONTENT";
|
||||||
|
|
||||||
public AlarmsListCursorLoader(Context context) {
|
public AlarmsListCursorLoader(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Cursor loadCursor() {
|
protected AlarmCursor loadCursor() {
|
||||||
return DatabaseManager.getInstance(getContext()).queryAlarms();
|
return new AlarmsTableManager(getContext()).queryItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOnContentChangeAction() {
|
||||||
|
return ACTION_CHANGE_CONTENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/30/2016.
|
||||||
|
*/
|
||||||
|
public final class AlarmsTable {
|
||||||
|
private AlarmsTable() {}
|
||||||
|
|
||||||
|
// TODO: Consider defining index constants for each column,
|
||||||
|
// and then removing all cursor getColumnIndex() calls.
|
||||||
|
public static final String TABLE_ALARMS = "alarms";
|
||||||
|
|
||||||
|
// TODO: Consider implementing BaseColumns instead to get _id column.
|
||||||
|
public static final String COLUMN_ID = "_id";
|
||||||
|
public static final String COLUMN_HOUR = "hour";
|
||||||
|
public static final String COLUMN_MINUTES = "minutes";
|
||||||
|
public static final String COLUMN_LABEL = "label";
|
||||||
|
public static final String COLUMN_RINGTONE = "ringtone";
|
||||||
|
public static final String COLUMN_VIBRATES = "vibrates";
|
||||||
|
public static final String COLUMN_ENABLED = "enabled";
|
||||||
|
|
||||||
|
// TODO: Delete this column, becuase new sort order does not consider it
|
||||||
|
@Deprecated
|
||||||
|
public static final String COLUMN_RING_TIME_MILLIS = "ring_time_millis";
|
||||||
|
|
||||||
|
public static final String COLUMN_SNOOZING_UNTIL_MILLIS = "snoozing_until_millis";
|
||||||
|
public static final String COLUMN_SUNDAY = "sunday";
|
||||||
|
public static final String COLUMN_MONDAY = "monday";
|
||||||
|
public static final String COLUMN_TUESDAY = "tuesday";
|
||||||
|
public static final String COLUMN_WEDNESDAY = "wednesday";
|
||||||
|
public static final String COLUMN_THURSDAY = "thursday";
|
||||||
|
public static final String COLUMN_FRIDAY = "friday";
|
||||||
|
public static final String COLUMN_SATURDAY = "saturday";
|
||||||
|
public static final String COLUMN_IGNORE_UPCOMING_RING_TIME = "ignore_upcoming_ring_time";
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
public 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
|
||||||
|
|
||||||
|
public static void onCreate(SQLiteDatabase db) {
|
||||||
|
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);");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_ALARMS);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.Alarm;
|
||||||
|
|
||||||
|
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 7/30/2016.
|
||||||
|
*/
|
||||||
|
public class AlarmsTableManager extends DatabaseTableManager<Alarm> {
|
||||||
|
|
||||||
|
public AlarmsTableManager(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getQuerySortOrder() {
|
||||||
|
return AlarmsTable.NEW_SORT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlarmCursor queryItem(long id) {
|
||||||
|
return wrapInAlarmCursor(super.queryItem(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlarmCursor queryItems() {
|
||||||
|
return wrapInAlarmCursor(super.queryItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlarmCursor queryEnabledAlarms() {
|
||||||
|
return queryItems(AlarmsTable.COLUMN_ENABLED + " = 1", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AlarmCursor queryItems(String where, String limit) {
|
||||||
|
return wrapInAlarmCursor(super.queryItems(where, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTableName() {
|
||||||
|
return AlarmsTable.TABLE_ALARMS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContentValues toContentValues(Alarm alarm) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(AlarmsTable.COLUMN_HOUR, alarm.hour());
|
||||||
|
values.put(AlarmsTable.COLUMN_MINUTES, alarm.minutes());
|
||||||
|
values.put(AlarmsTable.COLUMN_LABEL, alarm.label());
|
||||||
|
values.put(AlarmsTable.COLUMN_RINGTONE, alarm.ringtone());
|
||||||
|
values.put(AlarmsTable.COLUMN_VIBRATES, alarm.vibrates());
|
||||||
|
values.put(AlarmsTable.COLUMN_ENABLED, alarm.isEnabled());
|
||||||
|
values.put(AlarmsTable.COLUMN_RING_TIME_MILLIS, alarm.ringsAt());
|
||||||
|
values.put(AlarmsTable.COLUMN_SNOOZING_UNTIL_MILLIS, alarm.snoozingUntil());
|
||||||
|
values.put(AlarmsTable.COLUMN_SUNDAY, alarm.isRecurring(SUNDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_MONDAY, alarm.isRecurring(MONDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_TUESDAY, alarm.isRecurring(TUESDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_WEDNESDAY, alarm.isRecurring(WEDNESDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_THURSDAY, alarm.isRecurring(THURSDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_FRIDAY, alarm.isRecurring(FRIDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_SATURDAY, alarm.isRecurring(SATURDAY));
|
||||||
|
values.put(AlarmsTable.COLUMN_IGNORE_UPCOMING_RING_TIME, alarm.isIgnoringUpcomingRingTime());
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOnContentChangeAction() {
|
||||||
|
return AlarmsListCursorLoader.ACTION_CHANGE_CONTENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlarmCursor wrapInAlarmCursor(Cursor c) {
|
||||||
|
return new AlarmCursor(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
|||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 7/29/2016.
|
* Created by Phillip Hsu on 7/29/2016.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class BaseDatabaseHelper<T extends ObjectWithId> extends SQLiteOpenHelper {
|
public abstract class BaseDatabaseHelper<T extends ObjectWithId> extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public static final String COLUMN_ID = "_id";
|
public static final String COLUMN_ID = "_id";
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public abstract class BaseItemCursor<T extends ObjectWithId> extends CursorWrapp
|
|||||||
Log.e(TAG, "Failed to retrieve id, cursor out of range");
|
Log.e(TAG, "Failed to retrieve id, cursor out of range");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return getLong(getColumnIndexOrThrow(BaseDatabaseHelper.COLUMN_ID));
|
return getLong(getColumnIndexOrThrow("_id")); // TODO: Refer to a constant instead of a hardcoded value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/30/2016.
|
||||||
|
*/
|
||||||
|
public class ClockAppDatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
private static final String TAG = "ClockAppDatabaseHelper";
|
||||||
|
private static final String DB_NAME = "clock_app.db";
|
||||||
|
private static final int VERSION_1 = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context the Context with which the application context will be retrieved
|
||||||
|
*/
|
||||||
|
public ClockAppDatabaseHelper(Context context) {
|
||||||
|
super(context.getApplicationContext(), DB_NAME, null, VERSION_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
AlarmsTable.onCreate(db);
|
||||||
|
TimersTable.onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
AlarmsTable.onUpgrade(db, oldVersion, newVersion);
|
||||||
|
TimersTable.onUpgrade(db, oldVersion, newVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import java.util.ArrayList;
|
|||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 6/25/2016.
|
* Created by Phillip Hsu on 6/25/2016.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class DatabaseManager {
|
public class DatabaseManager {
|
||||||
|
|
||||||
private static DatabaseManager sDatabaseManager;
|
private static DatabaseManager sDatabaseManager;
|
||||||
|
|||||||
@ -0,0 +1,112 @@
|
|||||||
|
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/30/2016.
|
||||||
|
*/
|
||||||
|
public abstract class DatabaseTableManager<T extends ObjectWithId> {
|
||||||
|
// TODO: Consider implementing BaseColumns for your table schemas.
|
||||||
|
// This column should be present in all table schemas, and the value is simple enough
|
||||||
|
// we can reproduce it here instead of relying on our subclasses to retrieve it from
|
||||||
|
// their designated table schema.
|
||||||
|
private static final String COLUMN_ID = "_id";
|
||||||
|
|
||||||
|
private final SQLiteOpenHelper mDbHelper;
|
||||||
|
private final Context mAppContext;
|
||||||
|
|
||||||
|
public DatabaseTableManager(Context context) {
|
||||||
|
// Internally uses the app context
|
||||||
|
mDbHelper = new ClockAppDatabaseHelper(context);
|
||||||
|
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 the Intent action that will be used to send broadcasts
|
||||||
|
* to our designated {@link NewSQLiteCursorLoader} whenever an
|
||||||
|
* underlying change to our data is detected. The Loader should
|
||||||
|
* receive the broadcast and reload its data.
|
||||||
|
*/
|
||||||
|
protected abstract String getOnContentChangeAction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 = mDbHelper.getWritableDatabase().insert(
|
||||||
|
getTableName(), null, toContentValues(item));
|
||||||
|
item.setId(id);
|
||||||
|
notifyContentChanged();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int updateItem(long id, T newItem) {
|
||||||
|
newItem.setId(id);
|
||||||
|
SQLiteDatabase db = mDbHelper.getWritableDatabase();
|
||||||
|
int rowsUpdated = db.update(getTableName(),
|
||||||
|
toContentValues(newItem),
|
||||||
|
COLUMN_ID + " = " + id,
|
||||||
|
null);
|
||||||
|
notifyContentChanged();
|
||||||
|
return rowsUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int deleteItem(T item) {
|
||||||
|
SQLiteDatabase db = mDbHelper.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 mDbHelper.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
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyContentChanged() {
|
||||||
|
LocalBroadcastHelper.sendBroadcast(mAppContext, getOnContentChangeAction());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,9 +18,8 @@ public abstract class NewSQLiteCursorLoader<
|
|||||||
T extends ObjectWithId,
|
T extends ObjectWithId,
|
||||||
C extends BaseItemCursor<T>>
|
C extends BaseItemCursor<T>>
|
||||||
extends AsyncTaskLoader<C> {
|
extends AsyncTaskLoader<C> {
|
||||||
private static final String TAG = "SQLiteCursorLoader";
|
|
||||||
|
|
||||||
public static final String ACTION_CHANGE_CONTENT = "com.philliphsu.clock2.model.action.CHANGE_CONTENT";
|
private static final String TAG = "SQLiteCursorLoader";
|
||||||
|
|
||||||
private C mCursor;
|
private C mCursor;
|
||||||
private OnContentChangeReceiver mOnContentChangeReceiver;
|
private OnContentChangeReceiver mOnContentChangeReceiver;
|
||||||
@ -31,6 +30,13 @@ public abstract class NewSQLiteCursorLoader<
|
|||||||
|
|
||||||
protected abstract C 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 */
|
/* Runs on a worker thread */
|
||||||
@Override
|
@Override
|
||||||
public C loadInBackground() {
|
public C loadInBackground() {
|
||||||
@ -81,7 +87,7 @@ public abstract class NewSQLiteCursorLoader<
|
|||||||
if (mOnContentChangeReceiver == null) {
|
if (mOnContentChangeReceiver == null) {
|
||||||
mOnContentChangeReceiver = new OnContentChangeReceiver();
|
mOnContentChangeReceiver = new OnContentChangeReceiver();
|
||||||
LocalBroadcastHelper.registerReceiver(getContext(),
|
LocalBroadcastHelper.registerReceiver(getContext(),
|
||||||
mOnContentChangeReceiver, ACTION_CHANGE_CONTENT);
|
mOnContentChangeReceiver, getOnContentChangeAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (takeContentChanged() || mCursor == null) {
|
if (takeContentChanged() || mCursor == null) {
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.Timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/30/2016.
|
||||||
|
*/
|
||||||
|
public class TimerCursor extends BaseItemCursor<Timer> {
|
||||||
|
|
||||||
|
public TimerCursor(Cursor cursor) {
|
||||||
|
super(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timer getItem() {
|
||||||
|
if (isBeforeFirst() || isAfterLast())
|
||||||
|
return null;
|
||||||
|
int hour = getInt(getColumnIndexOrThrow(TimersTable.COLUMN_HOUR));
|
||||||
|
int minute = getInt(getColumnIndexOrThrow(TimersTable.COLUMN_MINUTE));
|
||||||
|
int second = getInt(getColumnIndexOrThrow(TimersTable.COLUMN_SECOND));
|
||||||
|
String label = getString(getColumnIndexOrThrow(TimersTable.COLUMN_LABEL));
|
||||||
|
// String group = getString(getColumnIndexOrThrow(COLUMN_GROUP));
|
||||||
|
Timer t = Timer.create(hour, minute, second, label, /*group*/"");
|
||||||
|
t.setEndTime(getInt(getColumnIndexOrThrow(TimersTable.COLUMN_END_TIME)));
|
||||||
|
t.setPauseTime(getInt(getColumnIndexOrThrow(TimersTable.COLUMN_PAUSE_TIME)));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,7 @@ import com.philliphsu.clock2.Timer;
|
|||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 7/29/2016.
|
* Created by Phillip Hsu on 7/29/2016.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class TimerDatabaseHelper extends BaseDatabaseHelper<Timer> {
|
public class TimerDatabaseHelper extends BaseDatabaseHelper<Timer> {
|
||||||
private static final String TAG = "TimerDatabaseHelper";
|
private static final String TAG = "TimerDatabaseHelper";
|
||||||
private static final String DB_NAME = "timers.db";
|
private static final String DB_NAME = "timers.db";
|
||||||
@ -108,25 +109,4 @@ public class TimerDatabaseHelper extends BaseDatabaseHelper<Timer> {
|
|||||||
return new TimerCursor(c);
|
return new TimerCursor(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TimerCursor extends BaseItemCursor<Timer> {
|
|
||||||
|
|
||||||
public TimerCursor(Cursor cursor) {
|
|
||||||
super(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timer getItem() {
|
|
||||||
if (isBeforeFirst() || isAfterLast())
|
|
||||||
return null;
|
|
||||||
int hour = getInt(getColumnIndexOrThrow(COLUMN_HOUR));
|
|
||||||
int minute = getInt(getColumnIndexOrThrow(COLUMN_MINUTE));
|
|
||||||
int second = getInt(getColumnIndexOrThrow(COLUMN_SECOND));
|
|
||||||
String label = getString(getColumnIndexOrThrow(COLUMN_LABEL));
|
|
||||||
// String group = getString(getColumnIndexOrThrow(COLUMN_GROUP));
|
|
||||||
Timer t = Timer.create(hour, minute, second, label, /*group*/"");
|
|
||||||
t.setEndTime(getInt(getColumnIndexOrThrow(COLUMN_END_TIME)));
|
|
||||||
t.setPauseTime(getInt(getColumnIndexOrThrow(COLUMN_PAUSE_TIME)));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,14 +7,21 @@ import com.philliphsu.clock2.Timer;
|
|||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 7/29/2016.
|
* Created by Phillip Hsu on 7/29/2016.
|
||||||
*/
|
*/
|
||||||
public class TimersListCursorLoader extends NewSQLiteCursorLoader<Timer, TimerDatabaseHelper.TimerCursor> {
|
public class TimersListCursorLoader extends NewSQLiteCursorLoader<Timer, TimerCursor> {
|
||||||
|
public static final String ACTION_CHANGE_CONTENT
|
||||||
|
= "com.philliphsu.clock2.model.TimersListCursorLoader.action.CHANGE_CONTENT";
|
||||||
|
|
||||||
public TimersListCursorLoader(Context context) {
|
public TimersListCursorLoader(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TimerDatabaseHelper.TimerCursor loadCursor() {
|
protected TimerCursor loadCursor() {
|
||||||
return new TimerDatabaseHelper(getContext()).queryItems();
|
return new TimersTableManager(getContext()).queryItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOnContentChangeAction() {
|
||||||
|
return ACTION_CHANGE_CONTENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/30/2016.
|
||||||
|
*/
|
||||||
|
public final class TimersTable {
|
||||||
|
private TimersTable() {}
|
||||||
|
|
||||||
|
// TODO: Consider defining index constants for each column,
|
||||||
|
// and then removing all cursor getColumnIndex() calls.
|
||||||
|
public static final String TABLE_TIMERS = "timers";
|
||||||
|
|
||||||
|
// TODO: Consider implementing BaseColumns instead to get _id column.
|
||||||
|
public static final String COLUMN_ID = "_id";
|
||||||
|
public static final String COLUMN_HOUR = "hour";
|
||||||
|
public static final String COLUMN_MINUTE = "minute";
|
||||||
|
public static final String COLUMN_SECOND = "second";
|
||||||
|
public 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!
|
||||||
|
// public static final String COLUMN_GROUP = "group";
|
||||||
|
|
||||||
|
public static final String COLUMN_END_TIME = "end_time";
|
||||||
|
public static final String COLUMN_PAUSE_TIME = "pause_time";
|
||||||
|
|
||||||
|
public static final String SORT_ORDER =
|
||||||
|
COLUMN_HOUR + " ASC, "
|
||||||
|
+ COLUMN_MINUTE + " ASC, "
|
||||||
|
+ COLUMN_SECOND + " ASC, "
|
||||||
|
// All else equal, newer timers first
|
||||||
|
+ COLUMN_ID + " DESC";
|
||||||
|
|
||||||
|
public static 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);");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TIMERS);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package com.philliphsu.clock2.model;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.philliphsu.clock2.Timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Phillip Hsu on 7/30/2016.
|
||||||
|
*/
|
||||||
|
public class TimersTableManager extends DatabaseTableManager<Timer> {
|
||||||
|
|
||||||
|
public TimersTableManager(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getQuerySortOrder() {
|
||||||
|
return TimersTable.SORT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 TimersTable.TABLE_TIMERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContentValues toContentValues(Timer timer) {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put(TimersTable.COLUMN_HOUR, timer.hour());
|
||||||
|
cv.put(TimersTable.COLUMN_MINUTE, timer.minute());
|
||||||
|
cv.put(TimersTable.COLUMN_SECOND, timer.second());
|
||||||
|
cv.put(TimersTable.COLUMN_LABEL, timer.label());
|
||||||
|
// cv.put(TimersTable.COLUMN_GROUP, timer.group());
|
||||||
|
cv.put(TimersTable.COLUMN_END_TIME, timer.endTime());
|
||||||
|
cv.put(TimersTable.COLUMN_PAUSE_TIME, timer.pauseTime());
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getOnContentChangeAction() {
|
||||||
|
return TimersListCursorLoader.ACTION_CHANGE_CONTENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimerCursor wrapInTimerCursor(Cursor c) {
|
||||||
|
return new TimerCursor(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,12 +5,12 @@ import android.view.ViewGroup;
|
|||||||
import com.philliphsu.clock2.BaseCursorAdapter;
|
import com.philliphsu.clock2.BaseCursorAdapter;
|
||||||
import com.philliphsu.clock2.OnListItemInteractionListener;
|
import com.philliphsu.clock2.OnListItemInteractionListener;
|
||||||
import com.philliphsu.clock2.Timer;
|
import com.philliphsu.clock2.Timer;
|
||||||
import com.philliphsu.clock2.model.TimerDatabaseHelper;
|
import com.philliphsu.clock2.model.TimerCursor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Phillip Hsu on 7/29/2016.
|
* Created by Phillip Hsu on 7/29/2016.
|
||||||
*/
|
*/
|
||||||
public class TimersCursorAdapter extends BaseCursorAdapter<Timer, TimerViewHolder, TimerDatabaseHelper.TimerCursor> {
|
public class TimersCursorAdapter extends BaseCursorAdapter<Timer, TimerViewHolder, TimerCursor> {
|
||||||
|
|
||||||
public TimersCursorAdapter(OnListItemInteractionListener<Timer> listener) {
|
public TimersCursorAdapter(OnListItemInteractionListener<Timer> listener) {
|
||||||
super(listener);
|
super(listener);
|
||||||
|
|||||||
@ -8,13 +8,14 @@ import android.support.v4.content.Loader;
|
|||||||
import com.philliphsu.clock2.RecyclerViewFragment;
|
import com.philliphsu.clock2.RecyclerViewFragment;
|
||||||
import com.philliphsu.clock2.Timer;
|
import com.philliphsu.clock2.Timer;
|
||||||
import com.philliphsu.clock2.edittimer.EditTimerActivity;
|
import com.philliphsu.clock2.edittimer.EditTimerActivity;
|
||||||
|
import com.philliphsu.clock2.model.TimerCursor;
|
||||||
import com.philliphsu.clock2.model.TimerDatabaseHelper;
|
import com.philliphsu.clock2.model.TimerDatabaseHelper;
|
||||||
import com.philliphsu.clock2.model.TimersListCursorLoader;
|
import com.philliphsu.clock2.model.TimersListCursorLoader;
|
||||||
|
|
||||||
public class TimersFragment extends RecyclerViewFragment<
|
public class TimersFragment extends RecyclerViewFragment<
|
||||||
Timer,
|
Timer,
|
||||||
TimerViewHolder,
|
TimerViewHolder,
|
||||||
TimerDatabaseHelper.TimerCursor,
|
TimerCursor,
|
||||||
TimersCursorAdapter> {
|
TimersCursorAdapter> {
|
||||||
public static final int REQUEST_CREATE_TIMER = 0;
|
public static final int REQUEST_CREATE_TIMER = 0;
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ public class TimersFragment extends RecyclerViewFragment<
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<TimerDatabaseHelper.TimerCursor> onCreateLoader(int id, Bundle args) {
|
public Loader<TimerCursor> onCreateLoader(int id, Bundle args) {
|
||||||
return new TimersListCursorLoader(getActivity());
|
return new TimersListCursorLoader(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user