diff --git a/app/src/main/java/com/philliphsu/clock2/Alarm.java b/app/src/main/java/com/philliphsu/clock2/Alarm.java index 8d9eca4..217d3ad 100644 --- a/app/src/main/java/com/philliphsu/clock2/Alarm.java +++ b/app/src/main/java/com/philliphsu/clock2/Alarm.java @@ -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; } diff --git a/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java index bd2ccbc..340ff75 100644 --- a/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java +++ b/app/src/main/java/com/philliphsu/clock2/PendingAlarmScheduler.java @@ -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(); } diff --git a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java index 97394c6..c886226 100644 --- a/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java +++ b/app/src/main/java/com/philliphsu/clock2/UpcomingAlarmReceiver.java @@ -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); diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java index 604c81f..2002cdb 100644 --- a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java +++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmViewHolder.java @@ -68,6 +68,8 @@ public class AlarmViewHolder extends BaseViewHolder 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 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 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); } diff --git a/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java b/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java index 1ce66f7..0cb23ac 100644 --- a/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java +++ b/app/src/main/java/com/philliphsu/clock2/model/AlarmDatabaseHelper.java @@ -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; } diff --git a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java index 8553c3a..bf28b9e 100644 --- a/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java +++ b/app/src/main/java/com/philliphsu/clock2/util/AlarmUtils.java @@ -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(); } } diff --git a/app/src/main/res/layout/fragment_alarms.xml b/app/src/main/res/layout/fragment_alarms.xml index c44e1cb..6b99a97 100644 --- a/app/src/main/res/layout/fragment_alarms.xml +++ b/app/src/main/res/layout/fragment_alarms.xml @@ -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"/>