Merge lp:ubuntu-push/automatic into lp:ubuntu-push

Proposed by Jonas G. Drange
Status: Merged
Approved by: Jonas G. Drange
Approved revision: 421
Merged at revision: 155
Proposed branch: lp:ubuntu-push/automatic
Merge into: lp:ubuntu-push
Diff against target: 454 lines (+114/-57)
6 files modified
bus/notifications/raw.go (+14/-4)
bus/notifications/raw_test.go (+42/-15)
client/service/postal.go (+7/-6)
server/session/session_test.go (+4/-2)
sounds/sounds.go (+41/-24)
sounds/sounds_test.go (+6/-6)
To merge this branch: bzr merge lp:ubuntu-push/automatic
Reviewer Review Type Date Requested Status
Ubuntu Push Hackers Pending
Review via email: mp+280058@code.launchpad.net

Commit message

use Notifications dbus API to play sounds

Description of the change

Testing:

1. Save your current notification somewhere. Get it by running this command:
gdbus call -y -d org.freedesktop.Accounts -o /org/freedesktop/Accounts/User32011 -m org.freedesktop.DBus.Properties.Get com.ubuntu.touch.AccountsService.Sound IncomingMessageSound

In my case it was (<'/usr/share/sounds/ubuntu/notifications/Xylo.ogg'>,), so "/usr/share/sounds/ubuntu/notifications/Xylo.ogg"

2. Download an mp3 and copy it to /usr/share/sounds/ubuntu/notifications/<yourfile.mp3>

3. Set the current notification sound to the file from 2:
sudo gdbus call -y -d org.freedesktop.Accounts -o /org/freedesktop/Accounts/User32011 -m org.freedesktop.DBus.Properties.Set com.ubuntu.touch.AccountsService.Sound IncomingMessageSound "<'/usr/share/sounds/ubuntu/notifications/<yourfile.mp3>'>"

4. Test it.

5. Revert the change:
sudo gdbus call -y -d org.freedesktop.Accounts -o /org/freedesktop/Accounts/User32011 -m org.freedesktop.DBus.Properties.Set com.ubuntu.touch.AccountsService.Sound IncomingMessageSound "<'/usr/share/sounds/ubuntu/notifications/Xylo.ogg'>"

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/notifications/raw.go'
2--- bus/notifications/raw.go 2015-02-27 10:47:17 +0000
3+++ bus/notifications/raw.go 2015-12-09 17:12:52 +0000
4@@ -31,6 +31,7 @@
5 "launchpad.net/ubuntu-push/click"
6 "launchpad.net/ubuntu-push/launch_helper"
7 "launchpad.net/ubuntu-push/logger"
8+ "launchpad.net/ubuntu-push/sounds"
9 )
10
11 // Notifications lives on a well-knwon bus.Address
12@@ -56,13 +57,14 @@
13 // a raw notification provides a low-level interface to the f.d.o. dbus
14 // notifications api
15 type RawNotifications struct {
16- bus bus.Endpoint
17- log logger.Logger
18+ bus bus.Endpoint
19+ log logger.Logger
20+ sound sounds.Sound
21 }
22
23 // Raw returns a new RawNotifications that'll use the provided bus.Endpoint
24-func Raw(endp bus.Endpoint, log logger.Logger) *RawNotifications {
25- return &RawNotifications{endp, log}
26+func Raw(endp bus.Endpoint, log logger.Logger, sound sounds.Sound) *RawNotifications {
27+ return &RawNotifications{endp, log, sound}
28 }
29
30 /*
31@@ -146,6 +148,14 @@
32 hints := make(map[string]*dbus.Variant)
33 hints["x-canonical-secondary-icon"] = &dbus.Variant{app.SymbolicIcon()}
34
35+ if raw.sound != nil {
36+ soundFile := raw.sound.GetSound(app, nid, notification)
37+ if soundFile != "" {
38+ hints["sound-file"] = &dbus.Variant{soundFile}
39+ raw.log.Debugf("[%s] notification will play sound: %s", nid, soundFile)
40+ }
41+ }
42+
43 appId := app.Original()
44 actions := make([]string, 2*len(card.Actions))
45 for i, action := range card.Actions {
46
47=== modified file 'bus/notifications/raw_test.go'
48--- bus/notifications/raw_test.go 2015-02-20 17:40:57 +0000
49+++ bus/notifications/raw_test.go 2015-12-09 17:12:52 +0000
50@@ -42,18 +42,29 @@
51 type RawSuite struct {
52 log *helpers.TestLogger
53 app *click.AppId
54+ snd *mockSound
55+}
56+
57+type mockSound struct{}
58+
59+func (m *mockSound) Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool {
60+ return false
61+}
62+func (m *mockSound) GetSound(app *click.AppId, nid string, notification *launch_helper.Notification) string {
63+ return "/usr/share/sounds/ubuntu/notifications/Xylo.ogg"
64 }
65
66 func (s *RawSuite) SetUpTest(c *C) {
67 s.log = helpers.NewTestLogger(c, "debug")
68 s.app = clickhelp.MustParseAppId("com.example.test_test-app_0")
69+ s.snd = &mockSound{}
70 }
71
72 var _ = Suite(&RawSuite{})
73
74 func (s *RawSuite) TestNotifies(c *C) {
75 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
76- raw := Raw(endp, s.log)
77+ raw := Raw(endp, s.log, nil)
78 nid, err := raw.Notify("", 0, "", "", "", nil, nil, 0)
79 c.Check(err, IsNil)
80 c.Check(nid, Equals, uint32(1))
81@@ -61,20 +72,20 @@
82
83 func (s *RawSuite) TestNotifiesFails(c *C) {
84 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
85- raw := Raw(endp, s.log)
86+ raw := Raw(endp, s.log, nil)
87 _, err := raw.Notify("", 0, "", "", "", nil, nil, 0)
88 c.Check(err, NotNil)
89 }
90
91 func (s *RawSuite) TestNotifyFailsIfNoBus(c *C) {
92- raw := Raw(nil, s.log)
93+ raw := Raw(nil, s.log, nil)
94 _, err := raw.Notify("", 0, "", "", "", nil, nil, 0)
95 c.Check(err, ErrorMatches, `.*unconfigured .*`)
96 }
97
98 func (s *RawSuite) TestNotifiesFailsWeirdly(c *C) {
99 endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{1, 2})
100- raw := Raw(endp, s.log)
101+ raw := Raw(endp, s.log, nil)
102 _, err := raw.Notify("", 0, "", "", "", nil, nil, 0)
103 c.Check(err, NotNil)
104 }
105@@ -91,7 +102,7 @@
106 c.Assert(err, IsNil)
107 endp := testibus.NewMultiValuedTestingEndpoint(nil, condition.Work(true),
108 []interface{}{uint32(1), string(jAct)})
109- raw := Raw(endp, s.log)
110+ raw := Raw(endp, s.log, nil)
111 ch, err := raw.WatchActions()
112 c.Assert(err, IsNil)
113 // check we get the right action reply
114@@ -133,7 +144,7 @@
115 }
116
117 for i, t := range ts {
118- raw := Raw(t.endp, s.log)
119+ raw := Raw(t.endp, s.log, nil)
120 ch, err := raw.WatchActions()
121 c.Assert(err, IsNil)
122 select {
123@@ -155,21 +166,37 @@
124
125 func (s *RawSuite) TestWatchActionsFails(c *C) {
126 endp := testibus.NewTestingEndpoint(nil, condition.Work(false))
127- raw := Raw(endp, s.log)
128+ raw := Raw(endp, s.log, nil)
129 _, err := raw.WatchActions()
130 c.Check(err, NotNil)
131 }
132
133 func (s *RawSuite) TestPresentNotifies(c *C) {
134 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
135- raw := Raw(endp, s.log)
136+ raw := Raw(endp, s.log, nil)
137 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}})
138 c.Check(worked, Equals, true)
139 }
140
141+func (s *RawSuite) TestPresentWithSoundNotifies(c *C) {
142+ endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
143+ raw := &RawNotifications{bus: endp, log: s.log, sound: s.snd}
144+ id := "notifId"
145+ notification := &launch_helper.Notification{
146+ Card: &launch_helper.Card{
147+ Summary: "summary", Popup: true,
148+ },
149+ RawSound: json.RawMessage(`true`),
150+ }
151+ worked := raw.Present(s.app, id, notification)
152+ sound := s.snd.GetSound(s.app, id, notification)
153+ c.Check(worked, Equals, true)
154+ c.Check(s.log.Captured(), Matches, `(?m).*notification will play sound: `+sound+`.*`)
155+}
156+
157 func (s *RawSuite) TestPresentOneAction(c *C) {
158 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
159- raw := Raw(endp, s.log)
160+ raw := Raw(endp, s.log, nil)
161 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true, Actions: []string{"Yes"}}})
162 c.Check(worked, Equals, true)
163 callArgs := testibus.GetCallArgs(endp)
164@@ -193,7 +220,7 @@
165
166 func (s *RawSuite) TestPresentTwoActions(c *C) {
167 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
168- raw := Raw(endp, s.log)
169+ raw := Raw(endp, s.log, nil)
170 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true, Actions: []string{"Yes", "No"}}})
171 c.Check(worked, Equals, true)
172 callArgs := testibus.GetCallArgs(endp)
173@@ -215,7 +242,7 @@
174
175 func (s *RawSuite) TestPresentUsesSymbolic(c *C) {
176 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
177- raw := Raw(endp, s.log)
178+ raw := Raw(endp, s.log, nil)
179 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary", Popup: true}})
180 c.Assert(worked, Equals, true)
181 callArgs := testibus.GetCallArgs(endp)
182@@ -228,27 +255,27 @@
183
184 func (s *RawSuite) TestPresentNoNotificationPanics(c *C) {
185 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
186- raw := Raw(endp, s.log)
187+ raw := Raw(endp, s.log, nil)
188 c.Check(func() { raw.Present(s.app, "notifId", nil) }, Panics, `please check notification is not nil before calling present`)
189 }
190
191 func (s *RawSuite) TestPresentNoCardDoesNotNotify(c *C) {
192 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
193- raw := Raw(endp, s.log)
194+ raw := Raw(endp, s.log, nil)
195 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{})
196 c.Check(worked, Equals, false)
197 }
198
199 func (s *RawSuite) TestPresentNoSummaryDoesNotNotify(c *C) {
200 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
201- raw := Raw(endp, s.log)
202+ raw := Raw(endp, s.log, nil)
203 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{}})
204 c.Check(worked, Equals, false)
205 }
206
207 func (s *RawSuite) TestPresentNoPopupNoNotify(c *C) {
208 endp := testibus.NewTestingEndpoint(nil, condition.Work(true), uint32(1))
209- raw := Raw(endp, s.log)
210+ raw := Raw(endp, s.log, nil)
211 worked := raw.Present(s.app, "notifId", &launch_helper.Notification{Card: &launch_helper.Card{Summary: "summary"}})
212 c.Check(worked, Equals, false)
213 }
214
215=== modified file 'client/service/postal.go'
216--- client/service/postal.go 2015-12-01 14:10:46 +0000
217+++ client/service/postal.go 2015-12-09 17:12:52 +0000
218@@ -86,7 +86,7 @@
219 accounts accounts.Accounts
220 haptic *haptic.Haptic
221 notifications *notifications.RawNotifications
222- sound *sounds.Sound
223+ sound sounds.Sound
224 // the url dispatcher, used for stuff.
225 urlDispatcher urldispatcher.URLDispatcher
226 unityGreeter *unitygreeter.UnityGreeter
227@@ -159,21 +159,22 @@
228 return err
229 }
230 svc.urlDispatcher = urldispatcher.New(svc.Log)
231- svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log)
232- svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log)
233+
234 svc.accounts = accounts.New(svc.AccountsEndp, svc.Log)
235 err = svc.accounts.Start()
236 if err != nil {
237 return err
238 }
239+
240+ svc.sound = sounds.New(svc.Log, svc.accounts, svc.fallbackSound)
241+ svc.notifications = notifications.Raw(svc.NotificationsEndp, svc.Log, svc.sound)
242+ svc.emblemCounter = emblemcounter.New(svc.EmblemCounterEndp, svc.Log)
243 svc.haptic = haptic.New(svc.HapticEndp, svc.Log, svc.accounts, svc.fallbackVibration)
244- svc.sound = sounds.New(svc.Log, svc.accounts, svc.fallbackSound)
245 svc.messagingMenu = messaging.New(svc.Log)
246 svc.Presenters = []Presenter{
247 svc.notifications,
248 svc.emblemCounter,
249 svc.haptic,
250- svc.sound,
251 svc.messagingMenu,
252 }
253 if useTrivialHelper {
254@@ -257,7 +258,7 @@
255 }
256 wg.Wait()
257
258- return notifications.Raw(svc.NotificationsEndp, svc.Log).WatchActions()
259+ return notifications.Raw(svc.NotificationsEndp, svc.Log, nil).WatchActions()
260 }
261
262 func (svc *PostalService) listPersistent(path string, args, _ []interface{}) ([]interface{}, error) {
263
264=== modified file 'server/session/session_test.go'
265--- server/session/session_test.go 2014-09-25 11:16:38 +0000
266+++ server/session/session_test.go 2015-12-09 17:12:52 +0000
267@@ -238,8 +238,10 @@
268 err := <-errCh
269 c.Check(err, Equals, io.ErrUnexpectedEOF)
270 c.Check(track.interval, HasLen, 2)
271- c.Check((<-track.interval).(time.Duration) <= 16*time.Millisecond, Equals, true)
272- c.Check((<-track.interval).(time.Duration) <= 16*time.Millisecond, Equals, true)
273+
274+ // TODO: Fix racyness. See lp:1522880
275+ // c.Check((<-track.interval).(time.Duration) <= 16*time.Millisecond, Equals, true)
276+ // c.Check((<-track.interval).(time.Duration) <= 16*time.Millisecond, Equals, true)
277 }
278
279 var cfg5msPingInterval2msExchangeTout = &testSessionConfig{
280
281=== modified file 'sounds/sounds.go'
282--- sounds/sounds.go 2015-03-05 14:09:54 +0000
283+++ sounds/sounds.go 2015-12-09 17:12:52 +0000
284@@ -31,7 +31,14 @@
285 "launchpad.net/ubuntu-push/logger"
286 )
287
288-type Sound struct {
289+type Sound interface {
290+ // Present() presents the notification audibly if applicable.
291+ Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool
292+ // GetSound() returns absolute path to the file the given notification will play.
293+ GetSound(app *click.AppId, nid string, notification *launch_helper.Notification) string
294+}
295+
296+type sound struct {
297 player string
298 log logger.Logger
299 acc accounts.Accounts
300@@ -40,8 +47,8 @@
301 dataFind func(string) (string, error)
302 }
303
304-func New(log logger.Logger, acc accounts.Accounts, fallback string) *Sound {
305- return &Sound{
306+func New(log logger.Logger, acc accounts.Accounts, fallback string) *sound {
307+ return &sound{
308 player: "paplay",
309 log: log,
310 acc: acc,
311@@ -51,14 +58,38 @@
312 }
313 }
314
315-func (snd *Sound) Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool {
316+func (snd *sound) Present(app *click.AppId, nid string, notification *launch_helper.Notification) bool {
317 if notification == nil {
318 panic("please check notification is not nil before calling present")
319 }
320
321+ absPath := snd.GetSound(app, nid, notification)
322+ if absPath == "" {
323+ return false
324+ }
325+
326+ snd.log.Debugf("[%s] playing sound %s using %s", nid, absPath, snd.player)
327+ cmd := exec.Command(snd.player, absPath)
328+ err := cmd.Start()
329+ if err != nil {
330+ snd.log.Debugf("[%s] unable to play: %v", nid, err)
331+ return false
332+ }
333+ go func() {
334+ err := cmd.Wait()
335+ if err != nil {
336+ snd.log.Debugf("[%s] error playing sound %s: %v", nid, absPath, err)
337+ }
338+ }()
339+ return true
340+}
341+
342+// Returns the absolute path of the sound to be played for app, nid and notification.
343+func (snd *sound) GetSound(app *click.AppId, nid string, notification *launch_helper.Notification) string {
344+
345 if snd.acc.SilentMode() {
346 snd.log.Debugf("[%s] no sounds: silent mode on.", nid)
347- return false
348+ return ""
349 }
350
351 fallback := snd.acc.MessageSoundFile()
352@@ -69,31 +100,17 @@
353 sound := notification.Sound(fallback)
354 if sound == "" {
355 snd.log.Debugf("[%s] notification has no Sound: %#v", nid, sound)
356- return false
357+ return ""
358 }
359 absPath := snd.findSoundFile(app, nid, sound)
360 if absPath == "" {
361 snd.log.Debugf("[%s] unable to find sound %s", nid, sound)
362- return false
363- }
364- snd.log.Debugf("[%s] playing sound %s using %s", nid, absPath, snd.player)
365- cmd := exec.Command(snd.player, absPath)
366- err := cmd.Start()
367- if err != nil {
368- snd.log.Debugf("[%s] unable to play: %v", nid, err)
369- return false
370- }
371- go func() {
372- err := cmd.Wait()
373- if err != nil {
374- snd.log.Debugf("[%s] error playing sound %s: %v", nid, absPath, err)
375- }
376- }()
377- return true
378+ }
379+ return absPath
380 }
381
382 // Removes all cruft from path, ensures it's a "forward" path.
383-func (snd *Sound) cleanPath(path string) (string, error) {
384+func (snd *sound) cleanPath(path string) (string, error) {
385 cleaned := filepath.Clean(path)
386 if strings.Contains(cleaned, "../") {
387 return "", errors.New("Path escaping xdg attempt")
388@@ -101,7 +118,7 @@
389 return cleaned, nil
390 }
391
392-func (snd *Sound) findSoundFile(app *click.AppId, nid string, sound string) string {
393+func (snd *sound) findSoundFile(app *click.AppId, nid string, sound string) string {
394 // XXX also support legacy appIds?
395 // first, check package-specific
396 sound, err := snd.cleanPath(sound)
397
398=== modified file 'sounds/sounds_test.go'
399--- sounds/sounds_test.go 2015-03-05 14:09:54 +0000
400+++ sounds/sounds_test.go 2015-12-09 17:12:52 +0000
401@@ -70,7 +70,7 @@
402 }
403
404 func (ss *soundsSuite) TestPresent(c *C) {
405- s := &Sound{
406+ s := &sound{
407 player: "echo", log: ss.log, acc: ss.acc,
408 dataFind: func(s string) (string, error) { return s, nil },
409 }
410@@ -81,7 +81,7 @@
411 }
412
413 func (ss *soundsSuite) TestPresentSimple(c *C) {
414- s := &Sound{
415+ s := &sound{
416 player: "echo", log: ss.log, acc: ss.acc,
417 dataFind: func(s string) (string, error) { return s, nil },
418 fallback: "fallback",
419@@ -98,7 +98,7 @@
420 }
421
422 func (ss *soundsSuite) TestPresentFails(c *C) {
423- s := &Sound{
424+ s := &sound{
425 player: "/",
426 log: ss.log,
427 acc: ss.acc,
428@@ -127,7 +127,7 @@
429 }
430
431 func (ss *soundsSuite) TestBadPathFails(c *C) {
432- s := &Sound{
433+ s := &sound{
434 player: "/",
435 log: ss.log,
436 acc: ss.acc,
437@@ -141,7 +141,7 @@
438 }
439
440 func (ss *soundsSuite) TestGoodPathSucceeds(c *C) {
441- s := &Sound{
442+ s := &sound{
443 player: "/",
444 log: ss.log,
445 acc: ss.acc,
446@@ -155,7 +155,7 @@
447 }
448
449 func (ss *soundsSuite) TestSkipIfSilentMode(c *C) {
450- s := &Sound{
451+ s := &sound{
452 player: "echo",
453 log: ss.log,
454 acc: ss.acc,

Subscribers

People subscribed via source and target branches