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) {
|
public AlarmsAdapter(List<Alarm> alarms, OnListItemInteractionListener<Alarm> listener) {
|
||||||
super(Alarm.class, alarms, listener);
|
super(Alarm.class, alarms, listener);
|
||||||
|
setHasStableIds(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return getItem(position).id();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -22,7 +22,14 @@ public class AlarmsCursorAdapter extends RecyclerView.Adapter<AlarmViewHolder> {
|
|||||||
|
|
||||||
public AlarmsCursorAdapter(OnListItemInteractionListener<Alarm> listener) {
|
public AlarmsCursorAdapter(OnListItemInteractionListener<Alarm> listener) {
|
||||||
mListener = 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
|
@Override
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||||
@ -16,6 +17,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.philliphsu.clock2.Alarm;
|
import com.philliphsu.clock2.Alarm;
|
||||||
|
import com.philliphsu.clock2.AsyncRecyclerViewItemChangeHandler;
|
||||||
import com.philliphsu.clock2.OnListItemInteractionListener;
|
import com.philliphsu.clock2.OnListItemInteractionListener;
|
||||||
import com.philliphsu.clock2.R;
|
import com.philliphsu.clock2.R;
|
||||||
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
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;
|
public static final int REQUEST_CREATE_ALARM = 1;
|
||||||
private static final String TAG = "AlarmsFragment";
|
private static final String TAG = "AlarmsFragment";
|
||||||
|
|
||||||
|
private AsyncRecyclerViewItemChangeHandler mAsyncRecyclerViewItemChangeHandler;
|
||||||
private AlarmsAdapter mAdapter;
|
private AlarmsAdapter mAdapter;
|
||||||
|
|
||||||
@Bind(R.id.list) RecyclerView mList;
|
@Bind(R.id.list) RecyclerView mList;
|
||||||
@ -79,6 +82,9 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
|||||||
mList.setLayoutManager(new LinearLayoutManager(context));
|
mList.setLayoutManager(new LinearLayoutManager(context));
|
||||||
mAdapter = new AlarmsAdapter(Collections.<Alarm>emptyList(), this);
|
mAdapter = new AlarmsAdapter(Collections.<Alarm>emptyList(), this);
|
||||||
mList.setAdapter(mAdapter);
|
mList.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
mAsyncRecyclerViewItemChangeHandler = new AsyncRecyclerViewItemChangeHandler(
|
||||||
|
mAdapter, getActivity().findViewById(R.id.main_content));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,12 +132,19 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<List<Ala
|
|||||||
|
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case REQUEST_CREATE_ALARM:
|
case REQUEST_CREATE_ALARM:
|
||||||
// TODO: notifyItemInserted?
|
Log.d(TAG, "Async add alarm");
|
||||||
getLoaderManager().restartLoader(0, null, this);
|
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:
|
case REQUEST_EDIT_ALARM:
|
||||||
Alarm deletedAlarm;
|
Alarm deletedAlarm;
|
||||||
if (data != null && (deletedAlarm = data.getParcelableExtra(
|
if (data != null && (deletedAlarm = data.getParcelableExtra(
|
||||||
EditAlarmActivity.EXTRA_DELETED_ALARM)) != null) {
|
EditAlarmActivity.EXTRA_MODIFIED_ALARM)) != null) {
|
||||||
onListItemDeleted(deletedAlarm);
|
onListItemDeleted(deletedAlarm);
|
||||||
}
|
}
|
||||||
// TODO: notifyItemRemoved?
|
// 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: This doesn't need to be defined in the interface.
|
||||||
// TODO: Rename to showDeletedSnackbar() or something
|
// TODO: Rename to showDeletedSnackbar() or something
|
||||||
// TODO: This needs to prompt a reload of the list.
|
// TODO: This needs to prompt a reload of the list.
|
||||||
|
@Deprecated // TODO: Delete this method.
|
||||||
@Override
|
@Override
|
||||||
public void onListItemDeleted(final Alarm item) {
|
public void onListItemDeleted(final Alarm item) {
|
||||||
Snackbar.make(getActivity().findViewById(R.id.main_content),
|
Snackbar.make(getActivity().findViewById(R.id.main_content),
|
||||||
|
|||||||
@ -58,7 +58,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
|||||||
LoaderManager.LoaderCallbacks<Alarm> {
|
LoaderManager.LoaderCallbacks<Alarm> {
|
||||||
private static final String TAG = "EditAlarmActivity";
|
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_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 RelativeSizeSpan AMPM_SIZE_SPAN = new RelativeSizeSpan(0.5f);
|
||||||
|
|
||||||
private static final int REQUEST_PICK_RINGTONE = 0;
|
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));
|
alarm.setRecurring(i, isRecurringDay(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
if (mOldAlarm != null) {
|
if (mOldAlarm != null) {
|
||||||
if (mOldAlarm.isEnabled()) {
|
if (mOldAlarm.isEnabled()) {
|
||||||
Log.d(TAG, "Cancelling old alarm first");
|
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?
|
// TODO: Do this in the background. AsyncTask?
|
||||||
mDatabaseManager.updateAlarm(mOldAlarm.id(), alarm);
|
mDatabaseManager.updateAlarm(mOldAlarm.id(), alarm);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Do this in the background. AsyncTask?
|
//mDatabaseManager.insertAlarm(alarm);
|
||||||
mDatabaseManager.insertAlarm(alarm);
|
intent.putExtra(EXTRA_MODIFIED_ALARM, alarm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alarm.isEnabled()) {
|
if (alarm.isEnabled()) {
|
||||||
scheduleAlarm(alarm);
|
//scheduleAlarm(alarm);
|
||||||
}
|
}
|
||||||
|
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK, intent);
|
||||||
showEditorClosed();
|
showEditorClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +269,7 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
|||||||
}
|
}
|
||||||
mDatabaseManager.deleteAlarm(mOldAlarm);
|
mDatabaseManager.deleteAlarm(mOldAlarm);
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.putExtra(EXTRA_DELETED_ALARM, mOldAlarm);
|
intent.putExtra(EXTRA_MODIFIED_ALARM, mOldAlarm);
|
||||||
setResult(RESULT_OK, intent);
|
setResult(RESULT_OK, intent);
|
||||||
}
|
}
|
||||||
showEditorClosed();
|
showEditorClosed();
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public class DatabaseManager {
|
|||||||
|
|
||||||
private static DatabaseManager sDatabaseManager;
|
private static DatabaseManager sDatabaseManager;
|
||||||
private final Context mContext;
|
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) {
|
private DatabaseManager(Context context) {
|
||||||
mContext = context.getApplicationContext();
|
mContext = context.getApplicationContext();
|
||||||
@ -28,10 +28,8 @@ public class DatabaseManager {
|
|||||||
return sDatabaseManager;
|
return sDatabaseManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: why return an Alarm?
|
public long insertAlarm(Alarm alarm) {
|
||||||
public Alarm insertAlarm(Alarm alarm) {
|
return mHelper.insertAlarm(alarm);
|
||||||
mHelper.insertAlarm(alarm);
|
|
||||||
return alarm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user