AlarmViewHolder hides dismiss button if recurring and upcoming alarm dismissed via notification

This commit is contained in:
Phillip Hsu 2016-07-08 19:31:03 -07:00
parent 9380f8f579
commit 20b5ff2d8a
7 changed files with 61 additions and 21 deletions

View File

@ -11,6 +11,7 @@ import org.json.JSONObject;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.TimeUnit;
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
@ -28,6 +29,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
private long snoozingUntilMillis;
private boolean enabled;
private final boolean[] recurringDays = new boolean[NUM_DAYS];
private boolean ignoreUpcomingRingTime;
// ====================================================
public abstract int hour();
@ -116,6 +118,14 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
return count;
}
public void ignoreUpcomingRingTime(boolean ignore) {
ignoreUpcomingRingTime = ignore;
}
public boolean isIgnoringUpcomingRingTime() {
return ignoreUpcomingRingTime;
}
public long ringsAt() {
// Always with respect to the current date and time
Calendar calendar = new GregorianCalendar();
@ -124,22 +134,26 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long baseRingTime = calendar.getTimeInMillis();
if (!hasRecurrence()) {
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
if (baseRingTime <= System.currentTimeMillis()) {
// The specified time has passed for today
calendar.add(Calendar.HOUR_OF_DAY, 24);
baseRingTime += TimeUnit.DAYS.toMillis(1);
}
return baseRingTime;
} else {
// Compute the ring time just for the next closest recurring day.
// Remember that day constants defined in the Calendar class are not zero-based like ours, so we have to
// compensate with an offset of magnitude one, with the appropriate sign based on the situation.
// Remember that day constants defined in the Calendar class are
// not zero-based like ours, so we have to compensate with an offset
// of magnitude one, with the appropriate sign based on the situation.
int weekdayToday = calendar.get(Calendar.DAY_OF_WEEK);
int numDaysFromToday = -1;
for (int i = weekdayToday; i <= Calendar.SATURDAY; i++) {
if (isRecurring(i - 1 /*match up with our day constant*/)) {
if (i == weekdayToday) {
if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
if (baseRingTime > System.currentTimeMillis()) {
// The normal ring time has not passed yet
numDaysFromToday = 0;
break;
@ -164,26 +178,29 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
// Still not computed yet. The only recurring day is weekdayToday,
// and its normal ring time has already passed.
if (numDaysFromToday < 0 && isRecurring(weekdayToday - 1)
&& calendar.getTimeInMillis() <= System.currentTimeMillis()) {
&& baseRingTime <= System.currentTimeMillis()) {
numDaysFromToday = 7;
}
if (numDaysFromToday < 0)
throw new IllegalStateException("How did we get here?");
calendar.add(Calendar.HOUR_OF_DAY, 24 * numDaysFromToday);
return baseRingTime + TimeUnit.DAYS.toMillis(numDaysFromToday);
}
return calendar.getTimeInMillis();
}
public long ringsIn() {
return ringsAt() - System.currentTimeMillis();
}
/** @return true if this Alarm will ring in the next {@code hours} hours */
/**
* Returns whether this Alarm is upcoming in the next {@code hours} hours.
* To return true, this Alarm must not have its {@link #ignoreUpcomingRingTime}
* member field set to true.
* @see #ignoreUpcomingRingTime(boolean)
*/
public boolean ringsWithinHours(int hours) {
return ringsIn() <= hours * 3600000;
return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours);
}
public int intId() {
@ -233,6 +250,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
dest.writeLong(snoozingUntilMillis);
dest.writeInt(enabled ? 1 : 0);
dest.writeBooleanArray(recurringDays);
dest.writeInt(ignoreUpcomingRingTime ? 1 : 0);
}
private static Alarm create(Parcel in) {
@ -247,6 +265,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
alarm.snoozingUntilMillis = in.readLong();
alarm.enabled = in.readInt() != 0;
in.readBooleanArray(alarm.recurringDays);
alarm.ignoreUpcomingRingTime = in.readInt() != 0;
return alarm;
}

View File

@ -46,10 +46,13 @@ public class PendingAlarmScheduler extends BroadcastReceiver {
if (!alarm.isEnabled()) {
throw new IllegalStateException("Alarm must be enabled!");
}
alarm.ignoreUpcomingRingTime(false); // allow #ringsWithinHours() to behave normally
// Because showToast = false, we don't do any UI work.
// TODO: Since we're in a worker thread, verify that the
// UI related code within will not cause us to crash.
AlarmUtils.scheduleAlarm(context, alarm, false);
// Update the db
AlarmUtils.save(context, alarm);
}
}).start();
}

View File

@ -72,7 +72,7 @@ public class UpcomingAlarmReceiver extends BroadcastReceiver {
}
}
Intent in = new Intent(context, getClass())
Intent in = new Intent(context, UpcomingAlarmReceiver.class)
.putExtra(EXTRA_ALARM_ID, id) // TOneverDO: cast to int
.setAction(ACTION_DISMISS_NOW);
PendingIntent pi = PendingIntent.getBroadcast(context, (int) id, in, FLAG_ONE_SHOT);

View File

@ -68,6 +68,8 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
@OnClick(R.id.dismiss)
void dismiss() {
// TODO: This is NOT correct for all alarms! This may be correct for single
// use alarms, but not so for recurring alarms!
mSwitch.setPressed(true); // needed so the OnCheckedChange event calls through
bindSwitch(false); // fires OnCheckedChange to do the binding for you
// TOneverDO: AlarmUtils.cancelAlarm() otherwise it will be called twice
@ -120,10 +122,15 @@ 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);
// TODO: We don't have to manually bind these if we update the alarm via the db,
// because that would trigger the loader to reload the dataset and hence the
// corresponding VH is rebound.
bindCountdown(true, alarm.ringsIn());
bindDismissButton(alarm);
} else {
AlarmUtils.cancelAlarm(getContext(), alarm, true); // saves repo
// TODO: Remove these, cancelAlarm will prompt update call to the db, so all VHs will
// be rebound.
bindCountdown(false, -1);
bindDismissButton(false, "");
}
@ -164,7 +171,6 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
String buttonText = alarm.isSnoozed()
? getContext().getString(R.string.title_snoozing_until, formatTime(getContext(), alarm.snoozingUntil()))
: getContext().getString(R.string.dismiss_now);
// when this alarm crosses the upcoming threshold, so we can show this button.
bindDismissButton(visible, buttonText);
}

View File

@ -52,6 +52,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
private static final String COLUMN_THURSDAY = "thursday";
private static final String COLUMN_FRIDAY = "friday";
private static final String COLUMN_SATURDAY = "saturday";
private static final String COLUMN_IGNORE_UPCOMING_RING_TIME = "ignore_upcoming_ring_time";
// https://www.sqlite.org/lang_select.html#orderby
// Rows are first sorted based on the results of evaluating the left-most expression in the
@ -98,7 +99,8 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
+ COLUMN_WEDNESDAY + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_THURSDAY + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_FRIDAY + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_SATURDAY + " INTEGER NOT NULL DEFAULT 0);");
+ COLUMN_SATURDAY + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_IGNORE_UPCOMING_RING_TIME + " INTEGER NOT NULL);");
}
@Override
@ -168,7 +170,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
}
public AlarmCursor queryEnabledAlarms() {
return queryAlarms(COLUMN_ENABLED + " = " + 1);
return queryAlarms(COLUMN_ENABLED + " = 1");
}
private AlarmCursor queryAlarms(String where) {
@ -194,6 +196,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
values.put(COLUMN_THURSDAY, alarm.isRecurring(THURSDAY));
values.put(COLUMN_FRIDAY, alarm.isRecurring(FRIDAY));
values.put(COLUMN_SATURDAY, alarm.isRecurring(SATURDAY));
values.put(COLUMN_IGNORE_UPCOMING_RING_TIME, alarm.isIgnoringUpcomingRingTime());
return values;
}
@ -241,6 +244,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
alarm.setRecurring(THURSDAY, isTrue(COLUMN_THURSDAY));
alarm.setRecurring(FRIDAY, isTrue(COLUMN_FRIDAY));
alarm.setRecurring(SATURDAY, isTrue(COLUMN_SATURDAY));
alarm.ignoreUpcomingRingTime(isTrue(COLUMN_IGNORE_UPCOMING_RING_TIME));
return alarm;
}

View File

@ -10,10 +10,10 @@ import android.util.Log;
import android.widget.Toast;
import com.philliphsu.clock2.Alarm;
import com.philliphsu.clock2.AsyncItemChangeHandler;
import com.philliphsu.clock2.PendingAlarmScheduler;
import com.philliphsu.clock2.R;
import com.philliphsu.clock2.UpcomingAlarmReceiver;
import com.philliphsu.clock2.model.DatabaseManager;
import com.philliphsu.clock2.ringtone.RingtoneActivity;
import com.philliphsu.clock2.ringtone.RingtoneService;
@ -119,8 +119,9 @@ public final class AlarmUtils {
} else {
if (a.isEnabled()) {
if (a.ringsWithinHours(hoursBeforeUpcoming(c))) {
// Still upcoming today, so wait until the normal ring time passes before
// rescheduling the alarm.
// Still upcoming today, so wait until the normal ring time
// passes before rescheduling the alarm.
a.ignoreUpcomingRingTime(true); // Useful only for VH binding
Intent intent = new Intent(c, PendingAlarmScheduler.class)
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id());
pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT);
@ -209,7 +210,13 @@ public final class AlarmUtils {
return pi;
}
private static void save(Context c, Alarm alarm) {
new AsyncItemChangeHandler(c, null, null).asyncUpdateAlarm(alarm);
public static void save(final Context c, final Alarm alarm) {
// TODO: Will using the Runnable like this cause a memory leak?
new Thread(new Runnable() {
@Override
public void run() {
DatabaseManager.getInstance(c).updateAlarm(alarm.id(), alarm);
}
}).start();
}
}

View File

@ -9,4 +9,5 @@
android:layout_height="match_parent"
app:layoutManager="LinearLayoutManager"
tools:context="com.philliphsu.clock2.alarms.AlarmsFragment"
tools:listitem="@layout/item_alarm"/>
tools:listitem="@layout/item_alarm"
android:scrollbars="vertical"/>