AlarmViewHolder hides dismiss button if recurring and upcoming alarm dismissed via notification
This commit is contained in:
parent
9380f8f579
commit
20b5ff2d8a
@ -11,6 +11,7 @@ import org.json.JSONObject;
|
|||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
|
import static com.philliphsu.clock2.DaysOfWeek.NUM_DAYS;
|
||||||
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
|
import static com.philliphsu.clock2.DaysOfWeek.SATURDAY;
|
||||||
@ -28,6 +29,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
private long snoozingUntilMillis;
|
private long snoozingUntilMillis;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private final boolean[] recurringDays = new boolean[NUM_DAYS];
|
private final boolean[] recurringDays = new boolean[NUM_DAYS];
|
||||||
|
private boolean ignoreUpcomingRingTime;
|
||||||
// ====================================================
|
// ====================================================
|
||||||
|
|
||||||
public abstract int hour();
|
public abstract int hour();
|
||||||
@ -116,6 +118,14 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ignoreUpcomingRingTime(boolean ignore) {
|
||||||
|
ignoreUpcomingRingTime = ignore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIgnoringUpcomingRingTime() {
|
||||||
|
return ignoreUpcomingRingTime;
|
||||||
|
}
|
||||||
|
|
||||||
public long ringsAt() {
|
public long ringsAt() {
|
||||||
// Always with respect to the current date and time
|
// Always with respect to the current date and time
|
||||||
Calendar calendar = new GregorianCalendar();
|
Calendar calendar = new GregorianCalendar();
|
||||||
@ -124,22 +134,26 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
calendar.set(Calendar.SECOND, 0);
|
calendar.set(Calendar.SECOND, 0);
|
||||||
calendar.set(Calendar.MILLISECOND, 0);
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
|
|
||||||
|
long baseRingTime = calendar.getTimeInMillis();
|
||||||
|
|
||||||
if (!hasRecurrence()) {
|
if (!hasRecurrence()) {
|
||||||
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
if (baseRingTime <= System.currentTimeMillis()) {
|
||||||
// The specified time has passed for today
|
// The specified time has passed for today
|
||||||
calendar.add(Calendar.HOUR_OF_DAY, 24);
|
baseRingTime += TimeUnit.DAYS.toMillis(1);
|
||||||
}
|
}
|
||||||
|
return baseRingTime;
|
||||||
} else {
|
} else {
|
||||||
// Compute the ring time just for the next closest recurring day.
|
// 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
|
// Remember that day constants defined in the Calendar class are
|
||||||
// compensate with an offset of magnitude one, with the appropriate sign based on the situation.
|
// 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 weekdayToday = calendar.get(Calendar.DAY_OF_WEEK);
|
||||||
int numDaysFromToday = -1;
|
int numDaysFromToday = -1;
|
||||||
|
|
||||||
for (int i = weekdayToday; i <= Calendar.SATURDAY; i++) {
|
for (int i = weekdayToday; i <= Calendar.SATURDAY; i++) {
|
||||||
if (isRecurring(i - 1 /*match up with our day constant*/)) {
|
if (isRecurring(i - 1 /*match up with our day constant*/)) {
|
||||||
if (i == weekdayToday) {
|
if (i == weekdayToday) {
|
||||||
if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
|
if (baseRingTime > System.currentTimeMillis()) {
|
||||||
// The normal ring time has not passed yet
|
// The normal ring time has not passed yet
|
||||||
numDaysFromToday = 0;
|
numDaysFromToday = 0;
|
||||||
break;
|
break;
|
||||||
@ -164,26 +178,29 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
// Still not computed yet. The only recurring day is weekdayToday,
|
// Still not computed yet. The only recurring day is weekdayToday,
|
||||||
// and its normal ring time has already passed.
|
// and its normal ring time has already passed.
|
||||||
if (numDaysFromToday < 0 && isRecurring(weekdayToday - 1)
|
if (numDaysFromToday < 0 && isRecurring(weekdayToday - 1)
|
||||||
&& calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
&& baseRingTime <= System.currentTimeMillis()) {
|
||||||
numDaysFromToday = 7;
|
numDaysFromToday = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numDaysFromToday < 0)
|
if (numDaysFromToday < 0)
|
||||||
throw new IllegalStateException("How did we get here?");
|
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() {
|
public long ringsIn() {
|
||||||
return ringsAt() - System.currentTimeMillis();
|
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) {
|
public boolean ringsWithinHours(int hours) {
|
||||||
return ringsIn() <= hours * 3600000;
|
return !ignoreUpcomingRingTime && ringsIn() <= TimeUnit.HOURS.toMillis(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int intId() {
|
public int intId() {
|
||||||
@ -233,6 +250,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
dest.writeLong(snoozingUntilMillis);
|
dest.writeLong(snoozingUntilMillis);
|
||||||
dest.writeInt(enabled ? 1 : 0);
|
dest.writeInt(enabled ? 1 : 0);
|
||||||
dest.writeBooleanArray(recurringDays);
|
dest.writeBooleanArray(recurringDays);
|
||||||
|
dest.writeInt(ignoreUpcomingRingTime ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Alarm create(Parcel in) {
|
private static Alarm create(Parcel in) {
|
||||||
@ -247,6 +265,7 @@ public abstract class Alarm implements JsonSerializable, Parcelable {
|
|||||||
alarm.snoozingUntilMillis = in.readLong();
|
alarm.snoozingUntilMillis = in.readLong();
|
||||||
alarm.enabled = in.readInt() != 0;
|
alarm.enabled = in.readInt() != 0;
|
||||||
in.readBooleanArray(alarm.recurringDays);
|
in.readBooleanArray(alarm.recurringDays);
|
||||||
|
alarm.ignoreUpcomingRingTime = in.readInt() != 0;
|
||||||
return alarm;
|
return alarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,10 +46,13 @@ public class PendingAlarmScheduler extends BroadcastReceiver {
|
|||||||
if (!alarm.isEnabled()) {
|
if (!alarm.isEnabled()) {
|
||||||
throw new IllegalStateException("Alarm must be enabled!");
|
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.
|
// Because showToast = false, we don't do any UI work.
|
||||||
// TODO: Since we're in a worker thread, verify that the
|
// TODO: Since we're in a worker thread, verify that the
|
||||||
// UI related code within will not cause us to crash.
|
// UI related code within will not cause us to crash.
|
||||||
AlarmUtils.scheduleAlarm(context, alarm, false);
|
AlarmUtils.scheduleAlarm(context, alarm, false);
|
||||||
|
// Update the db
|
||||||
|
AlarmUtils.save(context, alarm);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
.putExtra(EXTRA_ALARM_ID, id) // TOneverDO: cast to int
|
||||||
.setAction(ACTION_DISMISS_NOW);
|
.setAction(ACTION_DISMISS_NOW);
|
||||||
PendingIntent pi = PendingIntent.getBroadcast(context, (int) id, in, FLAG_ONE_SHOT);
|
PendingIntent pi = PendingIntent.getBroadcast(context, (int) id, in, FLAG_ONE_SHOT);
|
||||||
|
|||||||
@ -68,6 +68,8 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
|
|||||||
|
|
||||||
@OnClick(R.id.dismiss)
|
@OnClick(R.id.dismiss)
|
||||||
void 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
|
mSwitch.setPressed(true); // needed so the OnCheckedChange event calls through
|
||||||
bindSwitch(false); // fires OnCheckedChange to do the binding for you
|
bindSwitch(false); // fires OnCheckedChange to do the binding for you
|
||||||
// TOneverDO: AlarmUtils.cancelAlarm() otherwise it will be called twice
|
// TOneverDO: AlarmUtils.cancelAlarm() otherwise it will be called twice
|
||||||
@ -120,10 +122,15 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
|
|||||||
if (alarm.isEnabled()) {
|
if (alarm.isEnabled()) {
|
||||||
// TODO: On Moto X, upcoming notification doesn't post immediately
|
// TODO: On Moto X, upcoming notification doesn't post immediately
|
||||||
AlarmUtils.scheduleAlarm(getContext(), alarm, true);
|
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());
|
bindCountdown(true, alarm.ringsIn());
|
||||||
bindDismissButton(alarm);
|
bindDismissButton(alarm);
|
||||||
} else {
|
} else {
|
||||||
AlarmUtils.cancelAlarm(getContext(), alarm, true); // saves repo
|
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);
|
bindCountdown(false, -1);
|
||||||
bindDismissButton(false, "");
|
bindDismissButton(false, "");
|
||||||
}
|
}
|
||||||
@ -164,7 +171,6 @@ public class AlarmViewHolder extends BaseViewHolder<Alarm> implements AlarmCount
|
|||||||
String buttonText = alarm.isSnoozed()
|
String buttonText = alarm.isSnoozed()
|
||||||
? getContext().getString(R.string.title_snoozing_until, formatTime(getContext(), alarm.snoozingUntil()))
|
? getContext().getString(R.string.title_snoozing_until, formatTime(getContext(), alarm.snoozingUntil()))
|
||||||
: getContext().getString(R.string.dismiss_now);
|
: getContext().getString(R.string.dismiss_now);
|
||||||
// when this alarm crosses the upcoming threshold, so we can show this button.
|
|
||||||
bindDismissButton(visible, buttonText);
|
bindDismissButton(visible, buttonText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
private static final String COLUMN_THURSDAY = "thursday";
|
private static final String COLUMN_THURSDAY = "thursday";
|
||||||
private static final String COLUMN_FRIDAY = "friday";
|
private static final String COLUMN_FRIDAY = "friday";
|
||||||
private static final String COLUMN_SATURDAY = "saturday";
|
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
|
// https://www.sqlite.org/lang_select.html#orderby
|
||||||
// Rows are first sorted based on the results of evaluating the left-most expression in the
|
// 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_WEDNESDAY + " INTEGER NOT NULL DEFAULT 0, "
|
||||||
+ COLUMN_THURSDAY + " INTEGER NOT NULL DEFAULT 0, "
|
+ COLUMN_THURSDAY + " INTEGER NOT NULL DEFAULT 0, "
|
||||||
+ COLUMN_FRIDAY + " 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
|
@Override
|
||||||
@ -168,7 +170,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AlarmCursor queryEnabledAlarms() {
|
public AlarmCursor queryEnabledAlarms() {
|
||||||
return queryAlarms(COLUMN_ENABLED + " = " + 1);
|
return queryAlarms(COLUMN_ENABLED + " = 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
private AlarmCursor queryAlarms(String where) {
|
private AlarmCursor queryAlarms(String where) {
|
||||||
@ -194,6 +196,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
values.put(COLUMN_THURSDAY, alarm.isRecurring(THURSDAY));
|
values.put(COLUMN_THURSDAY, alarm.isRecurring(THURSDAY));
|
||||||
values.put(COLUMN_FRIDAY, alarm.isRecurring(FRIDAY));
|
values.put(COLUMN_FRIDAY, alarm.isRecurring(FRIDAY));
|
||||||
values.put(COLUMN_SATURDAY, alarm.isRecurring(SATURDAY));
|
values.put(COLUMN_SATURDAY, alarm.isRecurring(SATURDAY));
|
||||||
|
values.put(COLUMN_IGNORE_UPCOMING_RING_TIME, alarm.isIgnoringUpcomingRingTime());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,6 +244,7 @@ public class AlarmDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
alarm.setRecurring(THURSDAY, isTrue(COLUMN_THURSDAY));
|
alarm.setRecurring(THURSDAY, isTrue(COLUMN_THURSDAY));
|
||||||
alarm.setRecurring(FRIDAY, isTrue(COLUMN_FRIDAY));
|
alarm.setRecurring(FRIDAY, isTrue(COLUMN_FRIDAY));
|
||||||
alarm.setRecurring(SATURDAY, isTrue(COLUMN_SATURDAY));
|
alarm.setRecurring(SATURDAY, isTrue(COLUMN_SATURDAY));
|
||||||
|
alarm.ignoreUpcomingRingTime(isTrue(COLUMN_IGNORE_UPCOMING_RING_TIME));
|
||||||
return alarm;
|
return alarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import android.util.Log;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.philliphsu.clock2.Alarm;
|
import com.philliphsu.clock2.Alarm;
|
||||||
import com.philliphsu.clock2.AsyncItemChangeHandler;
|
|
||||||
import com.philliphsu.clock2.PendingAlarmScheduler;
|
import com.philliphsu.clock2.PendingAlarmScheduler;
|
||||||
import com.philliphsu.clock2.R;
|
import com.philliphsu.clock2.R;
|
||||||
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
import com.philliphsu.clock2.UpcomingAlarmReceiver;
|
||||||
|
import com.philliphsu.clock2.model.DatabaseManager;
|
||||||
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
import com.philliphsu.clock2.ringtone.RingtoneActivity;
|
||||||
import com.philliphsu.clock2.ringtone.RingtoneService;
|
import com.philliphsu.clock2.ringtone.RingtoneService;
|
||||||
|
|
||||||
@ -119,8 +119,9 @@ public final class AlarmUtils {
|
|||||||
} else {
|
} else {
|
||||||
if (a.isEnabled()) {
|
if (a.isEnabled()) {
|
||||||
if (a.ringsWithinHours(hoursBeforeUpcoming(c))) {
|
if (a.ringsWithinHours(hoursBeforeUpcoming(c))) {
|
||||||
// Still upcoming today, so wait until the normal ring time passes before
|
// Still upcoming today, so wait until the normal ring time
|
||||||
// rescheduling the alarm.
|
// passes before rescheduling the alarm.
|
||||||
|
a.ignoreUpcomingRingTime(true); // Useful only for VH binding
|
||||||
Intent intent = new Intent(c, PendingAlarmScheduler.class)
|
Intent intent = new Intent(c, PendingAlarmScheduler.class)
|
||||||
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id());
|
.putExtra(PendingAlarmScheduler.EXTRA_ALARM_ID, a.id());
|
||||||
pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT);
|
pi = PendingIntent.getBroadcast(c, a.intId(), intent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
@ -209,7 +210,13 @@ public final class AlarmUtils {
|
|||||||
return pi;
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void save(Context c, Alarm alarm) {
|
public static void save(final Context c, final Alarm alarm) {
|
||||||
new AsyncItemChangeHandler(c, null, null).asyncUpdateAlarm(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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,4 +9,5 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layoutManager="LinearLayoutManager"
|
app:layoutManager="LinearLayoutManager"
|
||||||
tools:context="com.philliphsu.clock2.alarms.AlarmsFragment"
|
tools:context="com.philliphsu.clock2.alarms.AlarmsFragment"
|
||||||
tools:listitem="@layout/item_alarm"/>
|
tools:listitem="@layout/item_alarm"
|
||||||
|
android:scrollbars="vertical"/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user