diff --git a/app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java b/app/src/main/java/com/philliphsu/clock2/AsyncAlarmsTableUpdateHandler.java similarity index 83% rename from app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java rename to app/src/main/java/com/philliphsu/clock2/AsyncAlarmsTableUpdateHandler.java index a88d9cf..15dcee4 100644 --- a/app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java +++ b/app/src/main/java/com/philliphsu/clock2/AsyncAlarmsTableUpdateHandler.java @@ -10,12 +10,10 @@ import com.philliphsu.clock2.util.AlarmController; /** * Created by Phillip Hsu on 7/1/2016. - * - * TODO: Rename to AsyncAlarmChangeHandler * TODO: Consider making an AsyncDatabaseChangeHandlerWithSnackbar abstract class */ -public final class AsyncItemChangeHandler extends AsyncDatabaseChangeHandler { - private static final String TAG = "AsyncItemChangeHandler"; +public final class AsyncAlarmsTableUpdateHandler extends AsyncDatabaseTableUpdateHandler { + private static final String TAG = "AsyncAlarmsTableUpdateHandler"; private final View mSnackbarAnchor; private final AlarmController mAlarmController; @@ -24,9 +22,9 @@ public final class AsyncItemChangeHandler extends AsyncDatabaseChangeHandler> { - private static final String TAG = "AsyncDatabaseChangeHandler"; + private static final String TAG = "AsyncDatabaseTableUpdateHandler"; private final Context mAppContext; private final ScrollHandler mScrollHandler; @@ -22,7 +22,7 @@ public abstract class AsyncDatabaseChangeHandler< /** * @param context the Context from which we get the application context */ - public AsyncDatabaseChangeHandler(Context context, ScrollHandler scrollHandler) { + public AsyncDatabaseTableUpdateHandler(Context context, ScrollHandler scrollHandler) { mAppContext = context.getApplicationContext(); // to prevent memory leaks mScrollHandler = scrollHandler; mTableManager = getTableManager(context); diff --git a/app/src/main/java/com/philliphsu/clock2/AsyncTimersTableUpdateHandler.java b/app/src/main/java/com/philliphsu/clock2/AsyncTimersTableUpdateHandler.java new file mode 100644 index 0000000..295b35c --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/AsyncTimersTableUpdateHandler.java @@ -0,0 +1,36 @@ +package com.philliphsu.clock2; + +import android.content.Context; + +import com.philliphsu.clock2.alarms.ScrollHandler; +import com.philliphsu.clock2.model.TimersTableManager; + +/** + * Created by Phillip Hsu on 8/2/2016. + */ +public final class AsyncTimersTableUpdateHandler extends AsyncDatabaseTableUpdateHandler { + + public AsyncTimersTableUpdateHandler(Context context, ScrollHandler scrollHandler) { + super(context, scrollHandler); + } + + @Override + protected TimersTableManager getTableManager(Context context) { + return new TimersTableManager(context); + } + + @Override + protected void onPostAsyncDelete(Integer result, Timer timer) { + // TODO: Cancel the alarm scheduled for this timer + } + + @Override + protected void onPostAsyncInsert(Long result, Timer timer) { + // TODO: if running, schedule alarm + } + + @Override + protected void onPostAsyncUpdate(Long result, Timer timer) { + // TODO: cancel and reschedule + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java index c3096b1..41ed964 100644 --- a/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/RecyclerViewFragment.java @@ -10,6 +10,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.philliphsu.clock2.alarms.ScrollHandler; import com.philliphsu.clock2.model.BaseItemCursor; import com.philliphsu.clock2.model.ObjectWithId; @@ -25,9 +26,11 @@ public abstract class RecyclerViewFragment< A extends BaseCursorAdapter> extends BaseFragment implements LoaderManager.LoaderCallbacks, - OnListItemInteractionListener { + OnListItemInteractionListener, + ScrollHandler { private A mAdapter; + private long mScrollToStableId = RecyclerView.NO_ID; // TODO: Rename id to recyclerView? // TODO: Rename variable to mRecyclerView? @@ -35,6 +38,14 @@ public abstract class RecyclerViewFragment< public abstract void onFabClick(); + /** + * Callback invoked when we have scrolled to the stable id as set in + * {@link #setScrollToStableId(long)}. + * @param id the stable id we have scrolled to + * @param position the position of the item with this stable id + */ + protected abstract void onScrolledToStableId(long id, int position); + /** * @return the adapter to set on the RecyclerView. SUBCLASSES MUST OVERRIDE THIS, BECAUSE THE * DEFAULT IMPLEMENTATION WILL ALWAYS RETURN AN UNINITIALIZED ADAPTER INSTANCE. @@ -71,6 +82,9 @@ public abstract class RecyclerViewFragment< @Override public void onLoadFinished(Loader loader, C data) { mAdapter.swapCursor(data); + // This may have been a requery due to content change. If the change + // was an insertion, scroll to the last modified alarm. + performScrollToStableId(); } @Override @@ -86,4 +100,32 @@ public abstract class RecyclerViewFragment< protected int contentLayout() { return R.layout.fragment_recycler_view; } + + @Override + public void setScrollToStableId(long id) { + mScrollToStableId = id; + } + + @Override + public void scrollToPosition(int position) { + mList.smoothScrollToPosition(position); + } + + private void performScrollToStableId() { + if (mScrollToStableId != RecyclerView.NO_ID) { + int position = -1; + for (int i = 0; i < mAdapter.getItemCount(); i++) { + if (mAdapter.getItemId(i) == mScrollToStableId) { + position = i; + break; + } + } + if (position >= 0) { + scrollToPosition(position); + onScrolledToStableId(mScrollToStableId, position); + } + } + // Reset + mScrollToStableId = RecyclerView.NO_ID; + } } diff --git a/app/src/main/java/com/philliphsu/clock2/Timer.java b/app/src/main/java/com/philliphsu/clock2/Timer.java index 5314ff1..0282baa 100644 --- a/app/src/main/java/com/philliphsu/clock2/Timer.java +++ b/app/src/main/java/com/philliphsu/clock2/Timer.java @@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit; * Created by Phillip Hsu on 7/25/2016. */ @AutoValue -public abstract class Timer extends ObjectWithId { +public abstract class Timer extends ObjectWithId /*implements Parcelable*/ { private static final long MINUTE = TimeUnit.MINUTES.toMillis(1); private long endTime; @@ -128,4 +128,18 @@ public abstract class Timer extends ObjectWithId { public long pauseTime() { return pauseTime; } + +// @Override +// public int describeContents() { +// return 0; +// } +// +// @Override +// public void writeToParcel(Parcel dest, int flags) { +// dest.writeInt(hour()); +// dest.writeInt(minute()); +// dest.writeInt(second()); +// dest.writeString(group()); +// dest.writeString(label()); +// } } diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java index 259424d..4f2c60f 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -6,12 +6,11 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.content.Loader; -import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import com.philliphsu.clock2.Alarm; -import com.philliphsu.clock2.AsyncItemChangeHandler; +import com.philliphsu.clock2.AsyncAlarmsTableUpdateHandler; import com.philliphsu.clock2.R; import com.philliphsu.clock2.RecyclerViewFragment; import com.philliphsu.clock2.editalarm.EditAlarmActivity; @@ -20,8 +19,6 @@ import com.philliphsu.clock2.model.AlarmsListCursorLoader; import com.philliphsu.clock2.util.AlarmController; import com.philliphsu.clock2.util.DelayedSnackbarHandler; -import butterknife.Bind; - public class AlarmsFragment extends RecyclerViewFragment< Alarm, BaseAlarmViewHolder, @@ -34,14 +31,10 @@ public class AlarmsFragment extends RecyclerViewFragment< public static final int REQUEST_CREATE_ALARM = 1; // private AlarmsCursorAdapter mAdapter; - // TODO: Since we only use this in onActivityResult(), we also don't need this anymore. - private AsyncItemChangeHandler mAsyncItemChangeHandler; + private AsyncAlarmsTableUpdateHandler mAsyncAlarmsTableUpdateHandler; private AlarmController mAlarmController; private Handler mHandler = new Handler(); private View mSnackbarAnchor; - private long mScrollToStableId = RecyclerView.NO_ID; - - @Bind(R.id.list) RecyclerView mList; /** * Mandatory empty constructor for the fragment manager to instantiate the @@ -71,7 +64,7 @@ public class AlarmsFragment extends RecyclerViewFragment< // See the Fragment lifecycle. mSnackbarAnchor = getActivity().findViewById(R.id.main_content); mAlarmController = new AlarmController(getActivity(), mSnackbarAnchor); - mAsyncItemChangeHandler = new AsyncItemChangeHandler(getActivity(), + mAsyncAlarmsTableUpdateHandler = new AsyncAlarmsTableUpdateHandler(getActivity(), mSnackbarAnchor, this, mAlarmController); } @@ -91,10 +84,8 @@ public class AlarmsFragment extends RecyclerViewFragment< @Override public void onLoadFinished(Loader loader, AlarmCursor data) { super.onLoadFinished(loader, data); - // This may have been a requery due to content change. If the change - // was an insertion, scroll to the last modified alarm. - // TODO: If the change was an update, this presents a problem. - performScrollToStableId(); + // TODO: If this was a content change due to an update, verify that + // we scroll to the updated alarm if its sort order changes. } @Override @@ -108,7 +99,10 @@ public class AlarmsFragment extends RecyclerViewFragment< protected AlarmsCursorAdapter getAdapter() { if (super.getAdapter() != null) return super.getAdapter(); - // Create a new adapter + // Create a new adapter. This is called before we can initialize mAlarmController, + // so right now it is null. However, after super.onCreate() returns, it is initialized, and + // the reference variable will be pointing to an actual object. This assignment "propagates" + // to all references to mAlarmController. return new AlarmsCursorAdapter(this, mAlarmController); } @@ -132,19 +126,19 @@ public class AlarmsFragment extends RecyclerViewFragment< switch (requestCode) { case REQUEST_CREATE_ALARM: mHandler.postDelayed( - new AsyncAddItemRunnable(mAsyncItemChangeHandler, alarm), + new AsyncAddItemRunnable(mAsyncAlarmsTableUpdateHandler, alarm), 300); break; case REQUEST_EDIT_ALARM: if (data.getBooleanExtra(EditAlarmActivity.EXTRA_IS_DELETING, false)) { // TODO: Should we delay this too? It seems animations run // some of the time. - mAsyncItemChangeHandler.asyncDelete(alarm); + mAsyncAlarmsTableUpdateHandler.asyncDelete(alarm); } else { // TODO: Increase the delay, because update animation is // more elusive than insert. mHandler.postDelayed( - new AsyncUpdateItemRunnable(mAsyncItemChangeHandler, alarm), + new AsyncUpdateItemRunnable(mAsyncAlarmsTableUpdateHandler, alarm), 300); } break; @@ -165,12 +159,16 @@ public class AlarmsFragment extends RecyclerViewFragment< } } + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TODO: Just like with TimersCursorAdapter, we could pass in the mAsyncAlarmsTableUpdateHandler + // to the AlarmsCursorAdapter and call these on the save and delete button click bindings. + @Override // TODO: Rename to onListItem***Delete*** because the item hasn't been deleted from our db yet public void onListItemDeleted(final Alarm item) { // The corresponding VH will be automatically removed from view following // the requery, so we don't have to do anything to it. - mAsyncItemChangeHandler.asyncDelete(item); + mAsyncAlarmsTableUpdateHandler.asyncDelete(item); } @Override @@ -182,70 +180,54 @@ public class AlarmsFragment extends RecyclerViewFragment< // TODO: Implement editing in the expanded VH. Then verify that changes // while in that VH are saved and updated after the requery. // getAdapter().collapse(position); - mAsyncItemChangeHandler.asyncUpdate(item.getId(), item); + mAsyncAlarmsTableUpdateHandler.asyncUpdate(item.getId(), item); } + ///////////////////////////////////////////////////////////////////////////////////////////////// + @Override - public void setScrollToStableId(long id) { - mScrollToStableId = id; - } - - @Override - public void scrollToPosition(int position) { - mList.smoothScrollToPosition(position); - } - - private void performScrollToStableId() { - if (mScrollToStableId != RecyclerView.NO_ID) { - int position = -1; - for (int i = 0; i < getAdapter().getItemCount(); i++) { - if (getAdapter().getItemId(i) == mScrollToStableId) { - position = i; - break; - } - } - if (position >= 0) { - scrollToPosition(position); - // We were called because of a requery. If it was due to an insertion, - // expand the newly added alarm. - boolean expanded = getAdapter().expand(position); - if (!expanded) { - // Otherwise, it was due to an item update. The VH is expanded - // at this point, so reset it. - getAdapter().collapse(position); - } - } + protected void onScrolledToStableId(long id, int position) { + // We were called because of a requery. If it was due to an insertion, + // expand the newly added alarm. + boolean expanded = getAdapter().expand(position); + if (!expanded) { + // Otherwise, it was due to an item update. The VH is expanded + // at this point, so reset it. + getAdapter().collapse(position); } - // Reset - mScrollToStableId = RecyclerView.NO_ID; } + ///////////////////////////////////////////////////////////////////////////////////// + // TODO: We won't need these anymore, since we won't handle the db + // update in onActivityResult() anymore. + + @Deprecated private static abstract class BaseAsyncItemChangeRunnable { // TODO: Will holding onto this cause a memory leak? - private final AsyncItemChangeHandler mAsyncItemChangeHandler; + private final AsyncAlarmsTableUpdateHandler mAsyncAlarmsTableUpdateHandler; private final Alarm mAlarm; - BaseAsyncItemChangeRunnable(AsyncItemChangeHandler asyncItemChangeHandler, Alarm alarm) { - mAsyncItemChangeHandler = asyncItemChangeHandler; + BaseAsyncItemChangeRunnable(AsyncAlarmsTableUpdateHandler asyncAlarmsTableUpdateHandler, Alarm alarm) { + mAsyncAlarmsTableUpdateHandler = asyncAlarmsTableUpdateHandler; mAlarm = alarm; } void asyncAddAlarm() { - mAsyncItemChangeHandler.asyncInsert(mAlarm); + mAsyncAlarmsTableUpdateHandler.asyncInsert(mAlarm); } void asyncUpdateAlarm() { - mAsyncItemChangeHandler.asyncUpdate(mAlarm.getId(), mAlarm); + mAsyncAlarmsTableUpdateHandler.asyncUpdate(mAlarm.getId(), mAlarm); } void asyncRemoveAlarm() { - mAsyncItemChangeHandler.asyncDelete(mAlarm); + mAsyncAlarmsTableUpdateHandler.asyncDelete(mAlarm); } } private static class AsyncAddItemRunnable extends BaseAsyncItemChangeRunnable implements Runnable { - AsyncAddItemRunnable(AsyncItemChangeHandler asyncItemChangeHandler, Alarm alarm) { - super(asyncItemChangeHandler, alarm); + AsyncAddItemRunnable(AsyncAlarmsTableUpdateHandler asyncAlarmsTableUpdateHandler, Alarm alarm) { + super(asyncAlarmsTableUpdateHandler, alarm); } @Override @@ -255,8 +237,8 @@ public class AlarmsFragment extends RecyclerViewFragment< } private static class AsyncUpdateItemRunnable extends BaseAsyncItemChangeRunnable implements Runnable { - AsyncUpdateItemRunnable(AsyncItemChangeHandler asyncItemChangeHandler, Alarm alarm) { - super(asyncItemChangeHandler, alarm); + AsyncUpdateItemRunnable(AsyncAlarmsTableUpdateHandler asyncAlarmsTableUpdateHandler, Alarm alarm) { + super(asyncAlarmsTableUpdateHandler, alarm); } @Override @@ -266,8 +248,8 @@ public class AlarmsFragment extends RecyclerViewFragment< } private static class AsyncRemoveItemRunnable extends BaseAsyncItemChangeRunnable implements Runnable { - AsyncRemoveItemRunnable(AsyncItemChangeHandler asyncItemChangeHandler, Alarm alarm) { - super(asyncItemChangeHandler, alarm); + AsyncRemoveItemRunnable(AsyncAlarmsTableUpdateHandler asyncAlarmsTableUpdateHandler, Alarm alarm) { + super(asyncAlarmsTableUpdateHandler, alarm); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/edittimer/EditTimerActivity.java b/app/src/main/java/com/philliphsu/clock2/edittimer/EditTimerActivity.java index d01b0c6..d5e9a63 100644 --- a/app/src/main/java/com/philliphsu/clock2/edittimer/EditTimerActivity.java +++ b/app/src/main/java/com/philliphsu/clock2/edittimer/EditTimerActivity.java @@ -1,5 +1,6 @@ package com.philliphsu.clock2.edittimer; +import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v7.widget.GridLayout; @@ -21,6 +22,11 @@ import butterknife.OnTouch; // TODO: Rename to CreateTimerActivity public class EditTimerActivity extends BaseActivity { private static final int FIELD_LENGTH = 2; + public static final String EXTRA_HOUR = "com.philliphsu.clock2.edittimer.extra.HOUR"; + public static final String EXTRA_MINUTE = "com.philliphsu.clock2.edittimer.extra.MINUTE"; + public static final String EXTRA_SECOND = "com.philliphsu.clock2.edittimer.extra.SECOND"; + public static final String EXTRA_LABEL = "com.philliphsu.clock2.edittimer.extra.LABEL"; + public static final String EXTRA_START_TIMER = "com.philliphsu.clock2.edittimer.extra.START_TIMER"; @Bind(R.id.appbar) ViewGroup mAppBar; @Bind(R.id.label) TextView mLabel; @@ -148,12 +154,15 @@ public class EditTimerActivity extends BaseActivity { int second = Integer.parseInt(mSecond.getText().toString()); if (hour == 0 && minute == 0 && second == 0) return; // TODO: we could show a toast instead if we cared - // TODO: do something with the label - mLabel.getText(); - // TODO: Pass back an intent with the data, or make Timer parcelable - // and pass back an instance of Timer. Consider overriding finish() - // and doing it there. - setResult(RESULT_OK); + // TODO: Consider overriding finish() and doing this there. + // TODO: Timer's group? + Intent data = new Intent() + .putExtra(EXTRA_HOUR, hour) + .putExtra(EXTRA_MINUTE, minute) + .putExtra(EXTRA_SECOND, second) + .putExtra(EXTRA_LABEL, mLabel.getText().toString()) + .putExtra(EXTRA_START_TIMER, true); + setResult(RESULT_OK, data); finish(); } diff --git a/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java b/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java index bcb49bc..9517fe6 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java +++ b/app/src/main/java/com/philliphsu/clock2/model/DatabaseTableManager.java @@ -73,6 +73,9 @@ public abstract class DatabaseTableManager { toContentValues(newItem), COLUMN_ID + " = " + id, null); + if (rowsUpdated == 0) { + throw new IllegalStateException("wtf?"); + } notifyContentChanged(); return rowsUpdated; } diff --git a/app/src/main/java/com/philliphsu/clock2/model/TimerCursor.java b/app/src/main/java/com/philliphsu/clock2/model/TimerCursor.java index 905ca6c..70d3b06 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/TimerCursor.java +++ b/app/src/main/java/com/philliphsu/clock2/model/TimerCursor.java @@ -23,8 +23,9 @@ public class TimerCursor extends BaseItemCursor { 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))); + t.setId(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_ID))); + t.setEndTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_END_TIME))); + t.setPauseTime(getLong(getColumnIndexOrThrow(TimersTable.COLUMN_PAUSE_TIME))); return t; } } diff --git a/app/src/main/java/com/philliphsu/clock2/model/TimersTableManager.java b/app/src/main/java/com/philliphsu/clock2/model/TimersTableManager.java index d7e1e89..88234d7 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/TimersTableManager.java +++ b/app/src/main/java/com/philliphsu/clock2/model/TimersTableManager.java @@ -3,6 +3,7 @@ package com.philliphsu.clock2.model; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; +import android.util.Log; import com.philliphsu.clock2.Timer; @@ -10,6 +11,7 @@ import com.philliphsu.clock2.Timer; * Created by Phillip Hsu on 7/30/2016. */ public class TimersTableManager extends DatabaseTableManager { + public static final String TAG = "TimersTableManager"; public TimersTableManager(Context context) { super(context); @@ -22,7 +24,7 @@ public class TimersTableManager extends DatabaseTableManager { @Override public TimerCursor queryItem(long id) { - return wrapInTimerCursor(queryItem(id)); + return wrapInTimerCursor(super.queryItem(id)); } @Override @@ -48,6 +50,7 @@ public class TimersTableManager extends DatabaseTableManager { cv.put(TimersTable.COLUMN_SECOND, timer.second()); cv.put(TimersTable.COLUMN_LABEL, timer.label()); // cv.put(TimersTable.COLUMN_GROUP, timer.group()); + Log.d(TAG, "endTime = " + timer.endTime() + ", pauseTime = " + timer.pauseTime()); cv.put(TimersTable.COLUMN_END_TIME, timer.endTime()); cv.put(TimersTable.COLUMN_PAUSE_TIME, timer.pauseTime()); return cv; diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java index a3d4205..94379cc 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimerAdapter.java @@ -11,6 +11,7 @@ import java.util.List; /** * Created by Phillip Hsu on 7/26/2016. */ +@Deprecated public class TimerAdapter extends BaseAdapter { public TimerAdapter(List items, OnListItemInteractionListener listener) { @@ -19,7 +20,7 @@ public class TimerAdapter extends BaseAdapter { @Override protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { - return new TimerViewHolder(parent, listener); + return new TimerViewHolder(parent, listener, null); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerController.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerController.java index 72bb45d..6f5bfeb 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimerController.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimerController.java @@ -1,7 +1,5 @@ package com.philliphsu.clock2.timers; -import android.os.SystemClock; -import android.view.View; import android.widget.ImageButton; import com.philliphsu.clock2.Timer; @@ -23,55 +21,57 @@ public class TimerController { mAddOneMinute = addOneMinute; mStartPause = startPause; mStop = stop; - init(); + +// init(); } - private void init() { - mChronometer.setBase(SystemClock.elapsedRealtime() + mTimer.duration()); - updateStartPauseIcon(); - setSecondaryButtonsVisible(false); - } +// private void init() { +// mChronometer.setBase(SystemClock.elapsedRealtime() + mTimer.duration()); +// updateStartPauseIcon(); +// setSecondaryButtonsVisible(false); +// } public void start() { mTimer.start(); - mChronometer.setBase(mTimer.endTime()); - mChronometer.start(); - updateStartPauseIcon(); - setSecondaryButtonsVisible(true); +// mChronometer.setBase(mTimer.endTime()); +// mChronometer.start(); +// updateStartPauseIcon(); +// setSecondaryButtonsVisible(true); + } public void pause() { mTimer.pause(); - mChronometer.stop(); - updateStartPauseIcon(); +// mChronometer.stop(); +// updateStartPauseIcon(); } public void resume() { mTimer.resume(); - mChronometer.setBase(mTimer.endTime()); - mChronometer.start(); - updateStartPauseIcon(); +// mChronometer.setBase(mTimer.endTime()); +// mChronometer.start(); +// updateStartPauseIcon(); } public void stop() { mTimer.stop(); - mChronometer.stop(); - init(); +// mChronometer.stop(); +// init(); } public void addOneMinute() { mTimer.addOneMinute(); - mChronometer.setBase(mTimer.endTime()); +// mChronometer.setBase(mTimer.endTime()); } - public void updateStartPauseIcon() { - // TODO: Pause and start icons, resp. +// public void updateStartPauseIcon() { +// // TODO: Pause and start icons, resp. // mStartPause.setImageResource(mTimer.isRunning() ? 0 : 0); - } +// } - public void setSecondaryButtonsVisible(boolean visible) { - int visibility = visible ? View.VISIBLE : View.INVISIBLE; - mAddOneMinute.setVisibility(visibility); - mStop.setVisibility(visibility); - } +// public void setSecondaryButtonsVisible(boolean visible) { +// int visibility = visible ? View.VISIBLE : View.INVISIBLE; +// mAddOneMinute.setVisibility(visibility); +// mStop.setVisibility(visibility); +// } } diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java b/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java index 6ea0059..4948193 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimerViewHolder.java @@ -1,10 +1,13 @@ package com.philliphsu.clock2.timers; +import android.util.Log; +import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; +import com.philliphsu.clock2.AsyncTimersTableUpdateHandler; import com.philliphsu.clock2.BaseViewHolder; import com.philliphsu.clock2.OnListItemInteractionListener; import com.philliphsu.clock2.R; @@ -17,8 +20,10 @@ import butterknife.OnClick; * Created by Phillip Hsu on 7/25/2016. */ public class TimerViewHolder extends BaseViewHolder { + private static final String TAG = "TimerViewHolder"; - private TimerController mController; +// private TimerController mController; + private final AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler; @Bind(R.id.label) TextView mLabel; @Bind(R.id.duration) CountdownChronometer mChronometer; @@ -28,17 +33,19 @@ public class TimerViewHolder extends BaseViewHolder { @Bind(R.id.stop) ImageButton mStop; // TODO: Controller param - public TimerViewHolder(ViewGroup parent, OnListItemInteractionListener listener) { + public TimerViewHolder(ViewGroup parent, OnListItemInteractionListener listener, + AsyncTimersTableUpdateHandler asyncTimersTableUpdateHandler) { super(parent, R.layout.item_timer, listener); + mAsyncTimersTableUpdateHandler = asyncTimersTableUpdateHandler; } @Override public void onBind(Timer timer) { super.onBind(timer); bindLabel(timer.label()); - // We can't create the controller until this VH binds, because - // the widgets only exist after this point. - mController = new TimerController(timer, mChronometer, mAddOneMinute, mStartPause, mStop); +// // We can't create the controller until this VH binds, because +// // the widgets only exist after this point. +// mController = new TimerController(timer, mChronometer, mAddOneMinute, mStartPause, mStop); bindChronometer(timer); bindButtonControls(timer); } @@ -47,24 +54,30 @@ public class TimerViewHolder extends BaseViewHolder { void startPause() { Timer t = getItem(); if (t.isRunning()) { - mController.pause(); +// mController.pause(); + t.pause(); } else { if (t.hasStarted()) { - mController.resume(); + t.resume(); } else { - mController.start(); + t.start(); } } + // Persist value changes + update(); } @OnClick(R.id.add_one_minute) void addOneMinute() { - mController.addOneMinute(); + getItem().addOneMinute(); + // Persist end time increase + update(); } @OnClick(R.id.stop) void stop() { - mController.stop(); + getItem().stop(); + update(); } private void bindLabel(String label) { @@ -85,9 +98,7 @@ public class TimerViewHolder extends BaseViewHolder { if (!timer.hasStarted()) { // Set the initial text - // TODO: Verify the controller should already have initialized - // the text when it was constructed. -// mChronometer.setDuration(timer.duration()); + mChronometer.setDuration(timer.duration()); } else if (timer.isRunning()) { // Re-initialize the base mChronometer.setBase(timer.endTime()); @@ -107,7 +118,19 @@ public class TimerViewHolder extends BaseViewHolder { } private void bindButtonControls(Timer timer) { - mController.updateStartPauseIcon(); - mController.setSecondaryButtonsVisible(timer.hasStarted()); + // TODO: Pause and start icons, resp. +// mStartPause.setImageResource(timer.isRunning() ? 0 : 0); + int visibility = timer.hasStarted() ? View.VISIBLE : View.INVISIBLE; + mAddOneMinute.setVisibility(visibility); + mStop.setVisibility(visibility); + } + + private void update() { + Timer t = getItem(); + mAsyncTimersTableUpdateHandler.asyncUpdate( + // Alternatively, use ViewHolder#getItemId() because we can forget + // to set the id on the object in BaseItemCursor#getItem(). We + // luckily remembered to this time! + t.getId(), t); } } diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java b/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java index 398d3f6..c86c3d4 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimersCursorAdapter.java @@ -2,6 +2,7 @@ package com.philliphsu.clock2.timers; import android.view.ViewGroup; +import com.philliphsu.clock2.AsyncTimersTableUpdateHandler; import com.philliphsu.clock2.BaseCursorAdapter; import com.philliphsu.clock2.OnListItemInteractionListener; import com.philliphsu.clock2.Timer; @@ -12,12 +13,18 @@ import com.philliphsu.clock2.model.TimerCursor; */ public class TimersCursorAdapter extends BaseCursorAdapter { - public TimersCursorAdapter(OnListItemInteractionListener listener) { + private final AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler; + + public TimersCursorAdapter(OnListItemInteractionListener listener, + AsyncTimersTableUpdateHandler asyncTimersTableUpdateHandler) { super(listener); + mAsyncTimersTableUpdateHandler = asyncTimersTableUpdateHandler; } @Override protected TimerViewHolder onCreateViewHolder(ViewGroup parent, OnListItemInteractionListener listener, int viewType) { - return new TimerViewHolder(parent, listener); + return new TimerViewHolder(parent, listener, mAsyncTimersTableUpdateHandler); } + + } diff --git a/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java index 83890d9..b7420f6 100644 --- a/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/timers/TimersFragment.java @@ -1,11 +1,13 @@ package com.philliphsu.clock2.timers; +import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.content.Loader; +import com.philliphsu.clock2.AsyncTimersTableUpdateHandler; import com.philliphsu.clock2.RecyclerViewFragment; import com.philliphsu.clock2.Timer; import com.philliphsu.clock2.edittimer.EditTimerActivity; @@ -19,10 +21,30 @@ public class TimersFragment extends RecyclerViewFragment< TimersCursorAdapter> { public static final int REQUEST_CREATE_TIMER = 0; + private AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAsyncTimersTableUpdateHandler = new AsyncTimersTableUpdateHandler(getActivity(), this); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { -// if (resultCode != Activity.RESULT_OK || data == null) -// return; + if (resultCode != Activity.RESULT_OK || data == null) + return; + int hour = data.getIntExtra(EditTimerActivity.EXTRA_HOUR, -1); + int minute = data.getIntExtra(EditTimerActivity.EXTRA_MINUTE, -1); + int second = data.getIntExtra(EditTimerActivity.EXTRA_SECOND, -1); + String label = data.getStringExtra(EditTimerActivity.EXTRA_LABEL); + boolean startTimer = data.getBooleanExtra(EditTimerActivity.EXTRA_START_TIMER, false); + // TODO: Timer's group? + + Timer t = Timer.createWithLabel(hour, minute, second, label); + if (startTimer) { + t.start(); + } + mAsyncTimersTableUpdateHandler.asyncInsert(t); } @Override @@ -36,8 +58,11 @@ public class TimersFragment extends RecyclerViewFragment< protected TimersCursorAdapter getAdapter() { if (super.getAdapter() != null) return super.getAdapter(); - // Create a new adapter - return new TimersCursorAdapter(this); + // Create a new adapter. This is called before we can initialize mAsyncTimersTableUpdateHandler, + // so right now it is null. However, after super.onCreate() returns, it is initialized, and + // the reference variable will be pointing to an actual object. This assignment "propagates" + // to all references to mAsyncTimersTableUpdateHandler. + return new TimersCursorAdapter(this, mAsyncTimersTableUpdateHandler); } @Override @@ -59,4 +84,9 @@ public class TimersFragment extends RecyclerViewFragment< public void onListItemUpdate(Timer item, int position) { } + + @Override + protected void onScrolledToStableId(long id, int position) { + + } }