Merge lp:~charlesk/indicator-datetime/lp-1581089-dont-open-clock-app-when-dismissing-alarms into lp:indicator-datetime

Proposed by Charles Kerr
Status: Merged
Approved by: Renato Araujo Oliveira Filho
Approved revision: 452
Merged at revision: 449
Proposed branch: lp:~charlesk/indicator-datetime/lp-1581089-dont-open-clock-app-when-dismissing-alarms
Merge into: lp:indicator-datetime
Diff against target: 375 lines (+175/-45)
7 files modified
include/datetime/snap.h (+3/-3)
src/main.cpp (+14/-8)
src/snap.cpp (+23/-16)
tests/glib-fixture.h (+27/-5)
tests/manual-test-snap.cpp (+9/-7)
tests/test-notification.cpp (+95/-2)
tests/test-sound.cpp (+4/-4)
To merge this branch: bzr merge lp:~charlesk/indicator-datetime/lp-1581089-dont-open-clock-app-when-dismissing-alarms
Reviewer Review Type Date Requested Status
Renato Araujo Oliveira Filho (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+294715@code.launchpad.net

Commit message

Differentiate between the 'OK' action in interactive and noninteractive notifications.

Description of the change

Differentiate between the 'OK' action in interactive and noninteractive notifications because we don't want to pop up clock-app when users hit 'OK' to dismiss an alarm.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
452. By Charles Kerr

in tests/glib-fixture.h, tweaks to the new idle_add() and timeout_add() helpers

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Jenkins isn't going to be happy tho:

 pbuilder-satisfydepends-dummy : Depends: libubuntu-app-launch2-dev (>= 0.9) but it is not going to be installed.

Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

looks good.

review: Approve
453. By Charles Kerr

work around g++ 4.9.2 initializer list issue for vivid

454. By Charles Kerr

fix glib-fixture.h copyright year in header comment

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/datetime/snap.h'
2--- include/datetime/snap.h 2015-10-13 16:06:35 +0000
3+++ include/datetime/snap.h 2016-05-16 21:30:46 +0000
4@@ -44,11 +44,11 @@
5 const std::shared_ptr<const Settings>& settings);
6 virtual ~Snap();
7
8- typedef std::function<void(const Appointment&, const Alarm&)> appointment_func;
9+ enum class Response { None, Snooze, ShowApp };
10+ typedef std::function<void(const Appointment&, const Alarm&, const Response&)> response_func;
11 void operator()(const Appointment& appointment,
12 const Alarm& alarm,
13- appointment_func snooze,
14- appointment_func ok);
15+ response_func on_response);
16
17 private:
18 class Impl;
19
20=== modified file 'src/main.cpp'
21--- src/main.cpp 2016-05-14 02:07:10 +0000
22+++ src/main.cpp 2016-05-16 21:30:46 +0000
23@@ -149,14 +149,20 @@
24 auto sound_builder = std::make_shared<uin::DefaultSoundBuilder>();
25 std::unique_ptr<Snap> snap (new Snap(notification_engine, sound_builder, state->settings));
26 auto alarm_queue = create_simple_alarm_queue(state->clock, snooze_planner, engine, timezone_);
27- auto on_snooze = [snooze_planner](const Appointment& appointment, const Alarm& alarm) {
28- snooze_planner->add(appointment, alarm);
29- };
30- auto on_ok = [actions](const Appointment& app, const Alarm&){
31- actions->open_appointment(app, app.begin);
32- };
33- auto on_alarm_reached = [&engine, &snap, &on_snooze, &on_ok](const Appointment& appointment, const Alarm& alarm) {
34- (*snap)(appointment, alarm, on_snooze, on_ok);
35+ auto on_response = [snooze_planner, actions](const Appointment& appointment, const Alarm& alarm, const Snap::Response& response) {
36+ switch(response) {
37+ case Snap::Response::Snooze:
38+ snooze_planner->add(appointment, alarm);
39+ break;
40+ case Snap::Response::ShowApp:
41+ actions->open_appointment(appointment, appointment.begin);
42+ break;
43+ case Snap::Response::None:
44+ break;
45+ }
46+ };
47+ auto on_alarm_reached = [&engine, &snap, &on_response](const Appointment& appointment, const Alarm& alarm) {
48+ (*snap)(appointment, alarm, on_response);
49 engine->disable_ubuntu_alarm(appointment);
50 };
51 alarm_queue->alarm_reached().connect(on_alarm_reached);
52
53=== modified file 'src/snap.cpp'
54--- src/snap.cpp 2016-05-14 02:07:10 +0000
55+++ src/snap.cpp 2016-05-16 21:30:46 +0000
56@@ -82,8 +82,7 @@
57
58 void operator()(const Appointment& appointment,
59 const Alarm& alarm,
60- appointment_func snooze,
61- appointment_func ok)
62+ response_func on_response)
63 {
64 // If calendar notifications are muted, don't show them
65 if (!appointment.is_ubuntu_alarm() && calendar_events_are_muted()) {
66@@ -151,28 +150,33 @@
67 if (interactive) {
68 b.add_hint (uin::Builder::HINT_SNAP);
69 b.add_hint (uin::Builder::HINT_AFFIRMATIVE_HINT);
70- b.add_action ("ok", _("OK"));
71- b.add_action ("snooze", _("Snooze"));
72+ b.add_action (ACTION_NONE, _("OK"));
73+ b.add_action (ACTION_SNOOZE, _("Snooze"));
74 } else {
75 b.add_hint (uin::Builder::HINT_INTERACTIVE);
76- b.add_action ("ok", _("OK"));
77+ b.add_action (ACTION_SHOW_APP, _("OK"));
78 }
79
80 // add 'sound', 'haptic', and 'awake' objects to the capture so
81 // they stay alive until the closed callback is called; i.e.,
82 // for the lifespan of the notficiation
83- b.set_closed_callback([appointment, alarm, snooze, ok, sound, awake, haptic]
84+ b.set_closed_callback([appointment, alarm, on_response, sound, awake, haptic]
85 (const std::string& action){
86- if (action == "snooze")
87- snooze(appointment, alarm);
88- else if (action == "ok")
89- ok(appointment, alarm);
90+ Snap::Response response;
91+ if (action == ACTION_SNOOZE)
92+ response = Snap::Response::Snooze;
93+ else if (action == ACTION_SHOW_APP)
94+ response = Snap::Response::ShowApp;
95+ else
96+ response = Snap::Response::None;
97+
98+ on_response(appointment, alarm, response);
99 });
100
101- //TODO: we need to extend it to support alarms appoitments
102+ //TODO: we need to extend it to support alarms appointments
103 if (!appointment.is_ubuntu_alarm()) {
104- b.set_timeout_callback([appointment, alarm, ok](){
105- ok(appointment, alarm);
106+ b.set_timeout_callback([appointment, alarm, on_response](){
107+ on_response(appointment, alarm, Snap::Response::ShowApp);
108 });
109 }
110
111@@ -266,6 +270,10 @@
112 std::set<int> m_notifications;
113 GCancellable * m_cancellable {nullptr};
114 AccountsServiceSound * m_accounts_service_sound_proxy {nullptr};
115+
116+ static constexpr char const * ACTION_NONE {"none"};
117+ static constexpr char const * ACTION_SNOOZE {"snooze"};
118+ static constexpr char const * ACTION_SHOW_APP {"show-app"};
119 };
120
121 /***
122@@ -286,10 +294,9 @@
123 void
124 Snap::operator()(const Appointment& appointment,
125 const Alarm& alarm,
126- appointment_func show,
127- appointment_func ok)
128+ response_func on_response)
129 {
130- (*impl)(appointment, alarm, show, ok);
131+ (*impl)(appointment, alarm, on_response);
132 }
133
134 /***
135
136=== modified file 'tests/glib-fixture.h'
137--- tests/glib-fixture.h 2016-04-12 17:26:42 +0000
138+++ tests/glib-fixture.h 2016-05-16 21:30:46 +0000
139@@ -1,5 +1,5 @@
140 /*
141- * Copyright 2013 Canonical Ltd.
142+ * Copyright 2013-2016 Canonical Ltd.
143 *
144 * Authors:
145 * Charles Kerr <charles.kerr@canonical.com>
146@@ -17,9 +17,9 @@
147 * with this program. If not, see <http://www.gnu.org/licenses/>.
148 */
149
150-#ifndef INDICATOR_DATETIME_TESTS_GLIB_FIXTURE_H
151-#define INDICATOR_DATETIME_TESTS_GLIB_FIXTURE_H
152+#pragma once
153
154+#include <chrono>
155 #include <functional> // std::function
156 #include <map>
157 #include <memory> // std::shared_ptr
158@@ -200,7 +200,29 @@
159 ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name;
160 }
161
162- GMainLoop * loop;
163+ using source_func = std::function<gboolean()>;
164+
165+ guint idle_add(source_func&& func)
166+ {
167+ return g_idle_add_full(
168+ G_PRIORITY_DEFAULT_IDLE,
169+ [](gpointer gf){return (*static_cast<source_func*>(gf))();},
170+ new std::function<gboolean()>(func),
171+ [](gpointer gf){delete static_cast<source_func*>(gf);}
172+ );
173+ }
174+
175+ guint timeout_add(source_func&& func, std::chrono::milliseconds msec)
176+ {
177+ return g_timeout_add_full(
178+ G_PRIORITY_DEFAULT,
179+ msec.count(),
180+ [](gpointer gf){return (*static_cast<source_func*>(gf))();},
181+ new std::function<gboolean()>(func),
182+ [](gpointer gf){delete static_cast<source_func*>(gf);}
183+ );
184+ }
185+
186+ GMainLoop* loop {};
187 };
188
189-#endif /* INDICATOR_DATETIME_TESTS_GLIB_FIXTURE_H */
190
191=== modified file 'tests/manual-test-snap.cpp'
192--- tests/manual-test-snap.cpp 2016-03-31 18:51:29 +0000
193+++ tests/manual-test-snap.cpp 2016-05-16 21:30:46 +0000
194@@ -74,12 +74,14 @@
195 a.alarms.push_back(Alarm{"Alarm Text", "", a.begin});
196
197 auto loop = g_main_loop_new(nullptr, false);
198- auto on_snooze = [loop](const Appointment& appt, const Alarm&){
199- g_message("You clicked 'Snooze' for appt url '%s'", appt.summary.c_str());
200- g_idle_add(quit_idle, loop);
201- };
202- auto on_ok = [loop](const Appointment&, const Alarm&){
203- g_message("You clicked 'OK'");
204+ auto on_response = [loop](const Appointment& appt, const Alarm&, const Snap::Response& response){
205+ const char* str {""};
206+ switch(response) {
207+ case Snap::Response::ShowApp: str = "show-app"; break;
208+ case Snap::Response::Snooze: str = "snooze"; break;
209+ case Snap::Response::None: str = "no-action"; break;
210+ };
211+ g_message("You clicked '%s' for appt url '%s'", str, appt.summary.c_str());
212 g_idle_add(quit_idle, loop);
213 };
214
215@@ -94,7 +96,7 @@
216 auto notification_engine = std::make_shared<uin::Engine>("indicator-datetime-service");
217 auto sound_builder = std::make_shared<uin::DefaultSoundBuilder>();
218 Snap snap (notification_engine, sound_builder, settings);
219- snap(a, a.alarms.front(), on_snooze, on_ok);
220+ snap(a, a.alarms.front(), on_response);
221 g_main_loop_run(loop);
222
223 g_main_loop_unref(loop);
224
225=== modified file 'tests/test-notification.cpp'
226--- tests/test-notification.cpp 2016-05-14 02:07:10 +0000
227+++ tests/test-notification.cpp 2016-05-16 21:30:46 +0000
228@@ -53,7 +53,7 @@
229 auto settings = std::make_shared<Settings>();
230 auto ne = std::make_shared<unity::indicator::notifications::Engine>(APP_NAME);
231 auto sb = std::make_shared<unity::indicator::notifications::DefaultSoundBuilder>();
232- auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);};
233+ auto func = [this](const Appointment&, const Alarm&, const Snap::Response&){g_idle_add(quit_idle, loop);};
234
235 // combinatorial factor #1: event type
236 struct {
237@@ -143,7 +143,7 @@
238
239 // run the test
240 auto snap = create_snap(ne, sb, settings);
241- (*snap)(test_appt.appt, appt.alarms.front(), func, func);
242+ (*snap)(test_appt.appt, appt.alarms.front(), func);
243
244 // confirm that the notification was as expected
245 if (expected_notify_called) {
246@@ -195,3 +195,96 @@
247 }
248 }
249
250+
251+TEST_F(NotificationFixture,Response)
252+{
253+ // create the world
254+ make_interactive();
255+ auto ne = std::make_shared<unity::indicator::notifications::Engine>(APP_NAME);
256+ auto sb = std::make_shared<unity::indicator::notifications::DefaultSoundBuilder>();
257+ auto settings = std::make_shared<Settings>();
258+ int next_notification_id {FIRST_NOTIFY_ID};
259+
260+ // set a response callback that remembers what response we got
261+ bool on_response_called {};
262+ Snap::Response response {};
263+ auto on_response = [this, &on_response_called, &response]
264+ (const Appointment&, const Alarm&, const Snap::Response& r){
265+ on_response_called = true;
266+ response = r;
267+ g_idle_add(quit_idle, loop);
268+ };
269+
270+ // our tests!
271+ const struct {
272+ Appointment appt;
273+ std::vector<std::string> expected_actions;
274+ std::string action;
275+ Snap::Response expected_response;
276+ } tests[] = {
277+ { appt, {"show-app"}, "show-app", Snap::Response::ShowApp },
278+ { ualarm, {"none", "snooze"}, "snooze", Snap::Response::Snooze },
279+ { ualarm, {"none", "snooze"}, "none", Snap::Response::None }
280+ };
281+
282+
283+ // walk through the tests
284+ for (const auto& test : tests)
285+ {
286+ // wait for previous iterations' bus noise to finish and reset the counters
287+ GError* error {};
288+ wait_msec(500);
289+ dbus_test_dbus_mock_object_clear_method_calls(notify_mock, notify_obj, &error);
290+ g_assert_no_error(error);
291+ on_response_called = false;
292+
293+ // create a snap decision
294+ auto snap = create_snap(ne, sb, settings);
295+ (*snap)(test.appt, test.appt.alarms.front(), on_response);
296+
297+ // wait for the notification to show up
298+ EXPECT_METHOD_CALLED_EVENTUALLY(notify_mock, notify_obj, METHOD_NOTIFY);
299+
300+ // test that Notify was called the right number of times
301+ static constexpr int expected_num_notify_calls {1};
302+ guint num_notify_calls {};
303+ const auto notify_calls = dbus_test_dbus_mock_object_get_method_calls(
304+ notify_mock,
305+ notify_obj,
306+ METHOD_NOTIFY,
307+ &num_notify_calls,
308+ &error);
309+ g_assert_no_error(error);
310+ EXPECT_EQ(expected_num_notify_calls, num_notify_calls);
311+
312+ // test that Notify was called with the correct list of actions
313+ if (num_notify_calls > 0) {
314+ std::vector<std::string> actions;
315+ const gchar** as {nullptr};
316+ g_variant_get_child(notify_calls[0].params, 5, "^a&s", &as);
317+ for (int i=0; as && as[i]; i+=2) // actions are in pairs of (name, i18n), skip the i18n
318+ actions.push_back(as[i]);
319+ EXPECT_EQ(test.expected_actions, actions);
320+ }
321+
322+ // make the notification mock tell the world that the user invoked an action
323+ const auto notification_id = next_notification_id++;
324+ idle_add([this, notification_id, test](){
325+ GError* err {};
326+ dbus_test_dbus_mock_object_emit_signal(notify_mock, notify_obj, "ActionInvoked",
327+ G_VARIANT_TYPE("(us)"),
328+ g_variant_new("(us)", guint(notification_id), test.action.c_str()),
329+ &err);
330+ dbus_test_dbus_mock_object_emit_signal(notify_mock, notify_obj, "NotificationClosed",
331+ G_VARIANT_TYPE("(uu)"),
332+ g_variant_new("(uu)", guint(notification_id), guint(NOTIFICATION_CLOSED_DISMISSED)),
333+ &err);
334+ g_assert_no_error(err);
335+ return G_SOURCE_REMOVE;
336+ });
337+
338+ // confirm that the response callback got the right response
339+ EXPECT_TRUE(wait_for([&on_response_called](){return on_response_called;}));
340+ EXPECT_EQ(int(test.expected_response), int(response)) << "notification_id " << notification_id;
341+ }
342+}
343
344=== modified file 'tests/test-sound.cpp'
345--- tests/test-sound.cpp 2016-05-14 02:07:10 +0000
346+++ tests/test-sound.cpp 2016-05-16 21:30:46 +0000
347@@ -62,8 +62,8 @@
348 make_interactive();
349
350 // call the Snap Decision
351- auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);};
352- (*snap)(appt, appt.alarms.front(), func, func);
353+ auto func = [this](const Appointment&, const Alarm&, const Snap::Response&){g_idle_add(quit_idle, loop);};
354+ (*snap)(appt, appt.alarms.front(), func);
355
356 // confirm that Notify got called once
357 guint len = 0;
358@@ -148,7 +148,7 @@
359 auto settings = std::make_shared<Settings>();
360 auto ne = std::make_shared<unity::indicator::notifications::Engine>(APP_NAME);
361 auto sb = std::make_shared<TestSoundBuilder>();
362- auto func = [this](const Appointment&, const Alarm&){g_idle_add(quit_idle, loop);};
363+ auto func = [this](const Appointment&, const Alarm&, const Snap::Response&){g_idle_add(quit_idle, loop);};
364
365 const struct {
366 Appointment appointment;
367@@ -163,7 +163,7 @@
368 auto snap = create_snap(ne, sb, settings);
369 for(const auto& test_case : test_cases)
370 {
371- (*snap)(test_case.appointment, test_case.appointment.alarms.front(), func, func);
372+ (*snap)(test_case.appointment, test_case.appointment.alarms.front(), func);
373 EXPECT_EQ(test_case.expected_uri, sb->uri());
374 EXPECT_EQ(test_case.expected_role, sb->role());
375 }

Subscribers

People subscribed via source and target branches