Local broadcast receiver used as observer in loader
This commit is contained in:
parent
9fb4727051
commit
cf46160b1e
@ -5,7 +5,6 @@ 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;
|
||||
|
||||
@ -14,18 +13,16 @@ import com.philliphsu.clock2.util.AlarmUtils;
|
||||
*
|
||||
* TODO: Generify this class for any item.
|
||||
*/
|
||||
public final class AsyncRecyclerViewItemChangeHandler {
|
||||
public final class AsyncItemChangeHandler {
|
||||
|
||||
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;
|
||||
public AsyncItemChangeHandler(Context context, View snackbarAnchor) {
|
||||
mContext = context.getApplicationContext(); // to prevent memory leaks
|
||||
mSnackbarAnchor = snackbarAnchor;
|
||||
}
|
||||
|
||||
@ -39,7 +36,7 @@ public final class AsyncRecyclerViewItemChangeHandler {
|
||||
@Override
|
||||
protected void onPostExecute(Long aLong) {
|
||||
// TODO: Snackbar/Toast here? If so, remove the code in AlarmUtils.scheduleAlarm() that does it.
|
||||
mAdapter.addItem(alarm);
|
||||
// Then, consider scheduling the alarm in the background.
|
||||
AlarmUtils.scheduleAlarm(mContext, alarm, true);
|
||||
}
|
||||
}.execute();
|
||||
@ -49,8 +46,6 @@ public final class AsyncRecyclerViewItemChangeHandler {
|
||||
new AsyncTask<Void, Void, Integer>() {
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
mAdapter.updateItem(oldAlarm, newAlarm);
|
||||
// Save the item to the db
|
||||
return DatabaseManager.getInstance(mContext).updateAlarm(oldAlarm.id(), newAlarm);
|
||||
}
|
||||
|
||||
@ -65,14 +60,11 @@ public final class AsyncRecyclerViewItemChangeHandler {
|
||||
new AsyncTask<Void, Void, Integer>() {
|
||||
@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));
|
||||
@ -3,8 +3,8 @@ package com.philliphsu.clock2.alarms;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
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;
|
||||
@ -17,30 +17,25 @@ 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;
|
||||
import com.philliphsu.clock2.model.AlarmListLoader;
|
||||
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
|
||||
import com.philliphsu.clock2.model.DatabaseManager;
|
||||
import com.philliphsu.clock2.util.AlarmUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
// TODO: Use native fragments since we're targeting API >=19?
|
||||
// TODO: Use native LoaderCallbacks.
|
||||
public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Alarm>>,
|
||||
public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
||||
OnListItemInteractionListener<Alarm> {
|
||||
private static final int REQUEST_EDIT_ALARM = 0;
|
||||
public static final int REQUEST_CREATE_ALARM = 1;
|
||||
private static final String TAG = "AlarmsFragment";
|
||||
|
||||
private AsyncRecyclerViewItemChangeHandler mAsyncRecyclerViewItemChangeHandler;
|
||||
private AlarmsAdapter mAdapter;
|
||||
private AlarmsCursorAdapter mAdapter;
|
||||
|
||||
@Bind(R.id.list) RecyclerView mList;
|
||||
|
||||
@ -80,11 +75,8 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
||||
// Set the adapter
|
||||
Context context = view.getContext();
|
||||
mList.setLayoutManager(new LinearLayoutManager(context));
|
||||
mAdapter = new AlarmsAdapter(Collections.<Alarm>emptyList(), this);
|
||||
mAdapter = new AlarmsCursorAdapter(this);
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
mAsyncRecyclerViewItemChangeHandler = new AsyncRecyclerViewItemChangeHandler(
|
||||
mAdapter, getActivity().findViewById(R.id.main_content));
|
||||
return view;
|
||||
}
|
||||
|
||||
@ -107,20 +99,18 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<List<Alarm>> onCreateLoader(int id, Bundle args) {
|
||||
return new AlarmListLoader(getActivity());
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new AlarmsListCursorLoader(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<Alarm>> loader, List<Alarm> data) {
|
||||
mAdapter.replaceData(data);
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
mAdapter.swapCursor(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<Alarm>> loader) {
|
||||
// Can't pass in null, because replaceData() will try to add all the elements
|
||||
// from the given collection, so we would run into an NPE.
|
||||
mAdapter.replaceData(Collections.<Alarm>emptyList());
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
mAdapter.swapCursor(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -132,14 +122,9 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CREATE_ALARM:
|
||||
Log.d(TAG, "Async add alarm");
|
||||
final Alarm createdAlarm = data.getParcelableExtra(EditAlarmActivity.EXTRA_MODIFIED_ALARM);
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAsyncRecyclerViewItemChangeHandler.asyncAddAlarm(createdAlarm);
|
||||
}
|
||||
}, 1000);
|
||||
// TODO: Should we still do the async add here?
|
||||
// We must if we want the async handler to post the toast/snackbar
|
||||
// for us.
|
||||
break;
|
||||
case REQUEST_EDIT_ALARM:
|
||||
Alarm deletedAlarm;
|
||||
|
||||
@ -26,6 +26,7 @@ import android.widget.Toast;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.AsyncItemChangeHandler;
|
||||
import com.philliphsu.clock2.BaseActivity;
|
||||
import com.philliphsu.clock2.DaysOfWeek;
|
||||
import com.philliphsu.clock2.R;
|
||||
@ -68,6 +69,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
||||
private Uri mSelectedRingtoneUri;
|
||||
private Alarm mOldAlarm;
|
||||
private DatabaseManager mDatabaseManager;
|
||||
private AsyncItemChangeHandler mAsyncItemChangeHandler;
|
||||
|
||||
@Bind(R.id.save) Button mSave;
|
||||
@Bind(R.id.delete) Button mDelete;
|
||||
@ -98,6 +100,8 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
||||
showDetails();
|
||||
}
|
||||
setTimeTextHint(); // TODO: private access
|
||||
|
||||
mAsyncItemChangeHandler = new AsyncItemChangeHandler(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -246,13 +250,15 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
||||
mDatabaseManager.updateAlarm(mOldAlarm.id(), alarm);
|
||||
} else {
|
||||
//mDatabaseManager.insertAlarm(alarm);
|
||||
intent.putExtra(EXTRA_MODIFIED_ALARM, alarm);
|
||||
mAsyncItemChangeHandler.asyncAddAlarm(alarm);
|
||||
//intent.putExtra(EXTRA_MODIFIED_ALARM, alarm);
|
||||
}
|
||||
|
||||
if (alarm.isEnabled()) {
|
||||
//scheduleAlarm(alarm);
|
||||
}
|
||||
|
||||
// TODO: Remove intent
|
||||
setResult(RESULT_OK, intent);
|
||||
showEditorClosed();
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||
|
||||
import static com.philliphsu.clock2.DaysOfWeek.FRIDAY;
|
||||
import static com.philliphsu.clock2.DaysOfWeek.MONDAY;
|
||||
@ -65,8 +66,13 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String SORT_ORDER =
|
||||
COLUMN_RING_TIME_MILLIS + " ASC, " + COLUMN_ID + " ASC";
|
||||
|
||||
private final Context mAppContext;
|
||||
|
||||
public AlarmDatabaseHelper(Context context) {
|
||||
super(context, DB_NAME, null, VERSION_1);
|
||||
// Since DatabaseManager calls this with the application
|
||||
// context, we can safely hold onto this context.
|
||||
mAppContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -105,6 +111,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||
long id = getWritableDatabase().insert(TABLE_ALARMS,
|
||||
null, toContentValues(alarm));
|
||||
alarm.setId(id);
|
||||
notifyContentChanged();
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -115,26 +122,32 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||
public int updateAlarm(Alarm oldAlarm, Alarm newAlarm) {
|
||||
newAlarm.setId(oldAlarm.id());
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
return db.update(TABLE_ALARMS,
|
||||
int rowsUpdated = db.update(TABLE_ALARMS,
|
||||
toContentValues(newAlarm),
|
||||
COLUMN_ID + " = " + newAlarm.id(),
|
||||
null);
|
||||
notifyContentChanged();
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
public int updateAlarm(long id, Alarm newAlarm) {
|
||||
newAlarm.setId(id);
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
return db.update(TABLE_ALARMS,
|
||||
int rowsUpdated = db.update(TABLE_ALARMS,
|
||||
toContentValues(newAlarm),
|
||||
COLUMN_ID + " = " + id,
|
||||
null);
|
||||
notifyContentChanged();
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
public int deleteAlarm(Alarm alarm) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
return db.delete(TABLE_ALARMS,
|
||||
int rowsDeleted = db.delete(TABLE_ALARMS,
|
||||
COLUMN_ID + " = " + alarm.id(),
|
||||
null);
|
||||
notifyContentChanged();
|
||||
return rowsDeleted;
|
||||
}
|
||||
|
||||
public AlarmCursor queryAlarm(long id) {
|
||||
@ -184,6 +197,12 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||
return values;
|
||||
}
|
||||
|
||||
private void notifyContentChanged() {
|
||||
Log.d(TAG, "notifyContentChanged()");
|
||||
LocalBroadcastHelper.sendBroadcast(mAppContext,
|
||||
SQLiteCursorLoader.ACTION_CHANGE_CONTENT);
|
||||
}
|
||||
|
||||
// An alternative method to creating an Alarm from a cursor is to
|
||||
// make an Alarm constructor that takes an Cursor param. However,
|
||||
// this method has the advantage of keeping all the constants
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
package com.philliphsu.clock2.model;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.util.Log;
|
||||
|
||||
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 6/28/2016.
|
||||
@ -12,7 +17,10 @@ import android.support.v4.content.AsyncTaskLoader;
|
||||
public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
|
||||
private static final String TAG = "SQLiteCursorLoader";
|
||||
|
||||
public static final String ACTION_CHANGE_CONTENT = "com.philliphsu.clock2.model.action.CHANGE_CONTENT";
|
||||
|
||||
private Cursor mCursor;
|
||||
private OnContentChangeReceiver mOnContentChangeReceiver;
|
||||
|
||||
public SQLiteCursorLoader(Context context) {
|
||||
super(context);
|
||||
@ -66,30 +74,54 @@ public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
|
||||
if (mCursor != null) {
|
||||
deliverResult(mCursor);
|
||||
}
|
||||
|
||||
if (mOnContentChangeReceiver == null) {
|
||||
mOnContentChangeReceiver = new OnContentChangeReceiver();
|
||||
LocalBroadcastHelper.registerReceiver(getContext(),
|
||||
mOnContentChangeReceiver, ACTION_CHANGE_CONTENT);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || mCursor == null) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopLoading() {
|
||||
// Attempt to cancel the current load task if possible.
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(Cursor cursor) {
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
// Ensure the loader is stopped
|
||||
onStopLoading();
|
||||
|
||||
if (mCursor != null && !mCursor.isClosed()) {
|
||||
mCursor.close();
|
||||
}
|
||||
mCursor = null;
|
||||
|
||||
if (mOnContentChangeReceiver != null) {
|
||||
LocalBroadcastHelper.unregisterReceiver(getContext(),
|
||||
mOnContentChangeReceiver);
|
||||
mOnContentChangeReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
private final class OnContentChangeReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "Received content change event");
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.philliphsu.clock2.util;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
/**
|
||||
@ -14,5 +16,16 @@ public final class LocalBroadcastHelper {
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(action));
|
||||
}
|
||||
|
||||
/** Registers a BroadcastReceiver that filters intents by the action specified */
|
||||
// TODO: Refactor any local registrations to use this utility method.
|
||||
public static void registerReceiver(Context context, BroadcastReceiver receiver, String action) {
|
||||
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, new IntentFilter(action));
|
||||
}
|
||||
|
||||
// TODO: Refactor any local unregistrations to use this utility method.
|
||||
public static void unregisterReceiver(Context context, BroadcastReceiver receiver) {
|
||||
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
private LocalBroadcastHelper() {}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user