Created AsyncDatabaseChangeHandler

This commit is contained in:
Phillip Hsu 2016-07-30 19:01:29 -07:00
parent 066ac67325
commit 7b0a32938e
5 changed files with 161 additions and 104 deletions

View File

@ -0,0 +1,128 @@
package com.philliphsu.clock2;
import android.content.Context;
import android.os.AsyncTask;
import com.philliphsu.clock2.alarms.ScrollHandler;
import com.philliphsu.clock2.model.DatabaseTableManager;
import com.philliphsu.clock2.model.ObjectWithId;
/**
* Created by Phillip Hsu on 7/1/2016.
*/
public abstract class AsyncDatabaseChangeHandler<
T extends ObjectWithId,
TM extends DatabaseTableManager<T>> {
private static final String TAG = "AsyncDatabaseChangeHandler";
private final Context mAppContext;
private final ScrollHandler mScrollHandler;
private final TM mTableManager;
/**
* @param context the Context from which we get the application context
*/
public AsyncDatabaseChangeHandler(Context context, ScrollHandler scrollHandler) {
mAppContext = context.getApplicationContext(); // to prevent memory leaks
mScrollHandler = scrollHandler;
mTableManager = getTableManager(context);
}
public final void asyncInsert(final T item) {
new InsertAsyncTask(item).execute();
}
public final void asyncUpdate(final long id, final T newItem) {
new UpdateAsyncTask(id, newItem).execute();
}
public final void asyncDelete(final T item) {
// TODO: If we want to scroll somewhere after we delete this item, we can
// create a DeleteAsyncTask subclass of the BaseAsyncTask. This involves
// using a Long result, however.
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... params) {
return mTableManager.deleteItem(item);
}
@Override
protected void onPostExecute(Integer integer) {
onPostAsyncDelete(integer, item);
}
}.execute();
}
protected final Context getContext() {
return mAppContext;
}
protected abstract TM getTableManager(Context context);
protected abstract void onPostAsyncDelete(Integer result, T item);
protected abstract void onPostAsyncInsert(Long result, T item);
protected abstract void onPostAsyncUpdate(Long result, T item);
////////////////////////////////////////////////////////////
// Insert and update AsyncTasks
////////////////////////////////////////////////////////////
/**
* Created because the code in insert and update AsyncTasks are exactly the same.
*/
private abstract class BaseAsyncTask extends AsyncTask<Void, Void, Long> {
final T mItem;
BaseAsyncTask(T item) {
mItem = item;
}
@Override
protected void onPostExecute(Long result) {
if (mScrollHandler != null) {
// Prepare to scroll to this alarm
mScrollHandler.setScrollToStableId(result);
}
}
}
private class InsertAsyncTask extends BaseAsyncTask {
InsertAsyncTask(T item) {
super(item);
}
@Override
protected Long doInBackground(Void... params) {
return mTableManager.insertItem(mItem);
}
@Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
onPostAsyncInsert(result, mItem);
}
}
private class UpdateAsyncTask extends BaseAsyncTask {
private final long mId;
UpdateAsyncTask(long id, T item) {
super(item);
mId = id;
}
@Override
protected Long doInBackground(Void... params) {
mTableManager.updateItem(mId, mItem);
return mId;
}
@Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
onPostAsyncUpdate(result, mItem);
}
}
}

View File

@ -1,25 +1,23 @@
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.ScrollHandler;
import com.philliphsu.clock2.model.DatabaseManager;
import com.philliphsu.clock2.model.AlarmsTableManager;
import com.philliphsu.clock2.util.AlarmController;
/**
* Created by Phillip Hsu on 7/1/2016.
*
* TODO: Generify this class for any item.
* TODO: Rename to AsyncAlarmChangeHandler
* TODO: Consider making an AsyncDatabaseChangeHandlerWithSnackbar abstract class
*/
public final class AsyncItemChangeHandler {
public final class AsyncItemChangeHandler extends AsyncDatabaseChangeHandler<Alarm, AlarmsTableManager> {
private static final String TAG = "AsyncItemChangeHandler";
private final Context mContext;
private final View mSnackbarAnchor;
private final ScrollHandler mScrollHandler;
private final AlarmController mAlarmController;
/**
@ -29,108 +27,41 @@ public final class AsyncItemChangeHandler {
public AsyncItemChangeHandler(Context context, View snackbarAnchor,
ScrollHandler scrollHandler,
AlarmController alarmController) {
mContext = context.getApplicationContext(); // to prevent memory leaks
super(context, scrollHandler);
mSnackbarAnchor = snackbarAnchor;
mScrollHandler = scrollHandler;
mAlarmController = alarmController;
}
public void asyncAddAlarm(final Alarm alarm) {
new InsertAlarmAsyncTask(alarm).execute();
@Override
protected AlarmsTableManager getTableManager(Context context) {
return new AlarmsTableManager(context);
}
/**
* We only need one Alarm param because we called newAlarm.setId(oldAlarm.id())
* when we were in the edit activity.
* TODO: Consider changing the signature of updateAlarm() in DatabaseManager and
* AlarmDatabaseHelper to only require one Alarm param.
*/
public void asyncUpdateAlarm(final Alarm newAlarm) {
new UpdateAlarmAsyncTask(newAlarm).execute();
}
public void asyncRemoveAlarm(final Alarm alarm) {
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... params) {
return DatabaseManager.getInstance(mContext).deleteAlarm(alarm);
}
@Override
protected void onPostExecute(Integer integer) {
mAlarmController.cancelAlarm(alarm, false);
if (mSnackbarAnchor != null) {
// TODO: Consider adding delay to allow the alarm item animation
// to finish first before we show the snackbar. Inbox app does this.
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) {
asyncAddAlarm(alarm);
}
}).show();
}
}
}.execute();
}
////////////////////////////////////////////////////////////
// Insert and update AsyncTasks
////////////////////////////////////////////////////////////
/**
* Created because the code in insert and update AsyncTasks are exactly the same.
*/
private abstract class BaseAsyncTask extends AsyncTask<Void, Void, Long> {
private final Alarm mAlarm;
BaseAsyncTask(Alarm alarm) {
mAlarm = alarm;
}
@Override
protected void onPostExecute(Long result) {
@Override
protected void onPostAsyncDelete(Integer result, final Alarm alarm) {
mAlarmController.cancelAlarm(alarm, false);
if (mSnackbarAnchor != null) {
// TODO: Consider adding delay to allow the alarm item animation
// to finish first before we show the snackbar. Inbox app does this.
mAlarmController.scheduleAlarm(mAlarm, true);
if (mScrollHandler != null) {
// Prepare to scroll to this alarm
mScrollHandler.setScrollToStableId(result);
}
}
final Long insertAlarm() {
return DatabaseManager.getInstance(mContext).insertAlarm(mAlarm);
}
final Long updateAlarm() {
long id = mAlarm.id();
DatabaseManager.getInstance(mContext).updateAlarm(id, mAlarm);
return id;
String message = getContext().getString(R.string.snackbar_item_deleted,
getContext().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) {
asyncInsert(alarm);
}
}).show();
}
}
private class InsertAlarmAsyncTask extends BaseAsyncTask {
InsertAlarmAsyncTask(Alarm alarm) {
super(alarm);
}
@Override
protected Long doInBackground(Void... params) {
return insertAlarm();
}
@Override
protected void onPostAsyncInsert(Long result, Alarm alarm) {
mAlarmController.scheduleAlarm(alarm, true);
}
private class UpdateAlarmAsyncTask extends BaseAsyncTask {
UpdateAlarmAsyncTask(Alarm alarm) {
super(alarm);
}
@Override
protected Long doInBackground(Void... params) {
return updateAlarm();
}
@Override
protected void onPostAsyncUpdate(Long result, Alarm alarm) {
mAlarmController.scheduleAlarm(alarm, true);
}
}

View File

@ -18,7 +18,8 @@ import butterknife.Bind;
/**
* Created by Phillip Hsu on 7/26/2016.
*/
public abstract class RecyclerViewFragment<T extends ObjectWithId,
public abstract class RecyclerViewFragment<
T extends ObjectWithId,
VH extends BaseViewHolder<T>,
C extends BaseItemCursor<T>,
A extends BaseCursorAdapter<T, VH, C>>

View File

@ -138,7 +138,7 @@ public class AlarmsFragment extends RecyclerViewFragment<
if (data.getBooleanExtra(EditAlarmActivity.EXTRA_IS_DELETING, false)) {
// TODO: Should we delay this too? It seems animations run
// some of the time.
mAsyncItemChangeHandler.asyncRemoveAlarm(alarm);
mAsyncItemChangeHandler.asyncDelete(alarm);
} else {
// TODO: Increase the delay, because update animation is
// more elusive than insert.
@ -206,15 +206,15 @@ public class AlarmsFragment extends RecyclerViewFragment<
}
void asyncAddAlarm() {
mAsyncItemChangeHandler.asyncAddAlarm(mAlarm);
mAsyncItemChangeHandler.asyncInsert(mAlarm);
}
void asyncUpdateAlarm() {
mAsyncItemChangeHandler.asyncUpdateAlarm(mAlarm);
mAsyncItemChangeHandler.asyncUpdate(mAlarm.getId(), mAlarm);
}
void asyncRemoveAlarm() {
mAsyncItemChangeHandler.asyncRemoveAlarm(mAlarm);
mAsyncItemChangeHandler.asyncDelete(mAlarm);
}
}

View File

@ -9,7 +9,6 @@ import com.philliphsu.clock2.RecyclerViewFragment;
import com.philliphsu.clock2.Timer;
import com.philliphsu.clock2.edittimer.EditTimerActivity;
import com.philliphsu.clock2.model.TimerCursor;
import com.philliphsu.clock2.model.TimerDatabaseHelper;
import com.philliphsu.clock2.model.TimersListCursorLoader;
public class TimersFragment extends RecyclerViewFragment<
@ -23,8 +22,6 @@ public class TimersFragment extends RecyclerViewFragment<
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// if (resultCode != Activity.RESULT_OK || data == null)
// return;
TimerDatabaseHelper db = new TimerDatabaseHelper(getActivity());
db.insertItem(Timer.create(1, 0, 0));
}
@Override