Merge lp:~unity-team/unity8/rtm-20150213 into lp:unity8/rtm-14.09
- rtm-20150213
- Merge into rtm-14.09
Proposed by
Michał Sawicz
Status: | Merged |
---|---|
Approved by: | Michael Zanetti |
Approved revision: | 1425 |
Merged at revision: | 1418 |
Proposed branch: | lp:~unity-team/unity8/rtm-20150213 |
Merge into: | lp:unity8/rtm-14.09 |
Diff against target: |
2193 lines (+1176/-382) 27 files modified
debian/changelog (+26/-0) plugins/Dash/CardAttributes.qml (+2/-0) plugins/Dash/CardCreator.js (+5/-1) qml/Components/Dialogs.qml (+4/-24) qml/Components/PhysicalKeysMapper.qml (+113/-0) qml/Components/VolumeKeyFilter.qml (+0/-63) qml/Notifications/Notification.qml (+36/-6) qml/Notifications/Notifications.qml (+2/-6) qml/Panel/IndicatorItem.qml (+2/-0) qml/Shell.qml (+12/-39) tests/mocks/Unity/Notifications/CMakeLists.txt (+2/-1) tests/mocks/Unity/Notifications/MockActionModel.cpp (+6/-4) tests/mocks/Unity/Notifications/MockActionModel.h (+4/-2) tests/mocks/Unity/Notifications/MockNotification.cpp (+156/-4) tests/mocks/Unity/Notifications/MockNotification.h (+63/-5) tests/mocks/Unity/Notifications/MockNotificationModel.cpp (+172/-0) tests/mocks/Unity/Notifications/MockNotificationModel.h (+72/-0) tests/mocks/Unity/Notifications/plugin.cpp (+5/-3) tests/mocks/Unity/Notifications/plugin.h (+1/-2) tests/qmltests/CMakeLists.txt (+1/-0) tests/qmltests/Components/tst_PhysicalKeysMapper.qml (+119/-0) tests/qmltests/Dash/tst_Card.qml (+9/-3) tests/qmltests/Notifications/Notification.qml (+31/-0) tests/qmltests/Notifications/tst_Notifications.qml (+271/-213) tests/qmltests/Notifications/tst_OptionToggle.qml (+11/-6) tests/qmltests/Notifications/tst_SwipeToAct.qml (+4/-0) tests/qmltests/tst_ShellWithPin.qml (+47/-0) |
To merge this branch: | bzr merge lp:~unity-team/unity8/rtm-20150213 |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity Team | Pending | ||
Review via email: mp+249680@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 1423. By Michał Sawicz
-
Add changelog
- 1424. By Josh Arenson
-
Create a PhysicalKeyMapper to handle all physical/hardware keys
In addition, change the key combination for taking a screenshot from (Volume Up + Volume Down) to (Power + Volume Down).
- 1425. By Michał Sawicz
-
Update changelog
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2015-01-22 20:47:05 +0000 |
3 | +++ debian/changelog 2015-02-16 22:20:44 +0000 |
4 | @@ -1,3 +1,29 @@ |
5 | +unity8 (8.02+15.04.20150213~rtm-0ubuntu1) UNRELEASED; urgency=medium |
6 | + |
7 | + [ Albert astals ] |
8 | + * Preserve the aspect ratio for the card attribute images |
9 | + |
10 | + [ Andrea Cimitan ] |
11 | + * support background on horizontal cards with summary (LP: #1393008) |
12 | + * Background needs to be specified to be visible in horizontal cards |
13 | + (LP: #1411748) |
14 | + |
15 | + [ Daniel d'Andrada ] |
16 | + * Don't show() the lockscreen if it's already being shown |
17 | + |
18 | + [ Joshua Arenson ] |
19 | + * Create a PhysicalKeyMapper to handle all physical/hardware keys |
20 | + |
21 | + [ Michał Sawicz ] |
22 | + * Create a PhysicalKeyMapper to handle all physical/hardware keys |
23 | + |
24 | + [ Mirco Müller ] |
25 | + * Allow swipe-to-dismiss for contracted snap-decision notifications, |
26 | + interactive notifications and ephemeral notifications. (LP: |
27 | + #1355422, #1334855) |
28 | + |
29 | + -- Michał Sawicz <michal.sawicz@canonical.com> Fri, 13 Feb 2015 17:23:13 +0100 |
30 | + |
31 | unity8 (8.02+15.04.20150122.2~rtm-0ubuntu1) 14.09; urgency=medium |
32 | |
33 | [ Michael Terry ] |
34 | |
35 | === modified file 'plugins/Dash/CardAttributes.qml' |
36 | --- plugins/Dash/CardAttributes.qml 2014-10-14 15:40:59 +0000 |
37 | +++ plugins/Dash/CardAttributes.qml 2015-02-16 22:20:44 +0000 |
38 | @@ -51,6 +51,8 @@ |
39 | id: icon |
40 | height: units.gu(2) |
41 | sets: ["actions", "status", "apps"] |
42 | + // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293 |
43 | + width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth |
44 | source: "icon" in modelData && modelData["icon"] || "" |
45 | color: grid.color |
46 | } |
47 | |
48 | === modified file 'plugins/Dash/CardCreator.js' |
49 | --- plugins/Dash/CardCreator.js 2015-01-08 12:51:33 +0000 |
50 | +++ plugins/Dash/CardCreator.js 2015-02-16 22:20:44 +0000 |
51 | @@ -224,6 +224,8 @@ |
52 | |
53 | // %1 is used as extra anchors of emblemIcon |
54 | // %2 is used as color of emblemIcon |
55 | +// FIXME The width code is a |
56 | +// Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293 |
57 | var kEmblemIconCode = 'StatusIcon { \n\ |
58 | id: emblemIcon; \n\ |
59 | objectName: "emblemIcon"; \n\ |
60 | @@ -235,6 +237,7 @@ |
61 | source: cardData && cardData["emblem"] || ""; \n\ |
62 | color: %2; \n\ |
63 | height: source != "" ? titleLabel.font.pixelSize : 0; \n\ |
64 | + width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth; \n\ |
65 | }\n'; |
66 | |
67 | // %1 is used as anchors of touchdown effect |
68 | @@ -320,7 +323,8 @@ |
69 | var hasSummary = components["summary"] || false; |
70 | var artAndSummary = hasArt && hasSummary; |
71 | var isHorizontal = template["card-layout"] === "horizontal"; |
72 | - var hasBackground = !isHorizontal && (template["card-background"] || components["background"] || artAndSummary); |
73 | + var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) || |
74 | + (hasSummary && (template["card-background"] || components["background"])); |
75 | var hasTitle = components["title"] || false; |
76 | var hasMascot = components["mascot"] || false; |
77 | var hasEmblem = components["emblem"] && !(hasMascot && template["card-size"] === "small") || false; |
78 | |
79 | === modified file 'qml/Components/Dialogs.qml' |
80 | --- qml/Components/Dialogs.qml 2015-01-14 09:09:08 +0000 |
81 | +++ qml/Components/Dialogs.qml 2015-02-16 22:20:44 +0000 |
82 | @@ -28,21 +28,12 @@ |
83 | // in other applications like the welcome wizard. |
84 | readonly property string domain: "unity8" |
85 | |
86 | - function onPowerKeyPressed() { |
87 | - // FIXME: event.isAutoRepeat is always false on Nexus 4. |
88 | - // So we use powerKeyTimer.running to avoid the PowerOff key repeat |
89 | - // https://launchpad.net/bugs/1349416 |
90 | - if (!powerKeyTimer.running) { |
91 | - powerKeyTimer.restart(); |
92 | - } |
93 | - } |
94 | - |
95 | - function onPowerKeyReleased() { |
96 | - powerKeyTimer.stop(); |
97 | - } |
98 | - |
99 | signal powerOffClicked(); |
100 | |
101 | + function showPowerDialog() { |
102 | + d.showPowerDialog(); |
103 | + } |
104 | + |
105 | QtObject { |
106 | id: d // private stuff |
107 | |
108 | @@ -56,17 +47,6 @@ |
109 | } |
110 | } |
111 | |
112 | - Timer { |
113 | - id: powerKeyTimer |
114 | - interval: 2000 |
115 | - repeat: false |
116 | - triggeredOnStart: false |
117 | - |
118 | - onTriggered: { |
119 | - d.showPowerDialog(); |
120 | - } |
121 | - } |
122 | - |
123 | Component { |
124 | id: logoutDialog |
125 | Dialog { |
126 | |
127 | === added file 'qml/Components/PhysicalKeysMapper.qml' |
128 | --- qml/Components/PhysicalKeysMapper.qml 1970-01-01 00:00:00 +0000 |
129 | +++ qml/Components/PhysicalKeysMapper.qml 2015-02-16 22:20:44 +0000 |
130 | @@ -0,0 +1,113 @@ |
131 | +/* |
132 | + * Copyright (C) 2014-2015 Canonical, Ltd. |
133 | + * |
134 | + * This program is free software; you can redistribute it and/or modify |
135 | + * it under the terms of the GNU General Public License as published by |
136 | + * the Free Software Foundation; version 3. |
137 | + * |
138 | + * This program is distributed in the hope that it will be useful, |
139 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
140 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
141 | + * GNU General Public License for more details. |
142 | + * |
143 | + * You should have received a copy of the GNU General Public License |
144 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
145 | + */ |
146 | + |
147 | +import QtQuick 2.0 |
148 | +import Powerd 0.1 |
149 | + |
150 | +/*! |
151 | + \brief A mapper for the physical keys on the device |
152 | + |
153 | + A mapper to handle events triggered by pressing physical keys on a device. |
154 | + Keys included are |
155 | + * Volume Decrease |
156 | + * Volume Increase |
157 | + * Power |
158 | + |
159 | + This allows for handling the following events |
160 | + * Power dialog |
161 | + * Volume Decreases/Increases |
162 | + * Screenshots |
163 | + |
164 | +*/ |
165 | + |
166 | +Item { |
167 | + id: root |
168 | + |
169 | + signal powerKeyLongPressed; |
170 | + signal volumeDownTriggered; |
171 | + signal volumeUpTriggered; |
172 | + signal screenshotTriggered; |
173 | + |
174 | + QtObject { |
175 | + id: d |
176 | + |
177 | + property bool volumeDownKeyPressed: false |
178 | + property bool volumeUpKeyPressed: false |
179 | + property bool ignoreVolumeEvents: false |
180 | + } |
181 | + |
182 | + Timer { |
183 | + id: powerKeyLongPressTimer |
184 | + |
185 | + interval: 2000 |
186 | + onTriggered: root.powerKeyLongPressed(); |
187 | + } |
188 | + |
189 | + |
190 | + function onKeyPressed(event) { |
191 | + if ((event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) |
192 | + && !event.isAutoRepeat) { |
193 | + |
194 | + // FIXME: We only consider power key presses if the screen is |
195 | + // on because of bugs 1410830/1409003. The theory is that when |
196 | + // those bugs are encountered, there is a >2s delay between the |
197 | + // power press event and the power release event, which causes |
198 | + // the shutdown dialog to appear on resume. So to avoid that |
199 | + // symptom while we investigate the root cause, we simply won't |
200 | + // initiate any dialogs when the screen is off. |
201 | + if (Powerd.status === Powerd.On) { |
202 | + powerKeyLongPressTimer.restart(); |
203 | + } |
204 | + event.accepted = true; |
205 | + } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) |
206 | + && !event.isAutoRepeat) { |
207 | + event.accepted = callManager.handleMediaKey(false); |
208 | + } else if (event.key == Qt.Key_VolumeDown) { |
209 | + if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeDownTriggered(); |
210 | + else if (!event.isAutoRepeat) { |
211 | + if (d.volumeUpKeyPressed) { |
212 | + if (Powerd.status === Powerd.On) root.screenshotTriggered(); |
213 | + d.ignoreVolumeEvents = true; |
214 | + } |
215 | + d.volumeDownKeyPressed = true; |
216 | + } |
217 | + } else if (event.key == Qt.Key_VolumeUp) { |
218 | + if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeUpTriggered(); |
219 | + else if (!event.isAutoRepeat) { |
220 | + if (d.volumeDownKeyPressed) { |
221 | + if (Powerd.status === Powerd.On) root.screenshotTriggered(); |
222 | + d.ignoreVolumeEvents = true; |
223 | + } |
224 | + d.volumeUpKeyPressed = true; |
225 | + } |
226 | + } |
227 | + } |
228 | + |
229 | + function onKeyReleased(event) { |
230 | + if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) { |
231 | + powerKeyLongPressTimer.stop(); |
232 | + event.accepted = true; |
233 | + } else if (event.key == Qt.Key_VolumeDown) { |
234 | + if (!d.ignoreVolumeEvents) root.volumeDownTriggered(); |
235 | + d.volumeDownKeyPressed = false; |
236 | + if (!d.volumeUpKeyPressed) d.ignoreVolumeEvents = false; |
237 | + } else if (event.key == Qt.Key_VolumeUp) { |
238 | + if (!d.ignoreVolumeEvents) root.volumeUpTriggered(); |
239 | + d.volumeUpKeyPressed = false; |
240 | + if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false; |
241 | + } |
242 | + } |
243 | +} |
244 | |
245 | === removed file 'qml/Components/VolumeKeyFilter.qml' |
246 | --- qml/Components/VolumeKeyFilter.qml 2015-01-14 10:24:49 +0000 |
247 | +++ qml/Components/VolumeKeyFilter.qml 1970-01-01 00:00:00 +0000 |
248 | @@ -1,63 +0,0 @@ |
249 | -/* |
250 | - * Copyright (C) 2014 Canonical, Ltd. |
251 | - * |
252 | - * This program is free software; you can redistribute it and/or modify |
253 | - * it under the terms of the GNU General Public License as published by |
254 | - * the Free Software Foundation; version 3. |
255 | - * |
256 | - * This program is distributed in the hope that it will be useful, |
257 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
258 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
259 | - * GNU General Public License for more details. |
260 | - * |
261 | - * You should have received a copy of the GNU General Public License |
262 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
263 | - */ |
264 | - |
265 | -import QtQuick 2.0 |
266 | -/*! |
267 | - \brief A filter for volume keys |
268 | - |
269 | -A filter which treats volume keys as single tri-state key with the states: |
270 | -VolumeUp Pressed, VolumeDown Pressed or Volume Up+Down pressed |
271 | -*/ |
272 | -QtObject { |
273 | - id: root |
274 | - |
275 | - signal volumeUpPressed() |
276 | - signal volumeDownPressed() |
277 | - signal bothVolumeKeysPressed() |
278 | - |
279 | - property bool volumeUpKeyPressed: false |
280 | - property bool volumeDownKeyPressed: false |
281 | - property bool aVolumeKeyWasReleased: true |
282 | - |
283 | - function onKeyPressed(key) { |
284 | - if (key == Qt.Key_VolumeUp) |
285 | - volumeUpKeyPressed = true; |
286 | - else if (key == Qt.Key_VolumeDown) |
287 | - volumeDownKeyPressed = true; |
288 | - |
289 | - if (volumeDownKeyPressed && volumeUpKeyPressed) { |
290 | - //avoids sending a signal repeatedly if both keys are held |
291 | - //instead one of the keys must have been previously released |
292 | - if (aVolumeKeyWasReleased) |
293 | - bothVolumeKeysPressed(); |
294 | - aVolumeKeyWasReleased = false; |
295 | - } else if (volumeDownKeyPressed) { |
296 | - volumeDownPressed(); |
297 | - } else if (volumeUpKeyPressed) { |
298 | - volumeUpPressed(); |
299 | - } |
300 | - } |
301 | - |
302 | - function onKeyReleased(key) { |
303 | - if (key == Qt.Key_VolumeUp) { |
304 | - volumeUpKeyPressed = false; |
305 | - aVolumeKeyWasReleased = true; |
306 | - } else if (key == Qt.Key_VolumeDown) { |
307 | - volumeDownKeyPressed = false; |
308 | - aVolumeKeyWasReleased = true; |
309 | - } |
310 | - } |
311 | -} |
312 | |
313 | === modified file 'qml/Notifications/Notification.qml' |
314 | --- qml/Notifications/Notification.qml 2014-11-10 09:14:30 +0000 |
315 | +++ qml/Notifications/Notification.qml 2015-02-16 22:20:44 +0000 |
316 | @@ -40,6 +40,7 @@ |
317 | property bool fullscreen: false |
318 | property int maxHeight |
319 | property int margins |
320 | + readonly property bool draggable: (type === Notification.SnapDecision && state === "contracted") || type === Notification.Interactive || type === Notification.Ephemeral |
321 | readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision |
322 | readonly property color red: "#fc4949" |
323 | readonly property color green: "#3fb24f" |
324 | @@ -52,7 +53,7 @@ |
325 | implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0 |
326 | |
327 | color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97) |
328 | - opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really |
329 | + opacity: 1 - (x / notification.width) // FIXME: non-zero initially because of LP: #1354406 workaround, we want this to start at 0 upon creation eventually |
330 | |
331 | state: { |
332 | var result = ""; |
333 | @@ -80,18 +81,28 @@ |
334 | id: sound |
335 | objectName: "sound" |
336 | audioRole: MediaPlayer.alert |
337 | - source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : "" |
338 | + source: hints["suppress-sound"] !== "true" && hints["sound-file"] !== undefined ? hints["sound-file"] : "" |
339 | } |
340 | |
341 | // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really |
342 | Component.onCompleted: { |
343 | - if (opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") { |
344 | + if (opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") { |
345 | sound.play(); |
346 | } |
347 | } |
348 | |
349 | + Behavior on x { |
350 | + id: normalXBehavior |
351 | + |
352 | + enabled: draggable |
353 | + UbuntuNumberAnimation { |
354 | + duration: UbuntuAnimation.FastDuration |
355 | + easing.type: Easing.OutBounce |
356 | + } |
357 | + } |
358 | + |
359 | onHintsChanged: { |
360 | - if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") { |
361 | + if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") { |
362 | sound.play(); |
363 | } |
364 | } |
365 | @@ -146,6 +157,12 @@ |
366 | opacity: parent.opacity |
367 | } |
368 | |
369 | + onXChanged: { |
370 | + if (draggable && notification.x > 0.75 * notification.width) { |
371 | + notification.notification.close() |
372 | + } |
373 | + } |
374 | + |
375 | Item { |
376 | id: contents |
377 | anchors.fill: fullscreen ? nonShapedBack : shapedBack |
378 | @@ -169,7 +186,7 @@ |
379 | actions: paths.actions |
380 | menuObjectPath: paths.menuObjectPath |
381 | onNameOwnerChanged: { |
382 | - if (lastNameOwner != "" && nameOwner == "" && notification.notification != undefined) { |
383 | + if (lastNameOwner !== "" && nameOwner === "" && notification.notification !== undefined) { |
384 | notification.notification.close() |
385 | } |
386 | lastNameOwner = nameOwner |
387 | @@ -181,6 +198,12 @@ |
388 | |
389 | anchors.fill: parent |
390 | objectName: "interactiveArea" |
391 | + |
392 | + drag.target: draggable ? notification : undefined |
393 | + drag.axis: Drag.XAxis |
394 | + drag.minimumX: 0 |
395 | + drag.maximumX: notification.width |
396 | + |
397 | onClicked: { |
398 | if (notification.type == Notification.Interactive) { |
399 | notification.notification.invokeAction(actionRepeater.itemAt(0).actionId) |
400 | @@ -188,6 +211,13 @@ |
401 | notificationList.currentIndex = index; |
402 | } |
403 | } |
404 | + onReleased: { |
405 | + if (notification.x < notification.width / 2) { |
406 | + notification.x = 0 |
407 | + } else { |
408 | + notification.x = notification.width |
409 | + } |
410 | + } |
411 | } |
412 | |
413 | Column { |
414 | @@ -447,7 +477,7 @@ |
415 | right: parent.right |
416 | margins: contentSpacing |
417 | } |
418 | - visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible |
419 | + visible: notification.type === Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible |
420 | spacing: units.gu(2) |
421 | layoutDirection: Qt.RightToLeft |
422 | |
423 | |
424 | === modified file 'qml/Notifications/Notifications.qml' |
425 | --- qml/Notifications/Notifications.qml 2014-10-30 21:42:32 +0000 |
426 | +++ qml/Notifications/Notifications.qml 2015-02-16 22:20:44 +0000 |
427 | @@ -41,15 +41,11 @@ |
428 | property bool topmostIsFullscreen: false |
429 | spacing: topmostIsFullscreen ? 0 : units.gu(.5) |
430 | |
431 | - // FIXME: This doesn't make any sense and results in a binding loop |
432 | - currentIndex: (currentIndex < 1 && count > 1) ? 1 : -1 |
433 | + currentIndex: count > 1 ? 1 : -1 |
434 | |
435 | delegate: Notification { |
436 | objectName: "notification" + index |
437 | - anchors { |
438 | - left: parent.left |
439 | - right: parent.right |
440 | - } |
441 | + width: parent.width |
442 | type: model.type |
443 | hints: model.hints |
444 | iconSource: model.icon |
445 | |
446 | === modified file 'qml/Panel/IndicatorItem.qml' |
447 | --- qml/Panel/IndicatorItem.qml 2015-01-14 10:17:12 +0000 |
448 | +++ qml/Panel/IndicatorItem.qml 2015-02-16 22:20:44 +0000 |
449 | @@ -95,6 +95,8 @@ |
450 | id: itemImage |
451 | objectName: "icon"+index |
452 | height: iconHeight |
453 | + // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293 |
454 | + width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth; |
455 | source: modelData |
456 | sets: ["status", "actions"] |
457 | color: root.color |
458 | |
459 | === modified file 'qml/Shell.qml' |
460 | --- qml/Shell.qml 2015-01-22 02:41:55 +0000 |
461 | +++ qml/Shell.qml 2015-02-16 22:20:44 +0000 |
462 | @@ -136,6 +136,15 @@ |
463 | objectName: "dashCommunicator" |
464 | } |
465 | |
466 | + PhysicalKeysMapper { |
467 | + id: physicalKeysMapper |
468 | + |
469 | + onPowerKeyLongPressed: dialogs.showPowerDialog() |
470 | + onVolumeDownTriggered: volumeControl.volumeDown(); |
471 | + onVolumeUpTriggered: volumeControl.volumeUp(); |
472 | + onScreenshotTriggered: screenGrabber.capture(); |
473 | + } |
474 | + |
475 | ScreenGrabber { |
476 | id: screenGrabber |
477 | z: edgeDemo.z + 10 |
478 | @@ -148,45 +157,9 @@ |
479 | value: launcher.shown || launcher.dashSwipe |
480 | } |
481 | |
482 | - VolumeKeyFilter { |
483 | - id: volumeKeyFilter |
484 | - onVolumeDownPressed: volumeControl.volumeDown() |
485 | - onVolumeUpPressed: volumeControl.volumeUp() |
486 | - onBothVolumeKeysPressed: screenGrabber.capture() |
487 | - } |
488 | - |
489 | WindowKeysFilter { |
490 | - Keys.onPressed: { |
491 | - // Nokia earpieces give TogglePlayPause, while the iPhone's earpiece gives Play |
492 | - if (event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) { |
493 | - event.accepted = callManager.handleMediaKey(false); |
494 | - } else if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) { |
495 | - // FIXME: We only consider power key presses if the screen is |
496 | - // on because of bugs 1410830/1409003. The theory is that when |
497 | - // those bugs are encountered, there is a >2s delay between the |
498 | - // power press event and the power release event, which causes |
499 | - // the shutdown dialog to appear on resume. So to avoid that |
500 | - // symptom while we investigate the root cause, we simply won't |
501 | - // initiate any dialogs when the screen is off. |
502 | - if (Powerd.status === Powerd.On) { |
503 | - dialogs.onPowerKeyPressed(); |
504 | - } |
505 | - event.accepted = true; |
506 | - } else { |
507 | - volumeKeyFilter.onKeyPressed(event.key); |
508 | - event.accepted = false; |
509 | - } |
510 | - } |
511 | - |
512 | - Keys.onReleased: { |
513 | - if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) { |
514 | - dialogs.onPowerKeyReleased(); |
515 | - event.accepted = true; |
516 | - } else { |
517 | - volumeKeyFilter.onKeyReleased(event.key); |
518 | - event.accepted = false; |
519 | - } |
520 | - } |
521 | + Keys.onPressed: physicalKeysMapper.onKeyPressed(event); |
522 | + Keys.onReleased: physicalKeysMapper.onKeyReleased(event); |
523 | } |
524 | |
525 | Item { |
526 | @@ -364,7 +337,7 @@ |
527 | onShownChanged: if (shown) greeter.lockedApp = "" |
528 | |
529 | function maybeShow() { |
530 | - if (!shell.forcedUnlock) { |
531 | + if (!shell.forcedUnlock && !shown) { |
532 | showNow(); |
533 | } |
534 | } |
535 | |
536 | === modified file 'tests/mocks/Unity/Notifications/CMakeLists.txt' |
537 | --- tests/mocks/Unity/Notifications/CMakeLists.txt 2014-11-05 00:17:50 +0000 |
538 | +++ tests/mocks/Unity/Notifications/CMakeLists.txt 2015-02-16 22:20:44 +0000 |
539 | @@ -4,7 +4,8 @@ |
540 | |
541 | set(MockNotificationsPlugin_SOURCES |
542 | plugin.cpp |
543 | - MockNotificationTypes.cpp |
544 | + MockNotification.cpp |
545 | + MockNotificationModel.cpp |
546 | MockActionModel.cpp |
547 | ) |
548 | |
549 | |
550 | === modified file 'tests/mocks/Unity/Notifications/MockActionModel.cpp' |
551 | --- tests/mocks/Unity/Notifications/MockActionModel.cpp 2014-11-05 00:17:50 +0000 |
552 | +++ tests/mocks/Unity/Notifications/MockActionModel.cpp 2015-02-16 22:20:44 +0000 |
553 | @@ -1,5 +1,5 @@ |
554 | /* |
555 | - * Copyright 2014 Canonical Ltd. |
556 | + * Copyright 2015 Canonical Ltd. |
557 | * |
558 | * This program is free software; you can redistribute it and/or modify |
559 | * it under the terms of the GNU Lesser General Public License as published by |
560 | @@ -25,8 +25,6 @@ |
561 | }; |
562 | |
563 | ActionModel::ActionModel(QObject *parent) : QStringListModel(parent), p(new ActionModelPrivate) { |
564 | - insertAction("ok_id", "Ok"); |
565 | - insertAction("cancel_id", "Cancel"); |
566 | } |
567 | |
568 | ActionModel::~ActionModel() { |
569 | @@ -66,7 +64,11 @@ |
570 | return data(index(row, 0), role); |
571 | } |
572 | |
573 | -void ActionModel::insertAction(const QString &id, const QString &label) { |
574 | +void ActionModel::append(const QString &id, const QString &label) { |
575 | p->ids.push_back(id); |
576 | p->labels.push_back(label); |
577 | } |
578 | + |
579 | +int ActionModel::getCount() const { |
580 | + return p->labels.size(); |
581 | +} |
582 | |
583 | === modified file 'tests/mocks/Unity/Notifications/MockActionModel.h' |
584 | --- tests/mocks/Unity/Notifications/MockActionModel.h 2014-11-05 00:17:50 +0000 |
585 | +++ tests/mocks/Unity/Notifications/MockActionModel.h 2015-02-16 22:20:44 +0000 |
586 | @@ -1,5 +1,5 @@ |
587 | /* |
588 | - * Copyright 2014 Canonical Ltd. |
589 | + * Copyright 2015 Canonical Ltd. |
590 | * |
591 | * This program is free software; you can redistribute it and/or modify |
592 | * it under the terms of the GNU Lesser General Public License as published by |
593 | @@ -26,6 +26,7 @@ |
594 | |
595 | class ActionModel : public QStringListModel { |
596 | Q_OBJECT |
597 | + Q_PROPERTY(int count READ getCount) |
598 | |
599 | public: |
600 | ActionModel(QObject *parent=nullptr); |
601 | @@ -42,7 +43,8 @@ |
602 | }; |
603 | Q_INVOKABLE QVariant data(int row, int role) const; |
604 | |
605 | - void insertAction(const QString &id, const QString &label); |
606 | + Q_INVOKABLE void append(const QString &id, const QString &label); |
607 | + int getCount() const; |
608 | |
609 | private: |
610 | QScopedPointer<ActionModelPrivate> p; |
611 | |
612 | === renamed file 'tests/mocks/Unity/Notifications/MockNotificationTypes.cpp' => 'tests/mocks/Unity/Notifications/MockNotification.cpp' |
613 | --- tests/mocks/Unity/Notifications/MockNotificationTypes.cpp 2014-11-05 00:17:50 +0000 |
614 | +++ tests/mocks/Unity/Notifications/MockNotification.cpp 2015-02-16 22:20:44 +0000 |
615 | @@ -1,5 +1,5 @@ |
616 | /* |
617 | - * Copyright 2014 Canonical Ltd. |
618 | + * Copyright 2015 Canonical Ltd. |
619 | * |
620 | * This program is free software; you can redistribute it and/or modify |
621 | * it under the terms of the GNU Lesser General Public License as published by |
622 | @@ -17,10 +17,162 @@ |
623 | * Mirco Mueller <mirco.mueller@canonical.com> |
624 | */ |
625 | |
626 | -#include "MockNotificationTypes.h" |
627 | - |
628 | -MockNotification::MockNotification(QObject *parent) : QObject(parent) { |
629 | +#include "MockNotification.h" |
630 | + |
631 | +#include <QDebug> |
632 | + |
633 | +struct MockNotificationPrivate { |
634 | + int id; |
635 | + QString summary; |
636 | + QString body; |
637 | + int value; |
638 | + MockNotification::Type type; |
639 | + QString icon; |
640 | + QString secondaryIcon; |
641 | + QStringList actions; |
642 | + ActionModel* actionsModel; |
643 | + QVariantMap hints; |
644 | +}; |
645 | + |
646 | +MockNotification::MockNotification(QObject *parent) : QObject(parent), p(new MockNotificationPrivate()) { |
647 | + p->actionsModel = new ActionModel(); |
648 | } |
649 | |
650 | MockNotification::~MockNotification() { |
651 | } |
652 | + |
653 | +QString MockNotification::getSummary() const { |
654 | + return p->summary; |
655 | +} |
656 | + |
657 | +void MockNotification::setSummary(const QString &summary) { |
658 | + if(p->summary != summary) { |
659 | + p->summary = summary; |
660 | + Q_EMIT summaryChanged(p->summary); |
661 | + Q_EMIT dataChanged(p->id); |
662 | + } |
663 | +} |
664 | + |
665 | +QString MockNotification::getBody() const { |
666 | + return p->body; |
667 | +} |
668 | + |
669 | +void MockNotification::setBody(const QString &body) { |
670 | + if(p->body != body) { |
671 | + p->body = body; |
672 | + Q_EMIT bodyChanged(p->body); |
673 | + Q_EMIT dataChanged(p->id); |
674 | + } |
675 | +} |
676 | + |
677 | +int MockNotification::getID() const { |
678 | + return p->id; |
679 | +} |
680 | + |
681 | +void MockNotification::setID(const int id) { |
682 | + p->id = id; |
683 | +} |
684 | + |
685 | +int MockNotification::getValue() const { |
686 | + return p->value; |
687 | +} |
688 | + |
689 | +void MockNotification::setValue(int value) { |
690 | + if(p->value != value) { |
691 | + p->value = value; |
692 | + Q_EMIT valueChanged(p->value); |
693 | + Q_EMIT dataChanged(p->id); |
694 | + } |
695 | +} |
696 | + |
697 | +QString MockNotification::getIcon() const { |
698 | + return p->icon; |
699 | +} |
700 | + |
701 | +void MockNotification::setIcon(const QString &icon) { |
702 | + if (icon.startsWith(" ") || icon.size() == 0) { |
703 | + p->icon = nullptr; |
704 | + } |
705 | + else { |
706 | + p->icon = icon; |
707 | + |
708 | + if (icon.indexOf("/") == -1) { |
709 | + p->icon.prepend("image://theme/"); |
710 | + } |
711 | + } |
712 | + |
713 | + Q_EMIT iconChanged(p->icon); |
714 | + Q_EMIT dataChanged(p->id); |
715 | +} |
716 | + |
717 | +QString MockNotification::getSecondaryIcon() const { |
718 | + return p->secondaryIcon; |
719 | +} |
720 | + |
721 | +void MockNotification::setSecondaryIcon(const QString &secondaryIcon) { |
722 | + if (secondaryIcon.startsWith(" ") || secondaryIcon.size() == 0) { |
723 | + p->secondaryIcon = nullptr; |
724 | + } |
725 | + else { |
726 | + p->secondaryIcon = secondaryIcon; |
727 | + |
728 | + if (secondaryIcon.indexOf("/") == -1) { |
729 | + p->secondaryIcon.prepend("image://theme/"); |
730 | + } |
731 | + } |
732 | + |
733 | + Q_EMIT secondaryIconChanged(p->secondaryIcon); |
734 | + Q_EMIT dataChanged(p->id); |
735 | +} |
736 | + |
737 | +MockNotification::Type MockNotification::getType() const { |
738 | + return p->type; |
739 | +} |
740 | + |
741 | +void MockNotification::setType(Type type) { |
742 | + if(p->type != type) { |
743 | + p->type = type; |
744 | + Q_EMIT typeChanged(p->type); |
745 | + } |
746 | +} |
747 | + |
748 | +ActionModel* MockNotification::getActions() const { |
749 | + return p->actionsModel; |
750 | +} |
751 | + |
752 | +void MockNotification::setActions(const QStringList &actions) { |
753 | + if(p->actions != actions) { |
754 | + p->actions = actions; |
755 | + Q_EMIT actionsChanged(p->actions); |
756 | + |
757 | + for (int i = 0; i < p->actions.size(); i += 2) { |
758 | + p->actionsModel->append(p->actions[i], p->actions[i+1]); |
759 | + } |
760 | + } |
761 | +} |
762 | + |
763 | +QVariantMap MockNotification::getHints() const { |
764 | + return p->hints; |
765 | +} |
766 | + |
767 | +void MockNotification::setHints(const QVariantMap& hints) { |
768 | + if (p->hints != hints) { |
769 | + p->hints = hints; |
770 | + Q_EMIT hintsChanged(p->hints); |
771 | + } |
772 | +} |
773 | + |
774 | +void MockNotification::invokeAction(const QString &action) { |
775 | + for(int i=0; i<p->actions.size(); i++) { |
776 | + if(p->actions[i] == action) { |
777 | + Q_EMIT actionInvoked(action); |
778 | + qDebug() << "Info: invoked action" << action; |
779 | + return; |
780 | + } |
781 | + } |
782 | + fprintf(stderr, "Error: tried to invoke action not in actionList.\n"); |
783 | +} |
784 | + |
785 | +void MockNotification::close() { |
786 | + Q_EMIT completed(p->id); |
787 | +} |
788 | |
789 | === renamed file 'tests/mocks/Unity/Notifications/MockNotificationTypes.h' => 'tests/mocks/Unity/Notifications/MockNotification.h' |
790 | --- tests/mocks/Unity/Notifications/MockNotificationTypes.h 2014-11-05 00:17:50 +0000 |
791 | +++ tests/mocks/Unity/Notifications/MockNotification.h 2015-02-16 22:20:44 +0000 |
792 | @@ -1,5 +1,5 @@ |
793 | /* |
794 | - * Copyright 2014 Canonical Ltd. |
795 | + * Copyright 2015 Canonical Ltd. |
796 | * |
797 | * This program is free software; you can redistribute it and/or modify |
798 | * it under the terms of the GNU Lesser General Public License as published by |
799 | @@ -17,20 +17,78 @@ |
800 | * Mirco Mueller <mirco.mueller@canonical.com> |
801 | */ |
802 | |
803 | -#ifndef MOCK_NOTIFICATION_TYPES_H |
804 | -#define MOCK_NOTIFICATION_TYPES_H |
805 | +#ifndef MOCK_NOTIFICATION_H |
806 | +#define MOCK_NOTIFICATION_H |
807 | |
808 | +#include "MockActionModel.h" |
809 | #include <QObject> |
810 | +#include <QString> |
811 | +#include <QStringList> |
812 | +#include <QScopedPointer> |
813 | + |
814 | +struct MockNotificationPrivate; |
815 | |
816 | class MockNotification : public QObject { |
817 | Q_OBJECT |
818 | Q_ENUMS(Type) |
819 | + Q_PROPERTY(QString summary READ getSummary WRITE setSummary NOTIFY summaryChanged) |
820 | + Q_PROPERTY(QString body READ getBody WRITE setBody NOTIFY bodyChanged) |
821 | + Q_PROPERTY(int nid READ getID WRITE setID NOTIFY idChanged) |
822 | + Q_PROPERTY(int value READ getValue WRITE setValue NOTIFY valueChanged) |
823 | + Q_PROPERTY(QString icon READ getIcon WRITE setIcon NOTIFY iconChanged) |
824 | + Q_PROPERTY(QString secondaryIcon READ getSecondaryIcon WRITE setSecondaryIcon NOTIFY secondaryIconChanged) |
825 | + Q_PROPERTY(Type type READ getType WRITE setType NOTIFY typeChanged) |
826 | + Q_PROPERTY(QStringList rawActions WRITE setActions) |
827 | + Q_PROPERTY(ActionModel* actions READ getActions NOTIFY actionsChanged) |
828 | + Q_PROPERTY(QVariantMap hints READ getHints WRITE setHints NOTIFY hintsChanged) |
829 | + |
830 | +private: |
831 | + QScopedPointer<MockNotificationPrivate> p; |
832 | + |
833 | +public: |
834 | + enum Urgency { Low, Normal, Critical }; |
835 | + enum Type { PlaceHolder, Confirmation, Ephemeral, Interactive, SnapDecision }; |
836 | + |
837 | +Q_SIGNALS: |
838 | + void summaryChanged(const QString &summary); |
839 | + void bodyChanged(const QString &body); |
840 | + void idChanged(const int id); |
841 | + void valueChanged(int value); |
842 | + void iconChanged(const QString &icon); |
843 | + void secondaryIconChanged(const QString &secondaryIcon); |
844 | + void typeChanged(Type type); |
845 | + void actionsChanged(const QStringList &actions); |
846 | + void hintsChanged(const QVariantMap& hints); |
847 | + |
848 | + void dataChanged(int nid); |
849 | + void completed(int nid); |
850 | + void actionInvoked(const QString &action); |
851 | |
852 | public: |
853 | MockNotification(QObject *parent=nullptr); |
854 | virtual ~MockNotification(); |
855 | |
856 | - enum Type { PlaceHolder, Confirmation, Ephemeral, Interactive, SnapDecision }; |
857 | + QString getSummary() const; |
858 | + void setSummary(const QString &summary); |
859 | + QString getBody() const; |
860 | + void setBody(const QString &body); |
861 | + int getID() const; |
862 | + void setID(const int id); |
863 | + int getValue() const; |
864 | + void setValue(int value); |
865 | + QString getIcon() const; |
866 | + void setIcon(const QString &icon); |
867 | + QString getSecondaryIcon() const; |
868 | + void setSecondaryIcon(const QString &secondaryIcon); |
869 | + Type getType() const; |
870 | + void setType(Type type); |
871 | + ActionModel* getActions() const; |
872 | + void setActions(const QStringList &actions); |
873 | + QVariantMap getHints() const; |
874 | + void setHints(const QVariantMap& hints); |
875 | + |
876 | + Q_INVOKABLE void invokeAction(const QString &action); |
877 | + Q_INVOKABLE void close(); |
878 | }; |
879 | |
880 | -#endif // MOCK_NOTIFICATION_TYPES_H |
881 | +#endif // MOCK_NOTIFICATION_H |
882 | |
883 | === added file 'tests/mocks/Unity/Notifications/MockNotificationModel.cpp' |
884 | --- tests/mocks/Unity/Notifications/MockNotificationModel.cpp 1970-01-01 00:00:00 +0000 |
885 | +++ tests/mocks/Unity/Notifications/MockNotificationModel.cpp 2015-02-16 22:20:44 +0000 |
886 | @@ -0,0 +1,172 @@ |
887 | +/* |
888 | + * Copyright 2015 Canonical Ltd. |
889 | + * |
890 | + * This program is free software; you can redistribute it and/or modify |
891 | + * it under the terms of the GNU Lesser General Public License as published by |
892 | + * the Free Software Foundation; version 3. |
893 | + * |
894 | + * This program is distributed in the hope that it will be useful, |
895 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
896 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
897 | + * GNU Lesser General Public License for more details. |
898 | + * |
899 | + * You should have received a copy of the GNU Lesser General Public License |
900 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
901 | + * |
902 | + * Authors: |
903 | + * Mirco Mueller <mirco.mueller@canonical.com> |
904 | + */ |
905 | + |
906 | +#include "MockNotificationModel.h" |
907 | +#include "MockNotification.h" |
908 | + |
909 | +#include <unity/shell/notifications/ModelInterface.h> |
910 | + |
911 | +#include <QTimer> |
912 | +#include <QList> |
913 | +#include <QVector> |
914 | +#include <QMap> |
915 | +#include <QStringListModel> |
916 | +#include <QQmlEngine> |
917 | + |
918 | +using namespace unity::shell::notifications; |
919 | + |
920 | +MockNotificationModel::MockNotificationModel(QObject *parent) : QAbstractListModel(parent) { |
921 | +} |
922 | + |
923 | +MockNotificationModel::~MockNotificationModel() { |
924 | +} |
925 | + |
926 | +int MockNotificationModel::rowCount(const QModelIndex &parent) const { |
927 | + return m_queue.size(); |
928 | +} |
929 | + |
930 | +int MockNotificationModel::getCount() const { |
931 | + return m_queue.size(); |
932 | +} |
933 | + |
934 | +QVariant MockNotificationModel::data(const QModelIndex &index, int role) const { |
935 | + if (!index.isValid()) |
936 | + return QVariant(); |
937 | + |
938 | + switch(role) { |
939 | + case ModelInterface::RoleType: |
940 | + return QVariant(m_queue[index.row()]->getType()); |
941 | + |
942 | + case ModelInterface::RoleId: |
943 | + return QVariant(m_queue[index.row()]->getID()); |
944 | + |
945 | + case ModelInterface::RoleSummary: |
946 | + return QVariant(m_queue[index.row()]->getSummary()); |
947 | + |
948 | + case ModelInterface::RoleBody: |
949 | + return QVariant(m_queue[index.row()]->getBody()); |
950 | + |
951 | + case ModelInterface::RoleValue: |
952 | + return QVariant(m_queue[index.row()]->getValue()); |
953 | + |
954 | + case ModelInterface::RoleIcon: |
955 | + return QVariant(m_queue[index.row()]->getIcon()); |
956 | + |
957 | + case ModelInterface::RoleSecondaryIcon: |
958 | + return QVariant(m_queue[index.row()]->getSecondaryIcon()); |
959 | + |
960 | + case ModelInterface::RoleActions: |
961 | + return QVariant::fromValue(m_queue[index.row()]->getActions()); |
962 | + |
963 | + case ModelInterface::RoleHints: |
964 | + return QVariant(m_queue[index.row()]->getHints()); |
965 | + |
966 | + case ModelInterface::RoleNotification: |
967 | + return QVariant::fromValue(m_queue[index.row()]); |
968 | + |
969 | + default: |
970 | + return QVariant(); |
971 | + } |
972 | +} |
973 | + |
974 | +void MockNotificationModel::append(MockNotification* n) { |
975 | + int location = m_queue.size(); |
976 | + QModelIndex insertionPoint = QModelIndex(); |
977 | + beginInsertRows(insertionPoint, location, location); |
978 | + m_queue.insert(location, n); |
979 | + endInsertRows(); |
980 | +} |
981 | + |
982 | +MockNotification* MockNotificationModel::getNotification(int id) const { |
983 | + for(int i=0; i < m_queue.size(); i++) { |
984 | + if(m_queue[i]->getID() == id) { |
985 | + return m_queue[i]; |
986 | + } |
987 | + } |
988 | + |
989 | + return nullptr; |
990 | +} |
991 | + |
992 | +void MockNotificationModel::remove(const int id) { |
993 | + for(int i = 0; i < m_queue.size(); i++) { |
994 | + if(m_queue[i]->getID() == id) { |
995 | + removeInternal(i); |
996 | + return; |
997 | + } |
998 | + } |
999 | +} |
1000 | + |
1001 | +void MockNotificationModel::removeSecond() { |
1002 | + if(m_queue.size() < 2) |
1003 | + return; |
1004 | + removeInternal(1); |
1005 | +} |
1006 | + |
1007 | +void MockNotificationModel::removeInternal(int loc) { |
1008 | + QModelIndex deletePoint = QModelIndex(); |
1009 | + beginRemoveRows(deletePoint, loc, loc); |
1010 | + m_queue.erase(m_queue.begin() + loc); |
1011 | + endRemoveRows(); |
1012 | +} |
1013 | + |
1014 | +MockNotification* MockNotificationModel::getRaw(const int notificationId) const { |
1015 | + for(int i = 0; i < m_queue.size(); i++) { |
1016 | + if(m_queue[i]->getID() == notificationId) { |
1017 | + MockNotification* n = m_queue[i]; |
1018 | + return n; |
1019 | + } |
1020 | + } |
1021 | + |
1022 | + return nullptr; |
1023 | +} |
1024 | + |
1025 | +int MockNotificationModel::queued() const { |
1026 | + return m_queue.size(); |
1027 | +} |
1028 | + |
1029 | +QHash<int, QByteArray> MockNotificationModel::roleNames() const { |
1030 | + QHash<int, QByteArray> roles; |
1031 | + |
1032 | + roles.insert(ModelInterface::RoleType, "type"); |
1033 | + roles.insert(ModelInterface::RoleUrgency, "urgency"); |
1034 | + roles.insert(ModelInterface::RoleId, "id"); |
1035 | + roles.insert(ModelInterface::RoleSummary, "summary"); |
1036 | + roles.insert(ModelInterface::RoleBody, "body"); |
1037 | + roles.insert(ModelInterface::RoleValue, "value"); |
1038 | + roles.insert(ModelInterface::RoleIcon, "icon"); |
1039 | + roles.insert(ModelInterface::RoleSecondaryIcon, "secondaryIcon"); |
1040 | + roles.insert(ModelInterface::RoleActions, "actions"); |
1041 | + roles.insert(ModelInterface::RoleHints, "hints"); |
1042 | + roles.insert(ModelInterface::RoleNotification, "notification"); |
1043 | + |
1044 | + return roles; |
1045 | +} |
1046 | + |
1047 | +void MockNotificationModel::onCompleted(int id) { |
1048 | + remove(id); |
1049 | +} |
1050 | + |
1051 | +void MockNotificationModel::onDataChanged(int id) { |
1052 | + for(int i = 0; i < m_queue.size(); i++) { |
1053 | + if(m_queue[i]->getID() == id) { |
1054 | + Q_EMIT dataChanged(index(i, 0), index(i, 0)); |
1055 | + break; |
1056 | + } |
1057 | + } |
1058 | +} |
1059 | |
1060 | === added file 'tests/mocks/Unity/Notifications/MockNotificationModel.h' |
1061 | --- tests/mocks/Unity/Notifications/MockNotificationModel.h 1970-01-01 00:00:00 +0000 |
1062 | +++ tests/mocks/Unity/Notifications/MockNotificationModel.h 2015-02-16 22:20:44 +0000 |
1063 | @@ -0,0 +1,72 @@ |
1064 | +/* |
1065 | + * Copyright 2015 Canonical Ltd. |
1066 | + * |
1067 | + * This program is free software; you can redistribute it and/or modify |
1068 | + * it under the terms of the GNU Lesser General Public License as published by |
1069 | + * the Free Software Foundation; version 3. |
1070 | + * |
1071 | + * This program is distributed in the hope that it will be useful, |
1072 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1073 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1074 | + * GNU Lesser General Public License for more details. |
1075 | + * |
1076 | + * You should have received a copy of the GNU Lesser General Public License |
1077 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1078 | + * |
1079 | + * Authors: |
1080 | + * Mirco Mueller <mirco.mueller@canonical.com> |
1081 | + */ |
1082 | + |
1083 | +#ifndef MOCK_NOTIFICATION_MODEL_H |
1084 | +#define MOCK_NOTIFICATION_MODEL_H |
1085 | + |
1086 | +#include <QAbstractListModel> |
1087 | +#include <QSharedPointer> |
1088 | +#include <QScopedPointer> |
1089 | +#include "MockNotification.h" |
1090 | + |
1091 | +class MockNotification; |
1092 | + |
1093 | +class MockNotificationModel : public QAbstractListModel { |
1094 | + Q_OBJECT |
1095 | + Q_PROPERTY(int count READ getCount) |
1096 | + |
1097 | +public: |
1098 | + MockNotificationModel(QObject *parent=nullptr); |
1099 | + virtual ~MockNotificationModel(); |
1100 | + |
1101 | + virtual int rowCount(const QModelIndex &parent) const; |
1102 | + virtual QVariant data(const QModelIndex &index, int role) const; |
1103 | + virtual QHash<int, QByteArray> roleNames() const; |
1104 | + |
1105 | + Q_INVOKABLE void append(MockNotification* n); |
1106 | + MockNotification* getNotification(int id) const; |
1107 | + |
1108 | + // getRaw() is only meant to be used from QML, since QML cannot handle |
1109 | + // QSharedPointers... on C++-side only use getNotification() |
1110 | + Q_INVOKABLE MockNotification* getRaw(const int notificationId) const; |
1111 | + |
1112 | + Q_INVOKABLE int queued() const; |
1113 | + Q_INVOKABLE void remove(const int id); |
1114 | + Q_INVOKABLE void removeSecond(); |
1115 | + |
1116 | + int getCount() const; |
1117 | + |
1118 | +Q_SIGNALS: |
1119 | + void actionInvoked(const QString &action); |
1120 | + |
1121 | +public Q_SLOTS: |
1122 | + void onCompleted(int id); |
1123 | + |
1124 | +private Q_SLOTS: |
1125 | + void onDataChanged(int id); |
1126 | + |
1127 | +Q_SIGNALS: |
1128 | + void queueSizeChanged(int newSize); |
1129 | + |
1130 | +private: |
1131 | + QList<MockNotification*> m_queue; |
1132 | + void removeInternal(int loc); |
1133 | +}; |
1134 | + |
1135 | +#endif |
1136 | |
1137 | === modified file 'tests/mocks/Unity/Notifications/plugin.cpp' |
1138 | --- tests/mocks/Unity/Notifications/plugin.cpp 2014-11-05 00:17:50 +0000 |
1139 | +++ tests/mocks/Unity/Notifications/plugin.cpp 2015-02-16 22:20:44 +0000 |
1140 | @@ -1,5 +1,5 @@ |
1141 | /* |
1142 | - * Copyright 2014 Canonical Ltd. |
1143 | + * Copyright 2015 Canonical Ltd. |
1144 | * |
1145 | * This program is free software; you can redistribute it and/or modify |
1146 | * it under the terms of the GNU Lesser General Public License as published by |
1147 | @@ -19,13 +19,15 @@ |
1148 | |
1149 | #include "plugin.h" |
1150 | #include "MockActionModel.h" |
1151 | -#include "MockNotificationTypes.h" |
1152 | +#include "MockNotification.h" |
1153 | +#include "MockNotificationModel.h" |
1154 | |
1155 | #include <QtQml/qqml.h> |
1156 | |
1157 | void TestNotificationPlugin::registerTypes(const char* uri) |
1158 | { |
1159 | // @uri Unity.Notifications |
1160 | - qmlRegisterUncreatableType<MockNotification>(uri, 1, 0, "Notification", "Notification objects can only be created by the plugin"); |
1161 | + qmlRegisterType<MockNotification>(uri, 1, 0, "Notification"); |
1162 | + qmlRegisterType<MockNotificationModel>(uri, 1, 0, "NotificationModel"); |
1163 | qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel"); |
1164 | } |
1165 | |
1166 | === modified file 'tests/mocks/Unity/Notifications/plugin.h' |
1167 | --- tests/mocks/Unity/Notifications/plugin.h 2014-11-05 00:17:50 +0000 |
1168 | +++ tests/mocks/Unity/Notifications/plugin.h 2015-02-16 22:20:44 +0000 |
1169 | @@ -1,5 +1,5 @@ |
1170 | /* |
1171 | - * Copyright 2014 Canonical Ltd. |
1172 | + * Copyright 2015 Canonical Ltd. |
1173 | * |
1174 | * This program is free software; you can redistribute it and/or modify |
1175 | * it under the terms of the GNU Lesser General Public License as published by |
1176 | @@ -17,7 +17,6 @@ |
1177 | * Mirco Mueller <mirco.mueller@canonical.com> |
1178 | */ |
1179 | |
1180 | - |
1181 | #ifndef TESTNOTIFICATION_PLUGIN_H |
1182 | #define TESTNOTIFICATION_PLUGIN_H |
1183 | |
1184 | |
1185 | === modified file 'tests/qmltests/CMakeLists.txt' |
1186 | --- tests/qmltests/CMakeLists.txt 2015-01-14 10:17:12 +0000 |
1187 | +++ tests/qmltests/CMakeLists.txt 2015-02-16 22:20:44 +0000 |
1188 | @@ -13,6 +13,7 @@ |
1189 | set(qmltest_DEFAULT_NO_ADD_TEST FALSE) |
1190 | set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal") |
1191 | add_qml_test(utils/Unity/Test UnityTest) |
1192 | +add_qml_test(Components PhysicalKeysMapper) |
1193 | |
1194 | set(qmltest_DEFAULT_TARGETS qmluitests) |
1195 | set(qmltest_DEFAULT_NO_ADD_TEST TRUE) |
1196 | |
1197 | === added file 'tests/qmltests/Components/tst_PhysicalKeysMapper.qml' |
1198 | --- tests/qmltests/Components/tst_PhysicalKeysMapper.qml 1970-01-01 00:00:00 +0000 |
1199 | +++ tests/qmltests/Components/tst_PhysicalKeysMapper.qml 2015-02-16 22:20:44 +0000 |
1200 | @@ -0,0 +1,119 @@ |
1201 | +/* |
1202 | + * Copyright (C) 2014-2015 Canonical, Ltd. |
1203 | + * |
1204 | + * This program is free software; you can redistribute it and/or modify |
1205 | + * it under the terms of the GNU General Public License as published by |
1206 | + * the Free Software Foundation; version 3. |
1207 | + * |
1208 | + * This program is distributed in the hope that it will be useful, |
1209 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1210 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1211 | + * GNU General Public License for more details. |
1212 | + * |
1213 | + * You should have received a copy of the GNU General Public License |
1214 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1215 | + */ |
1216 | + |
1217 | +import QtQuick 2.0 |
1218 | +import QtTest 1.0 |
1219 | +import "../../../qml/Components" |
1220 | + |
1221 | +TestCase { |
1222 | + name: "PhysicalKeysMapper" |
1223 | + |
1224 | + Loader { |
1225 | + // Using a Loader here to make sure mapper state is coherent |
1226 | + // regardless of unmatched KeyPress and KeyRelease events |
1227 | + id: loader |
1228 | + active: false |
1229 | + sourceComponent: PhysicalKeysMapper { } |
1230 | + } |
1231 | + |
1232 | + SignalSpy { |
1233 | + id: powerSpy |
1234 | + target: loader.item |
1235 | + signalName: "powerKeyLongPressed" |
1236 | + } |
1237 | + |
1238 | + SignalSpy { |
1239 | + id: volumeDownSpy |
1240 | + target: loader.item |
1241 | + signalName: "volumeDownTriggered" |
1242 | + } |
1243 | + |
1244 | + SignalSpy { |
1245 | + id: volumeUpSpy |
1246 | + target: loader.item |
1247 | + signalName: "volumeUpTriggered" |
1248 | + } |
1249 | + |
1250 | + SignalSpy { |
1251 | + id: screenshotSpy |
1252 | + target: loader.item |
1253 | + signalName: "screenshotTriggered" |
1254 | + } |
1255 | + |
1256 | + function init() { |
1257 | + loader.active = true; |
1258 | + tryCompare(loader.status == Loader.Ready); |
1259 | + } |
1260 | + |
1261 | + function cleanup() { |
1262 | + loader.active = false; |
1263 | + powerSpy.clear(); |
1264 | + volumeDownSpy.clear(); |
1265 | + volumeUpSpy.clear(); |
1266 | + screenshotSpy.clear(); |
1267 | + } |
1268 | + |
1269 | + function test_LongPressPowerButton() { |
1270 | + loader.item.onKeyPressed({ key: Qt.Key_PowerDown }); |
1271 | + |
1272 | + expectFailContinue("", "Power signal should not be emitted within a second"); |
1273 | + powerSpy.wait(1000); |
1274 | + powerSpy.clear(); |
1275 | + |
1276 | + loader.item.onKeyPressed({ key: Qt.Key_PowerDown, isAutoRepeat: true }); |
1277 | + powerSpy.wait(1500); |
1278 | + } |
1279 | + |
1280 | + function test_ScreenshotButtons_data() { |
1281 | + return [ |
1282 | + { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown }, |
1283 | + { tag: "DownFirst", first: Qt.Key_VolumeDown, second: Qt.Key_VolumeUp }, |
1284 | + ]; |
1285 | + } |
1286 | + |
1287 | + function test_ScreenshotButtons(data) { |
1288 | + loader.item.onKeyPressed({ key: data.first }); |
1289 | + loader.item.onKeyPressed({ key: data.second }); |
1290 | + screenshotSpy.wait(); |
1291 | + loader.item.onKeyReleased({ key: data.first }); |
1292 | + loader.item.onKeyReleased({ key: data.second }); |
1293 | + expectFailContinue("", "VolumeUp signal should not fire"); |
1294 | + volumeUpSpy.wait(100); |
1295 | + expectFailContinue("", "VolumeDown signal should not fire"); |
1296 | + volumeDownSpy.wait(100); |
1297 | + } |
1298 | + |
1299 | + function test_VolumeButton_data() { |
1300 | + return [ |
1301 | + { tag: "Down", key: Qt.Key_VolumeDown, spy: volumeDownSpy }, |
1302 | + { tag: "Up", key: Qt.Key_VolumeUp, spy: volumeUpSpy }, |
1303 | + ]; |
1304 | + } |
1305 | + |
1306 | + function test_VolumeButton(data) { |
1307 | + loader.item.onKeyPressed({ key: data.key }); |
1308 | + expectFailContinue("", "Signal should not fire on press"); |
1309 | + data.spy.wait(100); |
1310 | + data.spy.clear(); |
1311 | + |
1312 | + loader.item.onKeyReleased({ key: data.key }); |
1313 | + data.spy.wait(); |
1314 | + |
1315 | + loader.item.onKeyPressed({ key: data.key }); |
1316 | + loader.item.onKeyPressed({ key: data.key, isAutoRepeat: true }); |
1317 | + data.spy.wait(); |
1318 | + } |
1319 | +} |
1320 | |
1321 | === modified file 'tests/qmltests/Dash/tst_Card.qml' |
1322 | --- tests/qmltests/Dash/tst_Card.qml 2015-01-08 12:51:33 +0000 |
1323 | +++ tests/qmltests/Dash/tst_Card.qml 2015-02-16 22:20:44 +0000 |
1324 | @@ -60,8 +60,8 @@ |
1325 | "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 0.7 } }) } |
1326 | }, |
1327 | { |
1328 | - "name": "Art, header, summary - horizontal", |
1329 | - "layout": { "template": { "card-layout": "horizontal" }, |
1330 | + "name": "Art, header, summary, background - horizontal", |
1331 | + "layout": { "template": { "card-layout": "horizontal", "card-background": { "type": "gradient", "elements": ["grey", "white"] } }, |
1332 | "components": JSON.parse(Helpers.fullMapping) } |
1333 | }, |
1334 | { |
1335 | @@ -101,6 +101,11 @@ |
1336 | "layout": { "template": { "overlay": true }, |
1337 | "components": { "art": "art", "title": "title" } } |
1338 | }, |
1339 | + { |
1340 | + "name": "Art, header, summary - horizontal", |
1341 | + "layout": { "template": { "card-layout": "horizontal" }, |
1342 | + "components": JSON.parse(Helpers.fullMapping) } |
1343 | + }, |
1344 | ] |
1345 | |
1346 | CardTool { |
1347 | @@ -407,7 +412,7 @@ |
1348 | return [ |
1349 | { tag: "Art and summary", visible: true, color: "#ffffff", index: 0 }, |
1350 | { tag: "No Summary", visible: false, index: 6 }, |
1351 | - { tag: "Horizontal", visible: false, index: 5 }, |
1352 | + { tag: "Horizontal", visible: true, color: "#808080", index: 5 }, |
1353 | { tag: "Grey background", visible: true, color: "#808080", index: 10 }, |
1354 | { tag: "Overriden Gradient background", visible: true, color: "#808080", gradientColor: "#ffffff", |
1355 | background: {type: "color", elements: ["grey", "white"]}, index: 10 }, |
1356 | @@ -415,6 +420,7 @@ |
1357 | background: Qt.resolvedUrl("artwork/checkers.png"), index: 10 }, |
1358 | { tag: "Gradient background", visible: true, color: "#808080", gradientColor: "#ffffff", index: 11 }, |
1359 | { tag: "Image background", visible: true, image: Qt.resolvedUrl("artwork/checkers.png"), index: 12 }, |
1360 | + { tag: "Horizontal no background", visible: false, index: 14 }, |
1361 | ]; |
1362 | } |
1363 | |
1364 | |
1365 | === added file 'tests/qmltests/Notifications/Notification.qml' |
1366 | --- tests/qmltests/Notifications/Notification.qml 1970-01-01 00:00:00 +0000 |
1367 | +++ tests/qmltests/Notifications/Notification.qml 2015-02-16 22:20:44 +0000 |
1368 | @@ -0,0 +1,31 @@ |
1369 | +/* |
1370 | + * Copyright 2015 Canonical Ltd. |
1371 | + * |
1372 | + * This program is free software; you can redistribute it and/or modify |
1373 | + * it under the terms of the GNU Lesser General Public License as published by |
1374 | + * the Free Software Foundation; version 3. |
1375 | + * |
1376 | + * This program is distributed in the hope that it will be useful, |
1377 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1378 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1379 | + * GNU Lesser General Public License for more details. |
1380 | + * |
1381 | + * You should have received a copy of the GNU Lesser General Public License |
1382 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1383 | + * |
1384 | + * Authors: |
1385 | + * Mirco Mueller <mirco.mueller@canonical.com> |
1386 | + */ |
1387 | + |
1388 | +import Unity.Notifications 1.0 |
1389 | + |
1390 | +Notification { |
1391 | + nid: 0 |
1392 | + type: Notification.PlaceHolder |
1393 | + summary: "" |
1394 | + body: "" |
1395 | + icon: "" |
1396 | + secondaryIcon: "" |
1397 | + value: 0 |
1398 | + rawActions: [] |
1399 | +} |
1400 | |
1401 | === modified file 'tests/qmltests/Notifications/tst_Notifications.qml' |
1402 | --- tests/qmltests/Notifications/tst_Notifications.qml 2014-11-10 09:14:30 +0000 |
1403 | +++ tests/qmltests/Notifications/tst_Notifications.qml 2015-02-16 22:20:44 +0000 |
1404 | @@ -1,17 +1,20 @@ |
1405 | /* |
1406 | - * Copyright (C) 2013 Canonical, Ltd. |
1407 | + * Copyright 2015 Canonical Ltd. |
1408 | * |
1409 | * This program is free software; you can redistribute it and/or modify |
1410 | - * it under the terms of the GNU General Public License as published by |
1411 | + * it under the terms of the GNU Lesser General Public License as published by |
1412 | * the Free Software Foundation; version 3. |
1413 | * |
1414 | * This program is distributed in the hope that it will be useful, |
1415 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1416 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1417 | - * GNU General Public License for more details. |
1418 | + * GNU Lesser General Public License for more details. |
1419 | * |
1420 | - * You should have received a copy of the GNU General Public License |
1421 | + * You should have received a copy of the GNU Lesser General Public License |
1422 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1423 | + * |
1424 | + * Authors: |
1425 | + * Mirco Mueller <mirco.mueller@canonical.com> |
1426 | */ |
1427 | |
1428 | import QtQuick 2.0 |
1429 | @@ -24,146 +27,134 @@ |
1430 | import QtMultimedia 5.0 |
1431 | |
1432 | Item { |
1433 | + id: foobar |
1434 | + |
1435 | width: notificationsRect.width + interactiveControls.width |
1436 | height: notificationsRect.height |
1437 | + property int index: 0 |
1438 | |
1439 | Row { |
1440 | id: rootRow |
1441 | |
1442 | - Component { |
1443 | - id: mockNotification |
1444 | - |
1445 | - QtObject { |
1446 | - function invokeAction(actionId) { |
1447 | - mockModel.actionInvoked(actionId) |
1448 | - } |
1449 | - } |
1450 | - } |
1451 | - |
1452 | - ListModel { |
1453 | + NotificationModel { |
1454 | id: mockModel |
1455 | - dynamicRoles: true |
1456 | - |
1457 | - signal actionInvoked(string actionId) |
1458 | - |
1459 | - function getRaw(id) { |
1460 | - return mockNotification.createObject(mockModel) |
1461 | - } |
1462 | |
1463 | // add the default/PlaceHolder notification to the model |
1464 | Component.onCompleted: { |
1465 | - var n = { |
1466 | - type: Notification.PlaceHolder, |
1467 | - hints: {}, |
1468 | - summary: "", |
1469 | - body: "", |
1470 | - icon: "", |
1471 | - secondaryIcon: "", |
1472 | - actions: [] |
1473 | - } |
1474 | - |
1475 | + var component = Qt.createComponent("Notification.qml") |
1476 | + var n = component.createObject("notification", {"nid": index++, |
1477 | + "type": Notification.PlaceHolder, |
1478 | + "hints": {}, |
1479 | + "summary": "", |
1480 | + "body": "", |
1481 | + "icon": "", |
1482 | + "secondaryIcon": "", |
1483 | + "rawActions": []}) |
1484 | + n.completed.connect(mockModel.onCompleted) |
1485 | append(n) |
1486 | } |
1487 | } |
1488 | |
1489 | function add2over1SnapDecisionNotification() { |
1490 | - var n = { |
1491 | - type: Notification.SnapDecision, |
1492 | - hints: {"x-canonical-private-affirmative-tint": "true"}, |
1493 | - summary: "Theatre at Ferria Stadium", |
1494 | - body: "at Ferria Stadium in Bilbao, Spain\n07578545317", |
1495 | - icon: "", |
1496 | - secondaryIcon: "", |
1497 | - actions: [{ id: "ok_id", label: "Ok"}, |
1498 | - { id: "snooze_id", label: "Snooze"}, |
1499 | - { id: "view_id", label: "View"}] |
1500 | - } |
1501 | - |
1502 | + var component = Qt.createComponent("Notification.qml") |
1503 | + var n = component.createObject("notification", {"nid": index++, |
1504 | + "type": Notification.SnapDecision, |
1505 | + "hints": {"x-canonical-private-affirmative-tint": "true"}, |
1506 | + "summary": "Theatre at Ferria Stadium", |
1507 | + "body": "at Ferria Stadium in Bilbao, Spain\n07578545317", |
1508 | + "icon": "", |
1509 | + "secondaryIcon": "", |
1510 | + "rawActions": ["ok_id", "Ok", |
1511 | + "snooze_id", "Snooze", |
1512 | + "view_id", "View"]}) |
1513 | + n.completed.connect(mockModel.onCompleted) |
1514 | mockModel.append(n) |
1515 | } |
1516 | |
1517 | function addEphemeralNotification() { |
1518 | - var n = { |
1519 | - type: Notification.Ephemeral, |
1520 | - summary: "Cole Raby", |
1521 | - body: "I did not expect it to be that late.", |
1522 | - icon: "../graphics/avatars/amanda.png", |
1523 | - secondaryIcon: "../graphics/applicationIcons/facebook.png", |
1524 | - actions: [] |
1525 | - } |
1526 | - |
1527 | + var component = Qt.createComponent("Notification.qml") |
1528 | + var n = component.createObject("notification", {"nid": index++, |
1529 | + "type": Notification.Ephemeral, |
1530 | + "hints": {}, |
1531 | + "summary": "Cole Raby", |
1532 | + "body": "I did not expect it to be that late.", |
1533 | + "icon": "../graphics/avatars/amanda.png", |
1534 | + "secondaryIcon": "../graphics/applicationIcons/facebook.png", |
1535 | + "rawActions": ["reply_id", "Dummy"]}) |
1536 | + n.completed.connect(mockModel.onCompleted) |
1537 | mockModel.append(n) |
1538 | } |
1539 | |
1540 | function addEphemeralNonShapedIconNotification() { |
1541 | - var n = { |
1542 | - type: Notification.Ephemeral, |
1543 | - hints: {"x-canonical-non-shaped-icon": "true"}, |
1544 | - summary: "Contacts", |
1545 | - body: "Synchronised contacts-database with cloud-storage.", |
1546 | - icon: "../graphics/applicationIcons/contacts-app.png", |
1547 | - secondaryIcon: "", |
1548 | - actions: [] |
1549 | - } |
1550 | - |
1551 | + var component = Qt.createComponent("Notification.qml") |
1552 | + var n = component.createObject("notification", {"nid": index++, |
1553 | + "type": Notification.Ephemeral, |
1554 | + "hints": {"x-canonical-non-shaped-icon": "true"}, |
1555 | + "summary": "Contacts", |
1556 | + "body": "Synchronised contacts-database with cloud-storage.", |
1557 | + "icon": "../graphics/applicationIcons/contacts-app.png", |
1558 | + "secondaryIcon": "", |
1559 | + "rawActions": ["reply_id", "Dummy"]}) |
1560 | + n.completed.connect(mockModel.onCompleted) |
1561 | mockModel.append(n) |
1562 | } |
1563 | |
1564 | function addEphemeralIconSummaryNotification() { |
1565 | - var n = { |
1566 | - type: Notification.Ephemeral, |
1567 | - hints: {"x-canonical-non-shaped-icon": "false"}, |
1568 | - summary: "Photo upload completed", |
1569 | - body: "", |
1570 | - icon: "../graphics/applicationIcons/facebook.png", |
1571 | - secondaryIcon: "", |
1572 | - actions: [] |
1573 | - } |
1574 | - |
1575 | + var component = Qt.createComponent("Notification.qml") |
1576 | + var n = component.createObject("notification", {"nid": index++, |
1577 | + "type": Notification.Ephemeral, |
1578 | + "hints": {"x-canonical-non-shaped-icon": "false"}, |
1579 | + "summary": "Photo upload completed", |
1580 | + "body": "", |
1581 | + "icon": "../graphics/applicationIcons/facebook.png", |
1582 | + "secondaryIcon": "", |
1583 | + "rawActions": ["reply_id", "Dummy"]}) |
1584 | + n.completed.connect(mockModel.onCompleted) |
1585 | mockModel.append(n) |
1586 | } |
1587 | |
1588 | function addInteractiveNotification() { |
1589 | - var n = { |
1590 | - type: Notification.Interactive, |
1591 | - summary: "Interactive notification", |
1592 | - body: "This is a notification that can be clicked", |
1593 | - icon: "../graphics/avatars/anna_olsson.png", |
1594 | - secondaryIcon: "", |
1595 | - actions: [{ id: "reply_id", label: "Dummy"}], |
1596 | - } |
1597 | - |
1598 | + var component = Qt.createComponent("Notification.qml") |
1599 | + var n = component.createObject("notification", {"nid": index++, |
1600 | + "type": Notification.Interactive, |
1601 | + "hints": {}, |
1602 | + "summary": "Interactive notification", |
1603 | + "body": "This is a notification that can be clicked", |
1604 | + "icon": "../graphics/avatars/anna_olsson.png", |
1605 | + "secondaryIcon": "", |
1606 | + "rawActions": ["reply_id", "Dummy"]}) |
1607 | + n.completed.connect(mockModel.onCompleted) |
1608 | mockModel.append(n) |
1609 | } |
1610 | |
1611 | function addConfirmationNotification() { |
1612 | - var n = { |
1613 | - type: Notification.Confirmation, |
1614 | - hints: {"x-canonical-non-shaped-icon": "true"}, |
1615 | - summary: "Confirmation notification", |
1616 | - body: "", |
1617 | - icon: "image://theme/audio-volume-medium", |
1618 | - secondaryIcon: "", |
1619 | - value: 50, |
1620 | - actions: [], |
1621 | - } |
1622 | - |
1623 | + var component = Qt.createComponent("Notification.qml") |
1624 | + var n = component.createObject("notification", {"nid": index++, |
1625 | + "type": Notification.Confirmation, |
1626 | + "hints": {"x-canonical-non-shaped-icon": "true"}, |
1627 | + "summary": "Confirmation notification", |
1628 | + "body": "", |
1629 | + "icon": "image://theme/audio-volume-medium", |
1630 | + "secondaryIcon": "", |
1631 | + "value": 50, |
1632 | + "rawActions": ["reply_id", "Dummy"]}) |
1633 | + n.completed.connect(mockModel.onCompleted) |
1634 | mockModel.append(n) |
1635 | } |
1636 | |
1637 | function add2ndConfirmationNotification() { |
1638 | - var n = { |
1639 | - type: Notification.Confirmation, |
1640 | - hints: {"x-canonical-non-shaped-icon": "true", |
1641 | - "x-canonical-value-bar-tint": "true"}, |
1642 | - summary: "Confirmation notification", |
1643 | - body: "High Volume", |
1644 | - icon: "image://theme/audio-volume-high", |
1645 | - secondaryIcon: "", |
1646 | - value: 85, |
1647 | - actions: [], |
1648 | - } |
1649 | - |
1650 | + var component = Qt.createComponent("Notification.qml") |
1651 | + var n = component.createObject("notification", {"nid": index++, |
1652 | + "type": Notification.Confirmation, |
1653 | + "hints": {"x-canonical-non-shaped-icon": "true", |
1654 | + "x-canonical-value-bar-tint": "true"}, |
1655 | + "summary": "Confirmation notification", |
1656 | + "body": "High Volume", |
1657 | + "icon": "image://theme/audio-volume-high", |
1658 | + "secondaryIcon": "", |
1659 | + "value": 85, |
1660 | + "rawActions": ["reply_id", "Dummy"]}) |
1661 | + n.completed.connect(mockModel.onCompleted) |
1662 | mockModel.append(n) |
1663 | } |
1664 | |
1665 | @@ -174,8 +165,9 @@ |
1666 | } |
1667 | |
1668 | function remove1stNotification() { |
1669 | - if (mockModel.count > 1) |
1670 | - mockModel.remove(1) |
1671 | + if (mockModel.count > 1) { |
1672 | + mockModel.removeSecond() |
1673 | + } |
1674 | } |
1675 | |
1676 | Rectangle { |
1677 | @@ -273,41 +265,122 @@ |
1678 | name: "NotificationRendererTest" |
1679 | when: windowShown |
1680 | |
1681 | + property list<Notification> nlist: [ |
1682 | + Notification { |
1683 | + nid: 1 |
1684 | + type: Notification.Ephemeral |
1685 | + summary: "Photo upload completed" |
1686 | + body: "" |
1687 | + icon: "../graphics/applicationIcons/facebook.png" |
1688 | + secondaryIcon: "" |
1689 | + value: 0 |
1690 | + rawActions: [] |
1691 | + }, |
1692 | + Notification { |
1693 | + nid: 2 |
1694 | + type: Notification.Ephemeral |
1695 | + hints: {"x-canonical-private-affirmative-tint": "false", |
1696 | + "sound-file": "dummy.ogg", |
1697 | + "suppress-sound": "true"} |
1698 | + summary: "New comment successfully published" |
1699 | + body: "" |
1700 | + icon: "" |
1701 | + secondaryIcon: "../graphics/applicationIcons/facebook.png" |
1702 | + value: 0 |
1703 | + rawActions: [] |
1704 | + }, |
1705 | + Notification { |
1706 | + nid: 3 |
1707 | + type: Notification.Interactive |
1708 | + hints: {"x-canonical-private-affirmative-tint": "false", |
1709 | + "sound-file": "dummy.ogg"} |
1710 | + summary: "Interactive notification" |
1711 | + body: "This is a notification that can be clicked" |
1712 | + icon: "../graphics/avatars/amanda.png" |
1713 | + secondaryIcon: "" |
1714 | + value: 0 |
1715 | + rawActions: ["reply_id", "Dummy"] |
1716 | + }, |
1717 | + Notification { |
1718 | + nid: 4 |
1719 | + type: Notification.SnapDecision |
1720 | + hints: {"x-canonical-private-affirmative-tint": "false", |
1721 | + "sound-file": "dummy.ogg"} |
1722 | + summary: "Bro Coly" |
1723 | + 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." |
1724 | + icon: "../graphics/avatars/anna_olsson.png" |
1725 | + secondaryIcon: "" |
1726 | + value: 0 |
1727 | + rawActions: ["accept_id", "Accept", |
1728 | + "reject_id", "Reject"] |
1729 | + }, |
1730 | + Notification { |
1731 | + nid: 5 |
1732 | + type: Notification.Ephemeral |
1733 | + hints: {"x-canonical-private-affirmative-tint": "false", |
1734 | + "sound-file": "dummy.ogg"} |
1735 | + summary: "Cole Raby" |
1736 | + body: "I did not expect it to be that late." |
1737 | + icon: "../graphics/avatars/funky.png" |
1738 | + secondaryIcon: "../graphics/applicationIcons/facebook.png" |
1739 | + value: 0 |
1740 | + rawActions: [] |
1741 | + }, |
1742 | + Notification { |
1743 | + nid: 6 |
1744 | + type: Notification.Ephemeral |
1745 | + hints: {"x-canonical-private-affirmative-tint": "false", |
1746 | + "x-canonical-non-shaped-icon": "true"} |
1747 | + summary: "Contacts" |
1748 | + body: "Synchronised contacts-database with cloud-storage." |
1749 | + icon: "image://theme/contacts-app" |
1750 | + secondaryIcon: "" |
1751 | + value: 0 |
1752 | + rawActions: [] |
1753 | + }, |
1754 | + Notification { |
1755 | + nid: 7 |
1756 | + type: Notification.Confirmation |
1757 | + hints: {"x-canonical-non-shaped-icon": "true"} |
1758 | + summary: "" |
1759 | + body: "" |
1760 | + icon: "image://theme/audio-volume-medium" |
1761 | + secondaryIcon: "" |
1762 | + value: 50 |
1763 | + rawActions: [] |
1764 | + }, |
1765 | + Notification { |
1766 | + nid: 8 |
1767 | + type: Notification.Confirmation |
1768 | + hints: {"x-canonical-non-shaped-icon": "true", |
1769 | + "x-canonical-value-bar-tint" : "true"} |
1770 | + summary: "" |
1771 | + body: "High Volume" |
1772 | + icon: "image://theme/audio-volume-high" |
1773 | + secondaryIcon: "" |
1774 | + value: 85 |
1775 | + rawActions: [] |
1776 | + }, |
1777 | + Notification { |
1778 | + nid: 9 |
1779 | + type: Notification.SnapDecision |
1780 | + hints: {"x-canonical-private-affirmative-tint": "true"} |
1781 | + summary: "Theatre at Ferria Stadium" |
1782 | + body: "at Ferria Stadium in Bilbao, Spain\n07578545317" |
1783 | + icon: "" |
1784 | + secondaryIcon: "" |
1785 | + value: 0 |
1786 | + rawActions: ["ok_id", "Ok", |
1787 | + "snooze_id", "Snooze", |
1788 | + "view_id", "View"] |
1789 | + } |
1790 | + ] |
1791 | + |
1792 | function test_NotificationRenderer_data() { |
1793 | return [ |
1794 | { |
1795 | - tag: "2-over-1 Snap Decision with button-tint", |
1796 | - type: Notification.SnapDecision, |
1797 | - hints: {"x-canonical-private-affirmative-tint": "true"}, |
1798 | - summary: "Theatre at Ferria Stadium", |
1799 | - body: "at Ferria Stadium in Bilbao, Spain\n07578545317", |
1800 | - icon: "", |
1801 | - secondaryIcon: "", |
1802 | - actions: [{ id: "ok_id", label: "Ok"}, |
1803 | - { id: "snooze_id", label: "Snooze"}, |
1804 | - { id: "view_id", label: "View"}], |
1805 | - summaryVisible: true, |
1806 | - bodyVisible: true, |
1807 | - iconVisible: false, |
1808 | - centeredIconVisible: false, |
1809 | - shaped: false, |
1810 | - secondaryIconVisible: false, |
1811 | - buttonRowVisible: false, |
1812 | - buttonTinted: true, |
1813 | - hasSound: false, |
1814 | - valueVisible: false, |
1815 | - valueLabelVisible: false, |
1816 | - valueTinted: false |
1817 | - }, |
1818 | - { |
1819 | tag: "Ephemeral notification - icon-summary layout", |
1820 | - type: Notification.Ephemeral, |
1821 | - hints: {}, |
1822 | - summary: "Photo upload completed", |
1823 | - body: "", |
1824 | - icon: "../graphics/applicationIcons/facebook.png", |
1825 | - secondaryIcon: "", |
1826 | - actions: [], |
1827 | + n: nlist[0], |
1828 | summaryVisible: true, |
1829 | bodyVisible: false, |
1830 | iconVisible: true, |
1831 | @@ -323,15 +396,7 @@ |
1832 | }, |
1833 | { |
1834 | tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout", |
1835 | - type: Notification.Ephemeral, |
1836 | - hints: {"x-canonical-private-affirmative-tint": "false", |
1837 | - "sound-file": "dummy.ogg", |
1838 | - "suppress-sound": "true"}, |
1839 | - summary: "New comment successfully published", |
1840 | - body: "", |
1841 | - icon: "", |
1842 | - secondaryIcon: "../graphics/applicationIcons/facebook.png", |
1843 | - actions: [], |
1844 | + n: nlist[1], |
1845 | summaryVisible: true, |
1846 | bodyVisible: false, |
1847 | interactiveAreaEnabled: false, |
1848 | @@ -348,14 +413,7 @@ |
1849 | }, |
1850 | { |
1851 | tag: "Interactive notification", |
1852 | - type: Notification.Interactive, |
1853 | - hints: {"x-canonical-private-affirmative-tint": "false", |
1854 | - "sound-file": "dummy.ogg"}, |
1855 | - summary: "Interactive notification", |
1856 | - body: "This is a notification that can be clicked", |
1857 | - icon: "../graphics/avatars/amanda.png", |
1858 | - secondaryIcon: "", |
1859 | - actions: [{ id: "reply_id", label: "Dummy"}], |
1860 | + n: nlist[2], |
1861 | summaryVisible: true, |
1862 | bodyVisible: true, |
1863 | iconVisible: true, |
1864 | @@ -371,15 +429,7 @@ |
1865 | }, |
1866 | { |
1867 | tag: "Snap Decision without secondary icon and no button-tint", |
1868 | - type: Notification.SnapDecision, |
1869 | - hints: {"x-canonical-private-affirmative-tint": "false", |
1870 | - "sound-file": "dummy.ogg"}, |
1871 | - summary: "Bro Coly", |
1872 | - 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.", |
1873 | - icon: "../graphics/avatars/anna_olsson.png", |
1874 | - secondaryIcon: "", |
1875 | - actions: [{ id: "accept_id", label: "Accept"}, |
1876 | - { id: "reject_id", label: "Reject"}], |
1877 | + n: nlist[3], |
1878 | summaryVisible: true, |
1879 | bodyVisible: true, |
1880 | iconVisible: true, |
1881 | @@ -395,14 +445,7 @@ |
1882 | }, |
1883 | { |
1884 | tag: "Ephemeral notification", |
1885 | - type: Notification.Ephemeral, |
1886 | - hints: {"x-canonical-private-affirmative-tint": "false", |
1887 | - "sound-file": "dummy.ogg"}, |
1888 | - summary: "Cole Raby", |
1889 | - body: "I did not expect it to be that late.", |
1890 | - icon: "../graphics/avatars/funky.png", |
1891 | - secondaryIcon: "../graphics/applicationIcons/facebook.png", |
1892 | - actions: [], |
1893 | + n: nlist[4], |
1894 | summaryVisible: true, |
1895 | bodyVisible: true, |
1896 | iconVisible: true, |
1897 | @@ -418,14 +461,7 @@ |
1898 | }, |
1899 | { |
1900 | tag: "Ephemeral notification with non-shaped icon", |
1901 | - type: Notification.Ephemeral, |
1902 | - hints: {"x-canonical-private-affirmative-tint": "false", |
1903 | - "x-canonical-non-shaped-icon": "true"}, |
1904 | - summary: "Contacts", |
1905 | - body: "Synchronised contacts-database with cloud-storage.", |
1906 | - icon: "image://theme/contacts-app", |
1907 | - secondaryIcon: "", |
1908 | - actions: [], |
1909 | + n: nlist[5], |
1910 | summaryVisible: true, |
1911 | bodyVisible: true, |
1912 | iconVisible: true, |
1913 | @@ -441,14 +477,7 @@ |
1914 | }, |
1915 | { |
1916 | tag: "Confirmation notification with value", |
1917 | - type: Notification.Confirmation, |
1918 | - hints: {"x-canonical-non-shaped-icon": "true"}, |
1919 | - summary: "", |
1920 | - body: "", |
1921 | - icon: "image://theme/audio-volume-medium", |
1922 | - secondaryIcon: "", |
1923 | - value: 50, |
1924 | - actions: [], |
1925 | + n: nlist[6], |
1926 | summaryVisible: false, |
1927 | bodyVisible: false, |
1928 | iconVisible: false, |
1929 | @@ -464,15 +493,7 @@ |
1930 | }, |
1931 | { |
1932 | tag: "Confirmation notification with value, label and tint", |
1933 | - type: Notification.Confirmation, |
1934 | - hints: {"x-canonical-non-shaped-icon": "true", |
1935 | - "x-canonical-value-bar-tint" : "true"}, |
1936 | - summary: "", |
1937 | - body: "High Volume", |
1938 | - icon: "image://theme/audio-volume-high", |
1939 | - secondaryIcon: "", |
1940 | - value: 85, |
1941 | - actions: [], |
1942 | + n: nlist[7], |
1943 | summaryVisible: false, |
1944 | bodyVisible: false, |
1945 | iconVisible: false, |
1946 | @@ -485,6 +506,22 @@ |
1947 | valueVisible: true, |
1948 | valueLabelVisible: true, |
1949 | valueTinted: true |
1950 | + }, |
1951 | + { |
1952 | + tag: "2-over-1 Snap Decision with button-tint", |
1953 | + n: nlist[8], |
1954 | + summaryVisible: true, |
1955 | + bodyVisible: true, |
1956 | + iconVisible: false, |
1957 | + centeredIconVisible: false, |
1958 | + shaped: false, |
1959 | + secondaryIconVisible: false, |
1960 | + buttonRowVisible: false, |
1961 | + buttonTinted: true, |
1962 | + hasSound: false, |
1963 | + valueVisible: false, |
1964 | + valueLabelVisible: false, |
1965 | + valueTinted: false |
1966 | } |
1967 | ] |
1968 | } |
1969 | @@ -509,8 +546,14 @@ |
1970 | } |
1971 | |
1972 | function test_NotificationRenderer(data) { |
1973 | + // make sure the clicks on mocked notifications can be checked against by "actionSpy" (mimicking the NotificationServer component) |
1974 | + data.n.actionInvoked.connect(mockModel.actionInvoked) |
1975 | + |
1976 | + // hook up notification's completed-signal with model's onCompleted-slot, so that remove() (model) happens on close() (notification) |
1977 | + data.n.completed.connect(mockModel.onCompleted) |
1978 | + |
1979 | // populate model with some mock notifications |
1980 | - mockModel.append(data) |
1981 | + mockModel.append(data.n) |
1982 | |
1983 | // make sure the view is properly updated before going on |
1984 | notifications.forceLayout(); |
1985 | @@ -548,9 +591,9 @@ |
1986 | |
1987 | // test input does not fall through |
1988 | mouseClick(notification, notification.width / 2, notification.height / 2) |
1989 | - if(data.type == Notification.Interactive) { |
1990 | + if(data.n.type === Notification.Interactive) { |
1991 | actionSpy.wait() |
1992 | - compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id for interactive action") |
1993 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(0, ActionModel.RoleActionId), "got wrong id for interactive action") |
1994 | } |
1995 | compare(clickThroughSpy.count, 0, "click on notification fell through") |
1996 | |
1997 | @@ -559,17 +602,19 @@ |
1998 | compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect") |
1999 | compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect") |
2000 | |
2001 | - var audioItem = findInvisibleChild(notification, "sound") |
2002 | - compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state") |
2003 | + if (data.hasSound) { |
2004 | + var audioItem = findInvisibleChild(notification, "sound") |
2005 | + compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state") |
2006 | + } |
2007 | |
2008 | if(data.buttonRowVisible) { |
2009 | var buttonCancel = findChild(buttonRow, "notify_button1") |
2010 | var buttonAccept = findChild(buttonRow, "notify_button0") |
2011 | |
2012 | // only test the left/cancel-button if two actions have been passed in |
2013 | - if (data.actions.length == 2) { |
2014 | + if (data.n.actions.count === 2) { |
2015 | tryCompareFunction(function() { mouseClick(buttonCancel, buttonCancel.width / 2, buttonCancel.height / 2); return actionSpy.signalArguments.length > 0; }, true); |
2016 | - compare(actionSpy.signalArguments[0][0], data.actions[1]["id"], "got wrong id for negative action") |
2017 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action") |
2018 | actionSpy.clear() |
2019 | } |
2020 | |
2021 | @@ -578,36 +623,49 @@ |
2022 | |
2023 | // click the positive/right button |
2024 | tryCompareFunction(function() { mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2); return actionSpy.signalArguments.length > 0; }, true); |
2025 | - compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id positive action") |
2026 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(0, ActionModel.RoleActionId), "got wrong id positive action") |
2027 | actionSpy.clear() |
2028 | - waitForRendering (notification) |
2029 | |
2030 | // check if there's a ComboButton created due to more actions being passed |
2031 | - if (data.actions.length > 2) { |
2032 | + if (data.n.actions.count > 3) { |
2033 | var comboButton = findChild(notification, "notify_button2") |
2034 | - tryCompareFunction(function() { return comboButton.expanded == false; }, true); |
2035 | + tryCompareFunction(function() { return comboButton.expanded === false; }, true); |
2036 | |
2037 | // click to expand |
2038 | - tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == true; }, true); |
2039 | + tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded === true; }, true); |
2040 | |
2041 | // try clicking on choices in expanded comboList |
2042 | var choiceButton1 = findChild(notification, "notify_button3") |
2043 | tryCompareFunction(function() { mouseClick(choiceButton1, choiceButton1.width / 2, choiceButton1.height / 2); return actionSpy.signalArguments.length > 0; }, true); |
2044 | - compare(actionSpy.signalArguments[0][0], data.actions[3]["id"], "got wrong id choice action 1") |
2045 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(3, ActionModel.RoleActionId), "got wrong id choice action 1") |
2046 | actionSpy.clear() |
2047 | |
2048 | var choiceButton2 = findChild(notification, "notify_button4") |
2049 | tryCompareFunction(function() { mouseClick(choiceButton2, choiceButton2.width / 2, choiceButton2.height / 2); return actionSpy.signalArguments.length > 0; }, true); |
2050 | - compare(actionSpy.signalArguments[0][0], data.actions[4]["id"], "got wrong id choice action 2") |
2051 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(4, ActionModel.RoleActionId), "got wrong id choice action 2") |
2052 | actionSpy.clear() |
2053 | |
2054 | // click to collapse |
2055 | - //tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == false; }, true); |
2056 | + tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded == false; }, true); |
2057 | } else { |
2058 | mouseClick(buttonCancel, buttonCancel.width / 2, buttonCancel.height / 2) |
2059 | - compare(actionSpy.signalArguments[0][0], data.actions[1]["id"], "got wrong id for negative action") |
2060 | + compare(actionSpy.signalArguments[0][0], data.n.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action") |
2061 | } |
2062 | } |
2063 | + |
2064 | + // swipe-to-dismiss check |
2065 | + waitForRendering(notification) |
2066 | + var before = mockModel.count |
2067 | + var dragStart = notification.width * 0.25; |
2068 | + var dragEnd = notification.width; |
2069 | + var dragY = notification.height / 2; |
2070 | + touchFlick(notification, dragStart, dragY, dragEnd, dragY) |
2071 | + waitForRendering(notification) |
2072 | + if ((data.n.type === Notification.SnapDecision && notification.state === "expanded") || data.n.type === Notification.Confirmation) { |
2073 | + tryCompare(mockModel, "count", before) |
2074 | + } else { |
2075 | + tryCompare(mockModel, "count", before - 1) |
2076 | + } |
2077 | } |
2078 | } |
2079 | } |
2080 | |
2081 | === modified file 'tests/qmltests/Notifications/tst_OptionToggle.qml' |
2082 | --- tests/qmltests/Notifications/tst_OptionToggle.qml 2014-11-10 09:14:30 +0000 |
2083 | +++ tests/qmltests/Notifications/tst_OptionToggle.qml 2015-02-16 22:20:44 +0000 |
2084 | @@ -1,17 +1,20 @@ |
2085 | /* |
2086 | - * Copyright (C) 2014 Canonical, Ltd. |
2087 | + * Copyright 2015 Canonical Ltd. |
2088 | * |
2089 | * This program is free software; you can redistribute it and/or modify |
2090 | - * it under the terms of the GNU General Public License as published by |
2091 | + * it under the terms of the GNU Lesser General Public License as published by |
2092 | * the Free Software Foundation; version 3. |
2093 | * |
2094 | * This program is distributed in the hope that it will be useful, |
2095 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2096 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2097 | - * GNU General Public License for more details. |
2098 | + * GNU Lesser General Public License for more details. |
2099 | * |
2100 | - * You should have received a copy of the GNU General Public License |
2101 | + * You should have received a copy of the GNU Lesser General Public License |
2102 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2103 | + * |
2104 | + * Authors: |
2105 | + * Mirco Mueller <mirco.mueller@canonical.com> |
2106 | */ |
2107 | |
2108 | import QtQuick 2.0 |
2109 | @@ -235,8 +238,10 @@ |
2110 | compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect") |
2111 | compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect") |
2112 | |
2113 | - var audioItem = findInvisibleChild(notification, "sound") |
2114 | - compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state") |
2115 | + if (data.hasSound) { |
2116 | + var audioItem = findInvisibleChild(notification, "sound") |
2117 | + compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state") |
2118 | + } |
2119 | |
2120 | if(data.buttonRowVisible) { |
2121 | var buttonCancel = findChild(buttonRow, "notify_button1") |
2122 | |
2123 | === modified file 'tests/qmltests/Notifications/tst_SwipeToAct.qml' |
2124 | --- tests/qmltests/Notifications/tst_SwipeToAct.qml 2014-11-05 14:37:11 +0000 |
2125 | +++ tests/qmltests/Notifications/tst_SwipeToAct.qml 2015-02-16 22:20:44 +0000 |
2126 | @@ -218,6 +218,10 @@ |
2127 | // populate model with some mock notifications |
2128 | mockModel.append(data) |
2129 | |
2130 | + // add actions to action-model to test against |
2131 | + myActionModel.append("ok_id", "Ok") |
2132 | + myActionModel.append("cancel_id", "Cancel") |
2133 | + |
2134 | // make sure the view is properly updated before going on |
2135 | notifications.forceLayout(); |
2136 | waitForRendering(notifications); |
2137 | |
2138 | === modified file 'tests/qmltests/tst_ShellWithPin.qml' |
2139 | --- tests/qmltests/tst_ShellWithPin.qml 2014-12-05 16:27:27 +0000 |
2140 | +++ tests/qmltests/tst_ShellWithPin.qml 2015-02-16 22:20:44 +0000 |
2141 | @@ -472,5 +472,52 @@ |
2142 | |
2143 | } |
2144 | |
2145 | + /* |
2146 | + Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447 |
2147 | + |
2148 | + Do a left edge drag that is long enough to start displacing the greeter |
2149 | + but short engough so that the greeter comes back into place once the |
2150 | + finger is lifted. |
2151 | + |
2152 | + In this situation the launcher should remaing fully shown and hide itself |
2153 | + only after its idle timeout is triggered. |
2154 | + */ |
2155 | + function test_shortLeftEdgeSwipeMakesLauncherStayVisible() { |
2156 | + var greeter = testCase.findChild(shell, "greeter") |
2157 | + greeter.show(); |
2158 | + tryCompare(greeter, "showProgress", 1); |
2159 | + |
2160 | + var launcher = testCase.findChild(shell, "launcher") |
2161 | + { |
2162 | + var dismissTimer = testCase.findInvisibleChild(launcher, "dismissTimer"); |
2163 | + // effectively disable the dismiss timer |
2164 | + dismissTimer.interval = 24 * 60 * 60 * 1000 // 24 hours |
2165 | + } |
2166 | + var launcherPanel = testCase.findChild(launcher, "launcherPanel"); |
2167 | + |
2168 | + var toX = shell.width * 0.45; |
2169 | + touchFlick(shell, |
2170 | + 1 /* fromX */, shell.height * 0.5 /* fromY */, |
2171 | + toX /* toX */, shell.height * 0.5 /* toY */, |
2172 | + true /* beginTouch */, false /* endTouch */, |
2173 | + 50, 100); |
2174 | + |
2175 | + // Launcher must be fully shown by now |
2176 | + tryCompare(launcherPanel, "x", 0); |
2177 | + |
2178 | + // Greeter should be displaced |
2179 | + tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true); |
2180 | + |
2181 | + touchRelease(shell, toX, shell.height * 0.5); |
2182 | + |
2183 | + // Upon release the greeter should have slid back into full view |
2184 | + tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x === 0; }, true); |
2185 | + |
2186 | + // And the launcher should stay fully shown |
2187 | + for (var i = 0; i < 10; ++i) { |
2188 | + wait(50); |
2189 | + compare(launcherPanel.x, 0); |
2190 | + } |
2191 | + } |
2192 | } |
2193 | } |