From cf46160b1eb823d1fd465dbcc54ea8f84d85c6f2 Mon Sep 17 00:00:00 2001 From: Phillip Hsu Date: Wed, 6 Jul 2016 00:40:52 -0700 Subject: [PATCH] Local broadcast receiver used as observer in loader --- ...ndler.java => AsyncItemChangeHandler.java} | 16 ++----- .../clock2/alarms/AlarmsFragment.java | 43 ++++++------------- .../clock2/editalarm/EditAlarmActivity.java | 8 +++- .../clock2/model/AlarmDatabaseHelper.java | 25 +++++++++-- .../clock2/model/SQLiteCursorLoader.java | 32 ++++++++++++++ .../clock2/util/LocalBroadcastHelper.java | 13 ++++++ 6 files changed, 92 insertions(+), 45 deletions(-) rename app/src/main/java/com/philliphsu/clock2/{AsyncRecyclerViewItemChangeHandler.java => AsyncItemChangeHandler.java} (83%) diff --git a/app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java b/app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java similarity index 83% rename from app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java rename to app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java index c44356a..49b1649 100644 --- a/app/src/main/java/com/philliphsu/clock2/AsyncRecyclerViewItemChangeHandler.java +++ b/app/src/main/java/com/philliphsu/clock2/AsyncItemChangeHandler.java @@ -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() { @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() { @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)); 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 4bcbc6e..721711f 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java @@ -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>, +public class AlarmsFragment extends Fragment implements LoaderCallbacks, OnListItemInteractionListener { 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 LoaderCallbacksemptyList(), 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> onCreateLoader(int id, Bundle args) { - return new AlarmListLoader(getActivity()); + public Loader onCreateLoader(int id, Bundle args) { + return new AlarmsListCursorLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, List data) { - mAdapter.replaceData(data); + public void onLoadFinished(Loader loader, Cursor data) { + mAdapter.swapCursor(data); } @Override - public void onLoaderReset(Loader> 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.emptyList()); + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); } @Override @@ -132,14 +122,9 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks { 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 { 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(); + } + } } diff --git a/app/src/main/java/com/philliphsu/clock2/util/LocalBroadcastHelper.java b/app/src/main/java/com/philliphsu/clock2/util/LocalBroadcastHelper.java index dfbdc79..a2e7478 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/LocalBroadcastHelper.java +++ b/app/src/main/java/com/philliphsu/clock2/util/LocalBroadcastHelper.java @@ -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() {} }