aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/datetime/alarm-queue-simple.h18
-rw-r--r--include/datetime/alarm-queue.h2
-rw-r--r--include/datetime/date-time.h3
-rw-r--r--include/datetime/planner-snooze.h3
-rw-r--r--src/alarm-queue-simple.cpp226
-rw-r--r--src/date-time.cpp10
-rw-r--r--src/main.cpp8
-rw-r--r--src/planner-snooze.cpp16
-rw-r--r--tests/test-alarm-queue.cpp17
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]);