Merge lp:~macslow/unity8/synchronous-notification into lp:unity8/rtm-14.09
- synchronous-notification
- Merge into rtm-14.09
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Albert Astals Cid | ||||||||||||
Approved revision: | 1105 | ||||||||||||
Merged at revision: | 1354 | ||||||||||||
Proposed branch: | lp:~macslow/unity8/synchronous-notification | ||||||||||||
Merge into: | lp:unity8/rtm-14.09 | ||||||||||||
Diff against target: |
544 lines (+248/-52) 3 files modified
qml/Notifications/Notification.qml (+72/-8) qml/Notifications/Notifications.qml (+1/-0) tests/qmltests/Notifications/tst_Notifications.qml (+175/-44) |
||||||||||||
To merge this branch: | bzr merge lp:~macslow/unity8/synchronous-notification | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Approve | ||
Review via email: mp+238119@code.launchpad.net |
Commit message
Added synchronous/
Description of the change
Added synchronous/
You can test it with the icon-value.py Python-script from the corresponding lp:~macslow/unity-notifications/synchronous-notification branch.
For the reviewers convenience, here's a video of all three branches in action: http://
* Are there any related MPs required for this MP to build/function as expected? Please list.
Yes. For correct operation lp:~macslow/unity-notifications/synchronous-notification needs to be merged to lp:unity-notifications first.
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
* Did you make sure that your branch does not contain spurious tags?
Yes.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.
* If you changed the UI, has there been a design review?
Yes.
- 1103. By Mirco Müller
-
Solved top-margin issues in fullscreen snap-decision due to added sync. notification support.
- 1104. By Mirco Müller
-
Fix trailing whitespaces.
- 1105. By Mirco Müller
-
Simplify implicitHeight binding a bit.
- 1106. By Mirco Müller
-
Make sure any attached sound-file is played back for sync. notifications any time its hints are changed.
- 1107. By Mirco Müller
-
Add audioRole to fix LP: #1378920.
Preview Diff
1 | === modified file 'qml/Notifications/Notification.qml' |
2 | --- qml/Notifications/Notification.qml 2014-09-18 21:25:00 +0000 |
3 | +++ qml/Notifications/Notification.qml 2014-10-15 13:56:02 +0000 |
4 | @@ -30,6 +30,7 @@ |
5 | property alias secondaryIconSource: secondaryIcon.source |
6 | property alias summary: summaryLabel.text |
7 | property alias body: bodyLabel.text |
8 | + property alias value: valueIndicator.value |
9 | property var actions |
10 | property var notificationId |
11 | property var type |
12 | @@ -39,6 +40,7 @@ |
13 | property bool fullscreen: false |
14 | property int maxHeight |
15 | property int margins |
16 | + readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision |
17 | readonly property color red: "#fc4949" |
18 | readonly property color green: "#3fb24f" |
19 | readonly property color sdLightGrey: "#eaeaea" |
20 | @@ -47,9 +49,9 @@ |
21 | readonly property real contentSpacing: units.gu(2) |
22 | |
23 | objectName: "background" |
24 | - implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height + contentSpacing * 2) : 0 |
25 | + implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0 |
26 | |
27 | - color: type == Notification.SnapDecision ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97) |
28 | + color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97) |
29 | opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really |
30 | |
31 | state: { |
32 | @@ -77,6 +79,7 @@ |
33 | Audio { |
34 | id: sound |
35 | objectName: "sound" |
36 | + audioRole: MediaPlayer.alert |
37 | source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : "" |
38 | } |
39 | |
40 | @@ -87,6 +90,12 @@ |
41 | } |
42 | } |
43 | |
44 | + onHintsChanged: { |
45 | + if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") { |
46 | + sound.play(); |
47 | + } |
48 | + } |
49 | + |
50 | Behavior on height { |
51 | id: normalHeightBehavior |
52 | |
53 | @@ -125,6 +134,7 @@ |
54 | fill: parent |
55 | leftMargin: notification.margins |
56 | rightMargin: notification.margins |
57 | + topMargin: type === Notification.Confirmation ? units.gu(.5) : 0 |
58 | } |
59 | color: parent.color |
60 | opacity: parent.opacity |
61 | @@ -205,10 +215,10 @@ |
62 | right: parent.right |
63 | top: parent.top |
64 | margins: 0 |
65 | - topMargin: fullscreen ? 0 : units.gu(2) |
66 | + topMargin: fullscreen ? 0 : type === Notification.Confirmation ? units.gu(1) : units.gu(2) |
67 | } |
68 | |
69 | - spacing: units.gu(2) |
70 | + spacing: type === Notification.Confirmation ? units.gu(1) : units.gu(2) |
71 | |
72 | Row { |
73 | id: topRow |
74 | @@ -227,7 +237,7 @@ |
75 | width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6) |
76 | height: width |
77 | shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true |
78 | - visible: iconSource !== undefined && iconSource != "" |
79 | + visible: iconSource !== undefined && iconSource !== "" && type !== Notification.Confirmation |
80 | } |
81 | |
82 | Column { |
83 | @@ -244,8 +254,9 @@ |
84 | left: parent.left |
85 | right: parent.right |
86 | } |
87 | + visible: type !== Notification.Confirmation |
88 | fontSize: "medium" |
89 | - color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText |
90 | + color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText |
91 | elide: Text.ElideRight |
92 | textFormat: Text.PlainText |
93 | } |
94 | @@ -258,9 +269,9 @@ |
95 | left: parent.left |
96 | right: parent.right |
97 | } |
98 | - visible: body != "" |
99 | + visible: body != "" && type !== Notification.Confirmation |
100 | fontSize: "small" |
101 | - color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText |
102 | + color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText |
103 | wrapMode: Text.WordWrap |
104 | maximumLineCount: type == Notification.SnapDecision ? 12 : 2 |
105 | elide: Text.ElideRight |
106 | @@ -283,6 +294,59 @@ |
107 | visible: type == Notification.SnapDecision |
108 | } |
109 | |
110 | + ShapedIcon { |
111 | + id: centeredIcon |
112 | + objectName: "centeredIcon" |
113 | + width: units.gu(5) |
114 | + height: width |
115 | + shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true |
116 | + fileSource: icon.fileSource |
117 | + visible: fileSource !== undefined && fileSource !== "" && type === Notification.Confirmation |
118 | + anchors.horizontalCenter: parent.horizontalCenter |
119 | + } |
120 | + |
121 | + Label { |
122 | + id: valueLabel |
123 | + objectName: "valueLabel" |
124 | + text: body |
125 | + anchors.horizontalCenter: parent.horizontalCenter |
126 | + visible: type === Notification.Confirmation && body !== "" |
127 | + fontSize: "medium" |
128 | + color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText |
129 | + wrapMode: Text.WordWrap |
130 | + maximumLineCount: 1 |
131 | + elide: Text.ElideRight |
132 | + textFormat: Text.PlainText |
133 | + } |
134 | + |
135 | + UbuntuShape { |
136 | + id: valueIndicator |
137 | + objectName: "valueIndicator" |
138 | + visible: type === Notification.Confirmation |
139 | + property double value |
140 | + |
141 | + anchors { |
142 | + left: parent.left |
143 | + right: parent.right |
144 | + margins: contentSpacing |
145 | + } |
146 | + |
147 | + height: units.gu(1) |
148 | + color: darkOnBright ? UbuntuColors.darkGrey : UbuntuColors.lightGrey |
149 | + borderSource: "none" |
150 | + radius: "small" |
151 | + |
152 | + UbuntuShape { |
153 | + id: innerBar |
154 | + objectName: "innerBar" |
155 | + width: valueIndicator.width * valueIndicator.value / 100 |
156 | + height: units.gu(1) |
157 | + color: notification.hints["x-canonical-value-bar-tint"] === "true" ? UbuntuColors.orange : darkOnBright ? UbuntuColors.lightGrey : "white" |
158 | + borderSource: "none" |
159 | + radius: "small" |
160 | + } |
161 | + } |
162 | + |
163 | Column { |
164 | id: dialogColumn |
165 | objectName: "dialogListView" |
166 | |
167 | === modified file 'qml/Notifications/Notifications.qml' |
168 | --- qml/Notifications/Notifications.qml 2014-08-25 11:31:05 +0000 |
169 | +++ qml/Notifications/Notifications.qml 2014-10-15 13:56:02 +0000 |
170 | @@ -55,6 +55,7 @@ |
171 | secondaryIconSource: model.secondaryIcon |
172 | summary: model.summary |
173 | body: model.body |
174 | + value: model.value |
175 | actions: model.actions |
176 | notificationId: model.id |
177 | notification: notificationList.model.getRaw(notificationId) |
178 | |
179 | === modified file 'tests/qmltests/Notifications/tst_Notifications.qml' |
180 | --- tests/qmltests/Notifications/tst_Notifications.qml 2014-09-12 14:51:55 +0000 |
181 | +++ tests/qmltests/Notifications/tst_Notifications.qml 2014-10-15 13:56:02 +0000 |
182 | @@ -53,6 +53,7 @@ |
183 | summary: "", |
184 | body: "", |
185 | icon: "", |
186 | + value: 0, |
187 | secondaryIcon: "", |
188 | actions: [] |
189 | } |
190 | @@ -125,10 +126,11 @@ |
191 | function addEphemeralIconSummaryNotification() { |
192 | var n = { |
193 | type: Notification.Ephemeral, |
194 | + hints: {"x-canonical-non-shaped-icon": "false"}, |
195 | summary: "Photo upload completed", |
196 | body: "", |
197 | - icon: "", |
198 | - secondaryIcon: "../graphics/applicationIcons/facebook.png", |
199 | + icon: "../graphics/applicationIcons/facebook.png", |
200 | + secondaryIcon: "", |
201 | actions: [] |
202 | } |
203 | |
204 | @@ -148,20 +150,53 @@ |
205 | mockModel.append(n) |
206 | } |
207 | |
208 | + function addConfirmationNotification() { |
209 | + var n = { |
210 | + type: Notification.Confirmation, |
211 | + hints: {"x-canonical-non-shaped-icon": "true"}, |
212 | + summary: "Confirmation notification", |
213 | + body: "", |
214 | + icon: "image://theme/audio-volume-medium", |
215 | + secondaryIcon: "", |
216 | + value: 50, |
217 | + actions: [], |
218 | + } |
219 | + |
220 | + mockModel.append(n) |
221 | + } |
222 | + |
223 | + function add2ndConfirmationNotification() { |
224 | + var n = { |
225 | + type: Notification.Confirmation, |
226 | + hints: {"x-canonical-non-shaped-icon": "true", |
227 | + "x-canonical-value-bar-tint": "true"}, |
228 | + summary: "Confirmation notification", |
229 | + body: "High Volume", |
230 | + icon: "image://theme/audio-volume-high", |
231 | + secondaryIcon: "", |
232 | + value: 85, |
233 | + actions: [], |
234 | + } |
235 | + |
236 | + mockModel.append(n) |
237 | + } |
238 | + |
239 | function clearNotifications() { |
240 | - mockModel.clear() |
241 | + while(mockModel.count > 1) { |
242 | + remove1stNotification() |
243 | + } |
244 | } |
245 | |
246 | function remove1stNotification() { |
247 | - if (mockModel.count > 0) |
248 | - mockModel.remove(0) |
249 | + if (mockModel.count > 1) |
250 | + mockModel.remove(1) |
251 | } |
252 | |
253 | Rectangle { |
254 | id: notificationsRect |
255 | |
256 | width: units.gu(40) |
257 | - height: units.gu(71) |
258 | + height: units.gu(115) |
259 | |
260 | MouseArea{ |
261 | id: clickThroughCatcher |
262 | @@ -183,7 +218,7 @@ |
263 | id: interactiveControls |
264 | |
265 | width: units.gu(30) |
266 | - height: units.gu(81) |
267 | + height: units.gu(115) |
268 | color: "grey" |
269 | |
270 | Column { |
271 | @@ -229,6 +264,18 @@ |
272 | |
273 | Button { |
274 | width: parent.width |
275 | + text: "add a confirmation" |
276 | + onClicked: addConfirmationNotification() |
277 | + } |
278 | + |
279 | + Button { |
280 | + width: parent.width |
281 | + text: "add a 2nd confirmation" |
282 | + onClicked: add2ndConfirmationNotification() |
283 | + } |
284 | + |
285 | + Button { |
286 | + width: parent.width |
287 | text: "remove 1st notification" |
288 | onClicked: remove1stNotification() |
289 | } |
290 | @@ -264,12 +311,16 @@ |
291 | summaryVisible: true, |
292 | bodyVisible: true, |
293 | iconVisible: true, |
294 | - shapedIcon: true, |
295 | - nonShapedIcon: false, |
296 | + centeredIconVisible: false, |
297 | + shaped: true, |
298 | + nonShaped: false, |
299 | secondaryIconVisible: true, |
300 | buttonRowVisible: true, |
301 | buttonTinted: true, |
302 | - hasSound: false |
303 | + hasSound: false, |
304 | + valueVisible: false, |
305 | + valueLabelVisible: false, |
306 | + valueTinted: false |
307 | }, |
308 | { |
309 | tag: "2-over-1 Snap Decision with button-tint", |
310 | @@ -285,31 +336,37 @@ |
311 | summaryVisible: true, |
312 | bodyVisible: true, |
313 | iconVisible: false, |
314 | - shapedIcon: false, |
315 | - nonShapedIcon: false, |
316 | + centeredIconVisible: false, |
317 | + shaped: false, |
318 | secondaryIconVisible: false, |
319 | buttonRowVisible: false, |
320 | buttonTinted: true, |
321 | - hasSound: false |
322 | + hasSound: false, |
323 | + valueVisible: false, |
324 | + valueLabelVisible: false, |
325 | + valueTinted: false |
326 | }, |
327 | { |
328 | tag: "Ephemeral notification - icon-summary layout", |
329 | type: Notification.Ephemeral, |
330 | - hints: {"x-canonical-private-affirmative-tint": "false"}, |
331 | + hints: {}, |
332 | summary: "Photo upload completed", |
333 | body: "", |
334 | - icon: "", |
335 | - secondaryIcon: "../graphics/applicationIcons/facebook.png", |
336 | + icon: "../graphics/applicationIcons/facebook.png", |
337 | + secondaryIcon: "", |
338 | actions: [], |
339 | summaryVisible: true, |
340 | bodyVisible: false, |
341 | - iconVisible: false, |
342 | - shapedIcon: false, |
343 | - nonShapedIcon: false, |
344 | - secondaryIconVisible: true, |
345 | + iconVisible: true, |
346 | + centeredIconVisible: false, |
347 | + shaped: true, |
348 | + secondaryIconVisible: false, |
349 | buttonRowVisible: false, |
350 | buttonTinted: false, |
351 | - hasSound: false |
352 | + hasSound: false, |
353 | + valueVisible: false, |
354 | + valueLabelVisible: false, |
355 | + valueTinted: false |
356 | }, |
357 | { |
358 | tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout", |
359 | @@ -326,12 +383,15 @@ |
360 | bodyVisible: false, |
361 | interactiveAreaEnabled: false, |
362 | iconVisible: false, |
363 | - shapedIcon: false, |
364 | - nonShapedIcon: false, |
365 | + centeredIconVisible: false, |
366 | + shaped: false, |
367 | secondaryIconVisible: true, |
368 | buttonRowVisible: false, |
369 | buttonTinted: false, |
370 | - hasSound: false |
371 | + hasSound: false, |
372 | + valueVisible: false, |
373 | + valueLabelVisible: false, |
374 | + valueTinted: false |
375 | }, |
376 | { |
377 | tag: "Interactive notification", |
378 | @@ -346,12 +406,15 @@ |
379 | summaryVisible: true, |
380 | bodyVisible: true, |
381 | iconVisible: true, |
382 | - shapedIcon: true, |
383 | - nonShapedIcon: false, |
384 | + centeredIconVisible: false, |
385 | + shaped: true, |
386 | secondaryIconVisible: false, |
387 | buttonRowVisible: false, |
388 | buttonTinted: false, |
389 | - hasSound: true |
390 | + hasSound: true, |
391 | + valueVisible: false, |
392 | + valueLabelVisible: false, |
393 | + valueTinted: false |
394 | }, |
395 | { |
396 | tag: "Snap Decision without secondary icon and no button-tint", |
397 | @@ -367,12 +430,15 @@ |
398 | summaryVisible: true, |
399 | bodyVisible: true, |
400 | iconVisible: true, |
401 | - shapedIcon: true, |
402 | - nonShapedIcon: false, |
403 | + centeredIconVisible: false, |
404 | + shaped: true, |
405 | secondaryIconVisible: false, |
406 | buttonRowVisible: true, |
407 | buttonTinted: false, |
408 | - hasSound: true |
409 | + hasSound: true, |
410 | + valueVisible: false, |
411 | + valueLabelVisible: false, |
412 | + valueTinted: false |
413 | }, |
414 | { |
415 | tag: "Ephemeral notification", |
416 | @@ -387,12 +453,15 @@ |
417 | summaryVisible: true, |
418 | bodyVisible: true, |
419 | iconVisible: true, |
420 | - shapedIcon: true, |
421 | - nonShapedIcon: false, |
422 | + centeredIconVisible: false, |
423 | + shaped: true, |
424 | secondaryIconVisible: true, |
425 | buttonRowVisible: false, |
426 | buttonTinted: false, |
427 | - hasSound: true |
428 | + hasSound: true, |
429 | + valueVisible: false, |
430 | + valueLabelVisible: false, |
431 | + valueTinted: false |
432 | }, |
433 | { |
434 | tag: "Ephemeral notification with non-shaped icon", |
435 | @@ -401,18 +470,68 @@ |
436 | "x-canonical-non-shaped-icon": "true"}, |
437 | summary: "Contacts", |
438 | body: "Synchronised contacts-database with cloud-storage.", |
439 | - icon: "../graphics/applicationIcons/contacts-app.png", |
440 | + icon: "image://theme/contacts-app", |
441 | secondaryIcon: "", |
442 | actions: [], |
443 | summaryVisible: true, |
444 | bodyVisible: true, |
445 | iconVisible: true, |
446 | - shapedIcon: false, |
447 | - nonShapedIcon: true, |
448 | - secondaryIconVisible: false, |
449 | - buttonRowVisible: false, |
450 | - buttonTinted: false, |
451 | - hasSound: false |
452 | + centeredIconVisible: false, |
453 | + shaped: false, |
454 | + secondaryIconVisible: false, |
455 | + buttonRowVisible: false, |
456 | + buttonTinted: false, |
457 | + hasSound: false, |
458 | + valueVisible: false, |
459 | + valueLabelVisible: false, |
460 | + valueTinted: false |
461 | + }, |
462 | + { |
463 | + tag: "Confirmation notification with value", |
464 | + type: Notification.Confirmation, |
465 | + hints: {"x-canonical-non-shaped-icon": "true"}, |
466 | + summary: "", |
467 | + body: "", |
468 | + icon: "image://theme/audio-volume-medium", |
469 | + secondaryIcon: "", |
470 | + value: 50, |
471 | + actions: [], |
472 | + summaryVisible: false, |
473 | + bodyVisible: false, |
474 | + iconVisible: false, |
475 | + centeredIconVisible: true, |
476 | + shaped: false, |
477 | + secondaryIconVisible: false, |
478 | + buttonRowVisible: false, |
479 | + buttonTinted: false, |
480 | + hasSound: false, |
481 | + valueVisible: true, |
482 | + valueLabelVisible: false, |
483 | + valueTinted: false |
484 | + }, |
485 | + { |
486 | + tag: "Confirmation notification with value, label and tint", |
487 | + type: Notification.Confirmation, |
488 | + hints: {"x-canonical-non-shaped-icon": "true", |
489 | + "x-canonical-value-bar-tint" : "true"}, |
490 | + summary: "", |
491 | + body: "High Volume", |
492 | + icon: "image://theme/audio-volume-high", |
493 | + secondaryIcon: "", |
494 | + value: 85, |
495 | + actions: [], |
496 | + summaryVisible: false, |
497 | + bodyVisible: false, |
498 | + iconVisible: false, |
499 | + centeredIconVisible: true, |
500 | + shaped: false, |
501 | + secondaryIconVisible: false, |
502 | + buttonRowVisible: false, |
503 | + buttonTinted: false, |
504 | + hasSound: false, |
505 | + valueVisible: true, |
506 | + valueLabelVisible: true, |
507 | + valueTinted: true |
508 | } |
509 | ] |
510 | } |
511 | @@ -450,17 +569,29 @@ |
512 | waitForRendering(notification); |
513 | |
514 | var icon = findChild(notification, "icon") |
515 | - var shapedIcon = findChild(notification, "shapedIcon") |
516 | - var nonShapedIcon = findChild(notification, "nonShapedIcon") |
517 | + var centeredIcon = findChild(notification, "centeredIcon") |
518 | var interactiveArea = findChild(notification, "interactiveArea") |
519 | var secondaryIcon = findChild(notification, "secondaryIcon") |
520 | var summaryLabel = findChild(notification, "summaryLabel") |
521 | var bodyLabel = findChild(notification, "bodyLabel") |
522 | var buttonRow = findChild(notification, "buttonRow") |
523 | + var valueIndicator = findChild(notification, "valueIndicator") |
524 | + var valueLabel = findChild(notification, "valueLabel") |
525 | + var innerBar = findChild(notification, "innerBar") |
526 | |
527 | compare(icon.visible, data.iconVisible, "avatar-icon visibility is incorrect") |
528 | - compare(shapedIcon.visible, data.shapedIcon, "shaped-icon visibility is incorrect") |
529 | - compare(nonShapedIcon.visible, data.nonShapedIcon, "non-shaped-icon visibility is incorrect") |
530 | + if (icon.visible) { |
531 | + compare(icon.shaped, data.shaped, "shaped-status is incorrect") |
532 | + } |
533 | + compare(centeredIcon.visible, data.centeredIconVisible, "centered-icon visibility is incorrect") |
534 | + if (centeredIcon.visible) { |
535 | + compare(centeredIcon.shaped, data.shaped, "shaped-status is incorrect") |
536 | + } |
537 | + compare(valueIndicator.visible, data.valueVisible, "value-indicator visibility is incorrect") |
538 | + if (valueIndicator.visible) { |
539 | + verify(innerBar.color === data.valueTinted ? UbuntuColors.orange : "white", "value-bar has the wrong color-tint") |
540 | + } |
541 | + compare(valueLabel.visible, data.valueLabelVisible, "value-label visibility is incorrect") |
542 | |
543 | // test input does not fall through |
544 | mouseClick(notification, notification.width / 2, notification.height / 2) |
Approve this one for rtm too since at this stage is basically the same a trunk. See https:/ /code.launchpad .net/~macslow/ unity8/ synchronous- notification/ +merge/ 229059