Item animations working
This commit is contained in:
parent
4eec10f86a
commit
9fb4727051
@ -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<Void, Void, Long>() {
|
||||
@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<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);
|
||||
}
|
||||
|
||||
@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<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));
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -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<T> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,12 @@ public class AlarmsAdapter extends BaseAdapter<Alarm, AlarmViewHolder> {
|
||||
|
||||
public AlarmsAdapter(List<Alarm> alarms, OnListItemInteractionListener<Alarm> listener) {
|
||||
super(Alarm.class, alarms, listener);
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).id();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -22,7 +22,14 @@ public class AlarmsCursorAdapter extends RecyclerView.Adapter<AlarmViewHolder> {
|
||||
|
||||
public AlarmsCursorAdapter(OnListItemInteractionListener<Alarm> 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
|
||||
|
||||
@ -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 LoaderCallbacks<List<Ala
|
||||
public static final int REQUEST_CREATE_ALARM = 1;
|
||||
private static final String TAG = "AlarmsFragment";
|
||||
|
||||
private AsyncRecyclerViewItemChangeHandler mAsyncRecyclerViewItemChangeHandler;
|
||||
private AlarmsAdapter mAdapter;
|
||||
|
||||
@Bind(R.id.list) RecyclerView mList;
|
||||
@ -79,6 +82,9 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
||||
mList.setLayoutManager(new LinearLayoutManager(context));
|
||||
mAdapter = new AlarmsAdapter(Collections.<Alarm>emptyList(), 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<List<Ala
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CREATE_ALARM:
|
||||
// TODO: notifyItemInserted?
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
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);
|
||||
break;
|
||||
case REQUEST_EDIT_ALARM:
|
||||
Alarm deletedAlarm;
|
||||
if (data != null && (deletedAlarm = data.getParcelableExtra(
|
||||
EditAlarmActivity.EXTRA_DELETED_ALARM)) != null) {
|
||||
EditAlarmActivity.EXTRA_MODIFIED_ALARM)) != null) {
|
||||
onListItemDeleted(deletedAlarm);
|
||||
}
|
||||
// TODO: notifyItemRemoved?
|
||||
@ -153,6 +166,7 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
||||
// TODO: This doesn't need to be defined in the interface.
|
||||
// TODO: Rename to showDeletedSnackbar() or something
|
||||
// TODO: This needs to prompt a reload of the list.
|
||||
@Deprecated // TODO: Delete this method.
|
||||
@Override
|
||||
public void onListItemDeleted(final Alarm item) {
|
||||
Snackbar.make(getActivity().findViewById(R.id.main_content),
|
||||
|
||||
@ -58,7 +58,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
||||
LoaderManager.LoaderCallbacks<Alarm> {
|
||||
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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user