diff --git a/app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java b/app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java new file mode 100644 index 0000000..c44356a --- /dev/null +++ b/app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java @@ -0,0 +1,93 @@ +package com.philliphsu.clock2; + +import android.content.Context; +import android.os.AsyncTask; +import android.support.design.widget.Snackbar; +import android.view.View; + +import com.philliphsu.clock2.alarms.AlarmsAdapter; +import com.philliphsu.clock2.model.DatabaseManager; +import com.philliphsu.clock2.util.AlarmUtils; + +/** + * Created by Phillip Hsu on 7/1/2016. + * + * TODO: Generify this class for any item. + */ +public final class AsyncRecyclerViewItemChangeHandler { + + private final Context mContext; + private final AlarmsAdapter mAdapter; + private final View mSnackbarAnchor; + + /** + * @param snackbarAnchor an optional anchor for a Snackbar to anchor to + */ + public AsyncRecyclerViewItemChangeHandler(AlarmsAdapter adapter, View snackbarAnchor) { + mContext = snackbarAnchor.getContext(); + mAdapter = adapter; + mSnackbarAnchor = snackbarAnchor; + } + + public void asyncAddAlarm(final Alarm alarm) { + new AsyncTask() { + @Override + protected Long doInBackground(Void... params) { + return DatabaseManager.getInstance(mContext).insertAlarm(alarm); + } + + @Override + protected void onPostExecute(Long aLong) { + // TODO: Snackbar/Toast here? If so, remove the code in AlarmUtils.scheduleAlarm() that does it. + mAdapter.addItem(alarm); + AlarmUtils.scheduleAlarm(mContext, alarm, true); + } + }.execute(); + } + + public void asyncUpdateAlarm(final Alarm oldAlarm, final Alarm newAlarm) { + new AsyncTask() { + @Override + protected Integer doInBackground(Void... params) { + mAdapter.updateItem(oldAlarm, newAlarm); + // Save the item to the db + return DatabaseManager.getInstance(mContext).updateAlarm(oldAlarm.id(), newAlarm); + } + + @Override + protected void onPostExecute(Integer integer) { + // TODO: Snackbar/Toast here? If so, remove the code in AlarmUtils.scheduleAlarm() that does it. + } + }.execute(); + } + + public void asyncRemoveAlarm(final Alarm alarm) { + new AsyncTask() { + @Override + protected Integer doInBackground(Void... params) { + mAdapter.removeItem(alarm); + // Save the item to the db + return DatabaseManager.getInstance(mContext).deleteAlarm(alarm); + } + + @Override + protected void onPostExecute(Integer integer) { + // TODO: Snackbar + if (mSnackbarAnchor != null) { + String message = mContext.getString(R.string.snackbar_item_deleted, + mContext.getString(R.string.alarm)); + Snackbar.make(mSnackbarAnchor, message, Snackbar.LENGTH_LONG) + .setAction(R.string.snackbar_undo_item_deleted, new View.OnClickListener() { + @Override + public void onClick(View v) { + DatabaseManager.getInstance(mContext).insertAlarm(alarm); + if (alarm.isEnabled()) { + AlarmUtils.scheduleAlarm(mContext, alarm, true); + } + } + }).show(); + } + } + }.execute(); + } +} diff --git a/app/src/main/java/com/philliphsu/clock2/RecyclerViewItemChangeHandler.java b/app/src/main/java/com/philliphsu/clock2/RecyclerViewItemChangeHandler.java deleted file mode 100644 index 0b13f4d..0000000 --- a/app/src/main/java/com/philliphsu/clock2/RecyclerViewItemChangeHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.philliphsu.clock2; - -import android.support.v7.widget.RecyclerView; -import android.view.View; - -/** - * Created by Phillip Hsu on 7/1/2016. - */ -public final class RecyclerViewItemChangeHandler { - - private final RecyclerView mRecyclerView; - private final View mSnackbarAnchor; - - /** - * @param recyclerView the RecyclerView for which we should handle item change events - * @param snackbarAnchor an optional anchor for a Snackbar to anchor to - */ - public RecyclerViewItemChangeHandler(RecyclerView recyclerView, View snackbarAnchor) { - mRecyclerView = recyclerView; - mSnackbarAnchor = snackbarAnchor; - } - - /** - * Dispatches an item change event to the item in the - * RecyclerView with the given stable id. - */ - // This won't work if the change on the item would cause it - // to be sorted in a different position! - public void notifyItemChanged(long id) { - if (id < 0) throw new IllegalArgumentException("id < 0"); - int position = mRecyclerView.findViewHolderForItemId(id).getAdapterPosition(); - mRecyclerView.getAdapter().notifyItemChanged(position); - } - - public void notifyItemRemoved(long id) { - if (id < 0) throw new IllegalArgumentException("id < 0"); - int position = mRecyclerView.findViewHolderForItemId(id).getAdapterPosition(); - mRecyclerView.getAdapter().notifyItemRemoved(position); - } -} diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java index 438ef10..9bf5112 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java @@ -13,6 +13,12 @@ public class AlarmsAdapter extends BaseAdapter { public AlarmsAdapter(List alarms, OnListItemInteractionListener listener) { super(Alarm.class, alarms, listener); + setHasStableIds(true); + } + + @Override + public long getItemId(int position) { + return getItem(position).id(); } @Override diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java index 1cfd020..2046532 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsCursorAdapter.java @@ -22,7 +22,14 @@ public class AlarmsCursorAdapter extends RecyclerView.Adapter { public AlarmsCursorAdapter(OnListItemInteractionListener listener) { mListener = listener; - setHasStableIds(true); // TODO: why do we need this? + // 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 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 f62d5c7..4bcbc6e 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; @@ -16,6 +17,7 @@ import android.view.View; import android.view.ViewGroup; import com.philliphsu.clock2.Alarm; +import com.philliphsu.clock2.AsyncRecyclerViewItemChangeHandler; import com.philliphsu.clock2.OnListItemInteractionListener; import com.philliphsu.clock2.R; import com.philliphsu.clock2.editalarm.EditAlarmActivity; @@ -37,6 +39,7 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacksemptyList(), this); mList.setAdapter(mAdapter); + + mAsyncRecyclerViewItemChangeHandler = new AsyncRecyclerViewItemChangeHandler( + mAdapter, getActivity().findViewById(R.id.main_content)); return view; } @@ -126,12 +132,19 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks { private static final String TAG = "EditAlarmActivity"; public static final String EXTRA_ALARM_ID = "com.philliphsu.clock2.editalarm.extra.ALARM_ID"; - public static final String EXTRA_DELETED_ALARM = "com.philliphsu.clock2.editalarm.extra.DELETED_ALARM"; + public static final String EXTRA_MODIFIED_ALARM = "com.philliphsu.clock2.editalarm.extra.MODIFIED_ALARM"; private static final RelativeSizeSpan AMPM_SIZE_SPAN = new RelativeSizeSpan(0.5f); private static final int REQUEST_PICK_RINGTONE = 0; @@ -236,6 +236,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi alarm.setRecurring(i, isRecurringDay(i)); } + Intent intent = new Intent(); if (mOldAlarm != null) { if (mOldAlarm.isEnabled()) { Log.d(TAG, "Cancelling old alarm first"); @@ -244,15 +245,15 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi // TODO: Do this in the background. AsyncTask? mDatabaseManager.updateAlarm(mOldAlarm.id(), alarm); } else { - // TODO: Do this in the background. AsyncTask? - mDatabaseManager.insertAlarm(alarm); + //mDatabaseManager.insertAlarm(alarm); + intent.putExtra(EXTRA_MODIFIED_ALARM, alarm); } if (alarm.isEnabled()) { - scheduleAlarm(alarm); + //scheduleAlarm(alarm); } - setResult(RESULT_OK); + setResult(RESULT_OK, intent); showEditorClosed(); } @@ -268,7 +269,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi } mDatabaseManager.deleteAlarm(mOldAlarm); Intent intent = new Intent(); - intent.putExtra(EXTRA_DELETED_ALARM, mOldAlarm); + intent.putExtra(EXTRA_MODIFIED_ALARM, mOldAlarm); setResult(RESULT_OK, intent); } showEditorClosed(); diff --git a/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java b/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java index 7a4bd0a..e5f7d9d 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java +++ b/app/src/main/java/com/philliphsu/clock2/model/DatabaseManager.java @@ -14,7 +14,7 @@ public class DatabaseManager { private static DatabaseManager sDatabaseManager; private final Context mContext; - private final AlarmDatabaseHelper mHelper; + private final AlarmDatabaseHelper mHelper; // TODO: Should we call close() when we're done? private DatabaseManager(Context context) { mContext = context.getApplicationContext(); @@ -28,10 +28,8 @@ public class DatabaseManager { return sDatabaseManager; } - // TODO: why return an Alarm? - public Alarm insertAlarm(Alarm alarm) { - mHelper.insertAlarm(alarm); - return alarm; + public long insertAlarm(Alarm alarm) { + return mHelper.insertAlarm(alarm); } /**