Merge lp:~renatofilho/indicator-datetime/notify-missing-alarm into lp:indicator-datetime/15.10

Proposed by Renato Araujo Oliveira Filho
Status: Superseded
Proposed branch: lp:~renatofilho/indicator-datetime/notify-missing-alarm
Merge into: lp:indicator-datetime/15.10
Diff against target: 1707 lines (+700/-251)
30 files modified
CMakeLists.txt (+5/-2)
data/CMakeLists.txt (+6/-1)
data/indicator-datetime.desktop.in (+1/-0)
debian/control (+5/-0)
include/datetime/appointment.h (+2/-0)
include/datetime/engine-eds.h (+3/-2)
include/datetime/myself.h (+62/-0)
include/notifications/notifications.h (+9/-2)
src/CMakeLists.txt (+1/-0)
src/appointment.cpp (+11/-1)
src/engine-eds.cpp (+197/-200)
src/main.cpp (+6/-2)
src/myself.cpp (+76/-0)
src/notifications.cpp (+122/-5)
src/snap.cpp (+11/-3)
tests/CMakeLists.txt (+3/-1)
tests/notification-fixture.h (+10/-10)
tests/run-eds-ics-test.sh (+9/-1)
tests/test-eds-ics-all-day-events.cpp (+3/-2)
tests/test-eds-ics-missing-trigger.cpp (+2/-1)
tests/test-eds-ics-non-attending-alarms.cpp (+79/-0)
tests/test-eds-ics-non-attending-alarms.ics.in (+53/-0)
tests/test-eds-ics-nonrepeating-events.cpp (+3/-2)
tests/test-eds-ics-repeating-events.cpp (+3/-2)
tests/test-eds-ics-repeating-valarms.cpp (+10/-9)
tests/test-eds-ics-tzids-2.cpp (+2/-1)
tests/test-eds-ics-tzids-utc.cpp (+2/-1)
tests/test-eds-ics-tzids.cpp (+2/-1)
tests/test-notification.cpp (+1/-1)
tests/test-sound.cpp (+1/-1)
To merge this branch: bzr merge lp:~renatofilho/indicator-datetime/notify-missing-alarm
Reviewer Review Type Date Requested Status
Indicator Applet Developers Pending
Review via email: mp+292269@code.launchpad.net

This proposal has been superseded by a proposal from 2016-04-19.

Description of the change

Post message on messaging menu if the notification get timeout.

To post a comment you must log in.
463. By Renato Araujo Oliveira Filho

Fixed crash when clicking on messaging menu.

464. By Renato Araujo Oliveira Filho

Fix memory leak on messaging_menu.

465. By Renato Araujo Oliveira Filho

Vibrate only once when notification about calendar events.

466. By Renato Araujo Oliveira Filho

Fixed as reviewer requested.

467. By Renato Araujo Oliveira Filho

Make use of G_USEC_PER_SEC.

468. By Renato Araujo Oliveira Filho

Update notifications to use the new calendar icon.

469. By Renato Araujo Oliveira Filho

Use calendar app icon.

470. By Renato Araujo Oliveira Filho

Only creates messaging menu if calendar app is instaled.
Update tests.

471. By Renato Araujo Oliveira Filho

Small fixes requeted by charles during the review.

472. By Renato Araujo Oliveira Filho

Detect desktop to launch applications.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-02-03 16:33:39 +0000
3+++ CMakeLists.txt 2016-04-19 13:10:52 +0000
4@@ -20,7 +20,7 @@
5
6 ##
7 ## GNU standard installation directories
8-##
9+##
10
11 include (GNUInstallDirs)
12 if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian
13@@ -47,7 +47,10 @@
14 gstreamer-1.0>=1.2
15 libnotify>=0.7.6
16 url-dispatcher-1>=1
17- properties-cpp>=0.0.1)
18+ properties-cpp>=0.0.1
19+ libaccounts-glib>=1.18
20+ messaging-menu>=12.10
21+ uuid>=2.25)
22 include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
23
24 ##
25
26=== modified file 'data/CMakeLists.txt'
27--- data/CMakeLists.txt 2015-10-13 15:06:25 +0000
28+++ data/CMakeLists.txt 2016-04-19 13:10:52 +0000
29@@ -30,6 +30,7 @@
30
31 # build it
32 set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
33+set (messaging_menu_icon "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/suru/actions/scalable/appointment.svg")
34 configure_file ("${UPSTART_JOB_FILE_IN}" "${UPSTART_JOB_FILE}")
35
36 # install it
37@@ -52,7 +53,11 @@
38 set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
39 configure_file ("${XDG_AUTOSTART_FILE_IN}" "${XDG_AUTOSTART_FILE}")
40
41-# install it
42+# install desktop file used by messaging-menu
43+install (FILES "${XDG_AUTOSTART_FILE}"
44+ DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications/")
45+
46+# install XDG autostart
47 install (FILES "${XDG_AUTOSTART_FILE}"
48 DESTINATION "${XDG_AUTOSTART_DIR}")
49
50
51=== modified file 'data/indicator-datetime.desktop.in'
52--- data/indicator-datetime.desktop.in 2014-04-04 13:05:59 +0000
53+++ data/indicator-datetime.desktop.in 2016-04-19 13:10:52 +0000
54@@ -7,3 +7,4 @@
55 StartupNotify=false
56 Terminal=false
57 AutostartCondition=GNOME3 unless-session gnome
58+Icon=@messaging_menu_icon@
59
60=== modified file 'debian/control'
61--- debian/control 2016-03-10 16:20:36 +0000
62+++ debian/control 2016-04-19 13:10:52 +0000
63@@ -29,6 +29,11 @@
64 gvfs-daemons,
65 # for phone alarm/calendar notification sound tests:
66 ubuntu-touch-sounds,
67+# for query user e-mails based on accounts
68+ libaccounts-glib-dev,
69+# messaging menu integration
70+ libmessaging-menu-dev,
71+ uuid-dev,
72 Standards-Version: 3.9.3
73 Homepage: https://launchpad.net/indicator-datetime
74 # If you aren't a member of ~indicator-applet-developers but need to upload
75
76=== modified file 'include/datetime/appointment.h'
77--- include/datetime/appointment.h 2016-03-16 14:42:06 +0000
78+++ include/datetime/appointment.h 2016-04-19 13:10:52 +0000
79@@ -39,6 +39,8 @@
80 DateTime time;
81
82 bool operator== (const Alarm& that) const;
83+ bool has_sound() const;
84+ bool has_text() const;
85 };
86
87 /**
88
89=== modified file 'include/datetime/engine-eds.h'
90--- include/datetime/engine-eds.h 2014-12-08 02:52:50 +0000
91+++ include/datetime/engine-eds.h 2016-04-19 13:10:52 +0000
92@@ -36,16 +36,17 @@
93 /****
94 *****
95 ****/
96+class Myself;
97
98 /**
99 * Class wrapper around EDS so multiple #EdsPlanners can share resources
100- *
101+ *
102 * @see EdsPlanner
103 */
104 class EdsEngine: public Engine
105 {
106 public:
107- EdsEngine();
108+ EdsEngine(const std::shared_ptr<Myself> &myself);
109 ~EdsEngine();
110
111 void get_appointments(const DateTime& begin,
112
113=== added file 'include/datetime/myself.h'
114--- include/datetime/myself.h 1970-01-01 00:00:00 +0000
115+++ include/datetime/myself.h 2016-04-19 13:10:52 +0000
116@@ -0,0 +1,62 @@
117+/*
118+ * Copyright 2016 Canonical Ltd.
119+ *
120+ * This program is free software: you can redistribute it and/or modify it
121+ * under the terms of the GNU General Public License version 3, as published
122+ * by the Free Software Foundation.
123+ *
124+ * This program is distributed in the hope that it will be useful, but
125+ * WITHOUT ANY WARRANTY; without even the implied warranties of
126+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
127+ * PURPOSE. See the GNU General Public License for more details.
128+ *
129+ * You should have received a copy of the GNU General Public License along
130+ * with this program. If not, see <http://www.gnu.org/licenses/>.
131+ *
132+ * Authors:
133+ * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
134+ */
135+
136+#ifndef INDICATOR_DATETIME_MYSELF_H
137+#define INDICATOR_DATETIME_MYSELF_H
138+
139+#include <core/property.h>
140+
141+#include <string>
142+#include <set>
143+#include <memory.h>
144+#include <glib.h>
145+
146+typedef struct _AgManager AgManager;
147+
148+namespace unity {
149+namespace indicator {
150+namespace datetime {
151+
152+class Myself
153+{
154+public:
155+ Myself();
156+
157+ const core::Property<std::set<std::string>>& emails()
158+ {
159+ return m_emails;
160+ }
161+
162+ bool isMyEmail(const std::string &email);
163+
164+private:
165+ std::shared_ptr<AgManager> m_accounts_manager;
166+ core::Property<std::set<std::string> > m_emails;
167+
168+ static void on_accounts_changed(AgManager*, guint, Myself*);
169+ void reloadEmails();
170+
171+};
172+
173+
174+} // namespace datetime
175+} // namespace indicator
176+} // namespace unity
177+
178+#endif // INDICATOR_DATETIME_MYSELF_H
179
180=== modified file 'include/notifications/notifications.h'
181--- include/notifications/notifications.h 2014-09-19 14:35:07 +0000
182+++ include/notifications/notifications.h 2016-04-19 13:10:52 +0000
183@@ -50,6 +50,8 @@
184
185 void set_icon_name (const std::string& icon_name);
186
187+ void set_start_time(uint64_t time);
188+
189 /* Set an interval, after which the notification will automatically
190 be closed. If not set, the notification server's default timeout
191 is used. */
192@@ -62,19 +64,24 @@
193 static constexpr char const * HINT_NONSHAPED_ICON {"x-canonical-non-shaped-icon"};
194 static constexpr char const * HINT_AFFIRMATIVE_HINT {"x-canonical-private-affirmative-tint"};
195 static constexpr char const * HINT_REJECTION_TINT {"x-canonical-private-rejection-tint"};
196+ static constexpr char const * HINT_INTERACTIVE {"x-canonical-switch-to-application"};
197
198 /* Add an action button.
199 This may fail if the Engine doesn't support actions.
200 @see Engine::supports_actions() */
201 void add_action (const std::string& action, const std::string& label);
202
203- /** Sets the closed callback. This will be called exactly once. */
204+ /** Sets the closed callback. This will be called exactly once. After notification dissapear */
205 void set_closed_callback (std::function<void(const std::string& action)>);
206
207+ /** Sets the time-out callback. This will be called exactly once. */
208+ void set_missed_click_callback (std::function<void()>);
209+
210+
211 private:
212 friend class Engine;
213 class Impl;
214- std::unique_ptr<Impl> impl;
215+ std::shared_ptr<Impl> impl;
216 };
217
218 /**
219
220=== modified file 'src/CMakeLists.txt'
221--- src/CMakeLists.txt 2015-09-01 09:52:13 +0000
222+++ src/CMakeLists.txt 2016-04-19 13:10:52 +0000
223@@ -23,6 +23,7 @@
224 locations.cpp
225 locations-settings.cpp
226 menu.cpp
227+ myself.cpp
228 notifications.cpp
229 planner.cpp
230 planner-aggregate.cpp
231
232=== modified file 'src/appointment.cpp'
233--- src/appointment.cpp 2015-04-05 22:27:52 +0000
234+++ src/appointment.cpp 2016-04-19 13:10:52 +0000
235@@ -31,7 +31,17 @@
236 {
237 return (text==that.text)
238 && (audio_url==that.audio_url)
239- && (this->time==that.time);
240+ && (this->time==that.time);
241+}
242+
243+bool Alarm::has_sound() const
244+{
245+ return !audio_url.empty();
246+}
247+
248+bool Alarm::has_text() const
249+{
250+ return !text.empty();
251 }
252
253 bool Appointment::operator==(const Appointment& that) const
254
255=== modified file 'src/engine-eds.cpp'
256--- src/engine-eds.cpp 2016-03-22 19:32:25 +0000
257+++ src/engine-eds.cpp 2016-04-19 13:10:52 +0000
258@@ -18,6 +18,7 @@
259 */
260
261 #include <datetime/engine-eds.h>
262+#include <datetime/myself.h>
263
264 #include <libical/ical.h>
265 #include <libical/icaltime.h>
266@@ -48,7 +49,8 @@
267 {
268 public:
269
270- Impl()
271+ Impl(const std::shared_ptr<Myself> &myself)
272+ : m_myself(myself)
273 {
274 auto cancellable_deleter = [](GCancellable * c) {
275 g_cancellable_cancel(c);
276@@ -56,8 +58,10 @@
277 };
278
279 m_cancellable = std::shared_ptr<GCancellable>(g_cancellable_new(), cancellable_deleter);
280-
281 e_source_registry_new(m_cancellable.get(), on_source_registry_ready, this);
282+ m_myself->emails().changed().connect([this](const std::set<std::string> &) {
283+ set_dirty_soon();
284+ });
285 }
286
287 ~Impl()
288@@ -92,26 +96,19 @@
289 /**
290 *** init the default timezone
291 **/
292-
293 icaltimezone * default_timezone = nullptr;
294 const auto tz = timezone.timezone.get().c_str();
295- if (tz && *tz)
296- {
297- default_timezone = icaltimezone_get_builtin_timezone(tz);
298-
299- if (default_timezone == nullptr) // maybe str is a tzid?
300- default_timezone = icaltimezone_get_builtin_timezone_from_tzid(tz);
301-
302- g_debug("default_timezone is %p", (void*)default_timezone);
303+ auto gtz = timezone_from_name(tz, nullptr, nullptr, &default_timezone);
304+ if (gtz == nullptr) {
305+ gtz = g_time_zone_new_local();
306 }
307
308+ g_debug("default_timezone is %s", default_timezone ? icaltimezone_get_display_name(default_timezone) : "null");
309+
310 /**
311 *** walk through the sources to build the appointment list
312 **/
313
314- auto gtz = default_timezone != nullptr
315- ? g_time_zone_new(icaltimezone_get_location(default_timezone))
316- : g_time_zone_new_local();
317 auto main_task = std::make_shared<Task>(this, func, default_timezone, gtz, begin, end);
318
319 for (auto& kv : m_clients)
320@@ -125,35 +122,14 @@
321 auto extension = e_source_get_extension(source, E_SOURCE_EXTENSION_CALENDAR);
322 const auto color = e_source_selectable_get_color(E_SOURCE_SELECTABLE(extension));
323
324- auto begin_str = isodate_from_time_t(begin.to_unix());
325- auto end_str = isodate_from_time_t(end.to_unix());
326- auto sexp_fmt = g_strdup_printf("(%%s? (make-time \"%s\") (make-time \"%s\"))", begin_str, end_str);
327- g_clear_pointer(&begin_str, g_free);
328- g_clear_pointer(&end_str, g_free);
329-
330- // ask EDS about alarms that occur in this window...
331- auto sexp = g_strdup_printf(sexp_fmt, "has-alarms-in-range");
332- g_debug("%s alarm sexp is %s", G_STRLOC, sexp);
333- e_cal_client_get_object_list_as_comps(
334- client,
335- sexp,
336- m_cancellable.get(),
337- on_alarm_component_list_ready,
338- new ClientSubtask(main_task, client, m_cancellable, color));
339- g_clear_pointer(&sexp, g_free);
340-
341- // ask EDS about events that occur in this window...
342- sexp = g_strdup_printf(sexp_fmt, "occur-in-time-range");
343- g_debug("%s event sexp is %s", G_STRLOC, sexp);
344- e_cal_client_get_object_list_as_comps(
345- client,
346- sexp,
347- m_cancellable.get(),
348- on_event_component_list_ready,
349- new ClientSubtask(main_task, client, m_cancellable, color));
350- g_clear_pointer(&sexp, g_free);
351-
352- g_clear_pointer(&sexp_fmt, g_free);
353+ e_cal_client_generate_instances(
354+ client,
355+ begin.to_unix(),
356+ end.to_unix(),
357+ m_cancellable.get(),
358+ on_event_generated,
359+ new ClientSubtask(main_task, client, m_cancellable, color),
360+ on_event_generated_list_ready);
361 }
362 }
363
364@@ -591,6 +567,8 @@
365 ECalClient* client;
366 std::shared_ptr<GCancellable> cancellable;
367 std::string color;
368+ GList *components;
369+ GList *instance_components;
370
371 ClientSubtask(const std::shared_ptr<Task>& task_in,
372 ECalClient* client_in,
373@@ -598,10 +576,13 @@
374 const char* color_in):
375 task(task_in),
376 client(client_in),
377- cancellable(cancellable_in)
378+ cancellable(cancellable_in),
379+ components(nullptr),
380+ instance_components(nullptr)
381 {
382 if (color_in)
383 color = color_in;
384+
385 }
386 };
387
388@@ -622,7 +603,7 @@
389 return ret;
390 }
391
392- static std::string get_alarm_sound_url(ECalComponentAlarm * alarm)
393+ static std::string get_alarm_sound_url(ECalComponentAlarm * alarm, const std::string & default_sound)
394 {
395 std::string ret;
396
397@@ -643,92 +624,129 @@
398
399 icalattach_unref(attach);
400 }
401+ if (ret.empty())
402+ ret = default_sound;
403 }
404
405 return ret;
406 }
407
408- static void
409- on_alarm_component_list_ready(GObject * oclient,
410- GAsyncResult * res,
411- gpointer gsubtask)
412- {
413- GError * error = NULL;
414- GSList * comps_slist = NULL;
415- auto subtask = static_cast<ClientSubtask*>(gsubtask);
416-
417- if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient),
418- res,
419- &comps_slist,
420- &error))
421- {
422- // _generate_alarms takes a GList, so make a shallow one
423- GList * comps_list = nullptr;
424- for (auto l=comps_slist; l!=nullptr; l=l->next)
425- comps_list = g_list_prepend(comps_list, l->data);
426-
427- constexpr std::array<ECalComponentAlarmAction,1> omit = {
428- (ECalComponentAlarmAction)-1
429- }; // list of action types to omit, terminated with -1
430- GSList * comp_alarms = nullptr;
431- e_cal_util_generate_alarms_for_list(
432- comps_list,
433- subtask->task->begin.to_unix(),
434- subtask->task->end.to_unix(),
435- const_cast<ECalComponentAlarmAction*>(omit.data()),
436- &comp_alarms,
437- e_cal_client_resolve_tzid_cb,
438- oclient,
439- subtask->task->default_timezone);
440-
441- // walk the alarms & add them
442- for (auto l=comp_alarms; l!=nullptr; l=l->next)
443- add_alarms_to_subtask(static_cast<ECalComponentAlarms*>(l->data), subtask, subtask->task->gtz);
444-
445- // cleanup
446- e_cal_free_alarms(comp_alarms);
447- g_list_free(comps_list);
448- e_cal_client_free_ecalcomp_slist(comps_slist);
449- }
450- else if (error != nullptr)
451- {
452- if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
453- g_warning("can't get ecalcomponent list: %s", error->message);
454-
455- g_error_free(error);
456- }
457-
458- delete subtask;
459- }
460-
461- static void
462- on_event_component_list_ready(GObject * oclient,
463- GAsyncResult * res,
464- gpointer gsubtask)
465- {
466- GError * error = NULL;
467- GSList * comps_slist = NULL;
468- auto subtask = static_cast<ClientSubtask*>(gsubtask);
469-
470- if (e_cal_client_get_object_list_as_comps_finish(E_CAL_CLIENT(oclient),
471- res,
472- &comps_slist,
473- &error))
474- {
475- for (auto l=comps_slist; l!=nullptr; l=l->next)
476- add_event_to_subtask(static_cast<ECalComponent*>(l->data), subtask, subtask->task->gtz);
477-
478- e_cal_client_free_ecalcomp_slist(comps_slist);
479- }
480- else if (error != nullptr)
481- {
482- if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
483- g_warning("can't get ecalcomponent list: %s", error->message);
484-
485- g_error_free(error);
486- }
487-
488- delete subtask;
489+ static gboolean
490+ on_event_generated(ECalComponent *comp,
491+ time_t,
492+ time_t,
493+ gpointer gsubtask)
494+ {
495+ auto subtask = static_cast<ClientSubtask*>(gsubtask);
496+ const gchar *uid = nullptr;
497+ e_cal_component_get_uid (comp, &uid);
498+ g_object_ref(comp);
499+ if (e_cal_component_is_instance(comp))
500+ subtask->instance_components = g_list_append(subtask->instance_components, comp);
501+ else
502+ subtask->components = g_list_append(subtask->components, comp);
503+ return TRUE;
504+ }
505+
506+ static void
507+ on_event_generated_list_ready(gpointer gsubtask)
508+ {
509+ auto subtask = static_cast<ClientSubtask*>(gsubtask);
510+
511+ // generate alarms
512+ constexpr std::array<ECalComponentAlarmAction,1> omit = {
513+ (ECalComponentAlarmAction)-1
514+ }; // list of action types to omit, terminated with -1
515+ GSList * comp_alarms = nullptr;
516+
517+ // we do not need translate tz for instance events,
518+ // they are aredy in the correct time
519+ e_cal_util_generate_alarms_for_list(
520+ subtask->instance_components,
521+ subtask->task->begin.to_unix(),
522+ subtask->task->end.to_unix(),
523+ const_cast<ECalComponentAlarmAction*>(omit.data()),
524+ &comp_alarms,
525+ e_cal_client_resolve_tzid_cb,
526+ subtask->client,
527+ nullptr);
528+
529+ // convert timezone for non-instance events
530+ e_cal_util_generate_alarms_for_list(
531+ subtask->components,
532+ subtask->task->begin.to_unix(),
533+ subtask->task->end.to_unix(),
534+ const_cast<ECalComponentAlarmAction*>(omit.data()),
535+ &comp_alarms,
536+ e_cal_client_resolve_tzid_cb,
537+ subtask->client,
538+ subtask->task->default_timezone);
539+
540+ // walk the alarms & add them
541+ for (auto l=comp_alarms; l!=nullptr; l=l->next)
542+ add_alarms_to_subtask(static_cast<ECalComponentAlarms*>(l->data), subtask, subtask->task->gtz);
543+
544+ subtask->components = g_list_concat(subtask->components, subtask->instance_components);
545+ // add events without alarm
546+ for (auto l=subtask->components; l!=nullptr; l=l->next) {
547+ auto component = static_cast<ECalComponent*>(l->data);
548+ if (!e_cal_component_has_alarms(component))
549+ add_event_to_subtask(component, subtask, subtask->task->gtz);
550+ }
551+ g_list_free_full(subtask->components, g_object_unref);
552+ e_cal_free_alarms(comp_alarms);
553+ delete subtask;
554+ }
555+
556+ static GTimeZone *
557+ timezone_from_name (const char * tzid,
558+ ECalClient * client,
559+ GCancellable * cancellable,
560+ icaltimezone **itimezone)
561+ {
562+ if (tzid == nullptr)
563+ return nullptr;
564+
565+ auto itz = icaltimezone_get_builtin_timezone_from_tzid(tzid); // usually works
566+
567+ if (itz == nullptr) // fallback
568+ itz = icaltimezone_get_builtin_timezone(tzid);
569+
570+ if (client && (itz == nullptr)) // ok we have a strange tzid... ask EDS to look it up in VTIMEZONES
571+ e_cal_client_get_timezone_sync(client, tzid, &itz, cancellable, nullptr);
572+
573+ const char* identifier {};
574+ if (itimezone)
575+ *itimezone = itz;
576+
577+ if (itz != nullptr)
578+ {
579+ identifier = icaltimezone_get_display_name(itz);
580+
581+ if (identifier == nullptr)
582+ identifier = icaltimezone_get_location(itz);
583+ }
584+
585+ // handle the TZID /freeassociation.sourceforge.net/Tzfile/[Location] case
586+ if (identifier != nullptr)
587+ {
588+ const char* pch;
589+ const char* key = "/freeassociation.sourceforge.net/";
590+ if ((pch = strstr(identifier, key)))
591+ {
592+ identifier = pch + strlen(key);
593+ key = "Tzfile/"; // some don't have this, so check for it separately
594+ if ((pch = strstr(identifier, key)))
595+ identifier = pch + strlen(key);
596+ }
597+ }
598+
599+ if (identifier == nullptr)
600+ g_warning("Unrecognized TZID: '%s'", tzid);
601+ else
602+ return g_time_zone_new(identifier);
603+
604+ return nullptr;
605 }
606
607 static DateTime
608@@ -740,51 +758,9 @@
609 DateTime out;
610 g_return_val_if_fail(in.value != nullptr, out);
611
612- GTimeZone * gtz {};
613- if (in.tzid != nullptr)
614- {
615- auto itz = icaltimezone_get_builtin_timezone_from_tzid(in.tzid); // usually works
616-
617- if (itz == nullptr) // fallback
618- itz = icaltimezone_get_builtin_timezone(in.tzid);
619-
620- if (itz == nullptr) // ok we have a strange tzid... ask EDS to look it up in VTIMEZONES
621- e_cal_client_get_timezone_sync(client, in.tzid, &itz, cancellable.get(), nullptr);
622-
623- const char* identifier {};
624-
625- if (itz != nullptr)
626- {
627- identifier = icaltimezone_get_display_name(itz);
628-
629- if (identifier == nullptr)
630- identifier = icaltimezone_get_location(itz);
631- }
632-
633- // handle the TZID /freeassociation.sourceforge.net/Tzfile/[Location] case
634- if (identifier != nullptr)
635- {
636- const char* pch;
637- const char* key = "/freeassociation.sourceforge.net/";
638- if ((pch = strstr(identifier, key)))
639- {
640- identifier = pch + strlen(key);
641- key = "Tzfile/"; // some don't have this, so check for it separately
642- if ((pch = strstr(identifier, key)))
643- identifier = pch + strlen(key);
644- }
645- }
646-
647- if (identifier == nullptr)
648- g_warning("Unrecognized TZID: '%s'", in.tzid);
649-
650- gtz = g_time_zone_new(identifier);
651- g_debug("%s eccdt.tzid -> offset is %d", G_STRLOC, in.tzid, (int)g_time_zone_get_offset(gtz,0));
652- }
653- else
654- {
655+ GTimeZone * gtz = timezone_from_name(in.tzid, client, cancellable.get(), nullptr);
656+ if (gtz == nullptr)
657 gtz = g_time_zone_ref(default_timezone);
658- }
659
660 out = DateTime(gtz,
661 in.value->year,
662@@ -797,7 +773,7 @@
663 return out;
664 }
665
666- static bool
667+ bool
668 is_component_interesting(ECalComponent * component)
669 {
670 // we only want calendar events and vtodos
671@@ -823,6 +799,28 @@
672 disabled = true;
673 }
674 e_cal_component_free_categories_list(categ_list);
675+
676+ if (!disabled) {
677+ // we don't want not attending alarms
678+ // check if the user is part of attendee list if we found it check the status
679+ GSList *attendeeList = nullptr;
680+ e_cal_component_get_attendee_list(component, &attendeeList);
681+
682+ for (GSList *attendeeIter=attendeeList; attendeeIter != nullptr; attendeeIter = attendeeIter->next) {
683+ ECalComponentAttendee *attendee = static_cast<ECalComponentAttendee *>(attendeeIter->data);
684+ if (attendee->value) {
685+ if (strncmp(attendee->value, "mailto:", 7) == 0) {
686+ if (m_myself->isMyEmail(attendee->value+7)) {
687+ disabled = (attendee->status == ICAL_PARTSTAT_DECLINED);
688+ break;
689+ }
690+ }
691+ }
692+ }
693+ if (attendeeList)
694+ e_cal_component_free_attendee_list(attendeeList);
695+ }
696+
697 if (disabled)
698 return false;
699
700@@ -903,35 +901,13 @@
701 }
702
703 static void
704- add_event_to_subtask(ECalComponent * component,
705- ClientSubtask * subtask,
706- GTimeZone * gtz)
707- {
708- // events with alarms are covered by add_alarms_to_subtask(),
709- // so skip them here
710- auto auids = e_cal_component_get_alarm_uids(component);
711- const bool has_alarms = auids != nullptr;
712- cal_obj_uid_list_free(auids);
713- if (has_alarms)
714- return;
715-
716- // add it. simple, eh?
717- if (is_component_interesting(component))
718- {
719- Appointment appointment = get_appointment(subtask->client, subtask->cancellable, component, gtz);
720- appointment.color = subtask->color;
721- subtask->task->appointments.push_back(appointment);
722- }
723- }
724-
725- static void
726 add_alarms_to_subtask(ECalComponentAlarms * comp_alarms,
727 ClientSubtask * subtask,
728 GTimeZone * gtz)
729 {
730 auto& component = comp_alarms->comp;
731
732- if (!is_component_interesting(component))
733+ if (!subtask->task->p->is_component_interesting(component))
734 return;
735
736 Appointment baseline = get_appointment(subtask->client, subtask->cancellable, component, gtz);
737@@ -965,13 +941,15 @@
738 auto instance_time = std::make_pair(DateTime{gtz, ai->occur_start},
739 DateTime{gtz, ai->occur_end});
740 auto trigger_time = DateTime{gtz, ai->trigger};
741-
742 auto& alarm = alarms[instance_time][trigger_time];
743-
744 if (alarm.text.empty())
745 alarm.text = get_alarm_text(a);
746+
747 if (alarm.audio_url.empty())
748- alarm.audio_url = get_alarm_sound_url(a);
749+ alarm.audio_url = get_alarm_sound_url(a, (baseline.is_ubuntu_alarm() ?
750+ "file://" ALARM_DEFAULT_SOUND :
751+ "file://" CALENDAR_DEFAULT_SOUND));
752+
753 if (!alarm.time.is_set())
754 alarm.time = trigger_time;
755
756@@ -985,7 +963,25 @@
757 appointment.end = i.first.second;
758 appointment.alarms.reserve(i.second.size());
759 for (auto& j : i.second)
760- appointment.alarms.push_back(j.second);
761+ {
762+ if (j.second.has_text() || j.second.has_sound())
763+ appointment.alarms.push_back(j.second);
764+ }
765+ subtask->task->appointments.push_back(appointment);
766+ }
767+ }
768+
769+
770+ static void
771+ add_event_to_subtask(ECalComponent * component,
772+ ClientSubtask * subtask,
773+ GTimeZone * gtz)
774+ {
775+ // add it. simple, eh?
776+ if (subtask->task->p->is_component_interesting(component))
777+ {
778+ Appointment appointment = get_appointment(subtask->client, subtask->cancellable, component, gtz);
779+ appointment.color = subtask->color;
780 subtask->task->appointments.push_back(appointment);
781 }
782 }
783@@ -1062,14 +1058,15 @@
784 ESourceRegistry* m_source_registry {};
785 guint m_rebuild_tag {};
786 time_t m_rebuild_deadline {};
787+ std::shared_ptr<Myself> m_myself;
788 };
789
790 /***
791 ****
792 ***/
793
794-EdsEngine::EdsEngine():
795- p(new Impl())
796+EdsEngine::EdsEngine(const std::shared_ptr<Myself> &myself):
797+ p(new Impl(myself))
798 {
799 }
800
801
802=== modified file 'src/main.cpp'
803--- src/main.cpp 2016-04-12 17:03:36 +0000
804+++ src/main.cpp 2016-04-19 13:10:52 +0000
805@@ -25,6 +25,7 @@
806 #include <datetime/exporter.h>
807 #include <datetime/locations-settings.h>
808 #include <datetime/menu.h>
809+#include <datetime/myself.h>
810 #include <datetime/planner-aggregate.h>
811 #include <datetime/planner-snooze.h>
812 #include <datetime/planner-range.h>
813@@ -58,7 +59,7 @@
814 if (!g_strcmp0("lightdm", g_get_user_name()))
815 engine.reset(new MockEngine);
816 else
817- engine.reset(new EdsEngine);
818+ engine.reset(new EdsEngine(std::shared_ptr<Myself>(new Myself)));
819
820 return engine;
821 }
822@@ -151,7 +152,10 @@
823 auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm& alarm) {
824 snooze_planner->add(appointment, alarm);
825 };
826- auto on_ok = [](const Appointment&, const Alarm&){};
827+ auto on_ok = [actions](const Appointment& app, const Alarm&){
828+ //TODO: add support for desktop
829+ actions->phone_open_appointment(app, app.begin);
830+ };
831 auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment, const Alarm& alarm) {
832 (*snap)(appointment, alarm, on_snooze, on_ok);
833 engine->disable_ubuntu_alarm(appointment);
834
835=== added file 'src/myself.cpp'
836--- src/myself.cpp 1970-01-01 00:00:00 +0000
837+++ src/myself.cpp 2016-04-19 13:10:52 +0000
838@@ -0,0 +1,76 @@
839+/*
840+ * Copyright 2016 Canonical Ltd.
841+ *
842+ * This program is free software: you can redistribute it and/or modify it
843+ * under the terms of the GNU General Public License version 3, as published
844+ * by the Free Software Foundation.
845+ *
846+ * This program is distributed in the hope that it will be useful, but
847+ * WITHOUT ANY WARRANTY; without even the implied warranties of
848+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
849+ * PURPOSE. See the GNU General Public License for more details.
850+ *
851+ * You should have received a copy of the GNU General Public License along
852+ * with this program. If not, see <http://www.gnu.org/licenses/>.
853+ *
854+ * Authors:
855+ * Renato Araujo Oliveira Filho <renato.filho@canonical.com>
856+ */
857+
858+#include "datetime/myself.h"
859+
860+#include <libaccounts-glib/ag-manager.h>
861+#include <libaccounts-glib/ag-account.h>
862+
863+#include <algorithm>
864+
865+namespace unity {
866+namespace indicator {
867+namespace datetime {
868+
869+Myself::Myself()
870+ : m_accounts_manager(ag_manager_new(), g_object_unref)
871+{
872+ reloadEmails();
873+ g_object_connect(m_accounts_manager.get(),
874+ "signal::account-created", on_accounts_changed, this,
875+ "signal::account-deleted", on_accounts_changed, this,
876+ "signal::account-updated", on_accounts_changed, this,
877+ nullptr);
878+}
879+
880+bool Myself::isMyEmail(const std::string &email)
881+{
882+ return m_emails.get().count(email) > 0;
883+}
884+
885+void Myself::on_accounts_changed(AgManager *, guint, Myself *self)
886+{
887+ self->reloadEmails();
888+}
889+
890+void Myself::reloadEmails()
891+{
892+ std::set<std::string> emails;
893+
894+ auto manager = m_accounts_manager.get();
895+ auto ids = ag_manager_list(manager);
896+ for (auto l=ids; l!=nullptr; l=l->next)
897+ {
898+ auto acc = ag_manager_get_account(manager, GPOINTER_TO_UINT(l->data));
899+ if (acc) {
900+ auto account_name = ag_account_get_display_name(acc);
901+ if (account_name != nullptr)
902+ emails.insert(account_name);
903+ g_object_unref(acc);
904+ }
905+ }
906+ ag_manager_list_free(ids);
907+
908+ m_emails.set(emails);
909+}
910+
911+} // namespace datetime
912+} // namespace indicator
913+} // namespace unity
914+
915
916=== modified file 'src/notifications.cpp'
917--- src/notifications.cpp 2014-09-17 17:08:01 +0000
918+++ src/notifications.cpp 2016-04-19 13:10:52 +0000
919@@ -21,10 +21,18 @@
920
921 #include <libnotify/notify.h>
922
923+#include <messaging-menu/messaging-menu-app.h>
924+#include <messaging-menu/messaging-menu-message.h>
925+
926+
927+#include <uuid/uuid.h>
928+
929 #include <map>
930 #include <set>
931 #include <string>
932 #include <vector>
933+#include <memory>
934+
935
936 namespace unity {
937 namespace indicator {
938@@ -45,9 +53,11 @@
939 std::string m_body;
940 std::string m_icon_name;
941 std::chrono::seconds m_duration;
942+ gint64 m_start_time;
943 std::set<std::string> m_string_hints;
944 std::vector<std::pair<std::string,std::string>> m_actions;
945 std::function<void(const std::string&)> m_closed_callback;
946+ std::function<void()> m_missed_click_callback;
947 };
948
949 Builder::Builder():
950@@ -101,6 +111,18 @@
951 impl->m_closed_callback.swap (cb);
952 }
953
954+void
955+Builder::set_missed_click_callback (std::function<void()> cb)
956+{
957+ impl->m_missed_click_callback.swap (cb);
958+}
959+
960+void
961+Builder::set_start_time (uint64_t time)
962+{
963+ impl->m_start_time = time;
964+}
965+
966 /***
967 ****
968 ***/
969@@ -110,23 +132,39 @@
970 struct notification_data
971 {
972 std::shared_ptr<NotifyNotification> nn;
973- std::function<void(const std::string&)> closed_callback;
974+ Builder::Impl data;
975+ };
976+
977+ struct messaging_menu_data
978+ {
979+ std::shared_ptr<MessagingMenuMessage> mm;
980+ std::function<void()> callback;
981 };
982
983 public:
984
985 Impl(const std::string& app_name):
986+ m_messaging_app(messaging_menu_app_new(DATETIME_INDICATOR_DESKTOP_FILE), g_object_unref),
987 m_app_name(app_name)
988 {
989 if (!notify_init(app_name.c_str()))
990 g_critical("Unable to initialize libnotify!");
991+
992+ // messaging menu
993+ GIcon *icon = g_themed_icon_new("calendar-app");
994+
995+ messaging_menu_app_register(m_messaging_app.get());
996+ messaging_menu_app_append_source(m_messaging_app.get(), m_app_name.c_str(), icon, "Calendar");
997+ g_object_unref(icon);
998 }
999
1000 ~Impl()
1001 {
1002 close_all ();
1003+ remove_all ();
1004
1005 notify_uninit ();
1006+ messaging_menu_app_unregister (m_messaging_app.get());
1007 }
1008
1009 const std::string& app_name() const
1010@@ -217,7 +255,7 @@
1011 notification_key_quark(),
1012 GINT_TO_POINTER(key));
1013
1014- m_notifications[key] = { nn, info.m_closed_callback };
1015+ m_notifications[key] = { nn, info };
1016 g_signal_connect (nn.get(), "closed",
1017 G_CALLBACK(on_notification_closed), this);
1018
1019@@ -238,6 +276,59 @@
1020 return ret;
1021 }
1022
1023+ std::string post(const Builder::Impl& data)
1024+ {
1025+ uuid_t message_uuid;
1026+ uuid_generate(message_uuid);
1027+
1028+ char message_id[37];
1029+ uuid_unparse(message_uuid, message_id);
1030+
1031+ GIcon *icon = g_themed_icon_new(data.m_icon_name.c_str());
1032+ std::shared_ptr<MessagingMenuMessage> msg (messaging_menu_message_new(message_id,
1033+ icon,
1034+ data.m_title.c_str(),
1035+ nullptr,
1036+ data.m_body.c_str(),
1037+ data.m_start_time * 1000000), // secs -> microsecs
1038+ g_object_ref);
1039+ g_object_unref(icon);
1040+ if (msg)
1041+ {
1042+ m_messaging_messages[std::string(message_id)] = { msg, data.m_missed_click_callback };
1043+ g_signal_connect(msg.get(), "activate",
1044+ G_CALLBACK(on_message_activated), this);
1045+ messaging_menu_app_append_message(m_messaging_app.get(), msg.get(), m_app_name.c_str(), false);
1046+ return message_id;
1047+ } else {
1048+ g_warning("Fail to create messaging menu message");
1049+ }
1050+ return "";
1051+ }
1052+
1053+ void remove (const std::string &key)
1054+ {
1055+ auto it = m_messaging_messages.find(key);
1056+ if (it != m_messaging_messages.end())
1057+ {
1058+ // tell the server to remove message
1059+ messaging_menu_app_remove_message(m_messaging_app.get(), it->second.mm.get());
1060+ m_messaging_messages.erase(it);
1061+ }
1062+ }
1063+
1064+ void remove_all ()
1065+ {
1066+ // call remove() on all our keys
1067+
1068+ std::set<std::string> keys;
1069+ for (const auto& it : m_messaging_messages)
1070+ keys.insert (it.first);
1071+
1072+ for (const std::string &key : keys)
1073+ remove (key);
1074+ }
1075+
1076 private:
1077
1078 const std::set<std::string>& server_caps() const
1079@@ -279,6 +370,22 @@
1080 static_cast<Impl*>(gself)->remove_closed_notification(GPOINTER_TO_INT(gkey));
1081 }
1082
1083+ static void on_message_activated (MessagingMenuMessage *,
1084+ const char *actionId,
1085+ GVariant *,
1086+ gpointer gself)
1087+ {
1088+ auto self = static_cast<Impl*>(gself);
1089+ auto it = self->m_messaging_messages.find(actionId);
1090+ g_return_if_fail (it != self->m_messaging_messages.end());
1091+ const auto& ndata = it->second;
1092+
1093+ if (ndata.callback)
1094+ ndata.callback();
1095+
1096+ self->m_messaging_messages.erase(it);
1097+ }
1098+
1099 void remove_closed_notification (int key)
1100 {
1101 auto it = m_notifications.find(key);
1102@@ -286,16 +393,20 @@
1103
1104 const auto& ndata = it->second;
1105 auto nn = ndata.nn.get();
1106- if (ndata.closed_callback)
1107+
1108+ if (ndata.data.m_closed_callback)
1109 {
1110 std::string action;
1111-
1112 const GQuark q = notification_action_quark();
1113 const gpointer p = g_object_get_qdata(G_OBJECT(nn), q);
1114 if (p != nullptr)
1115 action = static_cast<const char*>(p);
1116
1117- ndata.closed_callback (action);
1118+ ndata.data.m_closed_callback (action);
1119+ // empty action means that the notification got timeout
1120+ // post a message on messaging menu
1121+ if (action.empty())
1122+ post(ndata.data);
1123 }
1124
1125 m_notifications.erase(it);
1126@@ -305,6 +416,10 @@
1127 ****
1128 ***/
1129
1130+ // messaging menu
1131+ std::shared_ptr<MessagingMenuApp> m_messaging_app;
1132+ std::map<std::string, messaging_menu_data> m_messaging_messages;
1133+
1134 const std::string m_app_name;
1135
1136 // key-to-data
1137@@ -315,6 +430,8 @@
1138 mutable std::set<std::string> m_lazy_caps;
1139
1140 static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"};
1141+ static constexpr char const * DATETIME_INDICATOR_DESKTOP_FILE {"indicator-datetime.desktop"};
1142+ static constexpr char const * DATETIME_INDICATOR_SOURCE_ID {"indicator-datetime"};
1143 };
1144
1145 /***
1146
1147=== modified file 'src/snap.cpp'
1148--- src/snap.cpp 2016-02-03 17:06:31 +0000
1149+++ src/snap.cpp 2016-04-19 13:10:52 +0000
1150@@ -102,7 +102,7 @@
1151
1152 // calendar events are muted in silent mode; alarm clocks never are
1153 std::shared_ptr<uin::Sound> sound;
1154- if (appointment.is_ubuntu_alarm() || !silent_mode()) {
1155+ if (appointment.is_ubuntu_alarm() || (alarm.has_sound() && !silent_mode())) {
1156 // create the sound.
1157 const auto role = appointment.is_ubuntu_alarm() ? "alarm" : "alert";
1158 const auto uri = get_alarm_uri(appointment, alarm, m_settings);
1159@@ -123,8 +123,9 @@
1160 const auto minutes = std::chrono::minutes(m_settings->alarm_duration.get());
1161 uin::Builder b;
1162 b.set_body (appointment.summary);
1163- b.set_icon_name (appointment.is_ubuntu_alarm() ? "alarm-clock" : "reminder");
1164+ b.set_icon_name (appointment.is_ubuntu_alarm() ? "alarm-clock" : "appointment");
1165 b.add_hint (uin::Builder::HINT_NONSHAPED_ICON);
1166+ b.set_start_time (appointment.begin.to_unix());
1167
1168 const char * timefmt;
1169 if (is_locale_12h()) {
1170@@ -150,6 +151,9 @@
1171 b.add_hint (uin::Builder::HINT_AFFIRMATIVE_HINT);
1172 b.add_action ("ok", _("OK"));
1173 b.add_action ("snooze", _("Snooze"));
1174+ } else {
1175+ b.add_hint (uin::Builder::HINT_INTERACTIVE);
1176+ b.add_action ("ok", _("OK"));
1177 }
1178
1179 // add 'sound', 'haptic', and 'awake' objects to the capture so
1180@@ -159,10 +163,14 @@
1181 (const std::string& action){
1182 if (action == "snooze")
1183 snooze(appointment, alarm);
1184- else
1185+ else if (action == "ok")
1186 ok(appointment, alarm);
1187 });
1188
1189+ b.set_missed_click_callback([appointment, alarm, ok](){
1190+ ok(appointment, alarm);
1191+ });
1192+
1193 const auto key = m_engine->show(b);
1194 if (key)
1195 m_notifications.insert (key);
1196
1197=== modified file 'tests/CMakeLists.txt'
1198--- tests/CMakeLists.txt 2016-03-22 18:51:50 +0000
1199+++ tests/CMakeLists.txt 2016-04-19 13:10:52 +0000
1200@@ -85,7 +85,8 @@
1201 ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} # arg2: test executable path
1202 ${TEST_NAME} # arg3: test name
1203 ${CMAKE_CURRENT_SOURCE_DIR}/test-eds-ics-config-files # arg4: base directory for config file template
1204- ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics) # arg5: the ical file for this test
1205+ ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}.ics # arg5: the ical file for this test
1206+ ${CMAKE_CURRENT_SOURCE_DIR}/accounts.db) # arg6: online accounts database
1207 endfunction()
1208 add_eds_ics_test_by_name(test-eds-ics-all-day-events)
1209 add_eds_ics_test_by_name(test-eds-ics-repeating-events)
1210@@ -95,6 +96,7 @@
1211 add_eds_ics_test_by_name(test-eds-ics-tzids)
1212 add_eds_ics_test_by_name(test-eds-ics-tzids-2)
1213 add_eds_ics_test_by_name(test-eds-ics-tzids-utc)
1214+add_eds_ics_test_by_name(test-eds-ics-non-attending-alarms)
1215
1216
1217 # disabling the timezone unit tests because they require
1218
1219=== added file 'tests/accounts.db'
1220Binary files tests/accounts.db 1970-01-01 00:00:00 +0000 and tests/accounts.db 2016-04-19 13:10:52 +0000 differ
1221=== modified file 'tests/notification-fixture.h'
1222--- tests/notification-fixture.h 2016-02-10 20:48:24 +0000
1223+++ tests/notification-fixture.h 2016-04-19 13:10:52 +0000
1224@@ -53,7 +53,7 @@
1225 static constexpr char const * HAPTIC_METHOD_VIBRATE_PATTERN {"VibratePattern"};
1226
1227 static constexpr int SCREEN_COOKIE {8675309};
1228- static constexpr char const * SCREEN_METHOD_KEEP_DISPLAY_ON {"keepDisplayOn"};
1229+ static constexpr char const * SCREEN_METHOD_KEEP_DISPLAY_ON {"keepDisplayOn"};
1230 static constexpr char const * SCREEN_METHOD_REMOVE_DISPLAY_ON_REQUEST {"removeDisplayOnRequest"};
1231
1232 static constexpr int POWERD_SYS_STATE_ACTIVE = 1;
1233@@ -111,7 +111,7 @@
1234 const auto christmas = unity::indicator::datetime::DateTime::Local(2015,12,25,0,0,0);
1235 appt.begin = christmas.start_of_day();
1236 appt.end = christmas.end_of_day();
1237- appt.alarms.push_back(unity::indicator::datetime::Alarm{"Ho Ho Ho!", "", appt.begin});
1238+ appt.alarms.push_back(unity::indicator::datetime::Alarm{"Ho Ho Ho!", CALENDAR_DEFAULT_SOUND, appt.begin});
1239
1240 // init an Ubuntu Alarm
1241 ualarm.color = "red";
1242@@ -165,8 +165,8 @@
1243 NOTIFY_INTERFACE,
1244 &error);
1245 g_assert_no_error(error);
1246-
1247- // METHOD_GET_INFO
1248+
1249+ // METHOD_GET_INFO
1250 str = g_strdup("ret = ('mock-notify', 'test vendor', '1.0', '1.1')");
1251 dbus_test_dbus_mock_object_add_method(notify_mock,
1252 notify_obj,
1253@@ -196,7 +196,7 @@
1254 g_assert_no_error (error);
1255 g_free (str);
1256
1257- // METHOD_CLOSE
1258+ // METHOD_CLOSE
1259 str = g_strdup_printf("self.EmitSignal('%s', '%s', 'uu', [ args[0], %d ])",
1260 NOTIFY_INTERFACE,
1261 SIGNAL_CLOSED,
1262@@ -223,8 +223,8 @@
1263 BUS_POWERD_INTERFACE,
1264 &error);
1265 g_assert_no_error(error);
1266-
1267- str = g_strdup_printf ("ret = '%s'", POWERD_COOKIE);
1268+
1269+ str = g_strdup_printf ("ret = '%s'", POWERD_COOKIE);
1270 dbus_test_dbus_mock_object_add_method(powerd_mock,
1271 powerd_obj,
1272 POWERD_METHOD_REQUEST_SYS_STATE,
1273@@ -256,8 +256,8 @@
1274 BUS_SCREEN_INTERFACE,
1275 &error);
1276 g_assert_no_error(error);
1277-
1278- str = g_strdup_printf ("ret = %d", SCREEN_COOKIE);
1279+
1280+ str = g_strdup_printf ("ret = %d", SCREEN_COOKIE);
1281 dbus_test_dbus_mock_object_add_method(screen_mock,
1282 screen_obj,
1283 SCREEN_METHOD_KEEP_DISPLAY_ON,
1284@@ -287,7 +287,7 @@
1285 BUS_HAPTIC_PATH,
1286 BUS_HAPTIC_INTERFACE,
1287 &error);
1288-
1289+
1290 dbus_test_dbus_mock_object_add_method(haptic_mock,
1291 haptic_obj,
1292 HAPTIC_METHOD_VIBRATE_PATTERN,
1293
1294=== modified file 'tests/run-eds-ics-test.sh'
1295--- tests/run-eds-ics-test.sh 2015-07-20 16:31:28 +0000
1296+++ tests/run-eds-ics-test.sh 2016-04-19 13:10:52 +0000
1297@@ -6,6 +6,7 @@
1298 TEST_NAME=$3 # test name
1299 CONFIG_DIR=$4 # config files
1300 ICS_FILE=$5 # ical file holding test data
1301+ACCOUNTS_DB=$6 # online account database
1302
1303 echo "this script: ${SELF}"
1304 echo "test-runner: ${TEST_RUNNER}"
1305@@ -54,8 +55,15 @@
1306 cp --verbose --archive ${ICS_FILE} ${XDG_DATA_HOME}/evolution/tasks/system/tasks.ics
1307 fi
1308
1309+# prepare online accounts database
1310+if [ -e ${ACCOUNTS_DB} ]; then
1311+ echo "copying ${ACCOUNTS_DB} into $HOME"
1312+ mkdir -p ${XDG_CONFIG_HOME}/libaccounts-glib/
1313+ cp --verbose --archive ${ACCOUNTS_DB} ${XDG_CONFIG_HOME}/libaccounts-glib/accounts.db
1314+fi
1315+
1316 # run the test
1317-${TEST_RUNNER} --keep-env --max-wait=90 --task ${TEST_EXEC} --task-name ${TEST_NAME} --wait-until-complete
1318+${TEST_RUNNER} --keep-env --max-wait=90 --task ${TEST_EXEC} --task-name ${TEST_NAME} --wait-until-complete
1319 rv=$?
1320
1321 # if the test passed, blow away the tmpdir
1322
1323=== modified file 'tests/test-eds-ics-all-day-events.cpp'
1324--- tests/test-eds-ics-all-day-events.cpp 2015-05-21 12:47:24 +0000
1325+++ tests/test-eds-ics-all-day-events.cpp 2016-04-19 13:10:52 +0000
1326@@ -22,6 +22,7 @@
1327 #include <datetime/alarm-queue-simple.h>
1328 #include <datetime/clock-mock.h>
1329 #include <datetime/engine-eds.h>
1330+#include <datetime/myself.h>
1331 #include <datetime/planner-range.h>
1332
1333 #include <gtest/gtest.h>
1334@@ -41,7 +42,7 @@
1335 TEST_F(VAlarmFixture, MultipleAppointments)
1336 {
1337 // start the EDS engine
1338- auto engine = std::make_shared<EdsEngine>();
1339+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1340
1341 // we need a consistent timezone for the planner and our local DateTimes
1342 constexpr char const * zone_str {"America/Chicago"};
1343@@ -66,7 +67,7 @@
1344 constexpr int max_wait_sec = 10;
1345 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
1346 }
1347-
1348+
1349 // what we expect to get...
1350 Appointment expected_appt;
1351 expected_appt.uid = "20150521T111538Z-7449-1000-3572-0@ghidorah";
1352
1353=== modified file 'tests/test-eds-ics-missing-trigger.cpp'
1354--- tests/test-eds-ics-missing-trigger.cpp 2015-10-13 14:40:02 +0000
1355+++ tests/test-eds-ics-missing-trigger.cpp 2016-04-19 13:10:52 +0000
1356@@ -22,6 +22,7 @@
1357 #include <datetime/alarm-queue-simple.h>
1358 #include <datetime/clock-mock.h>
1359 #include <datetime/engine-eds.h>
1360+#include <datetime/myself.h>
1361 #include <datetime/planner-range.h>
1362
1363 #include <gtest/gtest.h>
1364@@ -41,7 +42,7 @@
1365 TEST_F(VAlarmFixture, MissingTriggers)
1366 {
1367 // start the EDS engine
1368- auto engine = std::make_shared<EdsEngine>();
1369+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1370
1371 // we need a consistent timezone for the planner and our local DateTimes
1372 constexpr char const * zone_str {"America/Chicago"};
1373
1374=== added file 'tests/test-eds-ics-non-attending-alarms.cpp'
1375--- tests/test-eds-ics-non-attending-alarms.cpp 1970-01-01 00:00:00 +0000
1376+++ tests/test-eds-ics-non-attending-alarms.cpp 2016-04-19 13:10:52 +0000
1377@@ -0,0 +1,79 @@
1378+/*
1379+ * Copyright 2015 Canonical Ltd.
1380+ *
1381+ * This program is free software: you can redistribute it and/or modify it
1382+ * under the terms of the GNU General Public License version 3, as published
1383+ * by the Free Software Foundation.
1384+ *
1385+ * This program is distributed in the hope that it will be useful, but
1386+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1387+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1388+ * PURPOSE. See the GNU General Public License for more details.
1389+ *
1390+ * You should have received a copy of the GNU General Public License along
1391+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1392+ *
1393+ * Authors:
1394+ * Charles Kerr <charles.kerr@canonical.com>
1395+ */
1396+
1397+#include <algorithm>
1398+
1399+#include <datetime/alarm-queue-simple.h>
1400+#include <datetime/clock-mock.h>
1401+#include <datetime/engine-eds.h>
1402+#include <datetime/myself.h>
1403+#include <datetime/planner-range.h>
1404+
1405+#include <gtest/gtest.h>
1406+
1407+#include "glib-fixture.h"
1408+#include "print-to.h"
1409+#include "timezone-mock.h"
1410+#include "wakeup-timer-mock.h"
1411+
1412+using namespace unity::indicator::datetime;
1413+using VAlarmFixture = GlibFixture;
1414+
1415+/***
1416+****
1417+***/
1418+
1419+TEST_F(VAlarmFixture, NonAttendingEvent)
1420+{
1421+ // start the EDS engine
1422+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1423+
1424+ // we need a consistent timezone for the planner and our local DateTimes
1425+ constexpr char const * zone_str {"America/Recife"};
1426+ auto tz = std::make_shared<MockTimezone>(zone_str);
1427+ auto gtz = g_time_zone_new(zone_str);
1428+
1429+ // make a planner that looks at the first half of 2016 in EDS
1430+ auto planner = std::make_shared<SimpleRangePlanner>(engine, tz);
1431+ const DateTime range_begin {gtz, 2016,1, 1, 0, 0, 0.0};
1432+ const DateTime range_end {gtz, 2016,6,31,23,59,59.5};
1433+ planner->range().set(std::make_pair(range_begin, range_end));
1434+
1435+ // give EDS a moment to load
1436+ if (planner->appointments().get().empty()) {
1437+ g_message("waiting a moment for EDS to load...");
1438+ auto on_appointments_changed = [this](const std::vector<Appointment>& appointments){
1439+ g_message("ah, they loaded");
1440+ if (!appointments.empty())
1441+ g_main_loop_quit(loop);
1442+ };
1443+ core::ScopedConnection conn(planner->appointments().changed().connect(on_appointments_changed));
1444+ constexpr int max_wait_sec = 10;
1445+ wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
1446+ }
1447+
1448+ // the planner should match what we've got in the calendar.ics file
1449+ const auto appts = planner->appointments().get();
1450+ EXPECT_EQ(2, appts.size());
1451+ EXPECT_EQ(appts[0].begin, DateTime(gtz, 2016, 4, 4, 16, 0, 0));
1452+ EXPECT_EQ(appts[1].begin, DateTime(gtz, 2016, 4, 6, 16, 0, 0));
1453+
1454+ // cleanup
1455+ g_time_zone_unref(gtz);
1456+}
1457
1458=== added file 'tests/test-eds-ics-non-attending-alarms.ics.in'
1459--- tests/test-eds-ics-non-attending-alarms.ics.in 1970-01-01 00:00:00 +0000
1460+++ tests/test-eds-ics-non-attending-alarms.ics.in 2016-04-19 13:10:52 +0000
1461@@ -0,0 +1,53 @@
1462+BEGIN:VCALENDAR
1463+CALSCALE:GREGORIAN
1464+PRODID:-//Ximian//NONSGML Evolution Calendar//EN
1465+VERSION:2.0
1466+X-EVOLUTION-DATA-REVISION:2015-04-05T21:32:47.354433Z(2)
1467+BEGIN:VEVENT
1468+STATUS:CONFIRMED
1469+DTSTAMP:20160405T152128Z
1470+CREATED:20160405T152128Z
1471+UID:ddtvl069dn2cquo8dhg3j9c360@google.com
1472+SEQUENCE:1
1473+TRANSP:OPAQUE
1474+SUMMARY:Every day at 4PM
1475+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Recife:
1476+ 20160404T160000
1477+RRULE:FREQ=DAILY;UNTIL=20160406T190000Z
1478+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Recife:
1479+ 20160404T170000
1480+ATTENDEE;CN=Uphablet;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;
1481+ CUTYPE=INDIVIDUAL:mailto:uphablet@ubuntu.com
1482+LAST-MODIFIED:20160405T152128Z
1483+BEGIN:VALARM
1484+TRIGGER;VALUE=DURATION:-PT30M
1485+ACTION:EMAIL
1486+DESCRIPTION:This is an event reminder
1487+X-EVOLUTION-ALARM-UID:20160405T152128Z-2848-32011-1844-65@ubuntu-phablet
1488+END:VALARM
1489+END:VEVENT
1490+BEGIN:VEVENT
1491+STATUS:CONFIRMED
1492+DTSTAMP:20160405T152128Z
1493+CREATED:20160405T151054Z
1494+UID:ddtvl069dn2cquo8dhg3j9c360@google.com
1495+SEQUENCE:1
1496+TRANSP:OPAQUE
1497+SUMMARY::Every day at 4PM
1498+DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/America/Fortaleza:
1499+ 20160405T160000
1500+RECURRENCE-ID;TZID=/freeassociation.sourceforge.net/Tzfile/America/Recife:
1501+ 20160405T160000
1502+DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/America/Fortaleza:
1503+ 20160405T170000
1504+ATTENDEE;CN=Uphablet;PARTSTAT=DECLINED;ROLE=REQ-PARTICIPANT;
1505+ CUTYPE=INDIVIDUAL:mailto:uphablet@ubuntu.com
1506+LAST-MODIFIED:20160405T152128Z
1507+BEGIN:VALARM
1508+TRIGGER;VALUE=DURATION:-PT30M
1509+ACTION:EMAIL
1510+DESCRIPTION:This is an event reminder
1511+X-EVOLUTION-ALARM-UID:20160405T152128Z-2848-32011-1844-66@ubuntu-phablet
1512+END:VALARM
1513+END:VEVENT
1514+END:VCALENDAR
1515
1516=== modified file 'tests/test-eds-ics-nonrepeating-events.cpp'
1517--- tests/test-eds-ics-nonrepeating-events.cpp 2015-10-13 14:40:02 +0000
1518+++ tests/test-eds-ics-nonrepeating-events.cpp 2016-04-19 13:10:52 +0000
1519@@ -22,6 +22,7 @@
1520 #include <datetime/alarm-queue-simple.h>
1521 #include <datetime/clock-mock.h>
1522 #include <datetime/engine-eds.h>
1523+#include <datetime/myself.h>
1524 #include <datetime/planner-range.h>
1525
1526 #include <gtest/gtest.h>
1527@@ -41,7 +42,7 @@
1528 TEST_F(VAlarmFixture, MultipleAppointments)
1529 {
1530 // start the EDS engine
1531- auto engine = std::make_shared<EdsEngine>();
1532+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1533
1534 // we need a consistent timezone for the planner and our local DateTimes
1535 constexpr char const * zone_str {"America/Chicago"};
1536@@ -66,7 +67,7 @@
1537 constexpr int max_wait_sec = 10;
1538 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
1539 }
1540-
1541+
1542 // what we expect to get...
1543 Appointment expected_appt;
1544 expected_appt.uid = "20150520T000726Z-3878-32011-1770-81@ubuntu-phablet";
1545
1546=== modified file 'tests/test-eds-ics-repeating-events.cpp'
1547--- tests/test-eds-ics-repeating-events.cpp 2015-10-13 14:40:02 +0000
1548+++ tests/test-eds-ics-repeating-events.cpp 2016-04-19 13:10:52 +0000
1549@@ -22,6 +22,7 @@
1550 #include <datetime/alarm-queue-simple.h>
1551 #include <datetime/clock-mock.h>
1552 #include <datetime/engine-eds.h>
1553+#include <datetime/myself.h>
1554 #include <datetime/planner-range.h>
1555
1556 #include <gtest/gtest.h>
1557@@ -41,7 +42,7 @@
1558 TEST_F(VAlarmFixture, MultipleAppointments)
1559 {
1560 // start the EDS engine
1561- auto engine = std::make_shared<EdsEngine>();
1562+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1563
1564 // we need a consistent timezone for the planner and our local DateTimes
1565 constexpr char const * zone_str {"America/Chicago"};
1566@@ -66,7 +67,7 @@
1567 constexpr int max_wait_sec = 10;
1568 wait_msec(max_wait_sec * G_TIME_SPAN_MILLISECOND);
1569 }
1570-
1571+
1572 // what we expect to get...
1573 Appointment expected_appt;
1574 expected_appt.uid = "20150507T211449Z-4262-32011-1418-1@ubuntu-phablet";
1575
1576=== modified file 'tests/test-eds-ics-repeating-valarms.cpp'
1577--- tests/test-eds-ics-repeating-valarms.cpp 2015-05-20 23:23:32 +0000
1578+++ tests/test-eds-ics-repeating-valarms.cpp 2016-04-19 13:10:52 +0000
1579@@ -22,6 +22,7 @@
1580 #include <datetime/alarm-queue-simple.h>
1581 #include <datetime/clock-mock.h>
1582 #include <datetime/engine-eds.h>
1583+#include <datetime/myself.h>
1584 #include <datetime/planner-range.h>
1585
1586 #include <gtest/gtest.h>
1587@@ -41,7 +42,7 @@
1588 TEST_F(VAlarmFixture, MultipleAppointments)
1589 {
1590 // start the EDS engine
1591- auto engine = std::make_shared<EdsEngine>();
1592+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1593
1594 // we need a consistent timezone for the planner and our local DateTimes
1595 constexpr char const * zone_str {"America/Chicago"};
1596@@ -72,14 +73,14 @@
1597 ASSERT_EQ(1, appts.size());
1598 const auto& appt = appts.front();
1599 ASSERT_EQ(8, appt.alarms.size());
1600- EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,35,0)}), appt.alarms[0]);
1601- EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,37,0)}), appt.alarms[1]);
1602- EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,39,0)}), appt.alarms[2]);
1603- EXPECT_EQ(Alarm({"Time to pack!", "", DateTime(gtz,2015,4,23,13,41,0)}), appt.alarms[3]);
1604- EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,35,0)}), appt.alarms[4]);
1605- EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,37,0)}), appt.alarms[5]);
1606- EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,39,0)}), appt.alarms[6]);
1607- EXPECT_EQ(Alarm({"Go to the airport!", "", DateTime(gtz,2015,4,24,10,41,0)}), appt.alarms[7]);
1608+ EXPECT_EQ(Alarm({"Time to pack!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,23,13,35,0)}), appt.alarms[0]);
1609+ EXPECT_EQ(Alarm({"Time to pack!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,23,13,37,0)}), appt.alarms[1]);
1610+ EXPECT_EQ(Alarm({"Time to pack!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,23,13,39,0)}), appt.alarms[2]);
1611+ EXPECT_EQ(Alarm({"Time to pack!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,23,13,41,0)}), appt.alarms[3]);
1612+ EXPECT_EQ(Alarm({"Go to the airport!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,24,10,35,0)}), appt.alarms[4]);
1613+ EXPECT_EQ(Alarm({"Go to the airport!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,24,10,37,0)}), appt.alarms[5]);
1614+ EXPECT_EQ(Alarm({"Go to the airport!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,24,10,39,0)}), appt.alarms[6]);
1615+ EXPECT_EQ(Alarm({"Go to the airport!", "file://" CALENDAR_DEFAULT_SOUND, DateTime(gtz,2015,4,24,10,41,0)}), appt.alarms[7]);
1616
1617 // now let's try this out with AlarmQueue...
1618 // hook the planner up to a SimpleAlarmQueue and confirm that it triggers for each of the reminders
1619
1620=== modified file 'tests/test-eds-ics-tzids-2.cpp'
1621--- tests/test-eds-ics-tzids-2.cpp 2015-07-09 20:56:13 +0000
1622+++ tests/test-eds-ics-tzids-2.cpp 2016-04-19 13:10:52 +0000
1623@@ -22,6 +22,7 @@
1624 #include <datetime/alarm-queue-simple.h>
1625 #include <datetime/clock-mock.h>
1626 #include <datetime/engine-eds.h>
1627+#include <datetime/myself.h>
1628 #include <datetime/planner-range.h>
1629
1630 #include <gtest/gtest.h>
1631@@ -41,7 +42,7 @@
1632 TEST_F(VAlarmFixture, MultipleAppointments)
1633 {
1634 // start the EDS engine
1635- auto engine = std::make_shared<EdsEngine>();
1636+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1637
1638 // we need a consistent timezone for the planner and our local DateTimes
1639 constexpr char const * zone_str {"America/Los_Angeles"};
1640
1641=== modified file 'tests/test-eds-ics-tzids-utc.cpp'
1642--- tests/test-eds-ics-tzids-utc.cpp 2016-03-22 19:13:10 +0000
1643+++ tests/test-eds-ics-tzids-utc.cpp 2016-04-19 13:10:52 +0000
1644@@ -22,6 +22,7 @@
1645 #include <datetime/alarm-queue-simple.h>
1646 #include <datetime/clock-mock.h>
1647 #include <datetime/engine-eds.h>
1648+#include <datetime/myself.h>
1649 #include <datetime/planner-range.h>
1650
1651 #include <gtest/gtest.h>
1652@@ -41,7 +42,7 @@
1653 TEST_F(VAlarmFixture, UTCAppointments)
1654 {
1655 // start the EDS engine
1656- auto engine = std::make_shared<EdsEngine>();
1657+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1658
1659 // we need a consistent timezone for the planner and our local DateTimes
1660 constexpr char const * zone_str {"America/Recife"};
1661
1662=== modified file 'tests/test-eds-ics-tzids.cpp'
1663--- tests/test-eds-ics-tzids.cpp 2015-07-09 19:14:32 +0000
1664+++ tests/test-eds-ics-tzids.cpp 2016-04-19 13:10:52 +0000
1665@@ -22,6 +22,7 @@
1666 #include <datetime/alarm-queue-simple.h>
1667 #include <datetime/clock-mock.h>
1668 #include <datetime/engine-eds.h>
1669+#include <datetime/myself.h>
1670 #include <datetime/planner-range.h>
1671
1672 #include <gtest/gtest.h>
1673@@ -41,7 +42,7 @@
1674 TEST_F(VAlarmFixture, MultipleAppointments)
1675 {
1676 // start the EDS engine
1677- auto engine = std::make_shared<EdsEngine>();
1678+ auto engine = std::make_shared<EdsEngine>(std::make_shared<Myself>());
1679
1680 // we need a consistent timezone for the planner and our local DateTimes
1681 constexpr char const * zone_str {"Europe/Berlin"};
1682
1683=== modified file 'tests/test-notification.cpp'
1684--- tests/test-notification.cpp 2016-02-11 04:30:46 +0000
1685+++ tests/test-notification.cpp 2016-04-19 13:10:52 +0000
1686@@ -63,7 +63,7 @@
1687 bool expected_notify_called;
1688 bool expected_vibrate_called;
1689 } test_appts[] = {
1690- { appt, "reminder", "Event", true, true },
1691+ { appt, "appointment", "Event", true, true },
1692 { ualarm, "alarm-clock", "Alarm", true, true }
1693 };
1694
1695
1696=== modified file 'tests/test-sound.cpp'
1697--- tests/test-sound.cpp 2016-02-10 20:49:01 +0000
1698+++ tests/test-sound.cpp 2016-04-19 13:10:52 +0000
1699@@ -85,7 +85,7 @@
1700
1701 // confirm that the icon passed to Notify was "alarm-clock"
1702 g_variant_get_child (params, 2, "&s", &str);
1703- ASSERT_STREQ("reminder", str);
1704+ ASSERT_STREQ("appointment", str);
1705
1706 // confirm that the hints passed to Notify included a timeout matching duration_minutes
1707 int32_t i32;

Subscribers

People subscribed via source and target branches