on xenial help sign in
Implemented the needed visual updates on notifications requested by Design for RTM. Fixes: 1348092, 1365987
473ab33Mirco Müller
qml/Notifications/Notification.qml+132−46
@@ -39,19 +39,17 @@ Item {
39 property bool fullscreen: false
39 property bool fullscreen: false
40 property int maxHeight
40 property int maxHeight
41 property int margins
41 property int margins
42− property Gradient greenGradient : Gradient {
42+ readonly property color red: "#fc4949"
43− GradientStop { position: 0.0; color: "#3fb24f" }
43+ readonly property color green: "#3fb24f"
44− GradientStop { position: 1.0; color: "#3fb24f" }
44+ readonly property color sdLightGrey: "#eaeaea"
45− }
45+ readonly property color sdDarkGrey: "#dddddd"
46− property Gradient darkgreyGradient: Gradient {
46+ readonly property color sdFontColor: "#5d5d5d"
47− GradientStop { position: 0.0; color: "#4d4745" }
47+ readonly property real contentSpacing: units.gu(2)
48− GradientStop { position: 1.0; color: "#4d4745" }
49− }
50
48
51 objectName: "background"
49 objectName: "background"
52− implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : contentColumn.height + contentColumn.spacing * 2) : 0
50+ implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height + contentSpacing * 2) : 0
53
51
54− color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
52+ color: type == Notification.SnapDecision ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
55 opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
53 opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
56
54
57 state: {
55 state: {
@@ -107,7 +105,7 @@ Item {
107 states:[
105 states:[
108 State {
106 State {
109 name: "contracted"
107 name: "contracted"
110− PropertyChanges {target: notification; height: units.gu(8)}
108+ PropertyChanges {target: notification; height: units.gu(10)}
111 },
109 },
112 State {
110 State {
113 name: "expanded"
111 name: "expanded"
@@ -131,6 +129,7 @@ Item {
131 color: parent.color
129 color: parent.color
132 opacity: parent.opacity
130 opacity: parent.opacity
133 radius: "medium"
131 radius: "medium"
132+ borderSource: "none"
134 }
133 }
135
134
136 Rectangle {
135 Rectangle {
@@ -199,50 +198,41 @@ Item {
199 }
198 }
200
199
201 Column {
200 Column {
202− id: contentColumn
201+ id: outterColumn
203− objectName: "contentColumn"
204
202
205 anchors {
203 anchors {
206 left: parent.left
204 left: parent.left
207 right: parent.right
205 right: parent.right
208 top: parent.top
206 top: parent.top
209− margins: fullscreen ? 0 : spacing
207+ margins: 0
208+ topMargin: fullscreen ? 0 : units.gu(2)
210 }
209 }
211
210
212− spacing: units.gu(1)
211+ spacing: units.gu(2)
213
212
214 Row {
213 Row {
215 id: topRow
214 id: topRow
216
215
217− spacing: contentColumn.spacing
216+ spacing: contentSpacing
218 anchors {
217 anchors {
219 left: parent.left
218 left: parent.left
220 right: parent.right
219 right: parent.right
220+ margins: contentSpacing
221 }
221 }
222
222
223 ShapedIcon {
223 ShapedIcon {
224 id: icon
224 id: icon
225
225
226 objectName: "icon"
226 objectName: "icon"
227− width: units.gu(6)
227+ width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
228− height: units.gu(6)
228+ height: width
229 shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
229 shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
230 visible: iconSource !== undefined && iconSource != ""
230 visible: iconSource !== undefined && iconSource != ""
231− }
232−
233− Image {
234− id: secondaryIcon
235−
236− objectName: "secondaryIcon"
237− width: units.gu(2)
238− height: units.gu(2)
239− visible: source !== undefined && source != ""
240− fillMode: Image.PreserveAspectCrop
241− }
231+ }
242
232
243 Column {
233 Column {
244 id: labelColumn
234 id: labelColumn
245− width: parent.width - x
235+ width: secondaryIcon.visible ? parent.width - x - units.gu(4.5) : parent.width - x
246
236
247 anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
237 anchors.verticalCenter: (icon.visible && !bodyLabel.visible) ? icon.verticalCenter : undefined
248
238
@@ -255,8 +245,7 @@ Item {
255 right: parent.right
245 right: parent.right
256 }
246 }
257 fontSize: "medium"
247 fontSize: "medium"
258− font.bold: true
259− color: Theme.palette.selected.backgroundText
248+ color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText
260 elide: Text.ElideRight
249 elide: Text.ElideRight
261 textFormat: Text.PlainText
250 textFormat: Text.PlainText
262 }
251 }
@@ -271,14 +260,27 @@ Item {
271 }
260 }
272 visible: body != ""
261 visible: body != ""
273 fontSize: "small"
262 fontSize: "small"
274− color: Theme.palette.selected.backgroundText
263+ color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText
275− opacity: 0.6
276 wrapMode: Text.WordWrap
264 wrapMode: Text.WordWrap
277− maximumLineCount: 10
265+ maximumLineCount: 2
278 elide: Text.ElideRight
266 elide: Text.ElideRight
279 textFormat: Text.PlainText
267 textFormat: Text.PlainText
280 }
268 }
281 }
269 }
270+
271+ Image {
272+ id: secondaryIcon
273+
274+ objectName: "secondaryIcon"
275+ width: units.gu(3)
276+ height: units.gu(3)
277+ visible: status === Image.Ready
278+ fillMode: Image.PreserveAspectCrop
279+ }
280+ }
281+
282+ ListItem.ThinDivider {
283+ visible: type == Notification.SnapDecision
282 }
284 }
283
285
284 Column {
286 Column {
@@ -321,6 +323,74 @@ Item {
321 }
323 }
322 }
324 }
323
325
326+ Column {
327+ id: oneOverTwoCase
328+
329+ anchors {
330+ left: parent.left
331+ right: parent.right
332+ margins: contentSpacing
333+ }
334+
335+ spacing: contentSpacing
336+
337+ visible: notification.type == Notification.SnapDecision && oneOverTwoRepeaterTop.count == 3
338+
339+ Repeater {
340+ id: oneOverTwoRepeaterTop
341+
342+ model: notification.actions
343+ delegate: Loader {
344+ id: oneOverTwoLoaderTop
345+
346+ property string actionId: id
347+ property string actionLabel: label
348+
349+ Component {
350+ id: oneOverTwoButtonTop
351+
352+ Button {
353+ objectName: "notify_oot_button" + index
354+ width: oneOverTwoCase.width
355+ text: oneOverTwoLoaderTop.actionLabel
356+ color: notification.hints["x-canonical-private-affirmative-tint"] == "true" ? green : sdDarkGrey
357+ onClicked: notification.notification.invokeAction(oneOverTwoLoaderTop.actionId)
358+ }
359+ }
360+ sourceComponent: index == 0 ? oneOverTwoButtonTop : undefined
361+ }
362+ }
363+
364+ Row {
365+ spacing: contentSpacing
366+
367+ Repeater {
368+ id: oneOverTwoRepeaterBottom
369+
370+ model: notification.actions
371+ delegate: Loader {
372+ id: oneOverTwoLoaderBottom
373+
374+ property string actionId: id
375+ property string actionLabel: label
376+
377+ Component {
378+ id: oneOverTwoButtonBottom
379+
380+ Button {
381+ objectName: "notify_oot_button" + index
382+ width: oneOverTwoCase.width / 2 - spacing * 2
383+ text: oneOverTwoLoaderBottom.actionLabel
384+ color: index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true" ? red : sdDarkGrey
385+ onClicked: notification.notification.invokeAction(oneOverTwoLoaderBottom.actionId)
386+ }
387+ }
388+ sourceComponent: (index == 1 || index == 2) ? oneOverTwoButtonBottom : undefined
389+ }
390+ }
391+ }
392+ }
393+
324 Row {
394 Row {
325 id: buttonRow
395 id: buttonRow
326
396
@@ -328,9 +398,10 @@ Item {
328 anchors {
398 anchors {
329 left: parent.left
399 left: parent.left
330 right: parent.right
400 right: parent.right
401+ margins: contentSpacing
331 }
402 }
332− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0
403+ visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
333− spacing: units.gu(1)
404+ spacing: units.gu(2)
334 layoutDirection: Qt.RightToLeft
405 layoutDirection: Qt.RightToLeft
335
406
336 Repeater {
407 Repeater {
@@ -347,10 +418,19 @@ Item {
347 id: actionButton
418 id: actionButton
348
419
349 Button {
420 Button {
350− objectName: "button" + index
421+ objectName: "notify_button" + index
351− width: buttonRow.width / 2 - spacing
422+ width: buttonRow.width / 2 - spacing*2
352 text: loader.actionLabel
423 text: loader.actionLabel
353− gradient: notification.hints["x-canonical-private-button-tint"] == "true" && index == 0 ? greenGradient : darkgreyGradient
424+ color: {
425+ var result = sdDarkGrey;
426+ if (index == 0 && notification.hints["x-canonical-private-affirmative-tint"] == "true") {
427+ result = green;
428+ }
429+ if (index == 1 && notification.hints["x-canonical-private-rejection-tint"] == "true") {
430+ result = red;
431+ }
432+ return result;
433+ }
354 onClicked: notification.notification.invokeAction(loader.actionId)
434 onClicked: notification.notification.invokeAction(loader.actionId)
355 }
435 }
356 }
436 }
@@ -362,10 +442,16 @@ Item {
362 ComboButton {
442 ComboButton {
363 id: comboButton
443 id: comboButton
364
444
365− objectName: "button2"
445+ objectName: "notify_button2"
366 width: parent.width
446 width: parent.width
447+ anchors {
448+ left: parent.left
449+ right: parent.right
450+ margins: contentSpacing
451+ }
452+
367− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3
453+ visible: notification.type == Notification.SnapDecision && actionRepeater.count > 3 && !oneOverTwoCase.visible
368− gradient: darkgreyGradient
454+ color: sdDarkGrey
369 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
455 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
370 expanded: false
456 expanded: false
371 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
457 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
@@ -399,7 +485,7 @@ Item {
399 MouseArea {
485 MouseArea {
400 id: comboInputArea
486 id: comboInputArea
401
487
402− objectName: "button" + index
488+ objectName: "notify_button" + index
403 width: comboButton.width
489 width: comboButton.width
404 height: comboIcon.height + units.gu(2)
490 height: comboIcon.height + units.gu(2)
405
491
@@ -421,7 +507,7 @@ Item {
421 }
507 }
422 width: units.gu(2)
508 width: units.gu(2)
423 height: units.gu(2)
509 height: units.gu(2)
424− color: "white"
510+ color: sdFontColor
425 name: splitLabel[2]
511 name: splitLabel[2]
426 }
512 }
427
513
@@ -434,7 +520,7 @@ Item {
434 verticalCenter: comboIcon.verticalCenter
520 verticalCenter: comboIcon.verticalCenter
435 }
521 }
436 fontSize: "small"
522 fontSize: "small"
437− color: "white"
523+ color: sdFontColor
438 text: splitLabel[3]
524 text: splitLabel[3]
439 }
525 }
440 }
526 }
qml/Notifications/NotificationMenuItemFactory.qml+4
@@ -55,6 +55,7 @@ Loader {
55 anchors {
55 anchors {
56 left: parent.left
56 left: parent.left
57 right: parent.right
57 right: parent.right
58+ margins: spacing
58 }
59 }
59
60
60 Component.onCompleted: {
61 Component.onCompleted: {
@@ -65,6 +66,7 @@ Loader {
65
66
66 Label {
67 Label {
67 text: menuData.label
68 text: menuData.label
69+ color: notification.sdFontColor
68 }
70 }
69
71
70 TextField {
72 TextField {
@@ -98,11 +100,13 @@ Loader {
98 id: checkBox
100 id: checkBox
99
101
100 checked: false
102 checked: false
103+ activeFocusOnPress: false
101 }
104 }
102
105
103 Label {
106 Label {
104 anchors.verticalCenter: checkBox.verticalCenter
107 anchors.verticalCenter: checkBox.verticalCenter
105 text: i18n.tr("Show password")
108 text: i18n.tr("Show password")
109+ color: notification.sdFontColor
106 }
110 }
107 }
111 }
108 }
112 }
qml/Notifications/Notifications.qml+1−1
@@ -33,7 +33,7 @@ ListView {
33 id: snapDecisionProxyModel
33 id: snapDecisionProxyModel
34
34
35 model: notificationList.model
35 model: notificationList.model
36− filterRole: UnityNotifications.ModelInterface.RoleType
36+ filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
37 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
37 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
38 }
38 }
39
39
qml/Shell.qml+1−1
@@ -562,7 +562,7 @@ Item {
562 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
562 visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
563 color: "#000000"
563 color: "#000000"
564 anchors.fill: parent
564 anchors.fill: parent
565− opacity: 0.5
565+ opacity: 0.9
566
566
567 MouseArea {
567 MouseArea {
568 anchors.fill: parent
568 anchors.fill: parent
tests/autopilot/unity8/shell/tests/test_notifications.py+61−97
@@ -135,10 +135,7 @@ class InteractiveNotificationBase(NotificationsBase):
135 actions = [("action_id", "dummy")]
135 actions = [("action_id", "dummy")]
136 hints = [
136 hints = [
137 ("x-canonical-switch-to-application", "true"),
137 ("x-canonical-switch-to-application", "true"),
138− (
139− "x-canonical-secondary-icon",
138+ ("x-canonical-secondary-icon","dialer")
140− self._get_icon_path('applicationIcons/dialer-app.png')
141− )
142 ]
139 ]
143
140
144 self._create_interactive_notification(
141 self._create_interactive_notification(
@@ -170,20 +167,19 @@ class InteractiveNotificationBase(NotificationsBase):
170 body = "Frank Zappa\n+44 (0)7736 027340"
167 body = "Frank Zappa\n+44 (0)7736 027340"
171 icon_path = self._get_icon_path('avatars/anna_olsson.png')
168 icon_path = self._get_icon_path('avatars/anna_olsson.png')
172 hints = [
169 hints = [
173− (
174− "x-canonical-secondary-icon",
170+ ("x-canonical-secondary-icon", "incoming-call"),
175− self._get_icon_path('applicationIcons/dialer-app.png')
176− ),
177 ("x-canonical-snap-decisions", "true"),
171 ("x-canonical-snap-decisions", "true"),
172+ ("x-canonical-private-affirmative-tint", "true"),
173+ ("x-canonical-private-rejection-tint", "true"),
178 ]
174 ]
179
175
180 actions = [
176 actions = [
181 ('action_accept', 'Hold + Answer'),
177 ('action_accept', 'Hold + Answer'),
182 ('action_decline_1', 'End + Answer'),
178 ('action_decline_1', 'End + Answer'),
183 ('action_decline_2', 'Decline'),
179 ('action_decline_2', 'Decline'),
184− ('action_decline_3', 'messages:I missed your call - can you call me now?'),
180+ ('action_decline_3', 'message:I missed your call - can you call me now?'),
185− ('action_decline_4', 'messages:I\'m running late. I\'m on my way.'),
181+ ('action_decline_4', 'message:I\'m running late. I\'m on my way.'),
186− ('action_decline_5', 'messages:I\'m busy at the moment. I\'ll call later.'),
182+ ('action_decline_5', 'message:I\'m busy at the moment. I\'ll call later.'),
187 ('action_decline_6', 'edit:Custom'),
183 ('action_decline_6', 'edit:Custom'),
188 ]
184 ]
189
185
@@ -204,13 +200,53 @@ class InteractiveNotificationBase(NotificationsBase):
204 notification.pointing_device.click_object(
200 notification.pointing_device.click_object(
205 notification.select_single(objectName="combobutton_dropdown"))
201 notification.select_single(objectName="combobutton_dropdown"))
206 self.assertThat(
202 self.assertThat(
207− notification.select_single(objectName="button2").expanded,
203+ notification.select_single(objectName="notify_button2").expanded,
208 Eventually(Equals(True)))
204 Eventually(Equals(True)))
209 time.sleep(2)
205 time.sleep(2)
210 notification.pointing_device.click_object(
206 notification.pointing_device.click_object(
211− notification.select_single(objectName="button4"))
207+ notification.select_single(objectName="notify_button4"))
212 self.assert_notification_action_id_was_called("action_decline_4")
208 self.assert_notification_action_id_was_called("action_decline_4")
213
209
210+
211+ def test_sd_one_over_two_layout(self):
212+ """Snap-decision with three actions should use one-over two button layout."""
213+ unity_proxy = self.launch_unity()
214+ unlock_unity(unity_proxy)
215+
216+ summary = "Theatre at Ferria Stadium"
217+ body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
218+ hints = [
219+ ("x-canonical-snap-decisions", "true"),
220+ ("x-canonical-non-shaped-icon", "true"),
221+ ("x-canonical-private-affirmative-tint", "true")
222+ ]
223+
224+ actions = [
225+ ('action_accept', 'Ok'),
226+ ('action_decline_1', 'Snooze'),
227+ ('action_decline_2', 'View'),
228+ ]
229+
230+ self._create_interactive_notification(
231+ summary,
232+ body,
233+ None,
234+ "NORMAL",
235+ actions,
236+ hints
237+ )
238+
239+ # verify and interact with the triggered snap-decision notification
240+ notify_list = self._get_notifications_list()
241+ get_notification = lambda: notify_list.wait_select_single(
242+ 'Notification', objectName='notification1')
243+ notification = get_notification()
244+ self._assert_notification(
245+ notification, summary, body, False, False, 1.0)
246+ notification.pointing_device.click_object(
247+ notification.select_single(objectName="notify_oot_button0"))
248+ self.assert_notification_action_id_was_called("action_accept")
249+
214 def test_modal_sd_without_greeter(self):
250 def test_modal_sd_without_greeter(self):
215 """Snap-decision should block input to shell without greeter."""
251 """Snap-decision should block input to shell without greeter."""
216 unity_proxy = self.launch_unity()
252 unity_proxy = self.launch_unity()
@@ -222,6 +258,8 @@ class InteractiveNotificationBase(NotificationsBase):
222 hints = [
258 hints = [
223 ("x-canonical-snap-decisions", "true"),
259 ("x-canonical-snap-decisions", "true"),
224 ("x-canonical-non-shaped-icon", "true"),
260 ("x-canonical-non-shaped-icon", "true"),
261+ ("x-canonical-private-affirmative-tint", "true"),
262+ ("x-canonical-private-rejection-tint", "true"),
225 ]
263 ]
226
264
227 actions = [
265 actions = [
@@ -253,7 +291,7 @@ class InteractiveNotificationBase(NotificationsBase):
253 self._assert_notification(
291 self._assert_notification(
254 notification, summary, body, True, False, 1.0)
292 notification, summary, body, True, False, 1.0)
255 notification.pointing_device.click_object(
293 notification.pointing_device.click_object(
256− notification.select_single(objectName="button0"))
294+ notification.select_single(objectName="notify_button0"))
257 self.assert_notification_action_id_was_called("action_accept")
295 self.assert_notification_action_id_was_called("action_accept")
258
296
259 def test_modal_sd_with_greeter(self):
297 def test_modal_sd_with_greeter(self):
@@ -266,6 +304,8 @@ class InteractiveNotificationBase(NotificationsBase):
266 hints = [
304 hints = [
267 ("x-canonical-snap-decisions", "true"),
305 ("x-canonical-snap-decisions", "true"),
268 ("x-canonical-non-shaped-icon", "true"),
306 ("x-canonical-non-shaped-icon", "true"),
307+ ("x-canonical-private-affirmative-tint", "true"),
308+ ("x-canonical-private-rejection-tint", "true"),
269 ]
309 ]
270
310
271 actions = [
311 actions = [
@@ -296,7 +336,7 @@ class InteractiveNotificationBase(NotificationsBase):
296 self._assert_notification(
336 self._assert_notification(
297 notification, summary, body, True, False, 1.0)
337 notification, summary, body, True, False, 1.0)
298 notification.pointing_device.click_object(
338 notification.pointing_device.click_object(
299− notification.select_single(objectName="button0"))
339+ notification.select_single(objectName="notify_button0"))
300 self.assert_notification_action_id_was_called("action_accept")
340 self.assert_notification_action_id_was_called("action_accept")
301
341
302 def _create_interactive_notification(
342 def _create_interactive_notification(
@@ -438,10 +478,7 @@ class EphemeralNotificationsTests(NotificationsBase):
438 "join me and Anna?"
478 "join me and Anna?"
439 icon_path = self._get_icon_path('avatars/anna_olsson.png')
479 icon_path = self._get_icon_path('avatars/anna_olsson.png')
440 hints = [
480 hints = [
441− (
442− "x-canonical-secondary-icon",
481+ ("x-canonical-secondary-icon", "message")
443− self._get_icon_path('applicationIcons/dialer-app.png')
444− )
445 ]
482 ]
446
483
447 notification = shell.create_ephemeral_notification(
484 notification = shell.create_ephemeral_notification(
@@ -474,17 +511,13 @@ class EphemeralNotificationsTests(NotificationsBase):
474 notify_list = self._get_notifications_list()
511 notify_list = self._get_notifications_list()
475
512
476 summary = "Upload of image completed"
513 summary = "Upload of image completed"
477− hints = [
478− (
479− "x-canonical-secondary-icon",
480− self._get_icon_path('applicationIcons/facebook.png')
514+ icon_path = self._get_icon_path('applicationIcons/facebook.png')
481− )
515+ hints=[]
482− ]
483
516
484 notification = shell.create_ephemeral_notification(
517 notification = shell.create_ephemeral_notification(
485 summary,
518 summary,
486 None,
519 None,
487− None,
520+ icon_path,
488 hints,
521 hints,
489 "NORMAL",
522 "NORMAL",
490 )
523 )
@@ -497,8 +530,8 @@ class EphemeralNotificationsTests(NotificationsBase):
497 notification(),
530 notification(),
498 summary,
531 summary,
499 None,
532 None,
500− False,
501 True,
533 True,
534+ False,
502 1.0
535 1.0
503 )
536 )
504
537
@@ -683,7 +716,7 @@ class EphemeralNotificationsTests(NotificationsBase):
683 body = 'This bubble uses the icon-title-body layout with a ' \
716 body = 'This bubble uses the icon-title-body layout with a ' \
684 'secondary icon.'
717 'secondary icon.'
685 icon_path = self._get_icon_path('avatars/anna_olsson.png')
718 icon_path = self._get_icon_path('avatars/anna_olsson.png')
686− hint_icon = self._get_icon_path('applicationIcons/dialer-app.png')
719+ hint_icon = 'dialer'
687
720
688 notification = shell.create_ephemeral_notification(
721 notification = shell.create_ephemeral_notification(
689 summary,
722 summary,
@@ -718,72 +751,3 @@ class EphemeralNotificationsTests(NotificationsBase):
718 self.assertThat(get_notification, Eventually(NotEquals(None)))
751 self.assertThat(get_notification, Eventually(NotEquals(None)))
719 self._assert_notification(
752 self._assert_notification(
720 get_notification(), summary, body, False, False, 1.0)
753 get_notification(), summary, body, False, False, 1.0)
721−
722− def test_append_hint(self):
723− """Notification has to accumulate body-text using append-hint."""
724− unity_proxy = self.launch_unity()
725− unlock_unity(unity_proxy)
726−
727− notify_list = self._get_notifications_list()
728−
729− summary = 'Cole Raby'
730− body = 'Hey Bro Coly!'
731− icon_path = self._get_icon_path('avatars/amanda.png')
732− body_sum = body
733− notification = shell.create_ephemeral_notification(
734− summary,
735− body,
736− icon_path,
737− hints=[('x-canonical-append', 'true')]
738− )
739−
740− notification.show()
741−
742− get_notification = lambda: notify_list.wait_select_single(
743− 'Notification', objectName='notification1')
744−
745− notification = get_notification()
746− self._assert_notification(
747− notification,
748− summary,
749− body_sum,
750− True,
751− False,
752− 1.0
753− )
754−
755− bodies = [
756− 'What\'s up dude?',
757− 'Did you watch the air-race in Oshkosh last week?',
758− 'Phil owned the place like no one before him!',
759− 'Did really everything in the race work according to regulations?',
760− 'Somehow I think to remember Burt Williams did cut corners and '
761− 'was not punished for this.',
762− 'Hopefully the referees will watch the videos of the race.',
763− 'Burt could get fined with US$ 50000 for that rule-violation :)'
764− ]
765−
766− for new_body in bodies:
767− body = new_body
768− body_sum += '\n' + body
769− notification = shell.create_ephemeral_notification(
770− summary,
771− body,
772− icon_path,
773− hints=[('x-canonical-append', 'true')]
774− )
775− notification.show()
776−
777− get_notification = lambda: notify_list.wait_select_single(
778− 'Notification',
779− objectName='notification1'
780− )
781− notification = get_notification()
782− self._assert_notification(
783− notification,
784− summary,
785− body_sum,
786− True,
787− False,
788− 1.0
789− )
tests/qmltests/Notifications/tst_Notifications.qml+59−17
@@ -64,7 +64,7 @@ Row {
64 function addSnapDecisionNotification() {
64 function addSnapDecisionNotification() {
65 var n = {
65 var n = {
66 type: Notification.SnapDecision,
66 type: Notification.SnapDecision,
67− hints: {"x-canonical-private-button-tint": "true"},
67+ hints: {"x-canonical-private-affirmative-tint": "true"},
68 summary: "Tom Ato",
68 summary: "Tom Ato",
69 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
69 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
70 icon: "../graphics/avatars/funky.png",
70 icon: "../graphics/avatars/funky.png",
@@ -79,6 +79,22 @@ Row {
79 mockModel.append(n)
79 mockModel.append(n)
80 }
80 }
81
81
82+ function add2over1SnapDecisionNotification() {
83+ var n = {
84+ type: Notification.SnapDecision,
85+ hints: {"x-canonical-private-affirmative-tint": "true",},
86+ summary: "Theatre at Ferria Stadium",
87+ body: "at Ferria Stadium in Bilbao, Spain\n07578545317",
88+ icon: "",
89+ secondaryIcon: "",
90+ actions: [{ id: "ok_id", label: "Ok"},
91+ { id: "snooze_id", label: "Snooze"},
92+ { id: "view_id", label: "View"}]
93+ }
94+
95+ mockModel.append(n)
96+ }
97+
82 function addEphemeralNotification() {
98 function addEphemeralNotification() {
83 var n = {
99 var n = {
84 type: Notification.Ephemeral,
100 type: Notification.Ephemeral,
@@ -181,6 +197,12 @@ Row {
181 onClicked: addSnapDecisionNotification()
197 onClicked: addSnapDecisionNotification()
182 }
198 }
183
199
200+ Button {
201+ width: parent.width
202+ text: "add a 2over1 snap-decision"
203+ onClicked: add2over1SnapDecisionNotification()
204+ }
205+
184 Button {
206 Button {
185 width: parent.width
207 width: parent.width
186 text: "add an ephemeral"
208 text: "add an ephemeral"
@@ -229,7 +251,7 @@ Row {
229 {
251 {
230 tag: "Snap Decision with secondary icon and button-tint",
252 tag: "Snap Decision with secondary icon and button-tint",
231 type: Notification.SnapDecision,
253 type: Notification.SnapDecision,
232− hints: {"x-canonical-private-button-tint": "true"},
254+ hints: {"x-canonical-private-affirmative-tint": "true"},
233 summary: "Tom Ato",
255 summary: "Tom Ato",
234 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
256 body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
235 icon: "../graphics/avatars/funky.png",
257 icon: "../graphics/avatars/funky.png",
@@ -249,10 +271,31 @@ Row {
249 buttonTinted: true,
271 buttonTinted: true,
250 hasSound: false
272 hasSound: false
251 },
273 },
274+ {
275+ tag: "2-over-1 Snap Decision with button-tint",
276+ type: Notification.SnapDecision,
277+ hints: {"x-canonical-private-affirmative-tint": "true"},
278+ summary: "Theatre at Ferria Stadium",
279+ body: "at Ferria Stadium in Bilbao, Spain\n07578545317",
280+ icon: "",
281+ secondaryIcon: "",
282+ actions: [{ id: "ok_id", label: "Ok"},
283+ { id: "snooze_id", label: "Snooze"},
284+ { id: "view_id", label: "View"}],
285+ summaryVisible: true,
286+ bodyVisible: true,
287+ iconVisible: false,
288+ shapedIcon: false,
289+ nonShapedIcon: false,
290+ secondaryIconVisible: false,
291+ buttonRowVisible: false,
292+ buttonTinted: true,
293+ hasSound: false
294+ },
252 {
295 {
253 tag: "Ephemeral notification - icon-summary layout",
296 tag: "Ephemeral notification - icon-summary layout",
254 type: Notification.Ephemeral,
297 type: Notification.Ephemeral,
255− hints: {"x-canonical-private-button-tint": "false"},
298+ hints: {"x-canonical-private-affirmative-tint": "false"},
256 summary: "Photo upload completed",
299 summary: "Photo upload completed",
257 body: "",
300 body: "",
258 icon: "",
301 icon: "",
@@ -271,7 +314,7 @@ Row {
271 {
314 {
272 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
315 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
273 type: Notification.Ephemeral,
316 type: Notification.Ephemeral,
274− hints: {"x-canonical-private-button-tint": "false",
317+ hints: {"x-canonical-private-affirmative-tint": "false",
275 "sound-file": "dummy.ogg",
318 "sound-file": "dummy.ogg",
276 "suppress-sound": "true"},
319 "suppress-sound": "true"},
277 summary: "New comment successfully published",
320 summary: "New comment successfully published",
@@ -293,7 +336,7 @@ Row {
293 {
336 {
294 tag: "Interactive notification",
337 tag: "Interactive notification",
295 type: Notification.Interactive,
338 type: Notification.Interactive,
296− hints: {"x-canonical-private-button-tint": "false",
339+ hints: {"x-canonical-private-affirmative-tint": "false",
297 "sound-file": "dummy.ogg"},
340 "sound-file": "dummy.ogg"},
298 summary: "Interactive notification",
341 summary: "Interactive notification",
299 body: "This is a notification that can be clicked",
342 body: "This is a notification that can be clicked",
@@ -313,7 +356,7 @@ Row {
313 {
356 {
314 tag: "Snap Decision without secondary icon and no button-tint",
357 tag: "Snap Decision without secondary icon and no button-tint",
315 type: Notification.SnapDecision,
358 type: Notification.SnapDecision,
316− hints: {"x-canonical-private-button-tint": "false",
359+ hints: {"x-canonical-private-affirmative-tint": "false",
317 "sound-file": "dummy.ogg"},
360 "sound-file": "dummy.ogg"},
318 summary: "Bro Coly",
361 summary: "Bro Coly",
319 body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
362 body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
@@ -334,7 +377,7 @@ Row {
334 {
377 {
335 tag: "Ephemeral notification",
378 tag: "Ephemeral notification",
336 type: Notification.Ephemeral,
379 type: Notification.Ephemeral,
337− hints: {"x-canonical-private-button-tint": "false",
380+ hints: {"x-canonical-private-affirmative-tint": "false",
338 "sound-file": "dummy.ogg"},
381 "sound-file": "dummy.ogg"},
339 summary: "Cole Raby",
382 summary: "Cole Raby",
340 body: "I did not expect it to be that late.",
383 body: "I did not expect it to be that late.",
@@ -354,9 +397,8 @@ Row {
354 {
397 {
355 tag: "Ephemeral notification with non-shaped icon",
398 tag: "Ephemeral notification with non-shaped icon",
356 type: Notification.Ephemeral,
399 type: Notification.Ephemeral,
357− hints: {"x-canonical-private-button-tint": "false",
400+ hints: {"x-canonical-private-affirmative-tint": "false",
358− "x-canonical-non-shaped-icon": "true",
401+ "x-canonical-non-shaped-icon": "true"},
359− "sound-file": "dummy.ogg"},
360 summary: "Contacts",
402 summary: "Contacts",
361 body: "Synchronised contacts-database with cloud-storage.",
403 body: "Synchronised contacts-database with cloud-storage.",
362 icon: "../graphics/applicationIcons/contacts-app.png",
404 icon: "../graphics/applicationIcons/contacts-app.png",
@@ -370,7 +412,7 @@ Row {
370 secondaryIconVisible: false,
412 secondaryIconVisible: false,
371 buttonRowVisible: false,
413 buttonRowVisible: false,
372 buttonTinted: false,
414 buttonTinted: false,
373− hasSound: true
415+ hasSound: false
374 }
416 }
375 ]
417 ]
376 }
418 }
@@ -437,8 +479,8 @@ Row {
437 tryCompare(audioItem, "playbackState", data.hasSound ? Audio.PlayingState : Audio.StoppedState)
479 tryCompare(audioItem, "playbackState", data.hasSound ? Audio.PlayingState : Audio.StoppedState)
438
480
439 if(data.buttonRowVisible) {
481 if(data.buttonRowVisible) {
440− var buttonCancel = findChild(buttonRow, "button1")
482+ var buttonCancel = findChild(buttonRow, "notify_button1")
441− var buttonAccept = findChild(buttonRow, "button0")
483+ var buttonAccept = findChild(buttonRow, "notify_button0")
442
484
443 // only test the left/cancel-button if two actions have been passed in
485 // only test the left/cancel-button if two actions have been passed in
444 if (data.actions.length == 2) {
486 if (data.actions.length == 2) {
@@ -448,7 +490,7 @@ Row {
448 }
490 }
449
491
450 // check the tinting of the positive/right button
492 // check the tinting of the positive/right button
451− verify(buttonAccept.gradient === data.buttonTinted ? UbuntuColors.orangeGradient : UbuntuColors.greyGradient, "button has the wrong color-tint")
493+ verify(buttonAccept.color === data.buttonTinted ? "#3fb24f" : "#dddddd", "button has the wrong color-tint")
452
494
453 // click the positive/right button
495 // click the positive/right button
454 tryCompareFunction(function() { mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2); return actionSpy.signalArguments.length > 0; }, true);
496 tryCompareFunction(function() { mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2); return actionSpy.signalArguments.length > 0; }, true);
@@ -458,19 +500,19 @@ Row {
458
500
459 // check if there's a ComboButton created due to more actions being passed
501 // check if there's a ComboButton created due to more actions being passed
460 if (data.actions.length > 2) {
502 if (data.actions.length > 2) {
461− var comboButton = findChild(notification, "button2")
503+ var comboButton = findChild(notification, "notify_button2")
462 tryCompareFunction(function() { return comboButton.expanded == false; }, true);
504 tryCompareFunction(function() { return comboButton.expanded == false; }, true);
463
505
464 // click to expand
506 // click to expand
465 tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == true; }, true);
507 tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == true; }, true);
466
508
467 // try clicking on choices in expanded comboList
509 // try clicking on choices in expanded comboList
468− var choiceButton1 = findChild(notification, "button3")
510+ var choiceButton1 = findChild(notification, "notify_button3")
469 tryCompareFunction(function() { mouseClick(choiceButton1, choiceButton1.width / 2, choiceButton1.height / 2); return actionSpy.signalArguments.length > 0; }, true);
511 tryCompareFunction(function() { mouseClick(choiceButton1, choiceButton1.width / 2, choiceButton1.height / 2); return actionSpy.signalArguments.length > 0; }, true);
470 compare(actionSpy.signalArguments[0][0], data.actions[3]["id"], "got wrong id choice action 1")
512 compare(actionSpy.signalArguments[0][0], data.actions[3]["id"], "got wrong id choice action 1")
471 actionSpy.clear()
513 actionSpy.clear()
472
514
473− var choiceButton2 = findChild(notification, "button4")
515+ var choiceButton2 = findChild(notification, "notify_button4")
474 tryCompareFunction(function() { mouseClick(choiceButton2, choiceButton2.width / 2, choiceButton2.height / 2); return actionSpy.signalArguments.length > 0; }, true);
516 tryCompareFunction(function() { mouseClick(choiceButton2, choiceButton2.width / 2, choiceButton2.height / 2); return actionSpy.signalArguments.length > 0; }, true);
475 compare(actionSpy.signalArguments[0][0], data.actions[4]["id"], "got wrong id choice action 2")
517 compare(actionSpy.signalArguments[0][0], data.actions[4]["id"], "got wrong id choice action 2")
476 actionSpy.clear()
518 actionSpy.clear()