Commit eb7fa6f6 authored by Julian Sparber's avatar Julian Sparber
Browse files

debian: add 'missed_alarms' patches

parent d93251a9
From 0f95e65c9deb715b97cf20ee2ba08c32359608b3 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Thu, 12 Nov 2020 13:38:07 +0100
Subject: [PATCH] alarm-item: Make sure that the time property is always set
The time property for an AlarmItem isn't nullable therefore it needs to
be set during construction.
---
src/alarm-face.vala | 18 +++++++----
src/alarm-item.vala | 12 ++++----
src/alarm-setup-dialog.vala | 59 +++++++++++--------------------------
3 files changed, 36 insertions(+), 53 deletions(-)
diff --git a/src/alarm-face.vala b/src/alarm-face.vala
index 6000739..d2f7b7e 100644
--- a/src/alarm-face.vala
+++ b/src/alarm-face.vala
@@ -108,7 +108,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
}
internal void edit (Item alarm) {
- var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms);
+ var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms, true);
// Disable alarm while editing it and remember the original active state.
alarm.editing = true;
@@ -116,7 +116,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
dialog.response.connect ((dialog, response) => {
alarm.editing = false;
if (response == Gtk.ResponseType.OK) {
- ((SetupDialog) dialog).apply_to_alarm (alarm);
+ ((SetupDialog) dialog).apply_to_alarm ();
save ();
} else if (response == DELETE_ALARM) {
alarms.delete_item (alarm);
@@ -137,11 +137,19 @@ public class Face : Gtk.Stack, Clocks.Clock {
}
public void activate_new () {
- var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), null, alarms);
+ var wc = Utils.WallClock.get_default ();
+ var alarm = new Item ({ wc.date_time.get_hour (), wc.date_time.get_minute () }, false);
+ var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms);
+
+ // Disable alarm while editing it and remember the original active state.
+ alarm.editing = true;
+
dialog.response.connect ((dialog, response) => {
+ alarm.editing = false;
+ // Enable the newly created alarm
+ alarm.active = true;
if (response == Gtk.ResponseType.OK) {
- var alarm = new Item ();
- ((SetupDialog) dialog).apply_to_alarm (alarm);
+ ((SetupDialog) dialog).apply_to_alarm ();
alarms.add (alarm);
save ();
}
diff --git a/src/alarm-item.vala b/src/alarm-item.vala
index 8c86f78..df6631f 100644
--- a/src/alarm-item.vala
+++ b/src/alarm-item.vala
@@ -104,9 +104,12 @@ private class Item : Object, ContentItem {
private Utils.Bell bell;
private GLib.Notification notification;
- public Item (string? id = null) {
+ public Item (AlarmTime time, bool active = true, Utils.Weekdays? days = null, string? id = null) {
var guid = id != null ? (string) id : GLib.DBus.generate_guid ();
- Object (id: guid);
+ Object (id: guid,
+ active: active,
+ time: time,
+ days: days);
}
private void setup_bell () {
@@ -270,11 +273,8 @@ private class Item : Object, ContentItem {
}
if (hour >= 0 && minute >= 0) {
- Item alarm = new Item (id);
+ Item alarm = new Item ({ hour, minute }, active, days, id);
alarm.name = name;
- alarm.active = active;
- alarm.time = { hour, minute };
- alarm.days = days;
alarm.ring_minutes = ring_minutes;
alarm.snooze_minutes = snooze_minutes;
alarm.reset ();
diff --git a/src/alarm-setup-dialog.vala b/src/alarm-setup-dialog.vala
index 75d6baa..1e9a696 100644
--- a/src/alarm-setup-dialog.vala
+++ b/src/alarm-setup-dialog.vala
@@ -75,6 +75,7 @@ private class DurationModel : ListModel, Object {
[GtkTemplate (ui = "/org/gnome/clocks/ui/alarm-setup-dialog.ui")]
private class SetupDialog : Gtk.Dialog {
private Utils.WallClock.Format format;
+ public Item alarm { get; set; }
[GtkChild]
private Gtk.Grid time_grid;
[GtkChild]
@@ -103,20 +104,21 @@ private class SetupDialog : Gtk.Dialog {
typeof (DayPickerRow).ensure ();
}
- public SetupDialog (Gtk.Window parent, Item? alarm, ListModel all_alarms) {
+ public SetupDialog (Gtk.Window parent, Item alarm, ListModel all_alarms, bool edit_alarm = false) {
Object (transient_for: parent,
- title: alarm != null ? _("Edit Alarm") : _("New Alarm"),
+ alarm: alarm,
+ title: edit_alarm ? _("Edit Alarm") : _("New Alarm"),
use_header_bar: 1);
add_button (_("Cancel"), Gtk.ResponseType.CANCEL);
- if (alarm != null) {
+ if (edit_alarm) {
add_button (_("Done"), Gtk.ResponseType.OK);
} else {
add_button (_("Add"), Gtk.ResponseType.OK);
}
set_default_response (Gtk.ResponseType.OK);
- delete_button.visible = alarm != null;
+ delete_button.visible = edit_alarm;
other_alarms = new List<Item> ();
var n = all_alarms.get_n_items ();
@@ -158,39 +160,13 @@ private class SetupDialog : Gtk.Dialog {
am_pm_stack.visible_child = am_pm_button;
}
- set_from_alarm (alarm);
+ set_from_alarm ();
}
// Sets up the dialog to show the values of alarm.
- public void set_from_alarm (Item? alarm) {
- string? name;
- bool active;
- int hour;
- int minute;
- int snooze_minutes;
- int ring_minutes;
- unowned Utils.Weekdays? days;
-
- if (alarm == null) {
- var wc = Utils.WallClock.get_default ();
- // Not great but we can't null it
- name = "";
- hour = wc.date_time.get_hour ();
- minute = wc.date_time.get_minute ();
- days = null;
- active = true;
- ring_minutes = 5;
- snooze_minutes = 10;
- } else {
- name = ((Item) alarm).name;
- hour = ((Item) alarm).time.hour;
- minute = ((Item) alarm).time.minute;
- days = ((Item) alarm).days;
- active = ((Item) alarm).active;
- ring_minutes = ((Item) alarm).ring_minutes;
- snooze_minutes = ((Item) alarm).snooze_minutes;
- }
-
+ public void set_from_alarm () {
+ var hour = alarm.time.hour;
+ var minute = alarm.time.minute;
// Set the time.
if (format == Utils.WallClock.Format.TWELVE) {
if (hour < 12) {
@@ -204,22 +180,22 @@ private class SetupDialog : Gtk.Dialog {
hour = 12;
}
}
- ring_duration.set_selected_index (duration_model.find_by_duration (ring_minutes));
- snooze_duration.set_selected_index (duration_model.find_by_duration (snooze_minutes));
+ ring_duration.set_selected_index (duration_model.find_by_duration (alarm.ring_minutes));
+ snooze_duration.set_selected_index (duration_model.find_by_duration (alarm.snooze_minutes));
h_spinbutton.set_value (hour);
m_spinbutton.set_value (minute);
// Set the name.
- name_entry.set_text ((string) name);
+ name_entry.set_text ((string) alarm.name);
- if (days != null) {
- repeats.load ((Utils.Weekdays) days);
+ if (alarm.days != null) {
+ repeats.load ((Utils.Weekdays) alarm.days);
}
}
// Sets alarm according to the current dialog settings.
- public void apply_to_alarm (Item alarm) {
+ public void apply_to_alarm () {
var name = name_entry.get_text ();
var hour = h_spinbutton.get_value_as_int ();
var minute = m_spinbutton.get_value_as_int ();
@@ -254,8 +230,7 @@ private class SetupDialog : Gtk.Dialog {
}
private void avoid_duplicate_alarm () {
- var alarm = new Item ();
- apply_to_alarm (alarm);
+ apply_to_alarm ();
var duplicate = alarm.check_duplicate_alarm (other_alarms);
this.set_response_sensitive (Gtk.ResponseType.OK, !duplicate);
--
2.29.2
From c350d331d5b58548912e01b94b57ff76e0477882 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Mon, 16 Nov 2020 11:36:29 +0100
Subject: [PATCH 1/2] alarm: remove edit property
---
src/alarm-face.vala | 8 --------
src/alarm-item.vala | 5 +++--
2 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/alarm-face.vala b/src/alarm-face.vala
index d2f7b7e..2552149 100644
--- a/src/alarm-face.vala
+++ b/src/alarm-face.vala
@@ -110,11 +110,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
internal void edit (Item alarm) {
var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms, true);
- // Disable alarm while editing it and remember the original active state.
- alarm.editing = true;
-
dialog.response.connect ((dialog, response) => {
- alarm.editing = false;
if (response == Gtk.ResponseType.OK) {
((SetupDialog) dialog).apply_to_alarm ();
save ();
@@ -141,11 +137,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
var alarm = new Item ({ wc.date_time.get_hour (), wc.date_time.get_minute () }, false);
var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms);
- // Disable alarm while editing it and remember the original active state.
- alarm.editing = true;
-
dialog.response.connect ((dialog, response) => {
- alarm.editing = false;
// Enable the newly created alarm
alarm.active = true;
if (response == Gtk.ResponseType.OK) {
diff --git a/src/alarm-item.vala b/src/alarm-item.vala
index df6631f..dd44b15 100644
--- a/src/alarm-item.vala
+++ b/src/alarm-item.vala
@@ -34,7 +34,8 @@ private class Item : Object, ContentItem {
SNOOZING
}
- public bool editing { get; set; default = false; }
+ // Missed can't be a state because we couldn't scheduale next alarms without override missed
+ public bool missed { get; set; default = false; }
public string id { get; construct set; }
@@ -183,7 +184,7 @@ private class Item : Object, ContentItem {
}
private bool compare_with_item (Item i) {
- return (this.alarm_time.compare (i.alarm_time) == 0 && (this.active || this.editing) && i.active);
+ return (this.time.compare (i.time) == 0);
}
public bool check_duplicate_alarm (List<Item> alarms) {
--
2.29.2
From 20b8eded022854ec3aea8d326b26fe5f079f1c63 Mon Sep 17 00:00:00 2001
From: Julian Sparber <julian@sparber.net>
Date: Fri, 13 Nov 2020 17:59:06 +0100
Subject: [PATCH 2/2] alarms: Make alarms persisted and add missed alarms
notation
This stores the alarm state as well as the alarm and snooze time
to gsettings. This alllows us to tell the user about missed alarms.
A user could miss alarms for two reasons:
- Clocks wasn't running when the alarm went of.
- The user didn't stop the alarm within "ring-time"
This also adds the baseline for starting Clocks via systemd user .timer
units by making Clocks checks at startup if any alarms need to go off.
As a side effect: closing Clocks doesn't stop an alarm and when Clocks
is reopend within "ring-time" after an alarm or snooze the alarm starts
ringing again.
---
src/alarm-face.vala | 5 +-
src/alarm-item.vala | 218 +++++++++++++++++++++++-------------
src/alarm-setup-dialog.vala | 12 +-
src/application.vala | 5 +-
src/window.vala | 3 +
5 files changed, 156 insertions(+), 87 deletions(-)
diff --git a/src/alarm-face.vala b/src/alarm-face.vala
index 2552149..2caa602 100644
--- a/src/alarm-face.vala
+++ b/src/alarm-face.vala
@@ -66,7 +66,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
});
listbox.bind_model (alarms, (item) => {
- item.notify["active"].connect (save);
+ item.notify["state"].connect (save);
return new Row ((Item) item, this);
});
@@ -134,12 +134,13 @@ public class Face : Gtk.Stack, Clocks.Clock {
public void activate_new () {
var wc = Utils.WallClock.get_default ();
- var alarm = new Item ({ wc.date_time.get_hour (), wc.date_time.get_minute () }, false);
+ var alarm = new Item (wc.date_time.get_hour (), wc.date_time.get_minute ());
var dialog = new SetupDialog ((Gtk.Window) get_toplevel (), alarm, alarms);
dialog.response.connect ((dialog, response) => {
// Enable the newly created alarm
alarm.active = true;
+
if (response == Gtk.ResponseType.OK) {
((SetupDialog) dialog).apply_to_alarm ();
alarms.add (alarm);
diff --git a/src/alarm-item.vala b/src/alarm-item.vala
index dd44b15..5e995ee 100644
--- a/src/alarm-item.vala
+++ b/src/alarm-item.vala
@@ -20,15 +20,9 @@
namespace Clocks {
namespace Alarm {
-private struct AlarmTime {
- public int hour;
- public int minute;
-}
-
private class Item : Object, ContentItem {
- // FIXME: should we add a "MISSED" state where the alarm stopped
- // ringing but we keep showing the ringing panel?
public enum State {
+ DISABLED,
READY,
RINGING,
SNOOZING
@@ -43,6 +37,12 @@ private class Item : Object, ContentItem {
public int ring_minutes { get; set; default = 5; }
+ public bool recurring {
+ get {
+ return days != null && !((!) days).empty;
+ }
+ }
+
public string? name {
get {
return _name;
@@ -54,21 +54,34 @@ private class Item : Object, ContentItem {
}
}
- public AlarmTime time { get; set; }
-
public Utils.Weekdays? days { get; set; }
- public State state { get; private set; }
+ private State _state = State.DISABLED;
+ public State state {
+ get {
+ return _state;
+ }
+ private set {
+ if (_state == value)
+ return;
+
+ _state = value;
+ notify_property ("active");
+ }
+ }
public string time_label {
owned get {
- return Utils.WallClock.get_default ().format_time (alarm_time);
+ return Utils.WallClock.get_default ().format_time (time);
}
}
public string snooze_time_label {
owned get {
- return Utils.WallClock.get_default ().format_time (snooze_time);
+ if (snooze_time == null)
+ return Utils.WallClock.get_default ().format_time (time.add_minutes (snooze_minutes));
+ else
+ return Utils.WallClock.get_default ().format_time ((!) snooze_time);
}
}
@@ -78,37 +91,43 @@ private class Item : Object, ContentItem {
}
}
+ public GLib.DateTime time { get; set; }
+
[CCode (notify = false)]
public bool active {
get {
- return _active && !this.editing;
+ return this.state > State.DISABLED;
}
-
set {
- if (value != _active) {
- _active = value;
- if (_active) {
- reset ();
- } else if (state == State.RINGING) {
- stop ();
- }
+ if (this.state != State.DISABLED && !value) {
+ stop ();
+ this.state = State.DISABLED;
+ notify_property ("active");
+ } else if (this.state == State.DISABLED && value) {
+ this.missed = false;
+ this.time = get_next_alarm_time (time.get_hour (), time.get_minute (), days);
+ this.state = State.READY;
notify_property ("active");
}
}
}
private string _name;
- private bool _active = true;
- private GLib.DateTime alarm_time;
- private GLib.DateTime snooze_time;
- private GLib.DateTime ring_end_time;
+ private GLib.DateTime? snooze_time;
private Utils.Bell bell;
private GLib.Notification notification;
- public Item (AlarmTime time, bool active = true, Utils.Weekdays? days = null, string? id = null) {
+ public Item (int hour, int minute, Utils.Weekdays? days = null, string? id = null) {
+ var guid = id != null ? (string) id : GLib.DBus.generate_guid ();
+ var time = get_next_alarm_time (hour, minute, days);
+ Object (id: guid,
+ time: time,
+ days: days);
+ }
+
+ public Item.for_specific_time (GLib.DateTime time, Utils.Weekdays? days = null, string? id = null) {
var guid = id != null ? (string) id : GLib.DBus.generate_guid ();
Object (id: guid,
- active: active,
time: time,
days: days);
}
@@ -121,24 +140,39 @@ private class Item : Object, ContentItem {
notification.add_button (_("Snooze"), "app.snooze-alarm::".concat (id));
}
- public void reset () {
- update_alarm_time ();
- update_snooze_time (alarm_time);
- state = State.READY;
+ private void show_missed_notification () {
+ var app = (Clocks.Application) GLib.Application.get_default ();
+ var notification = new GLib.Notification (_("Missed alarm"));
+ string label;
+ if (name != null && ((!) name).length > 0) {
+ // Translators: The first %s is the time and the second %s is the title
+ label = _("%s: %s").printf (time_label, (!) name);
+ } else {
+ // Translators: %s is a time
+ label = _("%s").printf (time_label);
+ }
+
+ notification.set_body (label);
+ app.send_notification ("alarm-clock-missed", notification);
+ }
+
+ public void set_alarm_time (int hour, int minute, Utils.Weekdays? days) {
+ this.days = days;
+ this.time = get_next_alarm_time (hour, minute, days);
}
- private void update_alarm_time () {
+ private static GLib.DateTime get_next_alarm_time (int hour, int minute, Utils.Weekdays? days) {
var wallclock = Utils.WallClock.get_default ();
var now = wallclock.date_time;
var dt = new GLib.DateTime (wallclock.timezone,
now.get_year (),
now.get_month (),
now.get_day_of_month (),
- time.hour,
- time.minute,
+ hour,
+ minute,
0);
- if (days == null || ((Utils.Weekdays) days).empty) {
+ if (days == null || ((!) days).empty) {
// Alarm without days.
if (dt.compare (now) <= 0) {
// Time already passed, ring tomorrow.
@@ -147,16 +181,13 @@ private class Item : Object, ContentItem {
} else {
// Alarm with at least one day set.
// Find the next possible day for ringing
- while (dt.compare (now) <= 0 || ! ((Utils.Weekdays) days).get ((Utils.Weekdays.Day) (dt.get_day_of_week () - 1))) {
+ while (dt.compare (now) <= 0 ||
+ ! ((Utils.Weekdays) days).get ((Utils.Weekdays.Day) (dt.get_day_of_week () - 1))) {
dt = dt.add_days (1);
}
}
- alarm_time = dt;
- }
-
- private void update_snooze_time (GLib.DateTime start_time) {
- snooze_time = start_time.add_minutes (snooze_minutes);
+ return dt;
}
public virtual signal void ring () {
@@ -166,30 +197,43 @@ private class Item : Object, ContentItem {
}
private void start_ringing (GLib.DateTime now) {
- update_snooze_time (now);
- ring_end_time = now.add_minutes (ring_minutes);
state = State.RINGING;
ring ();
}
public void snooze () {
bell.stop ();
+ if (snooze_time == null)
+ snooze_time = time.add_minutes (snooze_minutes);
+ else
+ snooze_time = ((!) snooze_time).add_minutes (snooze_minutes);
+
state = State.SNOOZING;
}
public void stop () {
bell.stop ();
- update_snooze_time (alarm_time);
- state = State.READY;
+ snooze_time = null;
+
+ // scheduale the next alarm if recurring
+ if (recurring) {
+ time = get_next_alarm_time (time.get_hour (), time.get_minute (), days);
+ state = State.READY;
+ GLib.Timeout.add_seconds (120, () => {
+ missed = false;
+ return GLib.Source.REMOVE;
+ });
+ } else {
+ state = State.DISABLED;
+ }
}
private bool compare_with_item (Item i) {
- return (this.time.compare (i.time) == 0);
+ return (this.time.get_hour () == i.time.get_hour () &&
+ this.time.get_minute () == i.time.get_minute ());
}
public bool check_duplicate_alarm (List<Item> alarms) {
- update_alarm_time ();
-
foreach (var item in alarms) {
if (this.compare_with_item (item)) {
return true;
@@ -198,11 +242,21 @@ private class Item : Object, ContentItem {
return false;
}
+ private void start_ringing_or_missed (GLib.DateTime now, GLib.DateTime ring_end_time) {
+ if (now.compare (ring_end_time) > 0 ) {
+ missed = true;
+ show_missed_notification ();
+ stop ();
+ } else {
+ start_ringing (now);
+ }
+ }
+
// Update the state and ringing time. Ring or stop
// depending on the current time.
// Returns true if the state changed, false otherwise.
public bool tick () {
- if (!active) {
+ if (state == State.DISABLED) {
return false;