Scheduling alarm via AlarmUtils no longer shows Toast confirmation for you
This commit is contained in:
parent
4436d5852a
commit
08e12cd14f
@ -32,23 +32,7 @@ public final class AsyncItemChangeHandler {
|
||||
}
|
||||
|
||||
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.
|
||||
// Then, consider scheduling the alarm in the background.
|
||||
AlarmUtils.scheduleAlarm(mContext, alarm, true);
|
||||
if (mScrollHandler != null) {
|
||||
// Prepare to scroll to the newly added alarm
|
||||
mScrollHandler.setScrollToStableId(aLong);
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
new InsertAlarmAsyncTask(alarm).execute();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,37 +40,9 @@ public final class AsyncItemChangeHandler {
|
||||
* when we were in the edit activity.
|
||||
* TODO: Consider changing the signature of updateAlarm() in DatabaseManager and
|
||||
* AlarmDatabaseHelper to only require one Alarm param.
|
||||
* TODO: The AsyncTask employed here is very similar to the one employed in
|
||||
* asyncAddAlarm(). Figure out a way to refactor the code in common. Possible
|
||||
* starts are to:
|
||||
* * Change the Result type to Long, and then the onPostExecute() can be
|
||||
* expressed the same between the two methods.
|
||||
* * Similar to what you did in AlarmsFragment with the static
|
||||
* inner Runnables, write a static inner abstract class that extends
|
||||
* AsyncTask that takes in an Alarm; leave doInBackground() unimplemented
|
||||
* in this base class. Then, define methods in this base class that subclasses
|
||||
* can call to do their desired CRUD task in their doInBackground().
|
||||
*/
|
||||
public void asyncUpdateAlarm(final Alarm newAlarm) {
|
||||
new AsyncTask<Void, Void, Integer>() {
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
return DatabaseManager.getInstance(mContext).updateAlarm(newAlarm.id(), newAlarm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer integer) {
|
||||
// TODO: Snackbar/Toast here? If so, remove the code in AlarmUtils.scheduleAlarm() that does it.
|
||||
AlarmUtils.scheduleAlarm(mContext, newAlarm, true);
|
||||
if (mScrollHandler != null) {
|
||||
// The new alarm could have a different sort order from the old alarm.
|
||||
// TODO: Sometimes this won't scrolls to the new alarm if the old alarm is
|
||||
// towards the bottom and the new alarm is ordered towards the top. This
|
||||
// may have something to do with us breaking the stable id guarantee?
|
||||
mScrollHandler.setScrollToStableId(newAlarm.id());
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
new UpdateAlarmAsyncTask(newAlarm).execute();
|
||||
}
|
||||
|
||||
public void asyncRemoveAlarm(final Alarm alarm) {
|
||||
@ -99,6 +55,8 @@ public final class AsyncItemChangeHandler {
|
||||
@Override
|
||||
protected void onPostExecute(Integer integer) {
|
||||
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)
|
||||
@ -112,4 +70,66 @@ public final class AsyncItemChangeHandler {
|
||||
}
|
||||
}.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) {
|
||||
AlarmUtils.scheduleAlarm(mContext, mAlarm, true);
|
||||
if (mScrollHandler != null) {
|
||||
// Prepare to scroll to this alarm
|
||||
mScrollHandler.setScrollToStableId(result);
|
||||
}
|
||||
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 = AlarmUtils.getRingsInText(mContext, mAlarm.ringsIn());
|
||||
AlarmUtils.showSnackbar(mSnackbarAnchor, message);
|
||||
}
|
||||
}
|
||||
|
||||
final Long insertAlarm() {
|
||||
return DatabaseManager.getInstance(mContext).insertAlarm(mAlarm);
|
||||
}
|
||||
|
||||
final Long updateAlarm() {
|
||||
long id = mAlarm.id();
|
||||
DatabaseManager.getInstance(mContext).updateAlarm(id, mAlarm);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
private class InsertAlarmAsyncTask extends BaseAsyncTask {
|
||||
InsertAlarmAsyncTask(Alarm alarm) {
|
||||
super(alarm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long doInBackground(Void... params) {
|
||||
return insertAlarm();
|
||||
}
|
||||
}
|
||||
|
||||
private class UpdateAlarmAsyncTask extends BaseAsyncTask {
|
||||
UpdateAlarmAsyncTask(Alarm alarm) {
|
||||
super(alarm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long doInBackground(Void... params) {
|
||||
return updateAlarm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,6 +128,7 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
|
||||
if (alarm.isEnabled()) {
|
||||
// TODO: On Moto X, upcoming notification doesn't post immediately
|
||||
AlarmUtils.scheduleAlarm(getContext(), alarm, true);
|
||||
AlarmUtils.sendShowSnackbarBroadcast(getContext(), AlarmUtils.getRingsInText(getContext(), alarm.ringsIn()));
|
||||
AlarmUtils.save(getContext(), alarm);
|
||||
} else {
|
||||
AlarmUtils.cancelAlarm(getContext(), alarm, true);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.philliphsu.clock2.alarms;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
@ -22,6 +23,8 @@ import com.philliphsu.clock2.OnListItemInteractionListener;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.editalarm.EditAlarmActivity;
|
||||
import com.philliphsu.clock2.model.AlarmsListCursorLoader;
|
||||
import com.philliphsu.clock2.util.AlarmUtils;
|
||||
import com.philliphsu.clock2.util.LocalBroadcastHelper;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
@ -34,10 +37,16 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
||||
private static final int REQUEST_EDIT_ALARM = 0;
|
||||
// Public because MainActivity needs to use it.
|
||||
public static final int REQUEST_CREATE_ALARM = 1;
|
||||
/**
|
||||
* Local broadcast senders can tell us to show a snackbar with a message on their behalf.
|
||||
*/
|
||||
public static final String ACTION_SHOW_SNACKBAR_MSG = "com.philliphsu.clock2.alarms.action.SHOW_SNACKBAR_MSG";
|
||||
public static final String EXTRA_MSG = "com.philliphsu.clock2.alarms.extra.MSG";
|
||||
|
||||
private AlarmsCursorAdapter mAdapter;
|
||||
private AsyncItemChangeHandler mAsyncItemChangeHandler;
|
||||
private Handler mHandler = new Handler();
|
||||
private View mSnackbarAnchor;
|
||||
private long mScrollToStableId = RecyclerView.NO_ID;
|
||||
|
||||
@Bind(R.id.list) RecyclerView mList;
|
||||
@ -66,6 +75,10 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
||||
// TODO Read arguments
|
||||
}
|
||||
|
||||
// Will succeed because the activity is created at this point.
|
||||
// See the Fragment lifecycle.
|
||||
mSnackbarAnchor = getActivity().findViewById(R.id.main_content);
|
||||
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@ -80,14 +93,27 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
||||
mAdapter = new AlarmsCursorAdapter(this);
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
mAsyncItemChangeHandler = new AsyncItemChangeHandler(getActivity(),
|
||||
getActivity().findViewById(R.id.main_content), this);
|
||||
mAsyncItemChangeHandler = new AsyncItemChangeHandler(
|
||||
getActivity(), mSnackbarAnchor, this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
LocalBroadcastHelper.registerReceiver(getActivity(),
|
||||
mShowSnackbarReceiver, ACTION_SHOW_SNACKBAR_MSG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
// This will always be called when we leave this screen, either by exiting the app or
|
||||
// by navigating elsewhere. Since we unregister the receiver here, we will never receive
|
||||
// a "show alarm snoozed" broadcast, because the snooze action is always made elsewhere
|
||||
// in the app.
|
||||
super.onStop();
|
||||
Log.e(TAG, "onStop()");
|
||||
LocalBroadcastHelper.unregisterReceiver(getActivity(), mShowSnackbarReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -195,6 +221,19 @@ public class AlarmsFragment extends Fragment implements LoaderCallbacks<Cursor>,
|
||||
// TODO: Delete this method.
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mShowSnackbarReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// See Intent#putExtras(Bundle):
|
||||
// Putting a Bundle of extras into an intent will have its
|
||||
// contents added to the intent's collection of extras,
|
||||
// so we can individually retrieve the Bundle's extras
|
||||
// directly from the intent.
|
||||
String message = intent.getStringExtra(EXTRA_MSG);
|
||||
AlarmUtils.showSnackbar(mSnackbarAnchor, message);
|
||||
}
|
||||
};
|
||||
|
||||
private static abstract class BaseAsyncItemChangeRunnable {
|
||||
// TODO: Will holding onto this cause a memory leak?
|
||||
private final AsyncItemChangeHandler mAsyncItemChangeHandler;
|
||||
|
||||
@ -511,9 +511,11 @@ public class EditAlarmActivity extends BaseActivity implements AlarmNumpad.KeyLi
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Delete this
|
||||
@Deprecated
|
||||
@Override
|
||||
public void scheduleAlarm(Alarm alarm) {
|
||||
AlarmUtils.scheduleAlarm(this, alarm, true);
|
||||
//AlarmUtils.scheduleAlarm(this, alarm, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -64,6 +64,11 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
// First sort by ring time in ascending order (smaller values first),
|
||||
// then break ties by sorting by id in ascending order.
|
||||
// TODO: Consider changing the sort order to hour ASC, minutes ASC, enabled DESC. Then, we can
|
||||
// delete the COLUMN_RING_TIME_MILLIS.
|
||||
// As defined now, the ordering can be confusing; some examples are:
|
||||
// * If there are multiple single-use alarms in the list, and one of them is snoozed, then on the
|
||||
// next cursor load, this alarm will be reordered to the very bottom
|
||||
private static final String SORT_ORDER =
|
||||
COLUMN_RING_TIME_MILLIS + " ASC, " + COLUMN_ID + " ASC";
|
||||
|
||||
|
||||
@ -4,15 +4,19 @@ import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.philliphsu.clock2.Alarm;
|
||||
import com.philliphsu.clock2.PendingAlarmScheduler;
|
||||
import com.philliphsu.clock2.R;
|
||||
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
||||
import com.philliphsu.clock2.alarms.AlarmsFragment;
|
||||
import com.philliphsu.clock2.model.DatabaseManager;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||
@ -40,7 +44,11 @@ public final class AlarmUtils {
|
||||
* Schedules the alarm with the {@link AlarmManager}. If
|
||||
* {@code alarm.}{@link Alarm#isEnabled() isEnabled()} returns false,
|
||||
* this does nothing and returns immediately.
|
||||
*
|
||||
* @deprecated {@code showToast} is no longer working. Callers must
|
||||
* handle popup confirmations on their own.
|
||||
*/
|
||||
// TODO: Delete showToast param
|
||||
public static void scheduleAlarm(Context context, Alarm alarm, boolean showToast) {
|
||||
if (!alarm.isEnabled()) {
|
||||
Log.i(TAG, "Skipped scheduling an alarm because it was not enabled");
|
||||
@ -65,9 +73,9 @@ public final class AlarmUtils {
|
||||
notifyUpcomingAlarmIntent(context, alarm, false));
|
||||
am.setExact(AlarmManager.RTC_WAKEUP, ringAt, alarmIntent(context, alarm, false));
|
||||
|
||||
// TODO: Consider removing this and letting callers handle Toasts, because
|
||||
// TODO: Consider removing this and letting callers handle this, because
|
||||
// it could be beneficial for callers to schedule the alarm in a worker thread.
|
||||
if (showToast) {
|
||||
if (false && showToast) {
|
||||
String message;
|
||||
if (alarm.isSnoozed()) {
|
||||
message = context.getString(R.string.title_snoozing_until,
|
||||
@ -141,6 +149,14 @@ public final class AlarmUtils {
|
||||
public static void snoozeAlarm(Context c, Alarm a) {
|
||||
a.snooze(snoozeDuration(c));
|
||||
scheduleAlarm(c, a, true);
|
||||
// TODO: Based on the current lifecycle methods pair where we register/unregister the
|
||||
// receiver in AlarmsFragment, the snackbar won't be shown.
|
||||
// We have no reference to the snackbar anchor, so let AlarmsFragment
|
||||
// handle showing the snackbar for us. AlarmsFragment has no knowledge
|
||||
// of which alarm is snoozed (and actually doesn't need to know); we can build
|
||||
// the message for it. This is why we don't have a showAlarmSnoozedSnackbar(Alarm)
|
||||
// utility method.
|
||||
sendShowSnackbarBroadcast(c, getSnoozingUntilText(c, a.snoozingUntil()));
|
||||
save(c, a);
|
||||
}
|
||||
|
||||
@ -219,4 +235,30 @@ public final class AlarmUtils {
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static String getRingsInText(Context context, long ringsIn) {
|
||||
return context.getString(R.string.alarm_set_for,
|
||||
DurationUtils.toString(context, ringsIn, false /*abbreviate?*/));
|
||||
}
|
||||
|
||||
public static String getSnoozingUntilText(Context context, long snoozingUntil) {
|
||||
return context.getString(R.string.title_snoozing_until,
|
||||
formatTime(context, snoozingUntil));
|
||||
}
|
||||
|
||||
public static void sendShowSnackbarBroadcast(Context c, String message) {
|
||||
Bundle extra = new Bundle(1);
|
||||
extra.putString(AlarmsFragment.EXTRA_MSG, message);
|
||||
LocalBroadcastHelper.sendBroadcast(c, AlarmsFragment.ACTION_SHOW_SNACKBAR_MSG, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a snackbar confirmation about an event related to an alarm.
|
||||
* Used for showing an alarm has been snoozed.
|
||||
*/
|
||||
public static void showSnackbar(View snackbarAnchor, String message) {
|
||||
if (snackbarAnchor != null) {
|
||||
Snackbar.make(snackbarAnchor, message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
/**
|
||||
@ -13,7 +14,16 @@ public final class LocalBroadcastHelper {
|
||||
|
||||
/** Sends a local broadcast using an intent with the action specified */
|
||||
public static void sendBroadcast(Context context, String action) {
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(action));
|
||||
sendBroadcast(context, action, null);
|
||||
}
|
||||
|
||||
/** Sends a local broadcast using an intent with the action and the extras specified */
|
||||
public static void sendBroadcast(Context context, String action, Bundle extras) {
|
||||
Intent intent = new Intent(action);
|
||||
if (extras != null) {
|
||||
intent.putExtras(extras);
|
||||
}
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
/** Registers a BroadcastReceiver that filters intents by the actions specified */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user