diff options
-rw-r--r-- | include/datetime/alarm-queue-simple.h | 18 | ||||
-rw-r--r-- | include/datetime/alarm-queue.h | 2 | ||||
-rw-r--r-- | include/datetime/date-time.h | 3 | ||||
-rw-r--r-- | include/datetime/planner-snooze.h | 3 | ||||
-rw-r--r-- | src/alarm-queue-simple.cpp | 226 | ||||
-rw-r--r-- | src/date-time.cpp | 10 | ||||
-rw-r--r-- | src/main.cpp | 8 | ||||
-rw-r--r-- | src/planner-snooze.cpp | 16 | ||||
-rw-r--r-- | tests/test-alarm-queue.cpp | 17 |
9 files changed, 178 insertions, 125 deletions
diff --git a/include/datetime/alarm-queue-simple.h b/include/datetime/alarm-queue-simple.h index d191aec..838d28a 100644 --- a/include/datetime/alarm-queue-simple.h +++ b/include/datetime/alarm-queue-simple.h @@ -20,6 +20,8 @@ #ifndef INDICATOR_DATETIME_ALARM_QUEUE_SIMPLE_H #define INDICATOR_DATETIME_ALARM_QUEUE_SIMPLE_H +#include <memory> // std::shared_ptr + #include <datetime/alarm-queue.h> #include <datetime/clock.h> #include <datetime/planner.h> @@ -39,20 +41,12 @@ public: const std::shared_ptr<Planner>& upcoming_planner, const std::shared_ptr<WakeupTimer>& timer); ~SimpleAlarmQueue(); - core::Signal<const Appointment&>& alarm_reached(); + core::Signal<const Appointment&, const Alarm&>& alarm_reached() override; private: - void requeue(); - bool find_next_alarm(Appointment& setme) const; - std::vector<Appointment> find_current_alarms() const; - void check_alarms(); - - std::set<std::pair<std::string,DateTime>> m_triggered; - const std::shared_ptr<Clock> m_clock; - const std::shared_ptr<Planner> m_planner; - const std::shared_ptr<WakeupTimer> m_timer; - core::Signal<const Appointment&> m_alarm_reached; - DateTime m_datetime; + class Impl; + friend class Impl; + std::unique_ptr<Impl> impl; }; diff --git a/include/datetime/alarm-queue.h b/include/datetime/alarm-queue.h index ea51957..98abd7a 100644 --- a/include/datetime/alarm-queue.h +++ b/include/datetime/alarm-queue.h @@ -45,7 +45,7 @@ class AlarmQueue public: AlarmQueue() =default; virtual ~AlarmQueue() =default; - virtual core::Signal<const Appointment&>& alarm_reached() = 0; + virtual core::Signal<const Appointment&, const Alarm&>& alarm_reached() =0; }; /*** diff --git a/include/datetime/date-time.h b/include/datetime/date-time.h index 7dfc207..7e19a9d 100644 --- a/include/datetime/date-time.h +++ b/include/datetime/date-time.h @@ -22,6 +22,7 @@ #include <glib.h> // GDateTime +#include <chrono> #include <ctime> // time_t #include <memory> // std::shared_ptr @@ -43,6 +44,8 @@ public: DateTime(GTimeZone* tz, GDateTime* dt); DateTime(GTimeZone* tz, int year, int month, int day, int hour, int minute, double seconds); DateTime& operator=(const DateTime& in); + DateTime& operator+=(const std::chrono::minutes&); + DateTime& operator+=(const std::chrono::seconds&); DateTime to_timezone(const std::string& zone) const; DateTime start_of_month() const; DateTime start_of_day() const; diff --git a/include/datetime/planner-snooze.h b/include/datetime/planner-snooze.h index e2619fa..fc08d27 100644 --- a/include/datetime/planner-snooze.h +++ b/include/datetime/planner-snooze.h @@ -39,9 +39,8 @@ public: SnoozePlanner(const std::shared_ptr<Settings>&, const std::shared_ptr<Clock>&); ~SnoozePlanner(); - void add(const Appointment&); - core::Property<std::vector<Appointment>>& appointments() override; + void add(const Appointment&, const Alarm&); protected: class Impl; diff --git a/src/alarm-queue-simple.cpp b/src/alarm-queue-simple.cpp index f45e61a..db0fd21 100644 --- a/src/alarm-queue-simple.cpp +++ b/src/alarm-queue-simple.cpp @@ -20,134 +20,174 @@ #include <datetime/alarm-queue-simple.h> #include <cmath> +#include <set> namespace unity { namespace indicator { namespace datetime { /*** -**** Public API +**** ***/ -SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr<Clock>& clock, - const std::shared_ptr<Planner>& planner, - const std::shared_ptr<WakeupTimer>& timer): - m_clock(clock), - m_planner(planner), - m_timer(timer), - m_datetime(clock->localtime()) +class SimpleAlarmQueue::Impl { - m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ - g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); - requeue(); - }); - - m_clock->minute_changed.connect([=]{ - const auto now = m_clock->localtime(); - constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; - const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; - m_datetime = now; - if (clock_jumped) { - g_debug("AlarmQueue %p calling requeue() due to clock skew", this); +public: + + Impl(const std::shared_ptr<Clock>& clock, + const std::shared_ptr<Planner>& planner, + const std::shared_ptr<WakeupTimer>& timer): + m_clock{clock}, + m_planner{planner}, + m_timer{timer}, + m_datetime{clock->localtime()} + { + m_planner->appointments().changed().connect([this](const std::vector<Appointment>&){ + g_debug("AlarmQueue %p calling requeue() due to appointments changed", this); requeue(); - } - }); + }); + + m_clock->minute_changed.connect([=]{ + const auto now = m_clock->localtime(); + constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC; + const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec; + m_datetime = now; + if (clock_jumped) { + g_debug("AlarmQueue %p calling requeue() due to clock skew", this); + requeue(); + } + }); + + m_timer->timeout().connect([this](){ + g_debug("AlarmQueue %p calling requeue() due to timeout", this); + requeue(); + }); - m_timer->timeout().connect([this](){ - g_debug("AlarmQueue %p calling requeue() due to timeout", this); requeue(); - }); - - requeue(); -} + } -SimpleAlarmQueue::~SimpleAlarmQueue() -{ -} + ~Impl() + { + } -core::Signal<const Appointment&>& SimpleAlarmQueue::alarm_reached() -{ - return m_alarm_reached; -} + core::Signal<const Appointment&, const Alarm&>& alarm_reached() + { + return m_alarm_reached; + } -/*** -**** -***/ +private: -void SimpleAlarmQueue::requeue() -{ - // kick any current alarms - for (auto current : find_current_alarms()) + void requeue() { - const std::pair<std::string,DateTime> trig {current.uid, current.begin}; - m_triggered.insert(trig); - m_alarm_reached(current); + // kick any current alarms + for (const auto& appointment : m_planner->appointments().get()) + { + Alarm alarm; + if (appointment_get_current_alarm(appointment, alarm)) + { + m_triggered.insert(std::make_pair(appointment.uid, alarm.time)); + m_alarm_reached(appointment, alarm); + } + } + + // idle until the next alarm + Alarm next; + if (find_next_alarm(next)) + { + g_debug ("setting timer to wake up for next appointment '%s' at %s", + next.text.c_str(), + next.time.format("%F %T").c_str()); + + m_timer->set_wakeup_time(next.time); + } } - // idle until the next alarm - Appointment next; - if (find_next_alarm(next)) + // find the next alarm that will kick now or in the future + bool find_next_alarm(Alarm& setme) const { - g_debug ("setting timer to wake up for next appointment '%s' at %s", - next.summary.c_str(), - next.begin.format("%F %T").c_str()); + bool found = false; + Alarm best; + const auto now = m_clock->localtime(); + const auto beginning_of_minute = now.start_of_minute(); - m_timer->set_wakeup_time(next.begin); - } -} + const auto appointments = m_planner->appointments().get(); + g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); -// find the next alarm that will kick now or in the future -bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const -{ - bool found = false; - Appointment tmp; - const auto now = m_clock->localtime(); - const auto beginning_of_minute = now.start_of_minute(); + for(const auto& appointment : appointments) + { + for(const auto& alarm : appointment.alarms) + { + const std::pair<std::string,DateTime> trig{appointment.uid, alarm.time}; + if (m_triggered.count(trig)) + continue; - const auto appointments = m_planner->appointments().get(); - g_debug ("planner has %zu appointments in it", (size_t)appointments.size()); + if (alarm.time < beginning_of_minute) // has this one already passed? + continue; - for(const auto& walk : appointments) - { - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; - if (m_triggered.count(trig)) - continue; + if (found && (best.time < alarm.time)) // do we already have a better match? + continue; - if (walk.begin < beginning_of_minute) // has this one already passed? - continue; + best = alarm; + found = true; + } + } - if (found && (tmp.begin < walk.begin)) // do we already have a better match? - continue; + if (found) + setme = best; - tmp = walk; - found = true; + return found; } - if (found) - setme = tmp; - return found; -} + bool appointment_get_current_alarm(const Appointment& appointment, Alarm& setme) const + { + const auto now = m_clock->localtime(); -// find the alarm(s) that should kick right now -std::vector<Appointment> SimpleAlarmQueue::find_current_alarms() const -{ - std::vector<Appointment> appointments; + for (const auto& alarm : appointment.alarms) + { + const auto trig = std::make_pair(appointment.uid, alarm.time); + if (m_triggered.count(trig)) // did we already use this one? + continue; + + if (DateTime::is_same_minute(now, alarm.time)) + { + setme = alarm; + return true; + } + } - const auto now = m_clock->localtime(); + return false; + } - for(const auto& walk : m_planner->appointments().get()) - { - const std::pair<std::string,DateTime> trig {walk.uid, walk.begin}; - if (m_triggered.count(trig)) // did we already use this one? - continue; - if (!DateTime::is_same_minute(now, walk.begin)) - continue; - appointments.push_back(walk); - } + std::set<std::pair<std::string,DateTime>> m_triggered; + const std::shared_ptr<Clock> m_clock; + const std::shared_ptr<Planner> m_planner; + const std::shared_ptr<WakeupTimer> m_timer; + core::Signal<const Appointment&, const Alarm&> m_alarm_reached; + DateTime m_datetime; +}; + +/*** +**** Public API +***/ + - return appointments; +SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr<Clock>& clock, + const std::shared_ptr<Planner>& planner, + const std::shared_ptr<WakeupTimer>& timer): + impl{new Impl{clock, planner, timer}} +{ +} + +SimpleAlarmQueue::~SimpleAlarmQueue() +{ +} + +core::Signal<const Appointment&, const Alarm&>& +SimpleAlarmQueue::alarm_reached() +{ + return impl->alarm_reached(); } /*** diff --git a/src/date-time.cpp b/src/date-time.cpp index 689688c..ecfc971 100644 --- a/src/date-time.cpp +++ b/src/date-time.cpp @@ -55,6 +55,16 @@ DateTime& DateTime::operator=(const DateTime& that) return *this; } +DateTime& DateTime::operator+=(const std::chrono::minutes& minutes) +{ + return (*this = add_full(0, 0, 0, 0, minutes.count(), 0)); +} + +DateTime& DateTime::operator+=(const std::chrono::seconds& seconds) +{ + return (*this = add_full(0, 0, 0, 0, 0, seconds.count())); +} + DateTime::DateTime(time_t t) { auto gtz = g_time_zone_new_local(); diff --git a/src/main.cpp b/src/main.cpp index 38e5caa..907d49f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -138,11 +138,11 @@ main(int /*argc*/, char** /*argv*/) auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service"); std::unique_ptr<Snap> snap (new Snap(notification_engine, state->settings)); auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone_); - auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm&) {snooze_planner->add(appointment);}; + auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm& alarm) { + snooze_planner->add(appointment, alarm); + }; auto on_ok = [](const Appointment&, const Alarm&){}; - auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment) { -#warning placeholder; next step is to have AlarmQueue pass an appointment and alarm - Alarm alarm; + auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment, const Alarm& alarm) { (*snap)(appointment, alarm, on_snooze, on_ok); engine->disable_ubuntu_alarm(appointment); }; diff --git a/src/planner-snooze.cpp b/src/planner-snooze.cpp index 29d5f06..e4062d2 100644 --- a/src/planner-snooze.cpp +++ b/src/planner-snooze.cpp @@ -51,14 +51,18 @@ public: return m_appointments; } - void add(const Appointment& appt_in) + void add(const Appointment& appt_in, const Alarm& alarm) { + // make a copy of the appointment with only this alarm Appointment appt = appt_in; + appt.alarms.clear(); + appt.alarms.push_back(alarm); // reschedule the alarm to go off N minutes from now - const auto alarm_duration_secs = appt.end - appt.begin; - appt.begin = m_clock->localtime().add_full(0,0,0,0,m_settings->snooze_duration.get(),0); - appt.end = appt.begin.add_full(0,0,0,0,0,alarm_duration_secs); + const auto offset = std::chrono::minutes(m_settings->snooze_duration.get()); + appt.begin += offset; + appt.end += offset; + appt.alarms[0].time += offset; // give it a new ID gchar* uid = e_uid_new(); @@ -95,9 +99,9 @@ SnoozePlanner::~SnoozePlanner() } void -SnoozePlanner::add(const Appointment& appointment) +SnoozePlanner::add(const Appointment& appointment, const Alarm& alarm) { - impl->add(appointment); + impl->add(appointment, alarm); } core::Property<std::vector<Appointment>>& diff --git a/tests/test-alarm-queue.cpp b/tests/test-alarm-queue.cpp index 3fdf787..fdab425 100644 --- a/tests/test-alarm-queue.cpp +++ b/tests/test-alarm-queue.cpp @@ -48,7 +48,7 @@ protected: m_range_planner.reset(new MockRangePlanner); m_upcoming.reset(new UpcomingPlanner(m_range_planner, m_state->clock->localtime())); m_watcher.reset(new SimpleAlarmQueue(m_state->clock, m_upcoming, m_wakeup_timer)); - m_watcher->alarm_reached().connect([this](const Appointment& appt){ + m_watcher->alarm_reached().connect([this](const Appointment& appt, const Alarm& /*alarm*/){ m_triggered.push_back(appt.uid); }); @@ -71,7 +71,7 @@ protected: const auto tomorrow_begin = now.add_days(1).start_of_day(); const auto tomorrow_end = tomorrow_begin.end_of_day(); - Appointment a1; // an alarm clock appointment + Appointment a1; // an ubuntu alarm a1.color = "red"; a1.summary = "Alarm"; a1.summary = "http://www.example.com/"; @@ -79,18 +79,20 @@ protected: a1.type = Appointment::UBUNTU_ALARM; a1.begin = tomorrow_begin; a1.end = tomorrow_end; + a1.alarms.push_back(Alarm{"Alarm Text", "", a1.begin, std::chrono::seconds::zero()}); const auto ubermorgen_begin = now.add_days(2).start_of_day(); const auto ubermorgen_end = ubermorgen_begin.end_of_day(); - Appointment a2; // a non-alarm appointment + Appointment a2; // something else a2.color = "green"; a2.summary = "Other Text"; a2.summary = "http://www.monkey.com/"; a2.uid = "monkey"; - a1.type = Appointment::EVENT; + a2.type = Appointment::EVENT; a2.begin = ubermorgen_begin; a2.end = ubermorgen_end; + a2.alarms.push_back(Alarm{"Alarm Text", "", a2.begin, std::chrono::seconds::zero()}); return std::vector<Appointment>({a1, a2}); } @@ -105,7 +107,7 @@ TEST_F(AlarmQueueFixture, AppointmentsChanged) // Add some appointments to the planner. // One of these matches our state's localtime, so that should get triggered. std::vector<Appointment> a = build_some_appointments(); - a[0].begin = m_state->clock->localtime(); + a[0].begin = a[0].alarms.front().time = m_state->clock->localtime(); m_range_planner->appointments().set(a); // Confirm that it got fired @@ -135,7 +137,8 @@ TEST_F(AlarmQueueFixture, MoreThanOne) { const auto now = m_state->clock->localtime(); std::vector<Appointment> a = build_some_appointments(); - a[0].begin = a[1].begin = now; + a[0].alarms.front().time = now; + a[1].alarms.front().time = now; m_range_planner->appointments().set(a); ASSERT_EQ(2, m_triggered.size()); @@ -151,7 +154,7 @@ TEST_F(AlarmQueueFixture, NoDuplicates) const std::vector<Appointment> appointments = build_some_appointments(); std::vector<Appointment> a; a.push_back(appointments[0]); - a[0].begin = now; + a[0].alarms.front().time = now; m_range_planner->appointments().set(a); ASSERT_EQ(1, m_triggered.size()); EXPECT_EQ(a[0].uid, m_triggered[0]); |