on xenial help sign in
Merged with trunk.
a158219Leo Arias
cmake/modules/QmlTest.cmake+1−1
@@ -28,7 +28,7 @@ endif()
28
28
29set(qmlscene_exe ${CMAKE_BINARY_DIR}/tests/uqmlscene/uqmlscene)
29set(qmlscene_exe ${CMAKE_BINARY_DIR}/tests/uqmlscene/uqmlscene)
30
30
31−set(test_env UNITY_TESTING=1)
31+set(test_env LC_ALL=C UNITY_TESTING=1)
32
32
33macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
33macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
34 set(options NO_ADD_TEST NO_TARGETS)
34 set(options NO_ADD_TEST NO_TARGETS)
data/CMakeLists.txt+1−1
@@ -9,5 +9,5 @@ install(FILES
9 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
9 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
10 )
10 )
11
11
12−# install url dispatcher files
12+install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
13install(FILES unity8-dash.url-dispatcher DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
13install(FILES unity8-dash.url-dispatcher DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
com.canonical.Unity8.gschema.xml → data/com.canonical.Unity8.gschema.xmlrenamed

The content is the same.

debian/changelog+41
@@ -1,3 +1,44 @@
1+unity8 (8.02+15.04.20150226-0ubuntu1) vivid; urgency=medium
2+
3+ [ CI Train Bot ]
4+ * New rebuild forced.
5+
6+ [ Pete Woods ]
7+ * Only set scopes active when the screen is on (LP: #1422879)
8+
9+ -- CI Train Bot <ci-train-bot@canonical.com> Thu, 26 Feb 2015 19:39:54 +0000
10+
11+unity8 (8.02+15.04.20150224-0ubuntu1) vivid; urgency=medium
12+
13+ [ Albert Astals Cid ]
14+ * Dash manage hint should not be visible in previews+test (LP:
15+ #1422260)
16+ * Do not declare the hostname property twice
17+ * Fix some warnings in Mock Notifications
18+ * Make qmluittests pass with Qt 5.4
19+ * Preserve the aspect ratio for the card attribute images (LP:
20+ #1411751)
21+ * Update qmltypes
22+
23+ [ Andrea Cimitan ]
24+ * Add maximumLineCount for subtitle label in cards (LP: #1414870)
25+
26+ [ Michael Terry ]
27+ * Move the gschema file to data/ and install it via cmake, not dpkg.
28+ * Refactor the greeter code to be more compartmented and isolatable.
29+ * Silence a couple qml warnings. (LP: #1423286)
30+ * Wait for libqofono to finish initializing before we decide whether
31+ to skip the wizard's SIM page or not.
32+
33+ [ Michael Zanetti ]
34+ * enable revealing the launcher by hitting the left edge using the
35+ mouse
36+
37+ [ Ying-Chun Liu ]
38+ * Add Autopilot Notification helper.
39+
40+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 24 Feb 2015 10:17:16 +0000
41+
1unity8 (8.02+15.04.20150216.1-0ubuntu1) vivid; urgency=medium
42unity8 (8.02+15.04.20150216.1-0ubuntu1) vivid; urgency=medium
2
43
3 [ Andrea Cimitan ]
44 [ Andrea Cimitan ]
debian/unity8-common.install+1−1
@@ -1,7 +1,7 @@
1+usr/share/glib-2.0/schemas
1usr/share/locale
2usr/share/locale
2usr/share/unity8/Components
3usr/share/unity8/Components
3usr/share/unity8/Dash
4usr/share/unity8/Dash
4usr/share/unity8/Notifications
5usr/share/unity8/Notifications
5usr/share/unity8/graphics
6usr/share/unity8/graphics
6−com.canonical.Unity8.gschema.xml usr/share/glib-2.0/schemas/
7var/lib/polkit-1/localauthority/10-vendor.d
7var/lib/polkit-1/localauthority/10-vendor.d
plugins/AccountsService/AccountsService.qmltypes+3
@@ -29,5 +29,8 @@ Module {
29 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
29 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
31 Property { name: "failedLogins"; type: "uint" }
31 Property { name: "failedLogins"; type: "uint" }
32+ Property { name: "hereEnabled"; type: "bool" }
33+ Property { name: "hereLicensePath"; type: "string"; isReadonly: true }
34+ Property { name: "hereLicensePathValid"; type: "bool"; isReadonly: true }
32 }
35 }
33}
36}
plugins/Dash/CardAttributes.qml+2
@@ -49,6 +49,8 @@ GridLayout {
49 Icon {
49 Icon {
50 id: icon
50 id: icon
51 height: units.gu(2)
51 height: units.gu(2)
52+ // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
53+ width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth
52 source: "icon" in modelData && modelData["icon"] || ""
54 source: "icon" in modelData && modelData["icon"] || ""
53 color: grid.color
55 color: grid.color
54 }
56 }
plugins/Dash/CardCreator.js+4
@@ -224,6 +224,8 @@ var kTitleLabelCode = 'Label { \n\
224
224
225// %1 is used as extra anchors of emblemIcon
225// %1 is used as extra anchors of emblemIcon
226// %2 is used as color of emblemIcon
226// %2 is used as color of emblemIcon
227+// FIXME The width code is a
228+// Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
227var kEmblemIconCode = 'Icon { \n\
229var kEmblemIconCode = 'Icon { \n\
228 id: emblemIcon; \n\
230 id: emblemIcon; \n\
229 objectName: "emblemIcon"; \n\
231 objectName: "emblemIcon"; \n\
@@ -235,6 +237,7 @@ var kEmblemIconCode = 'Icon { \n\
235 source: cardData && cardData["emblem"] || ""; \n\
237 source: cardData && cardData["emblem"] || ""; \n\
236 color: %2; \n\
238 color: %2; \n\
237 height: source != "" ? titleLabel.font.pixelSize : 0; \n\
239 height: source != "" ? titleLabel.font.pixelSize : 0; \n\
240+ width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth; \n\
238 }\n';
241 }\n';
239
242
240// %1 is used as anchors of touchdown effect
243// %1 is used as anchors of touchdown effect
@@ -255,6 +258,7 @@ var kSubtitleLabelCode = 'Label { \n\
255 anchors { %1 } \n\
258 anchors { %1 } \n\
256 anchors.topMargin: units.dp(2); \n\
259 anchors.topMargin: units.dp(2); \n\
257 elide: Text.ElideRight; \n\
260 elide: Text.ElideRight; \n\
261+ maximumLineCount: 1; \n\
258 fontSize: "x-small"; \n\
262 fontSize: "x-small"; \n\
259 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
263 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
260 color: %2; \n\
264 color: %2; \n\
plugins/Dash/Dash.qmltypes+17−1
@@ -15,9 +15,25 @@ Module {
15 Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
15 Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
16 Property { name: "columnSpacing"; type: "double" }
16 Property { name: "columnSpacing"; type: "double" }
17 Property { name: "rowSpacing"; type: "double" }
17 Property { name: "rowSpacing"; type: "double" }
18+ Property { name: "cacheBuffer"; type: "int" }
18 Property { name: "displayMarginBeginning"; type: "double" }
19 Property { name: "displayMarginBeginning"; type: "double" }
19 Property { name: "displayMarginEnd"; type: "double" }
20 Property { name: "displayMarginEnd"; type: "double" }
20 }
21 }
22+ Component {
23+ name: "CroppedImageSizer"
24+ prototype: "QObject"
25+ exports: ["Dash/CroppedImageSizer 0.1"]
26+ exportMetaObjectRevisions: [0]
27+ Property { name: "source"; type: "QUrl" }
28+ Property { name: "width"; type: "double" }
29+ Property { name: "height"; type: "double" }
30+ Property { name: "sourceSize"; type: "QSize"; isReadonly: true }
31+ Signal { name: "inputParamsChanged" }
32+ Method {
33+ name: "setImageSize"
34+ Parameter { name: "imageSize"; type: "QSize" }
35+ }
36+ }
21 Component {
37 Component {
22 name: "HorizontalJournal"
38 name: "HorizontalJournal"
23 defaultProperty: "data"
39 defaultProperty: "data"
@@ -40,7 +56,7 @@ Module {
40 Property { name: "forceNoClip"; type: "bool" }
56 Property { name: "forceNoClip"; type: "bool" }
41 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
57 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
42 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
58 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
43− Property { name: "cacheBuffer"; type: "double" }
59+ Property { name: "cacheBuffer"; type: "int" }
44 Signal { name: "headerChanged" }
60 Signal { name: "headerChanged" }
45 Method { name: "positionAtBeginning" }
61 Method { name: "positionAtBeginning" }
46 Method { name: "showHeader" }
62 Method { name: "showHeader" }
plugins/Greeter/Unity/Launcher/Launcher.qmltypes+4−1
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.1
4// It is used for QML tooling purposes only.
4// It is used for QML tooling purposes only.
5//
5//
6// This file was auto-generated by:
6// This file was auto-generated by:
7−// 'qmlplugindump -notrelocatable Unity.Launcher 0.1 plugins'
7+// 'qmlplugindump -notrelocatable Unity.Launcher 0.1 plugins/Greeter'
8
8
9Module {
9Module {
10 Component {
10 Component {
@@ -25,6 +25,7 @@ Module {
25 name: "requestRemove"
25 name: "requestRemove"
26 Parameter { name: "appId"; type: "string" }
26 Parameter { name: "appId"; type: "string" }
27 }
27 }
28+ Method { name: "refresh" }
28 Method {
29 Method {
29 name: "get"
30 name: "get"
30 type: "unity::shell::launcher::LauncherItemInterface*"
31 type: "unity::shell::launcher::LauncherItemInterface*"
@@ -136,6 +137,8 @@ Module {
136 type: "unity::shell::application::ApplicationManagerInterface"
137 type: "unity::shell::application::ApplicationManagerInterface"
137 isPointer: true
138 isPointer: true
138 }
139 }
140+ Property { name: "onlyPinned"; type: "bool" }
141+ Signal { name: "hint" }
139 Method {
142 Method {
140 name: "move"
143 name: "move"
141 Parameter { name: "oldIndex"; type: "int" }
144 Parameter { name: "oldIndex"; type: "int" }
plugins/LightDM/LightDM.qmltypes+2−2
@@ -114,7 +114,7 @@ Module {
114 Method { name: "invalidate" }
114 Method { name: "invalidate" }
115 }
115 }
116 Component {
116 Component {
117− name: "QSortFilterProxyModelQML"
117+ name: "UnitySortFilterProxyModelQML"
118 prototype: "QSortFilterProxyModel"
118 prototype: "QSortFilterProxyModel"
119 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
119 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
120 Property { name: "totalCount"; type: "int"; isReadonly: true }
120 Property { name: "totalCount"; type: "int"; isReadonly: true }
@@ -242,7 +242,7 @@ Module {
242 }
242 }
243 Component {
243 Component {
244 name: "UsersModel"
244 name: "UsersModel"
245− prototype: "QSortFilterProxyModelQML"
245+ prototype: "UnitySortFilterProxyModelQML"
246 exports: ["LightDM/Users 0.1"]
246 exports: ["LightDM/Users 0.1"]
247 isCreatable: false
247 isCreatable: false
248 isSingleton: true
248 isSingleton: true
plugins/LightDM/liblightdm/Greeter.h−2
@@ -47,8 +47,6 @@ class Q_DECL_EXPORT Greeter : public QObject
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
48 Q_PROPERTY(bool locked READ lockHint CONSTANT)
48 Q_PROPERTY(bool locked READ lockHint CONSTANT)
49
49
50− Q_PROPERTY(QString hostname READ hostname CONSTANT)
51−
52 Q_ENUMS(PromptType MessageType)
50 Q_ENUMS(PromptType MessageType)
53
51
54public:
52public:
plugins/ScreenGrabber/ScreenGrabber.qmltypes+1−1
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.1
4// It is used for QML tooling purposes only.
4// It is used for QML tooling purposes only.
5//
5//
6// This file was auto-generated by:
6// This file was auto-generated by:
7−// 'qmlplugindump -nonrelocatable ScreenGrabber 0.1 plugins'
7+// 'qmlplugindump -notrelocatable ScreenGrabber 0.1 plugins'
8
8
9Module {
9Module {
10 Component {
10 Component {
plugins/Ubuntu/Gestures/Gestures.qmltypes+15−1
@@ -33,7 +33,8 @@ Module {
33 "Rightwards": 0,
33 "Rightwards": 0,
34 "Leftwards": 1,
34 "Leftwards": 1,
35 "Downwards": 2,
35 "Downwards": 2,
36− "Upwards": 3
36+ "Upwards": 3,
37+ "Horizontal": 4
37 }
38 }
38 }
39 }
39 Method {
40 Method {
@@ -151,4 +152,17 @@ Module {
151 exportMetaObjectRevisions: [0]
152 exportMetaObjectRevisions: [0]
152 Signal { name: "pressedOutside" }
153 Signal { name: "pressedOutside" }
153 }
154 }
155+ Component {
156+ name: "TouchGate"
157+ defaultProperty: "data"
158+ prototype: "QQuickItem"
159+ exports: ["Ubuntu.Gestures/TouchGate 0.1"]
160+ exportMetaObjectRevisions: [0]
161+ Property { name: "targetItem"; type: "QQuickItem"; isPointer: true }
162+ Signal {
163+ name: "targetItemChanged"
164+ Parameter { name: "item"; type: "QQuickItem"; isPointer: true }
165+ }
166+ Signal { name: "pressed" }
167+ }
154}
168}
plugins/Unity/Indicators/Indicators.qmltypes+10−38
@@ -55,10 +55,15 @@ Module {
55 exports: ["Unity.Indicators/IndicatorsManager 0.1"]
55 exports: ["Unity.Indicators/IndicatorsManager 0.1"]
56 exportMetaObjectRevisions: [0]
56 exportMetaObjectRevisions: [0]
57 Property { name: "loaded"; type: "bool"; isReadonly: true }
57 Property { name: "loaded"; type: "bool"; isReadonly: true }
58+ Property { name: "profile"; type: "string" }
58 Signal {
59 Signal {
59 name: "loadedChanged"
60 name: "loadedChanged"
60 Parameter { type: "bool" }
61 Parameter { type: "bool" }
61 }
62 }
63+ Signal {
64+ name: "profileChanged"
65+ Parameter { type: "string" }
66+ }
62 Signal {
67 Signal {
63 name: "indicatorLoaded"
68 name: "indicatorLoaded"
64 Parameter { name: "indicator_name"; type: "string" }
69 Parameter { name: "indicator_name"; type: "string" }
@@ -67,10 +72,7 @@ Module {
67 name: "indicatorAboutToBeUnloaded"
72 name: "indicatorAboutToBeUnloaded"
68 Parameter { name: "indicator_name"; type: "string" }
73 Parameter { name: "indicator_name"; type: "string" }
69 }
74 }
70− Method {
71− name: "load"
75+ Method { name: "load" }
72− Parameter { name: "profile"; type: "string" }
73− }
74 Method { name: "unload" }
76 Method { name: "unload" }
75 }
77 }
76 Component {
78 Component {
@@ -79,14 +81,12 @@ Module {
79 exports: ["Unity.Indicators/IndicatorsModel 0.1"]
81 exports: ["Unity.Indicators/IndicatorsModel 0.1"]
80 exportMetaObjectRevisions: [0]
82 exportMetaObjectRevisions: [0]
81 Property { name: "count"; type: "int"; isReadonly: true }
83 Property { name: "count"; type: "int"; isReadonly: true }
84+ Property { name: "profile"; type: "string" }
82 Signal {
85 Signal {
83 name: "indicatorDataChanged"
86 name: "indicatorDataChanged"
84 Parameter { name: "data"; type: "QVariant" }
87 Parameter { name: "data"; type: "QVariant" }
85 }
88 }
86− Method {
87− name: "load"
89+ Method { name: "load" }
88− Parameter { name: "profile"; type: "string" }
89− }
90 Method { name: "unload" }
90 Method { name: "unload" }
91 Method {
91 Method {
92 name: "data"
92 name: "data"
@@ -106,8 +106,7 @@ Module {
106 values: {
106 values: {
107 "Identifier": 0,
107 "Identifier": 0,
108 "Position": 1,
108 "Position": 1,
109− "IndicatorProperties": 2,
109+ "IndicatorProperties": 2
110− "IsVisible": 3
111 }
110 }
112 }
111 }
113 }
112 }
@@ -200,12 +199,6 @@ Module {
200 }
199 }
201 }
200 }
202 }
201 }
203− Component {
204− name: "QAbstractProxyModel"
205− prototype: "QAbstractItemModel"
206− Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
207− }
208− Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
209 Component {
202 Component {
210 name: "RootStateObject"
203 name: "RootStateObject"
211 prototype: "QObject"
204 prototype: "QObject"
@@ -215,7 +208,7 @@ Module {
215 Property { name: "rightLabel"; type: "string"; isReadonly: true }
208 Property { name: "rightLabel"; type: "string"; isReadonly: true }
216 Property { name: "icons"; type: "QStringList"; isReadonly: true }
209 Property { name: "icons"; type: "QStringList"; isReadonly: true }
217 Property { name: "accessibleName"; type: "string"; isReadonly: true }
210 Property { name: "accessibleName"; type: "string"; isReadonly: true }
218− Property { name: "visible"; type: "bool"; isReadonly: true }
211+ Property { name: "indicatorVisible"; type: "bool"; isReadonly: true }
219 Signal { name: "updated" }
212 Signal { name: "updated" }
220 }
213 }
221 Component {
214 Component {
@@ -268,25 +261,4 @@ Module {
268 }
261 }
269 Method { name: "pop"; type: "UnityMenuModel*" }
262 Method { name: "pop"; type: "UnityMenuModel*" }
270 }
263 }
271− Component {
272− name: "VisibleIndicatorsModel"
273− prototype: "QIdentityProxyModel"
274− exports: ["Unity.Indicators/VisibleIndicatorsModel 0.1"]
275− exportMetaObjectRevisions: [0]
276− Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
277− Property { name: "visible"; type: "QVariantMap" }
278− Signal { name: "totalCountChanged" }
279− Method {
280− name: "onBeginRowInserted"
281− Parameter { type: "QModelIndex" }
282− Parameter { name: "start"; type: "int" }
283− Parameter { name: "end"; type: "int" }
284− }
285− Method {
286− name: "onRowInserted"
287− Parameter { type: "QModelIndex" }
288− Parameter { name: "start"; type: "int" }
289− Parameter { name: "end"; type: "int" }
290− }
291− }
292}
264}
plugins/Unity/Launcher/Launcher.qmltypes+3
@@ -25,6 +25,7 @@ Module {
25 name: "requestRemove"
25 name: "requestRemove"
26 Parameter { name: "appId"; type: "string" }
26 Parameter { name: "appId"; type: "string" }
27 }
27 }
28+ Method { name: "refresh" }
28 Method {
29 Method {
29 name: "get"
30 name: "get"
30 type: "unity::shell::launcher::LauncherItemInterface*"
31 type: "unity::shell::launcher::LauncherItemInterface*"
@@ -136,6 +137,8 @@ Module {
136 type: "unity::shell::application::ApplicationManagerInterface"
137 type: "unity::shell::application::ApplicationManagerInterface"
137 isPointer: true
138 isPointer: true
138 }
139 }
140+ Property { name: "onlyPinned"; type: "bool" }
141+ Signal { name: "hint" }
139 Method {
142 Method {
140 name: "move"
143 name: "move"
141 Parameter { name: "oldIndex"; type: "int" }
144 Parameter { name: "oldIndex"; type: "int" }
plugins/Unity/Session/Session.qmltypes+5−1
@@ -9,7 +9,7 @@ import QtQuick.tooling 1.1
9Module {
9Module {
10 Component {
10 Component {
11 name: "DBusUnitySessionService"
11 name: "DBusUnitySessionService"
12− prototype: "QObject"
12+ prototype: "UnityDBusObject"
13 exports: ["Unity.Session/DBusUnitySessionService 0.1"]
13 exports: ["Unity.Session/DBusUnitySessionService 0.1"]
14 isCreatable: false
14 isCreatable: false
15 isSingleton: true
15 isSingleton: true
@@ -33,6 +33,9 @@ Module {
33 Method { name: "RequestLogout" }
33 Method { name: "RequestLogout" }
34 Method { name: "RequestReboot" }
34 Method { name: "RequestReboot" }
35 Method { name: "RequestShutdown" }
35 Method { name: "RequestShutdown" }
36+ Method { name: "logout" }
37+ Method { name: "reboot" }
38+ Method { name: "shutdown" }
36 }
39 }
37 Component {
40 Component {
38 name: "OrientationLock"
41 name: "OrientationLock"
@@ -44,4 +47,5 @@ Module {
44 Property { name: "enabled"; type: "bool"; isReadonly: true }
47 Property { name: "enabled"; type: "bool"; isReadonly: true }
45 Property { name: "savedOrientation"; type: "Qt::ScreenOrientation" }
48 Property { name: "savedOrientation"; type: "Qt::ScreenOrientation" }
46 }
49 }
50+ Component { name: "UnityDBusObject"; prototype: "QObject" }
47}
51}
plugins/Utils/Utils.qmltypes+60−41
@@ -60,9 +60,49 @@ Module {
60 Method { name: "invalidate" }
60 Method { name: "invalidate" }
61 }
61 }
62 Component {
62 Component {
63− name: "QSortFilterProxyModelQML"
63+ name: "RelativeTimeFormatter"
64+ prototype: "TimeFormatter"
65+ exports: ["Utils/RelativeTimeFormatter 0.1"]
66+ exportMetaObjectRevisions: [0]
67+ }
68+ Component {
69+ name: "TimeFormatter"
70+ prototype: "QObject"
71+ exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
72+ exportMetaObjectRevisions: [0, 0]
73+ Property { name: "format"; type: "string" }
74+ Property { name: "timeString"; type: "string"; isReadonly: true }
75+ Property { name: "time"; type: "qlonglong" }
76+ Signal {
77+ name: "formatChanged"
78+ Parameter { name: "format"; type: "string" }
79+ }
80+ Signal {
81+ name: "timeStringChanged"
82+ Parameter { name: "timeString"; type: "string" }
83+ }
84+ Signal {
85+ name: "timeChanged"
86+ Parameter { name: "time"; type: "qlonglong" }
87+ }
88+ }
89+ Component {
90+ name: "UnityMenuModelPaths"
91+ prototype: "QObject"
92+ exports: ["Utils/UnityMenuModelPaths 0.1"]
93+ exportMetaObjectRevisions: [0]
94+ Property { name: "source"; type: "QVariant" }
95+ Property { name: "busName"; type: "QByteArray"; isReadonly: true }
96+ Property { name: "actions"; type: "QVariantMap"; isReadonly: true }
97+ Property { name: "menuObjectPath"; type: "QByteArray"; isReadonly: true }
98+ Property { name: "busNameHint"; type: "QByteArray" }
99+ Property { name: "actionsHint"; type: "QByteArray" }
100+ Property { name: "menuObjectPathHint"; type: "QByteArray" }
101+ }
102+ Component {
103+ name: "UnitySortFilterProxyModelQML"
64 prototype: "QSortFilterProxyModel"
104 prototype: "QSortFilterProxyModel"
65− exports: ["Utils/SortFilterProxyModel 0.1"]
105+ exports: ["Utils/UnitySortFilterProxyModel 0.1"]
66 exportMetaObjectRevisions: [0]
106 exportMetaObjectRevisions: [0]
67 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
107 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
68 Property { name: "totalCount"; type: "int"; isReadonly: true }
108 Property { name: "totalCount"; type: "int"; isReadonly: true }
@@ -107,50 +147,29 @@ Module {
107 }
147 }
108 }
148 }
109 Component {
149 Component {
110− name: "RelativeTimeFormatter"
150+ name: "WindowKeysFilter"
151+ defaultProperty: "data"
111− prototype: "TimeFormatter"
152+ prototype: "QQuickItem"
112− exports: ["Utils/RelativeTimeFormatter 0.1"]
153+ exports: ["Utils/WindowKeysFilter 0.1"]
113 exportMetaObjectRevisions: [0]
154 exportMetaObjectRevisions: [0]
114 }
155 }
115 Component {
156 Component {
116− name: "TimeFormatter"
157+ name: "WindowStateStorage"
117 prototype: "QObject"
158 prototype: "QObject"
118− exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
159+ exports: ["Utils/WindowStateStorage 0.1"]
119− exportMetaObjectRevisions: [0, 0]
160+ isCreatable: false
120− Property { name: "format"; type: "string" }
161+ isSingleton: true
121− Property { name: "timeString"; type: "string"; isReadonly: true }
162+ exportMetaObjectRevisions: [0]
122− Property { name: "time"; type: "qlonglong" }
163+ Method {
123− Signal {
164+ name: "saveGeometry"
124− name: "formatChanged"
125− Parameter { name: "format"; type: "string" }
165+ Parameter { name: "windowId"; type: "string" }
126− }
127− Signal {
128− name: "timeStringChanged"
129− Parameter { name: "timeString"; type: "string" }
166+ Parameter { name: "rect"; type: "QRect" }
130 }
167 }
131− Signal {
168+ Method {
132− name: "timeChanged"
169+ name: "getGeometry"
170+ type: "QRect"
171+ Parameter { name: "windowId"; type: "string" }
133− Parameter { name: "time"; type: "qlonglong" }
172+ Parameter { name: "defaultValue"; type: "QRect" }
134 }
173 }
135 }
174 }
136− Component {
137− name: "UnityMenuModelPaths"
138− prototype: "QObject"
139− exports: ["Utils/UnityMenuModelPaths 0.1"]
140− exportMetaObjectRevisions: [0]
141− Property { name: "source"; type: "QVariant" }
142− Property { name: "busName"; type: "QByteArray"; isReadonly: true }
143− Property { name: "actions"; type: "QVariantMap"; isReadonly: true }
144− Property { name: "menuObjectPath"; type: "QByteArray"; isReadonly: true }
145− Property { name: "busNameHint"; type: "QByteArray" }
146− Property { name: "actionsHint"; type: "QByteArray" }
147− Property { name: "menuObjectPathHint"; type: "QByteArray" }
148− }
149− Component {
150− name: "WindowKeysFilter"
151− defaultProperty: "data"
152− prototype: "QQuickItem"
153− exports: ["Utils/WindowKeysFilter 0.1"]
154− exportMetaObjectRevisions: [0]
155− }
156}
175}
plugins/Wizard/Wizard.qmltypes−3
@@ -11,8 +11,6 @@ Module {
11 name: "PageList"
11 name: "PageList"
12 prototype: "QObject"
12 prototype: "QObject"
13 exports: ["Wizard/PageList 0.1"]
13 exports: ["Wizard/PageList 0.1"]
14− isCreatable: false
15− isSingleton: true
16 exportMetaObjectRevisions: [0]
14 exportMetaObjectRevisions: [0]
17 Property { name: "index"; type: "int"; isReadonly: true }
15 Property { name: "index"; type: "int"; isReadonly: true }
18 Property { name: "numPages"; type: "int"; isReadonly: true }
16 Property { name: "numPages"; type: "int"; isReadonly: true }
@@ -33,4 +31,3 @@ Module {
33 }
31 }
34 }
32 }
35}
33}
36−
po/de.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
11−"PO-Revision-Date: 2015-02-18 14:18+0000\n"
11+"PO-Revision-Date: 2015-02-27 13:29+0000\n"
12"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
12"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
13"Language-Team: German <de@li.org>\n"
13"Language-Team: German <de@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-02-19 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-28 06:19+0000\n"
19−"X-Generator: Launchpad (build 17341)\n"
19+"X-Generator: Launchpad (build 17361)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -362,7 +362,7 @@ msgstr ""
362
362
363#: qml/Tutorial/TutorialRight.qml:122
363#: qml/Tutorial/TutorialRight.qml:122
364msgid "View all your running tasks."
364msgid "View all your running tasks."
365−msgstr ""
365+msgstr "Sehen Sie alle Ihre laufenden Anwendungen."
366
366
367#: qml/Wizard/Page.qml:89
367#: qml/Wizard/Page.qml:89
368msgid "Back"
368msgid "Back"
po/gd.po+27−21
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
11−"PO-Revision-Date: 2015-02-11 15:01+0000\n"
11+"PO-Revision-Date: 2015-02-27 01:01+0000\n"
12−"Last-Translator: GunChleoc <Unknown>\n"
12+"Last-Translator: Akerbeltz <fios@akerbeltz.org>\n"
13"Language-Team: Fòram na Gàidhlig\n"
13"Language-Team: Fòram na Gàidhlig\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
18"(n > 2 && n < 20) ? 2 : 3;\n"
18"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-02-17 06:29+0000\n"
19+"X-Launchpad-Export-Date: 2015-02-27 05:54+0000\n"
20−"X-Generator: Launchpad (build 17341)\n"
20+"X-Generator: Launchpad (build 17361)\n"
21"Language: gd\n"
21"Language: gd\n"
22
22
23#: plugins/LightDM/Greeter.cpp:112
23#: plugins/LightDM/Greeter.cpp:112
@@ -187,7 +187,7 @@ msgstr "Leig às airson ath-nuadhachadh…"
187
187
188#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
188#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
189msgid "Enable location data"
189msgid "Enable location data"
190−msgstr ""
190+msgstr "Cuir an comas dàta an ionaid"
191
191
192#: qml/Dash/ScopesList.qml:66
192#: qml/Dash/ScopesList.qml:66
193msgid "Manage"
193msgid "Manage"
@@ -199,7 +199,7 @@ msgstr "Dhachaigh"
199
199
200#: qml/Dash/ScopesList.qml:113
200#: qml/Dash/ScopesList.qml:113
201msgid "Also installed"
201msgid "Also installed"
202−msgstr ""
202+msgstr "Air a stàladh cuideachd"
203
203
204#: qml/Greeter/Greeter.qml:141
204#: qml/Greeter/Greeter.qml:141
205msgid "Unlock"
205msgid "Unlock"
@@ -319,55 +319,55 @@ msgstr ""
319
319
320#: qml/Tutorial/TutorialBottomFinish.qml:24
320#: qml/Tutorial/TutorialBottomFinish.qml:24
321msgid "This action does different things for different apps"
321msgid "This action does different things for different apps"
322−msgstr ""
322+msgstr "Nì seo diofar rudan do dhiofar aplacaidean"
323
323
324#: qml/Tutorial/TutorialBottomFinish.qml:25
324#: qml/Tutorial/TutorialBottomFinish.qml:25
325msgid "Tap here to finish."
325msgid "Tap here to finish."
326−msgstr ""
326+msgstr "Thoir gnogag an-seo gus a choileanadh."
327
327
328#: qml/Tutorial/TutorialBottom.qml:28
328#: qml/Tutorial/TutorialBottom.qml:28
329msgid "Open special menus"
329msgid "Open special menus"
330−msgstr ""
330+msgstr "Fosgail na clàran-taice sònraichte"
331
331
332#: qml/Tutorial/TutorialBottom.qml:29
332#: qml/Tutorial/TutorialBottom.qml:29
333msgid "Swipe up from the bottom edge."
333msgid "Swipe up from the bottom edge."
334−msgstr ""
334+msgstr "Dèan grad-shlaighdeadh a-nìos on oir."
335
335
336#: qml/Tutorial/TutorialLeftFinish.qml:24
336#: qml/Tutorial/TutorialLeftFinish.qml:24
337msgid "These are the shortcuts to favorite apps"
337msgid "These are the shortcuts to favorite apps"
338−msgstr ""
338+msgstr "Seo na h-ath-ghoiridean ris na h-aplacaidean as fhearr leat"
339
339
340#: qml/Tutorial/TutorialLeftFinish.qml:25 qml/Tutorial/TutorialRight.qml:144
340#: qml/Tutorial/TutorialLeftFinish.qml:25 qml/Tutorial/TutorialRight.qml:144
341msgid "Tap here to continue."
341msgid "Tap here to continue."
342−msgstr ""
342+msgstr "Thoir gnogag an-seo gus leantainn air adhart."
343
343
344#: qml/Tutorial/TutorialLeft.qml:26
344#: qml/Tutorial/TutorialLeft.qml:26
345msgid "Open the launcher"
345msgid "Open the launcher"
346−msgstr ""
346+msgstr "Fosgail an lòinsear"
347
347
348#: qml/Tutorial/TutorialLeft.qml:27
348#: qml/Tutorial/TutorialLeft.qml:27
349msgid "Short swipe from the left edge."
349msgid "Short swipe from the left edge."
350−msgstr ""
350+msgstr "Grad-shlaighdeadh goirid on oir chlì."
351
351
352#: qml/Tutorial/TutorialPage.qml:178
352#: qml/Tutorial/TutorialPage.qml:178
353msgid "You almost got it!"
353msgid "You almost got it!"
354−msgstr ""
354+msgstr "Cha mhòr!"
355
355
356#: qml/Tutorial/TutorialPage.qml:195
356#: qml/Tutorial/TutorialPage.qml:195
357msgid "Try again."
357msgid "Try again."
358−msgstr ""
358+msgstr "Feuch ris a-rithist."
359
359
360#: qml/Tutorial/TutorialRight.qml:31
360#: qml/Tutorial/TutorialRight.qml:31
361msgid "To view open apps"
361msgid "To view open apps"
362−msgstr ""
362+msgstr "Gus aplacaidean fosgailte fhaicinn"
363
363
364#: qml/Tutorial/TutorialRight.qml:32
364#: qml/Tutorial/TutorialRight.qml:32
365msgid "Long swipe from the right edge."
365msgid "Long swipe from the right edge."
366−msgstr ""
366+msgstr "Grad-shlaighdeadh on oir dheas."
367
367
368#: qml/Tutorial/TutorialRight.qml:122
368#: qml/Tutorial/TutorialRight.qml:122
369msgid "View all your running tasks."
369msgid "View all your running tasks."
370−msgstr ""
370+msgstr "Seall gach saothair a tha thu a' ruith."
371
371
372#: qml/Wizard/Page.qml:89
372#: qml/Wizard/Page.qml:89
373msgid "Back"
373msgid "Back"
@@ -426,7 +426,7 @@ msgstr "Còd-faire"
426
426
427#: qml/Wizard/Pages/30-passwd-type.qml:101
427#: qml/Wizard/Pages/30-passwd-type.qml:101
428msgid "4 digits only"
428msgid "4 digits only"
429−msgstr ""
429+msgstr "4 àireamhan a-mhàin"
430
430
431#: qml/Wizard/Pages/30-passwd-type.qml:103
431#: qml/Wizard/Pages/30-passwd-type.qml:103
432msgid "Passphrase"
432msgid "Passphrase"
@@ -458,11 +458,13 @@ msgstr "Faodaidh am fòn aithneachadh far a bheil thu:"
458
458
459#: qml/Wizard/Pages/50-location.qml:69
459#: qml/Wizard/Pages/50-location.qml:69
460msgid "Using GPS only (less accurate)"
460msgid "Using GPS only (less accurate)"
461−msgstr ""
461+msgstr "Na cleachd ach GPS (chan eil seo cho pongail)"
462
462
463#: qml/Wizard/Pages/50-location.qml:86
463#: qml/Wizard/Pages/50-location.qml:86
464msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
464msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
465msgstr ""
465msgstr ""
466+"A' cleachdadh GPS, WiFi gun urra is fiosrachadh an lìonraidh mobile "
467+"(mholamaid seo)"
466
468
467#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
469#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
468#: qml/Wizard/Pages/50-location.qml:103
470#: qml/Wizard/Pages/50-location.qml:103
@@ -470,6 +472,8 @@ msgid ""
470"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
472"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
471"conditions</a>."
473"conditions</a>."
472msgstr ""
474msgstr ""
475+"Ma thaghas tu an roghainn seo, bidh thu ag aontachadh ri <a "
476+"href='#'>teirmichean is cumhaichean</a> an Nokia HERE."
473
477
474#: qml/Wizard/Pages/50-location.qml:112
478#: qml/Wizard/Pages/50-location.qml:112
475msgid "Not at all"
479msgid "Not at all"
@@ -478,6 +482,8 @@ msgstr "Chan fhaod idir"
478#: qml/Wizard/Pages/50-location.qml:124
482#: qml/Wizard/Pages/50-location.qml:124
479msgid "You can change your mind later in <b>System Settings</b>."
483msgid "You can change your mind later in <b>System Settings</b>."
480msgstr ""
484msgstr ""
485+"'S urrainn dhut a chaochladh a chur romhad às a dhèidh seo ann an "
486+"<b>Roghainnean an t-siostaim</b>."
481
487
482#: qml/Wizard/Pages/60-reporting.qml:24
488#: qml/Wizard/Pages/60-reporting.qml:24
483msgid "Improving your experience"
489msgid "Improving your experience"
po/lv.po+105−92
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
11−"PO-Revision-Date: 2014-08-21 09:46+0000\n"
11+"PO-Revision-Date: 2015-02-28 16:18+0000\n"
12−"Last-Translator: Jānis Marks Gailis <jm-gailis@fai-vianet.fr>\n"
12+"Last-Translator: Rūdolfs Mazurs <Unknown>\n"
13"Language-Team: Latvian <lv@li.org>\n"
13"Language-Team: Latvian <lv@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-02-17 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-03-01 05:31+0000\n"
19−"X-Generator: Launchpad (build 17341)\n"
19+"X-Generator: Launchpad (build 17361)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -25,38 +25,39 @@ msgstr "Parole: "
25#: plugins/Unity/Launcher/launcheritem.cpp:45
25#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:100
26#: plugins/Unity/Launcher/launcheritem.cpp:100
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28−msgstr ""
28+msgstr "Piespraust īsceļu"
29
29
30#: plugins/Unity/Launcher/launcheritem.cpp:100
30#: plugins/Unity/Launcher/launcheritem.cpp:100
31msgid "Unpin shortcut"
31msgid "Unpin shortcut"
32−msgstr ""
32+msgstr "Noņemt īsceļu"
33
33
34#: qml/Components/DelayedLockscreen.qml:47
34#: qml/Components/DelayedLockscreen.qml:47
35msgid "Device Locked"
35msgid "Device Locked"
36−msgstr ""
36+msgstr "Ierīce bloķēta"
37
37
38#: qml/Components/DelayedLockscreen.qml:62
38#: qml/Components/DelayedLockscreen.qml:62
39msgid "You have been locked out due to too many failed passphrase attempts."
39msgid "You have been locked out due to too many failed passphrase attempts."
40−msgstr ""
40+msgstr "Bloķēts, jo pārāk bieži kļūdaini ievadījāt paroli."
41
41
42#: qml/Components/DelayedLockscreen.qml:63
42#: qml/Components/DelayedLockscreen.qml:63
43msgid "You have been locked out due to too many failed passcode attempts."
43msgid "You have been locked out due to too many failed passcode attempts."
44−msgstr ""
44+msgstr "Bloķēts, jo pārāk bieži kļūdaini ievadījāt piekļuves kodu."
45
45
46#: qml/Components/DelayedLockscreen.qml:72
46#: qml/Components/DelayedLockscreen.qml:72
47#, qt-format
47#, qt-format
48msgid "Please wait %1 minute and then try again…"
48msgid "Please wait %1 minute and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
50+msgstr[0] "Lūdzu, uzgaidiet %1 minūti un tad mēģiniet vēlreiz..."
51−msgstr[1] ""
51+msgstr[1] "Lūdzu, uzgaidiet %1 minūtes un tad mēģiniet vēlreiz..."
52+msgstr[2] "Lūdzu, uzgaidiet %1 minūtes un tad mēģiniet vēlreiz..."
52
53
53#: qml/Components/Dialogs.qml:86
54#: qml/Components/Dialogs.qml:86
54msgid "Log out"
55msgid "Log out"
55−msgstr "Iziet"
56+msgstr "Izrakstīties"
56
57
57#: qml/Components/Dialogs.qml:87
58#: qml/Components/Dialogs.qml:87
58msgid "Are you sure you want to log out?"
59msgid "Are you sure you want to log out?"
59−msgstr ""
60+msgstr "Vai tiešām vēlaties izrakstīties?"
60
61
61#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62#: qml/Components/Dialogs.qml:134
63#: qml/Components/Dialogs.qml:134
@@ -74,7 +75,7 @@ msgstr "Izslēgt"
74
75
75#: qml/Components/Dialogs.qml:109
76#: qml/Components/Dialogs.qml:109
76msgid "Are you sure you want to shut down?"
77msgid "Are you sure you want to shut down?"
77−msgstr ""
78+msgstr "Vai tiešām vēlaties izslēgt?"
78
79
79#: qml/Components/Dialogs.qml:131
80#: qml/Components/Dialogs.qml:131
80msgid "Reboot"
81msgid "Reboot"
@@ -82,7 +83,7 @@ msgstr "Pārstartēt"
82
83
83#: qml/Components/Dialogs.qml:132
84#: qml/Components/Dialogs.qml:132
84msgid "Are you sure you want to reboot?"
85msgid "Are you sure you want to reboot?"
85−msgstr ""
86+msgstr "Vai tiešām vēlaties pārstartēt?"
86
87
87#: qml/Components/Dialogs.qml:154
88#: qml/Components/Dialogs.qml:154
88msgid "Power"
89msgid "Power"
@@ -93,6 +94,8 @@ msgid ""
93"Are you sure you would like\n"
94"Are you sure you would like\n"
94"to power off?"
95"to power off?"
95msgstr ""
96msgstr ""
97+"Vai tiešām vēlaties\n"
98+"izslēgt?"
96
99
97#: qml/Components/Dialogs.qml:157
100#: qml/Components/Dialogs.qml:157
98msgid "Power off"
101msgid "Power off"
@@ -100,7 +103,7 @@ msgstr "Izslēgt"
100
103
101#: qml/Components/Dialogs.qml:166
104#: qml/Components/Dialogs.qml:166
102msgid "Restart"
105msgid "Restart"
103−msgstr ""
106+msgstr "Pārstartēt"
104
107
105#: qml/Components/Dialogs.qml:175
108#: qml/Components/Dialogs.qml:175
106msgid "Cancel"
109msgid "Cancel"
@@ -108,19 +111,19 @@ msgstr "Atcelt"
108
111
109#: qml/Components/Lockscreen.qml:238
112#: qml/Components/Lockscreen.qml:238
110msgid "Return to Call"
113msgid "Return to Call"
111−msgstr ""
114+msgstr "Atgriezties pie zvana"
112
115
113#: qml/Components/Lockscreen.qml:238
116#: qml/Components/Lockscreen.qml:238
114msgid "Emergency Call"
117msgid "Emergency Call"
115−msgstr "Neatliekamās Palīdzības zvans"
118+msgstr "Ārkārtas situācijas zvans"
116
119
117#: qml/Components/Lockscreen.qml:270
120#: qml/Components/Lockscreen.qml:270
118msgid "OK"
121msgid "OK"
119−msgstr "LABI"
122+msgstr "Labi"
120
123
121#: qml/Dash/GenericScopeView.qml:528 qml/Dash/GenericScopeView.qml:686
124#: qml/Dash/GenericScopeView.qml:528 qml/Dash/GenericScopeView.qml:686
122msgid "See less"
125msgid "See less"
123−msgstr "Redzēt mazāk"
126+msgstr "Skatīt mazāk"
124
127
125#: qml/Dash/GenericScopeView.qml:528
128#: qml/Dash/GenericScopeView.qml:528
126msgid "See all"
129msgid "See all"
@@ -161,7 +164,7 @@ msgstr "Novērtēt šo"
161
164
162#: qml/Dash/Previews/PreviewRatingInput.qml:126
165#: qml/Dash/Previews/PreviewRatingInput.qml:126
163msgid "Add a review"
166msgid "Add a review"
164−msgstr "Komentēt"
167+msgstr "Pievienot atsauksmi"
165
168
166#: qml/Dash/Previews/PreviewRatingInput.qml:166
169#: qml/Dash/Previews/PreviewRatingInput.qml:166
167msgid "Send"
170msgid "Send"
@@ -169,31 +172,31 @@ msgstr "Sūtīt"
169
172
170#: qml/Dash/PullToRefreshScopeStyle.qml:55
173#: qml/Dash/PullToRefreshScopeStyle.qml:55
171msgid "Pull to refresh…"
174msgid "Pull to refresh…"
172−msgstr ""
175+msgstr "Vilt, lai atsvaidzinātu..."
173
176
174#: qml/Dash/PullToRefreshScopeStyle.qml:60
177#: qml/Dash/PullToRefreshScopeStyle.qml:60
175msgid "Release to refresh…"
178msgid "Release to refresh…"
176−msgstr ""
179+msgstr "Atlaist, lai atsvaidzinātu..."
177
180
178#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
181#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
179msgid "Enable location data"
182msgid "Enable location data"
180−msgstr ""
183+msgstr "Ieslēgt atrašanās vietas datus"
181
184
182#: qml/Dash/ScopesList.qml:66
185#: qml/Dash/ScopesList.qml:66
183msgid "Manage"
186msgid "Manage"
184−msgstr ""
187+msgstr "Pārvaldīt"
185
188
186#: qml/Dash/ScopesList.qml:112
189#: qml/Dash/ScopesList.qml:112
187msgid "Home"
190msgid "Home"
188−msgstr ""
191+msgstr "Mājas"
189
192
190#: qml/Dash/ScopesList.qml:113
193#: qml/Dash/ScopesList.qml:113
191msgid "Also installed"
194msgid "Also installed"
192−msgstr ""
195+msgstr "Arī instalēja"
193
196
194#: qml/Greeter/Greeter.qml:141
197#: qml/Greeter/Greeter.qml:141
195msgid "Unlock"
198msgid "Unlock"
196−msgstr ""
199+msgstr "Atbloķēt"
197
200
198#: qml/Notifications/NotificationMenuItemFactory.qml:128
201#: qml/Notifications/NotificationMenuItemFactory.qml:128
199msgid "Show password"
202msgid "Show password"
@@ -201,7 +204,7 @@ msgstr "Rādīt paroli"
201
204
202#: qml/Panel/ActiveCallHint.qml:79
205#: qml/Panel/ActiveCallHint.qml:79
203msgid "Tap to return to call..."
206msgid "Tap to return to call..."
204−msgstr ""
207+msgstr "Uzsitiet, lai atzvanītu..."
205
208
206#: qml/Panel/ActiveCallHint.qml:92
209#: qml/Panel/ActiveCallHint.qml:92
207msgid "Conference"
210msgid "Conference"
@@ -209,7 +212,7 @@ msgstr "Konference"
209
212
210#: qml/Panel/Indicators/MenuItemFactory.qml:643
213#: qml/Panel/Indicators/MenuItemFactory.qml:643
211msgid "Nothing is playing"
214msgid "Nothing is playing"
212−msgstr ""
215+msgstr "Nekas netiek atskaņots"
213
216
214#: qml/Panel/Indicators/MenuItemFactory.qml:791
217#: qml/Panel/Indicators/MenuItemFactory.qml:791
215msgid "In queue…"
218msgid "In queue…"
@@ -221,7 +224,7 @@ msgstr "Lejupielādē"
221
224
222#: qml/Panel/Indicators/MenuItemFactory.qml:797
225#: qml/Panel/Indicators/MenuItemFactory.qml:797
223msgid "Paused, tap to resume"
226msgid "Paused, tap to resume"
224−msgstr ""
227+msgstr "Pauzēts, uzsitiet, lai turpinātu"
225
228
226#: qml/Panel/Indicators/MenuItemFactory.qml:799
229#: qml/Panel/Indicators/MenuItemFactory.qml:799
227msgid "Canceled"
230msgid "Canceled"
@@ -233,11 +236,11 @@ msgstr "Pabeigts"
233
236
234#: qml/Panel/Indicators/MenuItemFactory.qml:803
237#: qml/Panel/Indicators/MenuItemFactory.qml:803
235msgid "Failed, tap to retry"
238msgid "Failed, tap to retry"
236−msgstr "Neizdevās, spiest lai mēģināt vēlreiz"
239+msgstr "Neizdevās, uzsitiet, lai mēģinātu vēlreiz"
237
240
238#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
241#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
239msgid "Reply"
242msgid "Reply"
240−msgstr ""
243+msgstr "Atbildēt"
241
244
242#: qml/Panel/Indicators/ModemInfoItem.qml:105
245#: qml/Panel/Indicators/ModemInfoItem.qml:105
243msgid "Unlock SIM"
246msgid "Unlock SIM"
@@ -250,40 +253,40 @@ msgstr "Viesabonēšana"
250#: qml/Shell.qml:402
253#: qml/Shell.qml:402
251#, qt-format
254#, qt-format
252msgid "Enter %1"
255msgid "Enter %1"
253−msgstr ""
256+msgstr "Ievadiet %1"
254
257
255#: qml/Shell.qml:403 qml/Wizard/Pages/passwd-set.qml:60
258#: qml/Shell.qml:403 qml/Wizard/Pages/passwd-set.qml:60
256msgid "Enter passphrase"
259msgid "Enter passphrase"
257−msgstr ""
260+msgstr "Ievadiet paroli"
258
261
259#: qml/Shell.qml:404
262#: qml/Shell.qml:404
260msgid "Enter passcode"
263msgid "Enter passcode"
261−msgstr ""
264+msgstr "Ievadiet piekļuves kodu"
262
265
263#: qml/Shell.qml:405
266#: qml/Shell.qml:405
264#, qt-format
267#, qt-format
265msgid "Sorry, incorrect %1"
268msgid "Sorry, incorrect %1"
266−msgstr ""
269+msgstr "Piedodiet, nepareizs %1"
267
270
268#: qml/Shell.qml:406
271#: qml/Shell.qml:406
269msgid "Sorry, incorrect passphrase"
272msgid "Sorry, incorrect passphrase"
270−msgstr ""
273+msgstr "Piedodiet, nepareiza parole"
271
274
272#: qml/Shell.qml:407
275#: qml/Shell.qml:407
273msgid "Please re-enter"
276msgid "Please re-enter"
274−msgstr ""
277+msgstr "Lūdzu, ievadiet vēlreiz"
275
278
276#: qml/Shell.qml:408
279#: qml/Shell.qml:408
277msgid "Sorry, incorrect passcode"
280msgid "Sorry, incorrect passcode"
278−msgstr ""
281+msgstr "Piedodiet, nepareizs piekļuves kods"
279
282
280#: qml/Shell.qml:502 qml/Wizard/Pages/passwd-confirm.qml:53
283#: qml/Shell.qml:502 qml/Wizard/Pages/passwd-confirm.qml:53
281msgid "Sorry, incorrect passphrase."
284msgid "Sorry, incorrect passphrase."
282−msgstr ""
285+msgstr "Piedodiet, nepareiza parole."
283
286
284#: qml/Shell.qml:503 qml/Wizard/Pages/passwd-confirm.qml:54
287#: qml/Shell.qml:503 qml/Wizard/Pages/passwd-confirm.qml:54
285msgid "Sorry, incorrect passcode."
288msgid "Sorry, incorrect passcode."
286−msgstr ""
289+msgstr "Piedodiet, nepareizs piekļuves kods."
287
290
288#: qml/Shell.qml:504
291#: qml/Shell.qml:504
289msgid "This will be your last attempt."
292msgid "This will be your last attempt."
@@ -294,159 +297,165 @@ msgid ""
294"If passphrase is entered incorrectly, your phone will conduct a factory "
297"If passphrase is entered incorrectly, your phone will conduct a factory "
295"reset and all personal data will be deleted."
298"reset and all personal data will be deleted."
296msgstr ""
299msgstr ""
300+"Ja parole netiks pareizi ievadīta, jūsu tālrunis tiks atstatīts uz rūpnīcas "
301+"iestatījumiem un visa personīgā informācija tiks izdzēsta."
297
302
298#: qml/Shell.qml:507
303#: qml/Shell.qml:507
299msgid ""
304msgid ""
300"If passcode is entered incorrectly, your phone will conduct a factory reset "
305"If passcode is entered incorrectly, your phone will conduct a factory reset "
301"and all personal data will be deleted."
306"and all personal data will be deleted."
302msgstr ""
307msgstr ""
308+"Ja piekļuves kods netiks pareizi ievadīts, jūsu tālrunis tiks atstatīts uz "
309+"rūpnīcas iestatījumiem un visa personīgā informācija tiks izdzēsta."
303
310
304#: qml/Tutorial/TutorialBottomFinish.qml:24
311#: qml/Tutorial/TutorialBottomFinish.qml:24
305msgid "This action does different things for different apps"
312msgid "This action does different things for different apps"
306−msgstr ""
313+msgstr "Šī darbība dažādās lietotnēs darīs dažādas lietas"
307
314
308#: qml/Tutorial/TutorialBottomFinish.qml:25
315#: qml/Tutorial/TutorialBottomFinish.qml:25
309msgid "Tap here to finish."
316msgid "Tap here to finish."
310−msgstr ""
317+msgstr "Uzsitiet šeit, lai pabeigtu."
311
318
312#: qml/Tutorial/TutorialBottom.qml:28
319#: qml/Tutorial/TutorialBottom.qml:28
313msgid "Open special menus"
320msgid "Open special menus"
314−msgstr ""
321+msgstr "Atvērt īpašas izvēlnes"
315
322
316#: qml/Tutorial/TutorialBottom.qml:29
323#: qml/Tutorial/TutorialBottom.qml:29
317msgid "Swipe up from the bottom edge."
324msgid "Swipe up from the bottom edge."
318−msgstr ""
325+msgstr "Velciet no apakšējās malas."
319
326
320#: qml/Tutorial/TutorialLeftFinish.qml:24
327#: qml/Tutorial/TutorialLeftFinish.qml:24
321msgid "These are the shortcuts to favorite apps"
328msgid "These are the shortcuts to favorite apps"
322−msgstr ""
329+msgstr "Šie ir īsceļi uz iecienītajām lietotnēm"
323
330
324#: qml/Tutorial/TutorialLeftFinish.qml:25 qml/Tutorial/TutorialRight.qml:144
331#: qml/Tutorial/TutorialLeftFinish.qml:25 qml/Tutorial/TutorialRight.qml:144
325msgid "Tap here to continue."
332msgid "Tap here to continue."
326−msgstr ""
333+msgstr "Uzsitiet šeit, lai turpinātu."
327
334
328#: qml/Tutorial/TutorialLeft.qml:26
335#: qml/Tutorial/TutorialLeft.qml:26
329msgid "Open the launcher"
336msgid "Open the launcher"
330−msgstr ""
337+msgstr "Atvērt palaidēju"
331
338
332#: qml/Tutorial/TutorialLeft.qml:27
339#: qml/Tutorial/TutorialLeft.qml:27
333msgid "Short swipe from the left edge."
340msgid "Short swipe from the left edge."
334−msgstr ""
341+msgstr "Īss vilciens no kreisās malas."
335
342
336#: qml/Tutorial/TutorialPage.qml:178
343#: qml/Tutorial/TutorialPage.qml:178
337msgid "You almost got it!"
344msgid "You almost got it!"
338−msgstr ""
345+msgstr "Gandrīz sanāca!"
339
346
340#: qml/Tutorial/TutorialPage.qml:195
347#: qml/Tutorial/TutorialPage.qml:195
341msgid "Try again."
348msgid "Try again."
342−msgstr ""
349+msgstr "Mēģiniet vēlreiz."
343
350
344#: qml/Tutorial/TutorialRight.qml:31
351#: qml/Tutorial/TutorialRight.qml:31
345msgid "To view open apps"
352msgid "To view open apps"
346−msgstr ""
353+msgstr "Lai redzētu atvērtās lietotnes"
347
354
348#: qml/Tutorial/TutorialRight.qml:32
355#: qml/Tutorial/TutorialRight.qml:32
349msgid "Long swipe from the right edge."
356msgid "Long swipe from the right edge."
350−msgstr ""
357+msgstr "Garš vilciens no labās malas."
351
358
352#: qml/Tutorial/TutorialRight.qml:122
359#: qml/Tutorial/TutorialRight.qml:122
353msgid "View all your running tasks."
360msgid "View all your running tasks."
354−msgstr ""
361+msgstr "Redzēt visus palaistos uzdevumus."
355
362
356#: qml/Wizard/Page.qml:89
363#: qml/Wizard/Page.qml:89
357msgid "Back"
364msgid "Back"
358−msgstr ""
365+msgstr "Atpakaļ"
359
366
360#: qml/Wizard/Pages/10-welcome.qml:27
367#: qml/Wizard/Pages/10-welcome.qml:27
361msgid "Hi!"
368msgid "Hi!"
362−msgstr ""
369+msgstr "Sveiki!"
363
370
364#: qml/Wizard/Pages/10-welcome.qml:44
371#: qml/Wizard/Pages/10-welcome.qml:44
365msgid "Welcome to your Ubuntu phone."
372msgid "Welcome to your Ubuntu phone."
366−msgstr ""
373+msgstr "Laipni lūdza Ubuntu viedtālrunī."
367
374
368#: qml/Wizard/Pages/10-welcome.qml:52
375#: qml/Wizard/Pages/10-welcome.qml:52
369msgid "Let’s get started."
376msgid "Let’s get started."
370−msgstr ""
377+msgstr "Sāksim."
371
378
372#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
379#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
373#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
380#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
374#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
381#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
375#: qml/Wizard/Pages/passwd-set.qml:90
382#: qml/Wizard/Pages/passwd-set.qml:90
376msgid "Continue"
383msgid "Continue"
377−msgstr ""
384+msgstr "Turpināt"
378
385
379#: qml/Wizard/Pages/20-sim.qml:25
386#: qml/Wizard/Pages/20-sim.qml:25
380msgid "Add a SIM card and restart your device"
387msgid "Add a SIM card and restart your device"
381−msgstr ""
388+msgstr "Pievienojiet SIM karti un pārstartējiet ierīci"
382
389
383#: qml/Wizard/Pages/20-sim.qml:55
390#: qml/Wizard/Pages/20-sim.qml:55
384msgid "Without it, you won’t be able to make calls or use text messaging."
391msgid "Without it, you won’t be able to make calls or use text messaging."
385−msgstr ""
392+msgstr "Bez tās jūs nevarēsiet piezvanīt vai sūtīt īsziņas."
386
393
387#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:215
394#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:215
388msgid "Skip"
395msgid "Skip"
389−msgstr ""
396+msgstr "Izlaist"
390
397
391#: qml/Wizard/Pages/30-passwd-type.qml:39
398#: qml/Wizard/Pages/30-passwd-type.qml:39
392msgid "Lock security"
399msgid "Lock security"
393−msgstr ""
400+msgstr "Bloķēšanas drošība"
394
401
395#: qml/Wizard/Pages/30-passwd-type.qml:74
402#: qml/Wizard/Pages/30-passwd-type.qml:74
396msgid "Please select how you’d like to unlock your phone."
403msgid "Please select how you’d like to unlock your phone."
397−msgstr ""
404+msgstr "Lūdzu, izvēlieties, kā vēlaties atbloķēt savu tālruni."
398
405
399#: qml/Wizard/Pages/30-passwd-type.qml:97
406#: qml/Wizard/Pages/30-passwd-type.qml:97
400msgid "Swipe"
407msgid "Swipe"
401−msgstr ""
408+msgstr "Vilkt"
402
409
403#: qml/Wizard/Pages/30-passwd-type.qml:98
410#: qml/Wizard/Pages/30-passwd-type.qml:98
404msgid "No security"
411msgid "No security"
405−msgstr ""
412+msgstr "Nav drošības"
406
413
407#: qml/Wizard/Pages/30-passwd-type.qml:100
414#: qml/Wizard/Pages/30-passwd-type.qml:100
408msgid "Passcode"
415msgid "Passcode"
409−msgstr ""
416+msgstr "Piekļuves kods"
410
417
411#: qml/Wizard/Pages/30-passwd-type.qml:101
418#: qml/Wizard/Pages/30-passwd-type.qml:101
412msgid "4 digits only"
419msgid "4 digits only"
413−msgstr ""
420+msgstr "Tikai četri cipari"
414
421
415#: qml/Wizard/Pages/30-passwd-type.qml:103
422#: qml/Wizard/Pages/30-passwd-type.qml:103
416msgid "Passphrase"
423msgid "Passphrase"
417−msgstr ""
424+msgstr "Parole"
418
425
419#: qml/Wizard/Pages/30-passwd-type.qml:104
426#: qml/Wizard/Pages/30-passwd-type.qml:104
420msgid "Numbers and letters"
427msgid "Numbers and letters"
421−msgstr ""
428+msgstr "Skaitļi un burti"
422
429
423#: qml/Wizard/Pages/40-wifi.qml:29
430#: qml/Wizard/Pages/40-wifi.qml:29
424msgid "Connect to Wi‑Fi"
431msgid "Connect to Wi‑Fi"
425−msgstr ""
432+msgstr "Savienoties ar Wi‑Fi"
426
433
427#: qml/Wizard/Pages/40-wifi.qml:168
434#: qml/Wizard/Pages/40-wifi.qml:168
428msgid "Available networks…"
435msgid "Available networks…"
429−msgstr ""
436+msgstr "Pieejamie tīkli…"
430
437
431#: qml/Wizard/Pages/40-wifi.qml:169
438#: qml/Wizard/Pages/40-wifi.qml:169
432msgid "No available networks."
439msgid "No available networks."
433−msgstr ""
440+msgstr "Nav pieejamu tīklu."
434
441
435#: qml/Wizard/Pages/50-location.qml:27
442#: qml/Wizard/Pages/50-location.qml:27
436msgid "Location"
443msgid "Location"
437−msgstr ""
444+msgstr "Atrašanās vieta"
438
445
439#: qml/Wizard/Pages/50-location.qml:62
446#: qml/Wizard/Pages/50-location.qml:62
440msgid "Let the phone detect your location:"
447msgid "Let the phone detect your location:"
441−msgstr ""
448+msgstr "Ļaut tālrunim noteikt jūsu atrašanās vietu:"
442
449
443#: qml/Wizard/Pages/50-location.qml:69
450#: qml/Wizard/Pages/50-location.qml:69
444msgid "Using GPS only (less accurate)"
451msgid "Using GPS only (less accurate)"
445−msgstr ""
452+msgstr "Izmantot tikai GPS (mazāk precīzi)"
446
453
447#: qml/Wizard/Pages/50-location.qml:86
454#: qml/Wizard/Pages/50-location.qml:86
448msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
455msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
449msgstr ""
456msgstr ""
457+"Izmantot GPS, anonimizētu bezvadu (Wi-Fi) un mobilā tīklā informāciju "
458+"(ieteicams)"
450
459
451#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
460#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
452#: qml/Wizard/Pages/50-location.qml:103
461#: qml/Wizard/Pages/50-location.qml:103
@@ -454,81 +463,85 @@ msgid ""
454"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
463"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
455"conditions</a>."
464"conditions</a>."
456msgstr ""
465msgstr ""
466+"Izvēloties šo opciju, jūs piekrītat Nokia HERE <a href='#'>nosacījumiem</a>."
457
467
458#: qml/Wizard/Pages/50-location.qml:112
468#: qml/Wizard/Pages/50-location.qml:112
459msgid "Not at all"
469msgid "Not at all"
460−msgstr ""
470+msgstr "Necik"
461
471
462#: qml/Wizard/Pages/50-location.qml:124
472#: qml/Wizard/Pages/50-location.qml:124
463msgid "You can change your mind later in <b>System Settings</b>."
473msgid "You can change your mind later in <b>System Settings</b>."
464−msgstr ""
474+msgstr "Vēlāk jūs varat to mainīt <b>Sistēmas iestatījumos</b>."
465
475
466#: qml/Wizard/Pages/60-reporting.qml:24
476#: qml/Wizard/Pages/60-reporting.qml:24
467msgid "Improving your experience"
477msgid "Improving your experience"
468−msgstr ""
478+msgstr "Jūsu pieredzes uzlabošana"
469
479
470#: qml/Wizard/Pages/60-reporting.qml:36
480#: qml/Wizard/Pages/60-reporting.qml:36
471msgid ""
481msgid ""
472"Your phone is set up to automatically report errors to Canonical and its "
482"Your phone is set up to automatically report errors to Canonical and its "
473"partners, the makers of the operating system."
483"partners, the makers of the operating system."
474msgstr ""
484msgstr ""
485+"Jūsu tālrunis automātiski sūtīts kļūdu ziņojumus Canonical un tā partneriem "
486+"— šīs operētājsistēmas izstrādātājiem."
475
487
476#: qml/Wizard/Pages/60-reporting.qml:43
488#: qml/Wizard/Pages/60-reporting.qml:43
477msgid ""
489msgid ""
478"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
490"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
479"Privacy</b>"
491"Privacy</b>"
480msgstr ""
492msgstr ""
493+"To varat atslēgt <b>Sistēmas iestatījumos</b> zem <b>Drošība un privātums</b>"
481
494
482#: qml/Wizard/Pages/80-finished.qml:24
495#: qml/Wizard/Pages/80-finished.qml:24
483msgid "All done"
496msgid "All done"
484−msgstr ""
497+msgstr "Viss paveikts"
485
498
486#: qml/Wizard/Pages/80-finished.qml:39
499#: qml/Wizard/Pages/80-finished.qml:39
487msgid "Nice work!"
500msgid "Nice work!"
488−msgstr ""
501+msgstr "Lieliski!"
489
502
490#: qml/Wizard/Pages/80-finished.qml:46
503#: qml/Wizard/Pages/80-finished.qml:46
491msgid "Your phone is now ready to use."
504msgid "Your phone is now ready to use."
492−msgstr ""
505+msgstr "Jūsu tālrunis tagad ir gatavs lietošanai."
493
506
494#: qml/Wizard/Pages/80-finished.qml:53
507#: qml/Wizard/Pages/80-finished.qml:53
495msgid "Finish"
508msgid "Finish"
496−msgstr ""
509+msgstr "Pabeigt"
497
510
498#: qml/Wizard/Pages/here-terms.qml:27
511#: qml/Wizard/Pages/here-terms.qml:27
499msgid "Terms & Conditions"
512msgid "Terms & Conditions"
500−msgstr ""
513+msgstr "Nosacījumi"
501
514
502#: qml/Wizard/Pages/passwd-confirm.qml:49
515#: qml/Wizard/Pages/passwd-confirm.qml:49
503msgid "Confirm passphrase"
516msgid "Confirm passphrase"
504−msgstr ""
517+msgstr "Apstipriniet paroli"
505
518
506#: qml/Wizard/Pages/passwd-confirm.qml:50
519#: qml/Wizard/Pages/passwd-confirm.qml:50
507msgid "Confirm passcode"
520msgid "Confirm passcode"
508−msgstr ""
521+msgstr "Apstipriniet piekļuves kodu"
509
522
510#: qml/Wizard/Pages/passwd-confirm.qml:53
523#: qml/Wizard/Pages/passwd-confirm.qml:53
511#: qml/Wizard/Pages/passwd-confirm.qml:54
524#: qml/Wizard/Pages/passwd-confirm.qml:54
512msgid "Please try again."
525msgid "Please try again."
513−msgstr ""
526+msgstr "Lūdzu, mēģiniet vēlreiz."
514
527
515#: qml/Wizard/Pages/passwd-set.qml:61
528#: qml/Wizard/Pages/passwd-set.qml:61
516msgid "Choose your passcode"
529msgid "Choose your passcode"
517−msgstr ""
530+msgstr "Izvēlieties savu paroli"
518
531
519#: qml/Wizard/Pages/passwd-set.qml:68
532#: qml/Wizard/Pages/passwd-set.qml:68
520msgid "Passphrase must be 4 characters long"
533msgid "Passphrase must be 4 characters long"
521−msgstr ""
534+msgstr "Piekļuves kodam ir jābūt 4 rakstzīmju garumā"
522
535
523#: qml/Wizard/StackButton.qml:39
536#: qml/Wizard/StackButton.qml:39
524#, qt-format
537#, qt-format
525msgid "〈 %1"
538msgid "〈 %1"
526−msgstr ""
539+msgstr "〈 %1"
527
540
528#: qml/Wizard/StackButton.qml:42
541#: qml/Wizard/StackButton.qml:42
529#, qt-format
542#, qt-format
530msgid "%1 〉"
543msgid "%1 〉"
531−msgstr ""
544+msgstr "%1 〉"
532
545
533#~ msgid "Confirm"
546#~ msgid "Confirm"
534#~ msgstr "Apstiprināt"
547#~ msgstr "Apstiprināt"
po/nl.po+5−3
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
10"POT-Creation-Date: 2015-02-13 11:45+0100\n"
11−"PO-Revision-Date: 2015-02-20 12:47+0000\n"
11+"PO-Revision-Date: 2015-02-26 10:21+0000\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
13"Language-Team: Dutch <nl@li.org>\n"
13"Language-Team: Dutch <nl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-02-21 05:35+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-27 05:54+0000\n"
19−"X-Generator: Launchpad (build 17355)\n"
19+"X-Generator: Launchpad (build 17361)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -314,6 +314,8 @@ msgstr ""
314#: qml/Tutorial/TutorialBottomFinish.qml:24
314#: qml/Tutorial/TutorialBottomFinish.qml:24
315msgid "This action does different things for different apps"
315msgid "This action does different things for different apps"
316msgstr ""
316msgstr ""
317+"Deze actie doet verschillende dingen afhankelijk van de app die gebruikt "
318+"wordt"
317
319
318#: qml/Tutorial/TutorialBottomFinish.qml:25
320#: qml/Tutorial/TutorialBottomFinish.qml:25
319msgid "Tap here to finish."
321msgid "Tap here to finish."
qml/Components/DelayedLockscreen.qml+3−1
@@ -20,11 +20,13 @@ import "../Components"
20
20
21Item {
21Item {
22 id: root
22 id: root
23− anchors.fill: parent
24
23
25 property int delayMinutes
24 property int delayMinutes
26 property bool alphaNumeric
25 property bool alphaNumeric
27
26
27+ signal entered(string passphrase) // unused
28+ signal cancel() // unused
29+
28 function clear(playAnimation) {}
30 function clear(playAnimation) {}
29
31
30 Column {
32 Column {
qml/Components/PassphraseLockscreen.qml+2−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2014,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -20,8 +20,7 @@ import "../Components"
20
20
21Item {
21Item {
22 id: root
22 id: root
23− anchors.top: parent.top
24− anchors.topMargin: units.gu(4)
23+ y: units.gu(4)
25 height: shakeContainer.height
24 height: shakeContainer.height
26
25
27 property string infoText
26 property string infoText
qml/Components/PinLockscreen.qml+3−4
@@ -21,9 +21,7 @@ import "../Components"
21
21
22Column {
22Column {
23 id: root
23 id: root
24− anchors.top: parent.top
25− anchors.topMargin: units.gu(4)
24+ y: units.gu(4)
26− anchors.horizontalCenter: parent.horizontalCenter
27 spacing: units.gu(2)
25 spacing: units.gu(2)
28
26
29 property string infoText
27 property string infoText
@@ -123,6 +121,7 @@ Column {
123 objectName: "backspaceIcon"
121 objectName: "backspaceIcon"
124 anchors { right: parent.right; top: parent.top; bottom: parent.bottom }
122 anchors { right: parent.right; top: parent.top; bottom: parent.bottom }
125 width: height
123 width: height
124+ enabled: root.entryEnabled
126
125
127 Icon {
126 Icon {
128 anchors.fill: parent
127 anchors.fill: parent
@@ -130,7 +129,7 @@ Column {
130 color: "#f3f3e7"
129 color: "#f3f3e7"
131 }
130 }
132
131
133− opacity: (pinentryField.text.length && !pinentryField.incorrectOverride) > 0 ? 1 : 0
132+ opacity: (pinentryField.text.length > 0 && !pinentryField.incorrectOverride) ? 1 : 0
134
133
135 Behavior on opacity {
134 Behavior on opacity {
136 UbuntuNumberAnimation {}
135 UbuntuNumberAnimation {}
qml/Components/ZoomableImage.qml+14−14
@@ -107,20 +107,6 @@ Item {
107 }
107 }
108 }
108 }
109
109
110− PinchArea {
111− id: pinchArea
112− objectName: "pinchArea"
113− property real minScale: 1.0
114− anchors.fill: parent
115− enabled: zoomable ? zoomable : false
116−
117− pinch.target: image
118− pinch.minimumScale: minScale
119− pinch.maximumScale: 10
120−
121− onPinchFinished: flickable.returnToBounds()
122− }
123−
124 MouseArea {
110 MouseArea {
125 id: mouseArea
111 id: mouseArea
126 objectName: "mouseArea"
112 objectName: "mouseArea"
@@ -153,4 +139,18 @@ Item {
153 }
139 }
154 }
140 }
155 }
141 }
142+
143+ PinchArea {
144+ id: pinchArea
145+ objectName: "pinchArea"
146+ property real minScale: 1.0
147+ anchors.fill: flickable
148+ enabled: zoomable ? zoomable : false
149+
150+ pinch.target: image
151+ pinch.minimumScale: minScale
152+ pinch.maximumScale: 10
153+
154+ onPinchFinished: flickable.returnToBounds()
155+ }
156}
156}
qml/Dash/Dash.qml+2−1
@@ -300,10 +300,11 @@ Showable {
300 }
300 }
301
301
302 Image {
302 Image {
303+ objectName: "overviewHint"
303 source: "graphics/overview_hint.png"
304 source: "graphics/overview_hint.png"
304 anchors.horizontalCenter: parent.horizontalCenter
305 anchors.horizontalCenter: parent.horizontalCenter
305 opacity: !scopeItem.scope && (scopes.count == 0 || dashContent.pageHeaderTotallyVisible) &&
306 opacity: !scopeItem.scope && (scopes.count == 0 || dashContent.pageHeaderTotallyVisible) &&
306− (overviewDragHandle.enabled || overviewDragHandle.status.progress != 0) ? 1 : 0
307+ (overviewDragHandle.enabled || bottomEdgeController.progress != 0) ? 1 : 0
307 Behavior on opacity {
308 Behavior on opacity {
308 enabled: bottomEdgeController.progress == 0
309 enabled: bottomEdgeController.progress == 0
309 UbuntuNumberAnimation {}
310 UbuntuNumberAnimation {}
qml/Dash/GenericScopeView.qml+2−1
@@ -19,6 +19,7 @@ import Ubuntu.Components 1.1
19import Utils 0.1
19import Utils 0.1
20import Unity 0.2
20import Unity 0.2
21import Dash 0.1
21import Dash 0.1
22+import Powerd 0.1
22import "../Components"
23import "../Components"
23import "../Components/ListItems" as ListItems
24import "../Components/ListItems" as ListItems
24
25
@@ -104,7 +105,7 @@ FocusScope {
104 Binding {
105 Binding {
105 target: scope
106 target: scope
106 property: "isActive"
107 property: "isActive"
107− value: isCurrent && !subPageLoader.open
108+ value: isCurrent && !subPageLoader.open && (Powerd.status === Powerd.On)
108 }
109 }
109
110
110 UnitySortFilterProxyModel {
111 UnitySortFilterProxyModel {
qml/Greeter/CoverPage.qmlnew file+216
@@ -0,0 +1,216 @@
1+/*
2+ * Copyright (C) 2013,2014,2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import "../Components"
21+
22+Showable {
23+ id: root
24+
25+ property real dragHandleLeftMargin
26+ property real launcherOffset
27+ property alias background: greeterBackground.source
28+ property real backgroundTopMargin
29+ property var infographicModel
30+ property bool draggable: true
31+
32+ property alias infographics: infographics
33+
34+ readonly property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
35+
36+ signal tease()
37+
38+ function hideRight() {
39+ d.forceRightOnNextHideAnimation = true;
40+ hide();
41+ }
42+
43+ QtObject {
44+ id: d
45+ property bool forceRightOnNextHideAnimation: false
46+ }
47+
48+ prepareToHide: function () {
49+ hideTranslation.from = root.x + translation.x
50+ hideTranslation.to = root.x > 0 || d.forceRightOnNextHideAnimation ? root.width : -root.width;
51+ d.forceRightOnNextHideAnimation = false;
52+ }
53+
54+ // We don't directly bind "x" because that's owned by the DragHandle. So
55+ // instead, we can get a little extra horizontal push by using transforms.
56+ transform: Translate { id: translation; x: root.draggable ? launcherOffset : 0 }
57+
58+ MouseArea { anchors.fill: parent; }
59+
60+ Rectangle {
61+ // In case background fails to load
62+ id: backgroundBackup
63+ anchors.fill: parent
64+ color: "black"
65+ }
66+
67+ CrossFadeImage {
68+ id: greeterBackground
69+ objectName: "greeterBackground"
70+ anchors {
71+ fill: parent
72+ topMargin: root.backgroundTopMargin
73+ }
74+ fillMode: Image.PreserveAspectCrop
75+ // Limit how much memory we'll reserve for this image
76+ sourceSize.height: height
77+ sourceSize.width: width
78+ }
79+
80+ Rectangle {
81+ anchors.fill: parent
82+ color: "black"
83+ opacity: 0.4
84+ }
85+
86+ Infographics {
87+ id: infographics
88+ objectName: "infographics"
89+ height: parent.height
90+ model: root.infographicModel
91+ clip: true // clip large data bubbles
92+
93+ anchors {
94+ verticalCenter: parent.verticalCenter
95+ left: parent.left
96+ right: parent.right
97+ }
98+ }
99+
100+ Label {
101+ id: swipeHint
102+ property real baseOpacity: 0.5
103+ opacity: 0.0
104+ anchors.horizontalCenter: parent.horizontalCenter
105+ anchors.bottom: parent.bottom
106+ anchors.bottomMargin: units.gu(5)
107+ text: "《 " + i18n.tr("Unlock") + " 》"
108+ color: "white"
109+ font.weight: Font.Light
110+
111+ SequentialAnimation on opacity {
112+ id: showLabelAnimation
113+ running: false
114+ loops: 2
115+
116+ StandardAnimation {
117+ from: 0.0
118+ to: swipeHint.baseOpacity
119+ duration: UbuntuAnimation.SleepyDuration
120+ }
121+ PauseAnimation { duration: UbuntuAnimation.BriskDuration }
122+ StandardAnimation {
123+ from: swipeHint.baseOpacity
124+ to: 0.0
125+ duration: UbuntuAnimation.SleepyDuration
126+ }
127+ }
128+ }
129+
130+ DragHandle {
131+ id: dragHandle
132+ anchors.fill: parent
133+ anchors.leftMargin: root.dragHandleLeftMargin
134+ enabled: root.draggable
135+ direction: Direction.Horizontal
136+
137+ onDraggingChanged: {
138+ if (dragging) {
139+ root.tease();
140+ showLabelAnimation.start();
141+ }
142+ }
143+ }
144+
145+ // right side shadow
146+ Image {
147+ anchors.left: parent.right
148+ anchors.top: parent.top
149+ anchors.bottom: parent.bottom
150+ fillMode: Image.Tile
151+ source: "../graphics/dropshadow_right.png"
152+ }
153+
154+ // left side shadow
155+ Image {
156+ anchors.right: parent.left
157+ anchors.top: parent.top
158+ anchors.bottom: parent.bottom
159+ fillMode: Image.Tile
160+ source: "../graphics/dropshadow_left.png"
161+ }
162+
163+ Binding {
164+ id: positionLock
165+
166+ property bool enabled: false
167+ onEnabledChanged: {
168+ if (enabled === __enabled) {
169+ return;
170+ }
171+
172+ if (enabled) {
173+ if (root.x > 0) {
174+ value = Qt.binding(function() { return root.width; })
175+ } else {
176+ value = Qt.binding(function() { return -root.width; })
177+ }
178+ }
179+
180+ __enabled = enabled;
181+ }
182+
183+ property bool __enabled: false
184+
185+ target: root
186+ when: __enabled
187+ property: "x"
188+ }
189+
190+ hideAnimation: SequentialAnimation {
191+ id: hideAnimation
192+ objectName: "hideAnimation"
193+ property var target // unused, here to silence Showable warning
194+ StandardAnimation {
195+ id: hideTranslation
196+ property: "x"
197+ target: root
198+ }
199+ PropertyAction { target: root; property: "visible"; value: false }
200+ PropertyAction { target: positionLock; property: "enabled"; value: true }
201+ }
202+
203+ showAnimation: SequentialAnimation {
204+ id: showAnimation
205+ objectName: "showAnimation"
206+ property var target // unused, here to silence Showable warning
207+ PropertyAction { target: root; property: "visible"; value: true }
208+ PropertyAction { target: positionLock; property: "enabled"; value: false }
209+ StandardAnimation {
210+ property: "x"
211+ target: root
212+ to: 0
213+ duration: UbuntuAnimation.FastDuration
214+ }
215+ }
216+}
qml/Greeter/Greeter.qml+319−147
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2014,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,66 +14,185 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.3
18−import Ubuntu.Components 0.1
18+import AccountsService 0.1
19−import Ubuntu.Gestures 0.1
20import LightDM 0.1 as LightDM
19import LightDM 0.1 as LightDM
20+import Ubuntu.Components 1.1
21+import Ubuntu.SystemImage 0.1
22+import Unity.Connectivity 0.1
23+import Unity.Launcher 0.1
21import "../Components"
24import "../Components"
22
25
23Showable {
26Showable {
24− id: greeter
27+ id: root
25− enabled: shown
26− created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
28+ created: loader.status == Loader.Ready
27
29
28 property real dragHandleLeftMargin: 0
30 property real dragHandleLeftMargin: 0
29− property alias dragging: dragHandle.dragging
30
31
31 property url background
32 property url background
32
33
33− // so that it can be replaced in tests with a mock object
34+ // How far to offset the top greeter layer during a launcher left-drag
34− property var inputMethod: Qt.inputMethod
35+ property real launcherOffset
35
36
36− prepareToHide: function () {
37+ readonly property bool active: shown || hasLockedApp
37− hideTranslation.to = greeter.x > 0 || d.forceRightOnNextHideAnimation ? greeter.width : -greeter.width;
38+ readonly property bool fullyShown: loader.item ? loader.item.fullyShown : false
38− d.forceRightOnNextHideAnimation = false;
39+
40+ // True when the greeter is waiting for PAM or other setup process
41+ readonly property alias waiting: d.waiting
42+
43+ property string lockedApp: ""
44+ readonly property bool hasLockedApp: lockedApp !== ""
45+
46+ property bool forcedUnlock
47+ readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
48+
49+ property bool tabletMode
50+ property url viewSource // only used for testing
51+
52+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
53+ property int failedLoginsDelayAttempts: 7 // number of failed logins
54+ property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
55+
56+ signal tease()
57+ signal sessionStarted()
58+ signal emergencyCall()
59+
60+ function forceShow() {
61+ showNow();
62+ loader.item.reset();
39 }
63 }
40
64
41− QtObject {
65+ function notifyAppFocused(appId) {
42− id: d
66+ if (!active) {
43− property bool forceRightOnNextHideAnimation: false
67+ return;
68+ }
69+
70+ if (hasLockedApp) {
71+ if (appId === lockedApp) {
72+ hide(); // show locked app
73+ } else {
74+ show();
75+ d.startUnlock(false /* toTheRight */);
76+ }
77+ } else if (appId !== "unity8-dash") { // dash isn't started by user
78+ d.startUnlock(false /* toTheRight */);
79+ }
44 }
80 }
45
81
46− property bool loadContent: required
82+ function notifyAboutToFocusApp(appId) {
83+ if (!active) {
84+ return;
85+ }
47
86
48− // 1 when fully shown and 0 when fully hidden
87+ // A hint that we're about to focus an app. This way we can look
49− property real showProgress: visible ? MathUtils.clamp((width - Math.abs(x)) / width, 0, 1) : 0
88+ // a little more responsive, rather than waiting for the above
89+ // notifyAppFocused call. We also need this in case we have a locked
90+ // app, in order to show lockscreen instead of new app.
91+ d.startUnlock(false /* toTheRight */);
92+ }
50
93
51− property alias model: greeterContentLoader.model
94+ // This is a just a glorified notifyAboutToFocusApp(), but it does one
52− property bool locked: true
95+ // other thing: it hides any cover pages to the RIGHT, because the user
96+ // just came from a launcher drag starting on the left.
97+ // It also returns a boolean value, indicating whether there was a visual
98+ // change or not (the shell only wants to hide the launcher if there was
99+ // a change).
100+ function notifyShowingDashFromDrag() {
101+ if (!active) {
102+ return false;
103+ }
53
104
54− readonly property bool narrowMode: !multiUser && height > width
105+ return d.startUnlock(true /* toTheRight */);
55− readonly property bool multiUser: LightDM.Users.count > 1
106+ }
56
107
57− readonly property int currentIndex: greeterContentLoader.currentIndex
108+ QtObject {
109+ id: d
58
110
59− signal selected(int uid)
111+ readonly property bool multiUser: LightDM.Users.count > 1
60− signal unlocked(int uid)
112+ property int currentIndex
61− signal tapped()
113+ property int delayMinutes
114+ property bool waiting
115+
116+ // We want 'launcherOffset' to animate down to zero. But not to animate
117+ // while being dragged. So ideally we change this only when the user
118+ // lets go and launcherOffset drops to zero. But we need to wait for
119+ // the behavior to be enabled first. So we cache the last known good
120+ // launcherOffset value to cover us during that brief gap between
121+ // release and the behavior turning on.
122+ property real lastKnownPositiveOffset // set in a launcherOffsetChanged below
123+ property real launcherOffsetProxy: (shown && !launcherOffsetProxyBehavior.enabled) ? lastKnownPositiveOffset : 0
124+ Behavior on launcherOffsetProxy {
125+ id: launcherOffsetProxyBehavior
126+ enabled: launcherOffset === 0
127+ UbuntuNumberAnimation {}
128+ }
129+
130+ function selectUser(uid, reset) {
131+ d.waiting = true;
132+ if (reset) {
133+ loader.item.reset();
134+ }
135+ currentIndex = uid;
136+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
137+ AccountsService.user = user;
138+ LauncherModel.setUser(user);
139+ LightDM.Greeter.authenticate(user); // always resets auth state
140+ }
141+
142+ function login() {
143+ enabled = false;
144+ if (LightDM.Greeter.startSessionSync()) {
145+ sessionStarted();
146+ loader.item.notifyAuthenticationSucceeded();
147+ } else {
148+ loader.item.notifyAuthenticationFailed();
149+ }
150+ enabled = true;
151+ }
62
152
63− function hideRight() {
153+ function startUnlock(toTheRight) {
64− d.forceRightOnNextHideAnimation = true;
154+ if (loader.item) {
65− hide();
155+ return loader.item.tryToUnlock(toTheRight);
156+ } else {
157+ return false;
158+ }
159+ }
66 }
160 }
67
161
68− function tryToUnlock() {
162+ onLauncherOffsetChanged: {
69− if (created) {
163+ if (launcherOffset > 0) {
70− greeterContentLoader.item.tryToUnlock()
164+ d.lastKnownPositiveOffset = launcherOffset;
71 }
165 }
72 }
166 }
73
167
74− function reset() {
168+ onForcedUnlockChanged: {
75− if (created) {
169+ if (forcedUnlock && shown) {
76− greeterContentLoader.item.reset()
170+ // pretend we were just authenticated
171+ loader.item.notifyAuthenticationSucceeded();
172+ }
173+ }
174+
175+ onRequiredChanged: {
176+ if (required) {
177+ d.waiting = true;
178+ lockedApp = "";
179+ }
180+ }
181+
182+ Component.onCompleted: {
183+ Connectivity.unlockAllModems();
184+ }
185+
186+ Timer {
187+ id: forcedDelayTimer
188+ interval: 1000 * 60
189+ onTriggered: {
190+ if (d.delayMinutes > 0) {
191+ d.delayMinutes -= 1;
192+ if (d.delayMinutes > 0) {
193+ start(); // go again
194+ }
195+ }
77 }
196 }
78 }
197 }
79
198
@@ -82,152 +201,205 @@ Showable {
82 MouseArea { anchors.fill: parent }
201 MouseArea { anchors.fill: parent }
83
202
84 Loader {
203 Loader {
85− id: greeterContentLoader
204+ id: loader
86− objectName: "greeterContentLoader"
205+ objectName: "loader"
206+
87 anchors.fill: parent
207 anchors.fill: parent
88− property var model: LightDM.Users
89− property int currentIndex: 0
90− property var infographicModel: LightDM.Infographic
91− readonly property int backgroundTopMargin: -greeter.y
92
208
93− source: loadContent ? "GreeterContent.qml" : ""
209+ active: root.required
210+ source: root.viewSource.toString() ? root.viewSource :
211+ (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml"
94
212
95 onLoaded: {
213 onLoaded: {
214+ root.lockedApp = "";
215+ root.forceActiveFocus();
96− selected(currentIndex);
216+ d.selectUser(d.currentIndex, true);
217+ LightDM.Infographic.readyForDataChange();
97 }
218 }
98
219
99 Connections {
220 Connections {
100− target: greeterContentLoader.item
221+ target: loader.item
101−
102 onSelected: {
222 onSelected: {
103− greeter.selected(uid);
223+ d.selectUser(index, true);
104− greeterContentLoader.currentIndex = uid;
224+ }
225+ onResponded: {
226+ if (root.locked) {
227+ LightDM.Greeter.respond(response);
228+ } else {
229+ if (LightDM.Greeter.active && !LightDM.Greeter.authenticated) { // could happen if forcedUnlock
230+ d.login();
231+ }
232+ loader.item.hide();
233+ }
234+ }
235+ onTease: root.tease()
236+ onEmergencyCall: root.emergencyCall()
237+ onRequiredChanged: {
238+ if (!loader.item.required) {
239+ root.hide();
240+ }
105 }
241 }
106− onUnlocked: greeter.unlocked(uid);
107 }
242 }
243+
108 Binding {
244 Binding {
109− target: greeterContentLoader.item
245+ target: loader.item
110− property: "inputMethod"
246+ property: "backgroundTopMargin"
111− value: greeter.inputMethod
247+ value: -root.y
112 }
248 }
113− }
114
249
115− DragHandle {
250+ Binding {
116− id: dragHandle
251+ target: loader.item
117− anchors.fill: parent
252+ property: "launcherOffset"
118− anchors.leftMargin: greeter.dragHandleLeftMargin
253+ value: d.launcherOffsetProxy
119− enabled: (greeter.narrowMode || !greeter.locked) && greeter.enabled && greeter.shown
254+ }
120− direction: Direction.Horizontal
121
255
122− onTapped: {
256+ Binding {
123− greeter.tapped();
257+ target: loader.item
124− showLabelAnimation.start();
258+ property: "dragHandleLeftMargin"
259+ value: root.dragHandleLeftMargin
125 }
260 }
126
261
127− onDraggingChanged: {
262+ Binding {
128− if (dragging) {
263+ target: loader.item
129− showLabelAnimation.start();
264+ property: "delayMinutes"
265+ value: d.delayMinutes
266+ }
267+
268+ Binding {
269+ target: loader.item
270+ property: "background"
271+ value: root.background
272+ }
273+
274+ Binding {
275+ target: loader.item
276+ property: "locked"
277+ value: root.locked
130− }
278+ }
279+
280+ Binding {
281+ target: loader.item
282+ property: "alphanumeric"
283+ value: AccountsService.passwordDisplayHint === AccountsService.Keyboard
284+ }
285+
286+ Binding {
287+ target: loader.item
288+ property: "currentIndex"
289+ value: d.currentIndex
290+ }
291+
292+ Binding {
293+ target: loader.item
294+ property: "userModel"
295+ value: LightDM.Users
296+ }
297+
298+ Binding {
299+ target: loader.item
300+ property: "infographicModel"
301+ value: LightDM.Infographic
131 }
302 }
132 }
303 }
133
304
134− Label {
305+ Connections {
135− id: swipeHint
306+ target: LightDM.Greeter
136− property real baseOpacity: 0.5
307+
137− opacity: 0.0
308+ onShowGreeter: root.forceShow()
138− anchors.horizontalCenter: parent.horizontalCenter
309+
139− anchors.bottom: parent.bottom
310+ onHideGreeter: {
140− anchors.bottomMargin: units.gu(5)
311+ d.login();
141− text: "《 " + i18n.tr("Unlock") + " 》"
312+ loader.item.hide();
142− color: "white"
313+ }
143− font.weight: Font.Light
314+
144−
315+ onShowMessage: {
145− SequentialAnimation on opacity {
316+ if (!LightDM.Greeter.active) {
146− id: showLabelAnimation
317+ return; // could happen if hideGreeter() comes in before we prompt
147− running: false
318+ }
148− loops: 2
319+
149−
320+ // inefficient, but we only rarely deal with messages
150− StandardAnimation {
321+ var html = text.replace(/&/g, "&amp;")
151− from: 0.0
322+ .replace(/</g, "&lt;")
152− to: swipeHint.baseOpacity
323+ .replace(/>/g, "&gt;")
153− duration: UbuntuAnimation.SleepyDuration
324+ .replace(/\n/g, "<br>");
325+ if (isError) {
326+ html = "<font color=\"#df382c\">" + html + "</font>";
154 }
327 }
155− PauseAnimation { duration: UbuntuAnimation.BriskDuration }
328+
156− StandardAnimation {
329+ loader.item.showMessage(html);
157− from: swipeHint.baseOpacity
330+ }
158− to: 0.0
331+
159− duration: UbuntuAnimation.SleepyDuration
332+ onShowPrompt: {
333+ d.waiting = false;
334+
335+ if (!LightDM.Greeter.active) {
336+ return; // could happen if hideGreeter() comes in before we prompt
160 }
337 }
338+
339+ loader.item.showPrompt(text, isSecret, isDefaultPrompt);
161 }
340 }
162− }
163
341
164− // right side shadow
342+ onAuthenticationComplete: {
165− Image {
343+ d.waiting = false;
166− anchors.left: parent.right
167− anchors.top: parent.top
168− anchors.bottom: parent.bottom
169− fillMode: Image.Tile
170− source: "../graphics/dropshadow_right.png"
171− }
172
344
173− // left side shadow
345+ if (LightDM.Greeter.authenticated) {
174− Image {
346+ AccountsService.failedLogins = 0;
175− anchors.right: parent.left
347+ d.login();
176− anchors.top: parent.top
348+ if (!LightDM.Greeter.promptless) {
177− anchors.bottom: parent.bottom
349+ loader.item.hide();
178− fillMode: Image.Tile
350+ }
179− source: "../graphics/dropshadow_left.png"
351+ } else {
352+ if (!LightDM.Greeter.promptless) {
353+ AccountsService.failedLogins++;
180− }
354+ }
181
355
182− Binding {
356+ // Check if we should initiate a factory reset
183− id: positionLock
357+ if (maxFailedLogins >= 2) { // require at least a warning
358+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
359+ loader.item.showLastChance();
360+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
361+ SystemImage.factoryReset(); // Ouch!
362+ }
363+ }
184
364
185− property bool enabled: false
365+ // Check if we should initiate a forced login delay
186− onEnabledChanged: {
366+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
187− if (enabled === __enabled) {
367+ d.delayMinutes = failedLoginsDelayMinutes;
188− return;
368+ forcedDelayTimer.start();
189− }
369+ }
190
370
191− if (enabled) {
371+ loader.item.notifyAuthenticationFailed();
192− if (greeter.x > 0) {
372+ if (!LightDM.Greeter.promptless) {
193− value = Qt.binding(function() { return greeter.width; })
373+ d.selectUser(d.currentIndex, false);
194− } else {
195− value = Qt.binding(function() { return -greeter.width; })
196 }
374 }
197 }
375 }
198−
199− __enabled = enabled;
200 }
376 }
201
377
202− property bool __enabled: false
378+ onRequestAuthenticationUser: {
379+ // Find index for requested user, if it exists
380+ for (var i = 0; i < LightDM.Users.count; i++) {
381+ if (user === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
382+ d.selectUser(i, true);
383+ return;
384+ }
385+ }
386+ }
387+ }
203
388
204− target: greeter
389+ Binding {
205− when: __enabled
390+ target: LightDM.Greeter
206− property: "x"
391+ property: "active"
392+ value: root.active
207 }
393 }
208
394
209− hideAnimation: SequentialAnimation {
395+ Binding {
210− id: hideAnimation
396+ target: LightDM.Infographic
211− objectName: "hideAnimation"
212− StandardAnimation {
213− id: hideTranslation
214− property: "x"
397+ property: "username"
215− target: greeter
398+ value: AccountsService.statsWelcomeScreen ? LightDM.Users.data(d.currentIndex, LightDM.UserRoles.NameRole) : ""
216− }
217− PropertyAction { target: greeter; property: "visible"; value: false }
218− PropertyAction { target: positionLock; property: "enabled"; value: true }
219 }
399 }
220
400
221− showAnimation: SequentialAnimation {
401+ Connections {
222− id: showAnimation
402+ target: i18n
223− objectName: "showAnimation"
403+ onLanguageChanged: LightDM.Infographic.readyForDataChange()
224− PropertyAction { target: greeter; property: "visible"; value: true }
225− PropertyAction { target: positionLock; property: "enabled"; value: false }
226− StandardAnimation {
227− property: "x"
228− target: greeter
229− to: 0
230− duration: UbuntuAnimation.FastDuration
231− }
232 }
404 }
233}
405}
qml/Greeter/GreeterContent.qmldeleted−163
@@ -1,163 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import AccountsService 0.1
19−import Ubuntu.Components 0.1
20−import LightDM 0.1 as LightDM
21−import "../Components"
22−
23−Item {
24− id: root
25− anchors.fill: parent
26−
27− property var inputMethod
28−
29− property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
30−
31− signal selected(int uid)
32− signal unlocked(int uid)
33−
34− function tryToUnlock() {
35− if (loginLoader.item) {
36− loginLoader.item.tryToUnlock()
37− }
38− }
39−
40− function reset() {
41− if (loginLoader.item) {
42− loginLoader.item.reset()
43− }
44− }
45−
46− Rectangle {
47− // In case background fails to load
48− id: backgroundBackup
49− anchors.fill: parent
50− color: "black"
51− }
52−
53− CrossFadeImage {
54− id: background
55− objectName: "greeterBackground"
56− anchors {
57− fill: parent
58− topMargin: backgroundTopMargin
59− }
60− fillMode: Image.PreserveAspectCrop
61− // Limit how much memory we'll reserve for this image
62− sourceSize.height: height
63− sourceSize.width: width
64− source: greeter.background
65− }
66−
67− Rectangle {
68− anchors.fill: parent
69− color: "black"
70− opacity: 0.4
71− }
72−
73− Loader {
74− id: loginLoader
75− objectName: "loginLoader"
76− anchors {
77− left: parent.left
78− leftMargin: Math.min(parent.width * 0.16, units.gu(20))
79− top: parent.top
80− }
81− width: units.gu(29)
82− height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height
83− : parent.height
84− Behavior on height { UbuntuNumberAnimation {} }
85−
86− // TODO: Once we have a system API for determining which mode we are
87− // in, tablet/phone/desktop, that should be used instead of narrowMode.
88− source: greeter.narrowMode ? "" : "LoginList.qml"
89−
90− onLoaded: {
91− item.currentIndex = greeterContentLoader.currentIndex;
92− }
93−
94− Binding {
95− target: loginLoader.item
96− property: "model"
97− value: greeterContentLoader.model
98− }
99−
100− Connections {
101− target: loginLoader.item
102−
103− onSelected: {
104− root.selected(uid);
105− }
106−
107− onUnlocked: {
108− root.unlocked(uid);
109− }
110−
111− onCurrentIndexChanged: {
112− if (greeterContentLoader.currentIndex !== loginLoader.item.currentIndex) {
113− greeterContentLoader.currentIndex = loginLoader.item.currentIndex;
114− }
115− }
116− }
117− }
118−
119− Infographics {
120− id: infographics
121− objectName: "infographics"
122− height: narrowMode ? parent.height : 0.75 * parent.height
123− model: greeterContentLoader.infographicModel
124− clip: true // clip large data bubbles
125−
126− property string selectedUser
127− property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : ""
128− onInfographicUserChanged: greeterContentLoader.infographicModel.username = infographicUser
129−
130− Component.onCompleted: {
131− selectedUser = greeterContentLoader.model.data(greeterContentLoader.currentIndex, LightDM.UserRoles.NameRole)
132− greeterContentLoader.infographicModel.username = infographicUser
133− greeterContentLoader.infographicModel.readyForDataChange()
134− }
135−
136− Connections {
137− target: root
138− onSelected: infographics.selectedUser = greeterContentLoader.model.data(uid, LightDM.UserRoles.NameRole)
139− }
140−
141− Connections {
142− target: i18n
143− onLanguageChanged: greeterContentLoader.infographicModel.readyForDataChange()
144− }
145−
146− anchors {
147− verticalCenter: parent.verticalCenter
148− left: narrowMode ? root.left : loginLoader.right
149− right: root.right
150− }
151− }
152−
153− Clock {
154− id: clock
155− visible: narrowMode
156−
157− anchors {
158− top: parent.top
159− topMargin: units.gu(2)
160− horizontalCenter: parent.horizontalCenter
161− }
162− }
163−}
qml/Greeter/Infographics.qml+1
@@ -38,6 +38,7 @@ Item {
38
38
39 Connections {
39 Connections {
40 target: model
40 target: model
41+ ignoreUnknownSignals: model === undefined
41
42
42 onDataAboutToAppear: startHideAnimation() // hide "no data" label
43 onDataAboutToAppear: startHideAnimation() // hide "no data" label
43 onDataAppeared: startShowAnimation()
44 onDataAppeared: startShowAnimation()
qml/Greeter/LoginList.qml+67−85
@@ -14,44 +14,76 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.3
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19−import LightDM 0.1 as LightDM
20import "../Components"
19import "../Components"
21
20
22Item {
21Item {
23 id: root
22 id: root
24
23
25− property alias userList: userList
26 property alias model: userList.model
24 property alias model: userList.model
27− property alias currentIndex: userList.currentIndex
25+ property int currentIndex
26+ property bool locked
28
27
29 readonly property int numAboveBelow: 4
28 readonly property int numAboveBelow: 4
30 readonly property int cellHeight: units.gu(5)
29 readonly property int cellHeight: units.gu(5)
31 readonly property int highlightedHeight: units.gu(10)
30 readonly property int highlightedHeight: units.gu(10)
32 readonly property int moveDuration: 200
31 readonly property int moveDuration: 200
32+ readonly property string currentUser: userList.currentItem.username
33 property bool wasPrompted: false
33 property bool wasPrompted: false
34
34
35− signal selected(int uid)
35+ signal selected(int index)
36− signal unlocked(int uid)
36+ signal responded(string response)
37
37
38 function tryToUnlock() {
38 function tryToUnlock() {
39− if (LightDM.Greeter.promptless) {
39+ if (wasPrompted) {
40− if (LightDM.Greeter.authenticated) {
40+ passwordInput.forceActiveFocus();
41+ } else {
42+ if (root.locked) {
41− root.unlocked(userList.currentIndex)
43+ root.selected(currentIndex);
42 } else {
44 } else {
43− root.resetAuthentication()
45+ root.responded("");
44 }
46 }
47+ }
48+ }
49+
50+ function showMessage(html) {
51+ if (infoLabel.text === "") {
52+ infoLabel.text = html;
45 } else {
53 } else {
54+ infoLabel.text += "<br>" + html;
55+ }
56+ }
57+
58+ function showPrompt(text, isSecret, isDefaultPrompt) {
59+ passwordInput.text = "";
60+ passwordInput.promptText = text;
61+ passwordInput.enabled = true;
62+ passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal;
63+ if (wasPrompted) // stay in text field if second prompt
46− passwordInput.forceActiveFocus()
64+ passwordInput.focus = true;
65+ wasPrompted = true;
66+ }
67+
68+ function showError() {
69+ wrongPasswordAnimation.start();
70+ root.resetAuthentication();
71+ if (wasPrompted) {
72+ passwordInput.focus = true;
47 }
73 }
48 }
74 }
49
75
50 function reset() {
76 function reset() {
51− root.resetAuthentication()
77+ root.resetAuthentication();
52 }
78 }
53
79
54− Keys.onEscapePressed: root.resetAuthentication()
80+ Keys.onEscapePressed: {
81+ selected(currentIndex);
82+ }
83+
84+ onCurrentIndexChanged: {
85+ userList.currentIndex = currentIndex;
86+ }
55
87
56 Rectangle {
88 Rectangle {
57 id: highlightItem
89 id: highlightItem
@@ -81,26 +113,24 @@ Item {
81 flickDeceleration: 10000
113 flickDeceleration: 10000
82
114
83 readonly property bool movingInternally: moveTimer.running || userList.moving
115 readonly property bool movingInternally: moveTimer.running || userList.moving
84−
85− onCurrentIndexChanged: {
86− if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
87− root.resetAuthentication();
88− }
89− }
90−
91 onMovingInternallyChanged: {
116 onMovingInternallyChanged: {
92− // Only emit the selected signal once we stop moving to avoid frequent background changes
93 if (!movingInternally) {
117 if (!movingInternally) {
94− root.selected(userList.currentIndex);
118+ root.selected(currentIndex);
95 }
119 }
96 }
120 }
97
121
122+ onCurrentIndexChanged: {
123+ root.resetAuthentication();
124+ moveTimer.start();
125+ }
126+
98 delegate: Item {
127 delegate: Item {
99 width: parent.width
128 width: parent.width
100 height: root.cellHeight
129 height: root.cellHeight
101
130
102 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
131 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
103 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
132 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
133+ readonly property string username: name
104
134
105 opacity: {
135 opacity: {
106 // The goal here is to make names less and less opaque as they
136 // The goal here is to make names less and less opaque as they
@@ -148,11 +178,8 @@ Item {
148 topMargin: parent.belowHighlight ? parent.belowOffset : 0
178 topMargin: parent.belowHighlight ? parent.belowOffset : 0
149 }
179 }
150 height: parent.height
180 height: parent.height
151− enabled: userList.currentIndex !== index
181+ enabled: userList.currentIndex !== index && parent.opacity > 0
152− onClicked: {
182+ onClicked: root.selected(index)
153− moveTimer.start();
154− userList.currentIndex = index;
155− }
156
183
157 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
184 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
158 }
185 }
@@ -204,6 +231,11 @@ Item {
204 width: parent.width - anchors.margins * 2
231 width: parent.width - anchors.margins * 2
205 opacity: userList.movingInternally ? 0 : 1
232 opacity: userList.movingInternally ? 0 : 1
206
233
234+ property string promptText
235+ placeholderText: root.wasPrompted ? promptText
236+ : (root.locked ? i18n.tr("Retry")
237+ : i18n.tr("Tap to unlock"))
238+
207 Behavior on opacity {
239 Behavior on opacity {
208 NumberAnimation { duration: 100 }
240 NumberAnimation { duration: 100 }
209 }
241 }
@@ -213,9 +245,11 @@ Item {
213 return;
245 return;
214 root.focus = true; // so that it can handle Escape presses for us
246 root.focus = true; // so that it can handle Escape presses for us
215 enabled = false;
247 enabled = false;
216− LightDM.Greeter.respond(text);
248+ root.responded(text);
249+ }
250+ Keys.onEscapePressed: {
251+ root.selected(currentIndex);
217 }
252 }
218− Keys.onEscapePressed: root.resetAuthentication()
219
253
220 Image {
254 Image {
221 anchors {
255 anchors {
@@ -223,7 +257,7 @@ Item {
223 rightMargin: units.gu(2)
257 rightMargin: units.gu(2)
224 verticalCenter: parent.verticalCenter
258 verticalCenter: parent.verticalCenter
225 }
259 }
226− visible: LightDM.Greeter.promptless
260+ visible: !root.wasPrompted
227 source: "graphics/icon_arrow.png"
261 source: "graphics/icon_arrow.png"
228 }
262 }
229
263
@@ -247,7 +281,7 @@ Item {
247 id: passwordMouseArea
281 id: passwordMouseArea
248 objectName: "passwordMouseArea"
282 objectName: "passwordMouseArea"
249 anchors.fill: passwordInput
283 anchors.fill: passwordInput
250− enabled: LightDM.Greeter.promptless
284+ enabled: !root.wasPrompted
251 onClicked: root.tryToUnlock()
285 onClicked: root.tryToUnlock()
252 }
286 }
253
287
@@ -256,62 +290,10 @@ Item {
256 return;
290 return;
257 }
291 }
258 infoLabel.text = "";
292 infoLabel.text = "";
259− passwordInput.placeholderText = "";
293+ passwordInput.promptText = "";
260 passwordInput.text = "";
294 passwordInput.text = "";
261 passwordInput.focus = false;
295 passwordInput.focus = false;
262 passwordInput.enabled = true;
296 passwordInput.enabled = true;
263 root.wasPrompted = false;
297 root.wasPrompted = false;
264− LightDM.Greeter.authenticate(userList.model.data(currentIndex, LightDM.UserRoles.NameRole));
265− }
266−
267− Connections {
268− target: LightDM.Greeter
269−
270− onShowMessage: {
271− // inefficient, but we only rarely deal with messages
272− var html = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<br>")
273− if (isError)
274− html = "<font color=\"#df382c\">" + html + "</font>"
275− if (infoLabel.text == "")
276− infoLabel.text = html
277− else
278− infoLabel.text = infoLabel.text + "<br>" + html
279− }
280−
281− onShowPrompt: {
282− passwordInput.text = "";
283− passwordInput.placeholderText = text;
284− passwordInput.enabled = true;
285− passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal;
286− if (root.wasPrompted) // stay in text field if second prompt
287− passwordInput.focus = true;
288− root.wasPrompted = true;
289− }
290−
291− onAuthenticationComplete: {
292− if (LightDM.Greeter.promptless) {
293− passwordInput.placeholderText = LightDM.Greeter.authenticated ? "Tap to unlock" : "Retry";
294− return;
295− }
296− if (LightDM.Greeter.authenticated) {
297− root.unlocked(userList.currentIndex);
298− } else {
299− wrongPasswordAnimation.start();
300− root.resetAuthentication();
301− passwordInput.focus = true;
302− }
303− passwordInput.text = "";
304− }
305−
306− onRequestAuthenticationUser: {
307− // Find index for requested user, if it exists
308− for (var i = 0; i < userList.model.count; i++) {
309− if (user == userList.model.data(i, LightDM.UserRoles.NameRole)) {
310− moveTimer.start();
311− userList.currentIndex = i;
312− return;
313− }
314− }
315− }
316 }
298 }
317}
299}
qml/Greeter/NarrowView.qmlnew file+170
@@ -0,0 +1,170 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+FocusScope {
22+ id: root
23+
24+ property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
25+ property alias launcherOffset: coverPage.launcherOffset
26+ property int currentIndex // unused
27+ property alias delayMinutes: lockscreen.delayMinutes
28+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
29+ property url background
30+ property bool locked
31+ property bool alphanumeric
32+ property var userModel // unused
33+ property alias infographicModel: coverPage.infographicModel
34+ readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
35+ readonly property bool required: coverPage.required || lockscreen.required
36+
37+ signal selected(int index) // unused
38+ signal responded(string response)
39+ signal tease()
40+ signal emergencyCall()
41+
42+ function showMessage(html) {
43+ // TODO
44+ }
45+
46+ function showPrompt(text, isSecret, isDefaultPrompt) {
47+ lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
48+ lockscreen.maybeShow();
49+ }
50+
51+ function showLastChance() {
52+ var title = lockscreen.alphaNumeric ?
53+ i18n.tr("Sorry, incorrect passphrase.") :
54+ i18n.tr("Sorry, incorrect passcode.");
55+ var text = i18n.tr("This will be your last attempt.") + " " +
56+ (lockscreen.alphaNumeric ?
57+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
58+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."));
59+ lockscreen.showInfoPopup(title, text);
60+ }
61+
62+ function hide() {
63+ lockscreen.hide();
64+ coverPage.hide();
65+ }
66+
67+ function notifyAuthenticationSucceeded() {
68+ lockscreen.hide();
69+ }
70+
71+ function notifyAuthenticationFailed() {
72+ lockscreen.clear(true);
73+ }
74+
75+ function reset() {
76+ coverPage.show();
77+ }
78+
79+ function tryToUnlock(toTheRight) {
80+ var coverChanged = coverPage.shown;
81+ lockscreen.maybeShow();
82+ if (toTheRight) {
83+ coverPage.hideRight();
84+ } else {
85+ coverPage.hide();
86+ }
87+ return coverChanged;
88+ }
89+
90+ onLockedChanged: {
91+ if (locked) {
92+ lockscreen.maybeShow();
93+ } else {
94+ lockscreen.hide();
95+ }
96+ }
97+
98+ Lockscreen {
99+ id: lockscreen
100+ objectName: "lockscreen"
101+
102+ shown: false
103+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
104+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
105+ anchors.fill: parent
106+ visible: required
107+ enabled: !coverPage.shown
108+ background: root.background
109+ darkenBackground: 0.4
110+ alphaNumeric: root.alphanumeric
111+ minPinLength: 4
112+ maxPinLength: 4
113+
114+ property string promptText
115+ infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
116+ alphaNumeric ? i18n.tr("Enter passphrase") :
117+ i18n.tr("Enter passcode")
118+ errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
119+ alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
120+ i18n.tr("Please re-enter") :
121+ i18n.tr("Sorry, incorrect passcode")
122+
123+ onEntered: root.responded(passphrase)
124+ onCancel: coverPage.show()
125+ onEmergencyCall: root.emergencyCall()
126+
127+ function maybeShow() {
128+ if (root.locked && !shown) {
129+ showNow();
130+ }
131+ }
132+ }
133+
134+ Rectangle {
135+ anchors.fill: parent
136+ color: "black"
137+ opacity: coverPage.showProgress * 0.8
138+ }
139+
140+ CoverPage {
141+ id: coverPage
142+ objectName: "coverPage"
143+ height: parent.height
144+ width: parent.width
145+ background: root.background
146+ onTease: root.tease()
147+
148+ onShowProgressChanged: {
149+ if (showProgress === 1) {
150+ lockscreen.reset();
151+ }
152+
153+ if (showProgress === 0) {
154+ if (root.locked) {
155+ lockscreen.clear(false); // to reset focus if necessary
156+ } else {
157+ root.responded("");
158+ }
159+ }
160+ }
161+
162+ Clock {
163+ anchors {
164+ top: parent.top
165+ topMargin: units.gu(2)
166+ horizontalCenter: parent.horizontalCenter
167+ }
168+ }
169+ }
170+}
qml/Greeter/WideView.qmlnew file+134
@@ -0,0 +1,134 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+FocusScope {
21+ id: root
22+
23+ property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
24+ property alias launcherOffset: coverPage.launcherOffset
25+ property alias currentIndex: loginList.currentIndex
26+ property int delayMinutes // TODO
27+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
28+ property alias background: coverPage.background
29+ property bool locked
30+ property bool alphanumeric // unused
31+ property alias userModel: loginList.model
32+ property alias infographicModel: coverPage.infographicModel
33+ readonly property bool fullyShown: coverPage.showProgress === 1
34+ readonly property bool required: coverPage.required
35+
36+ // so that it can be replaced in tests with a mock object
37+ property var inputMethod: Qt.inputMethod
38+
39+ signal selected(int index)
40+ signal responded(string response)
41+ signal tease()
42+ signal emergencyCall() // unused
43+
44+ function showMessage(html) {
45+ loginList.showMessage(html);
46+ }
47+
48+ function showPrompt(text, isSecret, isDefaultPrompt) {
49+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
50+ }
51+
52+ function showLastChance() {
53+ // TODO
54+ }
55+
56+ function hide() {
57+ coverPage.hide();
58+ }
59+
60+ function notifyAuthenticationSucceeded() {
61+ // Nothing needed
62+ }
63+
64+ function notifyAuthenticationFailed() {
65+ loginList.showError();
66+ }
67+
68+ function reset() {
69+ loginList.reset();
70+ }
71+
72+ function tryToUnlock(toTheRight) {
73+ if (root.locked) {
74+ coverPage.show();
75+ loginList.tryToUnlock();
76+ return false;
77+ } else {
78+ var coverChanged = coverPage.shown;
79+ if (toTheRight) {
80+ coverPage.hideRight();
81+ } else {
82+ coverPage.hide();
83+ }
84+ return coverChanged;
85+ }
86+ }
87+
88+ Rectangle {
89+ anchors.fill: parent
90+ color: "black"
91+ opacity: coverPage.showProgress * 0.8
92+ }
93+
94+ CoverPage {
95+ id: coverPage
96+ objectName: "coverPage"
97+ height: parent.height
98+ width: parent.width
99+ draggable: !root.locked
100+
101+ infographics {
102+ height: 0.75 * parent.height
103+ anchors.leftMargin: loginList.x + loginList.width
104+ }
105+
106+ onTease: root.tease()
107+
108+ onShowProgressChanged: {
109+ if (showProgress === 0 && !root.locked) {
110+ root.responded("");
111+ }
112+ }
113+
114+ LoginList {
115+ id: loginList
116+ objectName: "loginList"
117+
118+ anchors {
119+ left: parent.left
120+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
121+ top: parent.top
122+ }
123+ width: units.gu(29)
124+ height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height
125+ : parent.height
126+ Behavior on height { UbuntuNumberAnimation {} }
127+
128+ locked: root.locked
129+
130+ onSelected: root.selected(index)
131+ onResponded: root.responded(response)
132+ }
133+ }
134+}
qml/Launcher/Launcher.qml+40−3
@@ -93,10 +93,10 @@ Item {
93 Timer {
93 Timer {
94 id: dismissTimer
94 id: dismissTimer
95 objectName: "dismissTimer"
95 objectName: "dismissTimer"
96− interval: 5000
96+ interval: 500
97 onTriggered: {
97 onTriggered: {
98 if (root.autohideEnabled) {
98 if (root.autohideEnabled) {
99− if (!panel.preventHiding) {
99+ if (!panel.preventHiding && !hoverArea.containsMouse) {
100 root.state = ""
100 root.state = ""
101 } else {
101 } else {
102 dismissTimer.restart()
102 dismissTimer.restart()
@@ -159,7 +159,7 @@ Item {
159
159
160 MouseArea {
160 MouseArea {
161 id: launcherDragArea
161 id: launcherDragArea
162− enabled: root.available && root.state == "visible"
162+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary")
163 anchors.fill: panel
163 anchors.fill: panel
164 anchors.rightMargin: -units.gu(2)
164 anchors.rightMargin: -units.gu(2)
165 drag {
165 drag {
@@ -245,6 +245,33 @@ Item {
245 }
245 }
246 }
246 }
247
247
248+ // TODO: This should be replaced by some mechanism that reveals the launcher
249+ // after a certain resistance has been overcome, like unity7 does. However,
250+ // as we don't get relative mouse coordinates yet, this will do for now.
251+ MouseArea {
252+ id: hoverArea
253+ anchors { fill: panel; rightMargin: -1 }
254+ hoverEnabled: true
255+ propagateComposedEvents: true
256+ onContainsMouseChanged: {
257+ if (containsMouse) {
258+ root.switchToNextState("visibleTemporary");
259+ } else {
260+ dismissTimer.restart();
261+ }
262+ }
263+ onPressed: mouse.accepted = false;
264+
265+ // We need to eat touch events here in order to make sure that
266+ // we don't trigger both, the dragArea and the hoverArea
267+ MultiPointTouchArea {
268+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
269+ width: units.dp(1)
270+ mouseEnabled: false
271+ enabled: parent.enabled
272+ }
273+ }
274+
248 EdgeDragArea {
275 EdgeDragArea {
249 id: dragArea
276 id: dragArea
250 objectName: "launcherDragArea"
277 objectName: "launcherDragArea"
@@ -300,6 +327,16 @@ Item {
300 target: panel
327 target: panel
301 x: -root.x // so we never go past panelWidth, even when teased by tutorial
328 x: -root.x // so we never go past panelWidth, even when teased by tutorial
302 }
329 }
330+ PropertyChanges { target: hoverArea; enabled: false }
331+ },
332+ State {
333+ name: "visibleTemporary"
334+ extend: "visible"
335+ PropertyChanges {
336+ target: root
337+ autohideEnabled: true
338+ }
339+ PropertyChanges { target: hoverArea; enabled: true }
303 },
340 },
304 State {
341 State {
305 name: "teasing"
342 name: "teasing"
qml/Panel/IndicatorItem.qml+2
@@ -95,6 +95,8 @@ IndicatorDelegate {
95 id: itemImage
95 id: itemImage
96 objectName: "icon"+index
96 objectName: "icon"+index
97 height: iconHeight
97 height: iconHeight
98+ // FIXME Workaround for bug https://bugs.launchpad.net/ubuntu/+source/ubuntu-ui-toolkit/+bug/1421293
99+ width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth;
98 source: modelData
100 source: modelData
99 color: root.color
101 color: root.color
100 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
102 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
qml/Shell.qml+67−353
@@ -22,9 +22,7 @@ import Unity.Application 0.1
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
23import Ubuntu.Components.Popups 1.0
24import Ubuntu.Gestures 0.1
24import Ubuntu.Gestures 0.1
25−import Ubuntu.SystemImage 0.1
26import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
27−import Unity.Connectivity 0.1
28import Unity.Launcher 0.1
26import Unity.Launcher 0.1
29import Utils 0.1
27import Utils 0.1
30import LightDM 0.1 as LightDM
28import LightDM 0.1 as LightDM
@@ -46,9 +44,9 @@ import Unity.Indicators 0.1 as Indicators
46Item {
44Item {
47 id: shell
45 id: shell
48
46
49− // Disable everything so that user can't swipe greeter or launcher until
47+ // Disable everything while greeter is waiting, so that the user can't swipe
50− // we get first prompt/authenticate, which will re-enable the shell.
48+ // the greeter or launcher until we know whether the session is locked.
51− enabled: false
49+ enabled: !greeter.waiting
52
50
53 // this is only here to select the width / height of the window if not running fullscreen
51 // this is only here to select the width / height of the window if not running fullscreen
54 property bool tablet: false
52 property bool tablet: false
@@ -61,18 +59,9 @@ Item {
61 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
59 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
62 readonly property real panelHeight: panel.panelHeight
60 readonly property real panelHeight: panel.panelHeight
63
61
64− readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
65− readonly property alias hasLockedApp: greeter.hasLockedApp
66− readonly property bool forcedUnlock: tutorial.running
67− onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
68−
69 property bool sideStageEnabled: shell.width >= units.gu(100)
62 property bool sideStageEnabled: shell.width >= units.gu(100)
70 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
63 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
71
64
72− property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
73− property int failedLoginsDelayAttempts: 7 // number of failed logins
74− property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
75−
76 property int orientation
65 property int orientation
77 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
78 onDeviceOrientationAngleChanged: {
67 onDeviceOrientationAngleChanged: {
@@ -99,7 +88,7 @@ Item {
99 }
88 }
100
89
101 function startLockedApp(app) {
90 function startLockedApp(app) {
102− if (shell.locked) {
91+ if (greeter.locked) {
103 greeter.lockedApp = app;
92 greeter.lockedApp = app;
104 }
93 }
105 shell.activateApplication(app);
94 shell.activateApplication(app);
@@ -117,6 +106,7 @@ Item {
117
106
118 GSettings {
107 GSettings {
119 id: backgroundSettings
108 id: backgroundSettings
109+ objectName: "backgroundSettings"
120 schema.id: "org.gnome.desktop.background"
110 schema.id: "org.gnome.desktop.background"
121 }
111 }
122
112
@@ -222,53 +212,32 @@ Item {
222
212
223 Connections {
213 Connections {
224 target: ApplicationManager
214 target: ApplicationManager
225− onFocusRequested: {
226− if (greeter.narrowMode) {
227− if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
228− // If we are in the middle of a call, make dialer lockedApp and show it.
229− // This can happen if user backs out of dialer back to greeter, then
230− // launches dialer again.
231− greeter.lockedApp = appId;
232− }
233− if (greeter.hasLockedApp) {
234− if (appId === greeter.lockedApp) {
235− lockscreen.hide() // show locked app
236− } else {
237− greeter.startUnlock() // show lockscreen if necessary
238− }
239− }
240− greeter.hide();
241− } else {
242− if (LightDM.Greeter.active) {
243− greeter.startUnlock()
244− }
245− }
246− }
247
215
216+ // This signal is also fired when we try to focus the current app
217+ // again. We rely on this!
248 onFocusedApplicationIdChanged: {
218 onFocusedApplicationIdChanged: {
249− if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
219+ var appId = ApplicationManager.focusedApplicationId;
250− greeter.startUnlock()
251− }
252− panel.indicators.hide();
253− }
254−
255− onApplicationAdded: {
256− if (appId != "unity8-dash") {
257− if (greeter.shown) {
258− greeter.startUnlock();
259− }
260
220
221+ if (tutorial.running && appId != "unity8-dash") {
261 // If this happens on first boot, we may be in edge
222 // If this happens on first boot, we may be in edge
262 // tutorial or wizard while receiving a call. But a call
223 // tutorial or wizard while receiving a call. But a call
263 // is more important than wizard so just bail out of those.
224 // is more important than wizard so just bail out of those.
264− if (tutorial.running) {
265− tutorial.finish();
225+ tutorial.finish();
266− wizard.hide();
226+ wizard.hide();
267− }
268 }
227 }
269− if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
228+
270− lockscreen.hide() // show locked app
229+ if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
230+ // If we are in the middle of a call, make dialer lockedApp and show it.
231+ // This can happen if user backs out of dialer back to greeter, then
232+ // launches dialer again.
233+ greeter.lockedApp = appId;
271 }
234 }
235+ greeter.notifyAppFocused(appId);
236+
237+ panel.indicators.hide();
238+ }
239+
240+ onApplicationAdded: {
272 launcher.hide();
241 launcher.hide();
273 }
242 }
274 }
243 }
@@ -292,7 +261,6 @@ Item {
292
261
293 property bool interactive: tutorial.spreadEnabled
262 property bool interactive: tutorial.spreadEnabled
294 && !greeter.shown
263 && !greeter.shown
295− && !lockscreen.shown
296 && panel.indicators.fullyClosed
264 && panel.indicators.fullyClosed
297 && launcher.progress == 0
265 && launcher.progress == 0
298 && !notifications.useModal
266 && !notifications.useModal
@@ -328,7 +296,7 @@ Item {
328 Binding {
296 Binding {
329 target: applicationsDisplayLoader.item
297 target: applicationsDisplayLoader.item
330 property: "inverseProgress"
298 property: "inverseProgress"
331− value: launcher.progress
299+ value: greeter.locked ? 0 : launcher.progress
332 }
300 }
333 Binding {
301 Binding {
334 target: applicationsDisplayLoader.item
302 target: applicationsDisplayLoader.item
@@ -379,299 +347,50 @@ Item {
379 }
347 }
380 }
348 }
381
349
382− Lockscreen {
350+ Greeter {
383− id: lockscreen
351+ id: greeter
384− objectName: "lockscreen"
352+ objectName: "greeter"
385
353
386 hides: [launcher, panel.indicators]
354 hides: [launcher, panel.indicators]
387− shown: false
355+ tabletMode: shell.sideStageEnabled
388− enabled: true
356+ launcherOffset: launcher.progress
389− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
357+ forcedUnlock: tutorial.running
390− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
391− y: panel.panelHeight
392− visible: required
393− width: parent.width
394− height: parent.height - panel.panelHeight
395 background: shell.background
358 background: shell.background
396− darkenBackground: 0.4
397− alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
398− minPinLength: 4
399− maxPinLength: 4
400−
401− property string promptText
402− infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
403− alphaNumeric ? i18n.tr("Enter passphrase") :
404− i18n.tr("Enter passcode")
405− errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
406− alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
407− i18n.tr("Please re-enter") :
408− i18n.tr("Sorry, incorrect passcode")
409−
410− // FIXME: We *should* show emergency dialer if there is a SIM present,
411− // regardless of whether the side stage is enabled. But right now,
412− // the assumption is that narrow screens are phones which have SIMs
413− // and wider screens are tablets which don't. When we do allow this
414− // on devices with a side stage and a SIM, work should be done to
415− // ensure that the main stage is disabled while the dialer is present
416− // in the side stage. See the FIXME in the stage loader in this file.
417− showEmergencyCallButton: !shell.sideStageEnabled
418−
419− onEntered: LightDM.Greeter.respond(passphrase);
420− onCancel: greeter.show()
421− onEmergencyCall: startLockedApp("dialer-app")
422−
423− onShownChanged: if (shown) greeter.lockedApp = ""
424−
425− function maybeShow() {
426− if (!shell.forcedUnlock && !shown) {
427− showNow();
428− }
429− }
430−
431− Timer {
432− id: forcedDelayTimer
433− interval: 1000 * 60
434− onTriggered: {
435− if (lockscreen.delayMinutes > 0) {
436− lockscreen.delayMinutes -= 1
437− if (lockscreen.delayMinutes > 0) {
438− start() // go again
439− }
440− }
441− }
442− }
443−
444− Component.onCompleted: {
445− if (greeter.narrowMode) {
446− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
447− }
448− }
449− }
450−
451− Connections {
452− target: LightDM.Greeter
453
359
454− onShowGreeter: greeter.show()
455− onHideGreeter: greeter.login()
456−
457− onShowPrompt: {
458− shell.enabled = true;
459− if (!LightDM.Greeter.active) {
460− return; // could happen if hideGreeter() comes in before we prompt
461− }
462− if (greeter.narrowMode) {
463− lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
464− lockscreen.maybeShow();
465− }
466− }
467−
468− onPromptlessChanged: {
469− if (!LightDM.Greeter.active) {
470− return; // could happen if hideGreeter() comes in before we prompt
471− }
472− if (greeter.narrowMode) {
473− if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
474− lockscreen.hide()
475− } else {
476− lockscreen.reset();
477− lockscreen.maybeShow();
478− }
479− }
480− }
481−
482− onAuthenticationComplete: {
483− shell.enabled = true;
484− if (LightDM.Greeter.authenticated) {
485− AccountsService.failedLogins = 0
486− }
487− // Else only penalize user for a failed login if they actually were
488− // prompted for a password. We do this below after the promptless
489− // early exit.
490−
491− if (LightDM.Greeter.promptless) {
492− return;
493− }
494−
495− if (LightDM.Greeter.authenticated) {
496− greeter.login();
497− } else {
498− AccountsService.failedLogins++
499− if (maxFailedLogins >= 2) { // require at least a warning
500− if (AccountsService.failedLogins === maxFailedLogins - 1) {
501− var title = lockscreen.alphaNumeric ?
502− i18n.tr("Sorry, incorrect passphrase.") :
503− i18n.tr("Sorry, incorrect passcode.")
504− var text = i18n.tr("This will be your last attempt.") + " " +
505− (lockscreen.alphaNumeric ?
506− i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
507− i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
508− lockscreen.showInfoPopup(title, text)
509− } else if (AccountsService.failedLogins >= maxFailedLogins) {
510− SystemImage.factoryReset() // Ouch!
511− }
512− }
513− if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
514− lockscreen.delayMinutes = failedLoginsDelayMinutes
515− forcedDelayTimer.start()
516− }
517−
518− lockscreen.clear(true);
519− if (greeter.narrowMode) {
520− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
521− }
522− }
523− }
524− }
525−
526− Binding {
527− target: LightDM.Greeter
528− property: "active"
529− value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
530− }
531−
532− Rectangle {
533 anchors.fill: parent
360 anchors.fill: parent
534− color: "black"
535− opacity: greeterWrapper.showProgress * 0.8
536− }
537−
538− Item {
539− // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
540− id: greeterWrapper
541− objectName: "greeterWrapper"
542− x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
543− y: panel.panelHeight
544− width: parent.width
545− height: parent.height - panel.panelHeight
361+ anchors.topMargin: panel.panelHeight
546
362
547− Behavior on x {
363+ // avoid overlapping with Launcher's edge drag area
548− enabled: !launcher.dashSwipe
364+ // FIXME: Fix TouchRegistry & friends and remove this workaround
549− StandardAnimation {}
365+ // Issue involves launcher's DDA getting disabled on a long
550− }
366+ // left-edge drag
367+ dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
551
368
552− property bool fullyShown: showProgress === 1.0
369+ onSessionStarted: {
553− onFullyShownChanged: {
370+ launcher.hide();
554− // Wait until the greeter is completely covering lockscreen before resetting it.
555− if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
556− lockscreen.reset();
557− lockscreen.maybeShow();
558− }
559 }
371 }
560
372
561− readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
373+ onTease: {
562− onShowProgressChanged: {
374+ if (!tutorial.running) {
563− if (showProgress === 0) {
375+ launcher.tease();
564− if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
565− greeter.login()
566− } else if (greeter.narrowMode) {
567− lockscreen.clear(false) // to reset focus if necessary
568− }
569 }
376 }
570 }
377 }
571
378
572− Greeter {
379+ onEmergencyCall: startLockedApp("dialer-app")
573− id: greeter
574− objectName: "greeter"
575−
576− signal sessionStarted() // helpful for tests
577−
578− property string lockedApp: ""
579− property bool hasLockedApp: lockedApp !== ""
580−
581− hides: [launcher, panel.indicators]
582− loadContent: required || lockscreen.required // keeps content in memory for quick show()
583−
584− locked: shell.locked
585−
586− background: shell.background
587−
588− width: parent.width
589− height: parent.height
590−
591−
592− // avoid overlapping with Launcher's edge drag area
593− // FIXME: Fix TouchRegistry & friends and remove this workaround
594− // Issue involves launcher's DDA getting disabled on a long
595− // left-edge drag
596− dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
597−
598− function startUnlock() {
599− if (narrowMode) {
600− if (!LightDM.Greeter.authenticated) {
601− lockscreen.maybeShow()
602− }
603− hide()
604− } else {
605− show()
606− tryToUnlock()
607− }
608− }
609−
610− function login() {
611− enabled = false;
612− if (LightDM.Greeter.startSessionSync()) {
613− sessionStarted();
614− greeter.hide();
615− lockscreen.hide();
616− launcher.hide();
617− }
618− enabled = true;
619− }
620−
621− Timer {
622− // See powerConnection for why this is useful
623− id: showGreeterDelayed
624− interval: 1
625− onTriggered: {
626− greeter.showNow();
627− }
628− }
629−
630− onShownChanged: {
631− if (shown) {
632− // Disable everything so that user can't swipe greeter or
633− // launcher until we get the next prompt/authenticate, which
634− // will re-enable the shell.
635− shell.enabled = false;
636−
637− if (greeter.narrowMode) {
638− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
639− } else {
640− reset()
641− }
642− greeter.lockedApp = "";
643− greeter.forceActiveFocus();
644− }
645− }
646−
647− Component.onCompleted: {
648− Connectivity.unlockAllModems()
649− }
650−
651− onUnlocked: greeter.hide()
652− onSelected: {
653− // Update launcher items for new user
654− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
655− AccountsService.user = user;
656− LauncherModel.setUser(user);
657− }
658
380
659− onTapped: {
381+ Timer {
660− if (!tutorial.running) {
382+ // See powerConnection for why this is useful
661− launcher.tease();
383+ id: showGreeterDelayed
662− }
384+ interval: 1
663− }
385+ onTriggered: {
664− onDraggingChanged: {
386+ greeter.forceShow();
665− if (dragging && !tutorial.running) {
666− launcher.tease();
667− }
668 }
387 }
388+ }
669
389
670− Binding {
390+ Binding {
671− target: ApplicationManager
391+ target: ApplicationManager
672− property: "suspended"
392+ property: "suspended"
673− value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
393+ value: greeter.shown
674− }
675 }
394 }
676 }
395 }
677
396
@@ -680,7 +399,7 @@ Item {
680 target: callManager
399 target: callManager
681
400
682 onHasCallsChanged: {
401 onHasCallsChanged: {
683− if (shell.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
402+ if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
684 // We just received an incoming call while locked. The
403 // We just received an incoming call while locked. The
685 // indicator will have already launched dialer-app for us, but
404 // indicator will have already launched dialer-app for us, but
686 // there is a race between "hasCalls" changing and the dialer
405 // there is a race between "hasCalls" changing and the dialer
@@ -720,9 +439,7 @@ Item {
720 return
439 return
721 }
440 }
722
441
723− if (LightDM.Greeter.active) {
442+ greeter.notifyAboutToFocusApp("unity8-dash");
724− greeter.startUnlock()
725− }
726
443
727 var animate = !LightDM.Greeter.active && !stages.shown
444 var animate = !LightDM.Greeter.active && !stages.shown
728 dash.setCurrentScope(0, animate, false)
445 dash.setCurrentScope(0, animate, false)
@@ -730,17 +447,11 @@ Item {
730 }
447 }
731
448
732 function showDash() {
449 function showDash() {
733− if (greeter.hasLockedApp || // just in case user gets here
450+ if (greeter.notifyShowingDashFromDrag()) {
734− (!greeter.narrowMode && shell.locked)) {
735− return
736− }
737−
738− if (greeter.shown) {
739− greeter.hideRight();
740 launcher.fadeOut();
451 launcher.fadeOut();
741 }
452 }
742
453
743− if (ApplicationManager.focusedApplicationId != "unity8-dash") {
454+ if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") {
744 ApplicationManager.requestFocusApplication("unity8-dash")
455 ApplicationManager.requestFocusApplication("unity8-dash")
745 launcher.fadeOut();
456 launcher.fadeOut();
746 }
457 }
@@ -758,7 +469,9 @@ Item {
758 anchors.fill: parent //because this draws indicator menus
469 anchors.fill: parent //because this draws indicator menus
759 indicators {
470 indicators {
760 hides: [launcher]
471 hides: [launcher]
472+ available: tutorial.panelEnabled
761− available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
473+ && (!greeter.locked || AccountsService.enableIndicatorsWhileLocked)
474+ && !greeter.hasLockedApp
762 contentEnabled: tutorial.panelContentEnabled
475 contentEnabled: tutorial.panelContentEnabled
763 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
476 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
764
477
@@ -772,7 +485,7 @@ Item {
772 }
485 }
773 }
486 }
774 callHint {
487 callHint {
775− greeterShown: greeter.shown || lockscreen.shown
488+ greeterShown: greeter.shown
776 }
489 }
777
490
778 property bool topmostApplicationIsFullscreen:
491 property bool topmostApplicationIsFullscreen:
@@ -794,7 +507,9 @@ Item {
794 anchors.bottom: parent.bottom
507 anchors.bottom: parent.bottom
795 width: parent.width
508 width: parent.width
796 dragAreaWidth: shell.edgeSize
509 dragAreaWidth: shell.edgeSize
510+ available: tutorial.launcherEnabled
797− available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
511+ && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
512+ && !greeter.hasLockedApp
798 inverted: usageModeSettings.usageMode === "Staged"
513 inverted: usageModeSettings.usageMode === "Staged"
799 shadeBackground: !tutorial.running
514 shadeBackground: !tutorial.running
800
515
@@ -806,11 +521,10 @@ Item {
806 }
521 }
807 }
522 }
808 onLauncherApplicationSelected: {
523 onLauncherApplicationSelected: {
809− if (greeter.hasLockedApp) {
810− greeter.startUnlock()
811− }
812− if (!tutorial.running)
524+ if (!tutorial.running) {
525+ greeter.notifyAboutToFocusApp(appId);
813 shell.activateApplication(appId)
526 shell.activateApplication(appId)
527+ }
814 }
528 }
815 onShownChanged: {
529 onShownChanged: {
816 if (shown) {
530 if (shown) {
qml/Stages/ApplicationWindow.qml+1−1
@@ -43,7 +43,7 @@ FocusScope {
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
46− readonly property url defaultScreenshot: root.application ? root.application.defaultScreenshot : ""
46+ readonly property url defaultScreenshot: (root.application && root.application.defaultScreenshot !== undefined) ? root.application.defaultScreenshot : ""
47
47
48 // Whether the Application had a surface before but lost it.
48 // Whether the Application had a surface before but lost it.
49 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
49 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
qml/Stages/PhoneStage.qml+5−1
@@ -125,7 +125,11 @@ Rectangle {
125
125
126 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
126 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
127
127
128− onFocusedAppDelegateChanged: focusedAppDelegate.focus = true;
128+ onFocusedAppDelegateChanged: {
129+ if (focusedAppDelegate) {
130+ focusedAppDelegate.focus = true;
131+ }
132+ }
129
133
130 function indexOf(appId) {
134 function indexOf(appId) {
131 for (var i = 0; i < applicationManager.count; i++) {
135 for (var i = 0; i < applicationManager.count; i++) {
qml/Wizard/Pages/20-sim.qml+5−1
@@ -25,11 +25,15 @@ LocalComponents.Page {
25 title: i18n.tr("Add a SIM card and restart your device")
25 title: i18n.tr("Add a SIM card and restart your device")
26 forwardButtonSourceComponent: forwardButton
26 forwardButtonSourceComponent: forwardButton
27
27
28− // No need for skipValid, since OfonoManager does everything synchronously
28+ skipValid: !manager.available ||
29+ (manager.ready && (manager.modems.length < 1 || simManager0.ready)
30+ && (manager.modems.length < 2 || simManager1.ready))
29 skip: !manager.available || manager.modems.length === 0 || simManager0.present || simManager1.present
31 skip: !manager.available || manager.modems.length === 0 || simManager0.present || simManager1.present
30
32
31 OfonoManager {
33 OfonoManager {
32 id: manager
34 id: manager
35+ property bool ready: false
36+ onModemsChanged: ready = true
33 }
37 }
34
38
35 // Ideally we would query the system more cleverly than hardcoding two
39 // Ideally we would query the system more cleverly than hardcoding two
tests/autopilot/unity8/shell/__init__.py+1
@@ -27,6 +27,7 @@ import logging
27
27
28logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
29
29
30+
30def disable_qml_mocking(fn):
31def disable_qml_mocking(fn):
31 """Simple decorator that disables the QML mocks from being loaded."""
32 """Simple decorator that disables the QML mocks from being loaded."""
32 @wraps(fn)
33 @wraps(fn)
tests/autopilot/unity8/shell/emulators/greeter.py+19−3
@@ -19,6 +19,7 @@
19
19
20import ubuntuuitoolkit
20import ubuntuuitoolkit
21
21
22+from autopilot.utilities import sleep
22from unity8.shell.emulators import UnityEmulatorBase
23from unity8.shell.emulators import UnityEmulatorBase
23
24
24
25
@@ -26,12 +27,27 @@ class Greeter(UnityEmulatorBase):
26 """An emulator that understands the greeter screen."""
27 """An emulator that understands the greeter screen."""
27
28
28 def wait_swiped_away(self):
29 def wait_swiped_away(self):
29− self.showProgress.wait_for(0)
30+ # We have to be careful here, because coverPage can go away at any time
31+ # if there isn't a lockscreen behind it (it hides completely, then
32+ # the greeter disposes it). But if there *is* a lockscreen, then we
33+ # need a different check, by looking at its showProgress. So make our
34+ # own wait_for loop and check both possibilities.
35+ for i in range(10):
36+ if not self.required:
37+ return
38+ coverPage = self.select_single(objectName='coverPage')
39+ if coverPage.showProgress == 0:
40+ return
41+ sleep(1)
42+
43+ raise AssertionError("Greeter cover page still up after 10s")
44+
30
45
31 def swipe(self):
46 def swipe(self):
32 """Swipe the greeter screen away."""
47 """Swipe the greeter screen away."""
33− self.created.wait_for(True)
48+ self.waiting.wait_for(False)
49+ coverPage = self.select_single(objectName='coverPage')
34− self.showProgress.wait_for(1)
50+ coverPage.showProgress.wait_for(1)
35
51
36 rect = self.globalRect
52 rect = self.globalRect
37 start_x = rect[0] + rect[2] - 3
53 start_x = rect[0] + rect[2] - 3
tests/autopilot/unity8/shell/emulators/main_window.py+17−6
@@ -35,12 +35,6 @@ class QQuickView(emulators.UnityEmulatorBase):
35 def get_greeter(self):
35 def get_greeter(self):
36 return self.select_single(Greeter)
36 return self.select_single(Greeter)
37
37
38− def get_greeter_content_loader(self):
39− return self.wait_select_single(
40− "QQuickLoader",
41− objectName="greeterContentLoader"
42− )
43−
44 def get_login_loader(self):
38 def get_login_loader(self):
45 return self.select_single("QQuickLoader", objectName="loginLoader")
39 return self.select_single("QQuickLoader", objectName="loginLoader")
46
40
@@ -174,3 +168,20 @@ class QQuickView(emulators.UnityEmulatorBase):
174 'PinPadButton',
168 'PinPadButton',
175 objectName='pinPadButton{}'.format(button_id)
169 objectName='pinPadButton{}'.format(button_id)
176 )
170 )
171+
172+ @autopilot_logging.log_action(logger.info)
173+ def wait_for_notification(self):
174+ """Wait for a notification dialog to appear.
175+
176+ :return: An object for the notification dialog data.
177+ :raise StateNotFoundError: if the timeout expires when the
178+ notification has not appeared.
179+
180+ """
181+ notify_list = self.select_single('Notifications',
182+ objectName='notificationList')
183+ visible_notification = notify_list.wait_select_single('Notification',
184+ visible=True)
185+ return {'summary': visible_notification.summary,
186+ 'body': visible_notification.body,
187+ 'iconSource': visible_notification.iconSource}
tests/autopilot/unity8/shell/tests/__init__.py+2−3
@@ -375,9 +375,8 @@ class UnityTestCase(AutopilotTestCase):
375 self._proxy = None
375 self._proxy = None
376
376
377 def wait_for_unity(self):
377 def wait_for_unity(self):
378− greeter_content_loader = self.main_window.wait_select_single(
378+ greeter = self.main_window.wait_select_single(objectName='greeter')
379− objectName='greeterContentLoader')
379+ greeter.waiting.wait_for(False)
380− greeter_content_loader.progress.wait_for(1)
381
380
382 def get_dash(self):
381 def get_dash(self):
383 pid = process_helpers.get_job_pid('unity8-dash')
382 pid = process_helpers.get_job_pid('unity8-dash')
tests/autopilot/unity8/shell/tests/test_lock_screen.py+8−12
@@ -39,14 +39,13 @@ class TestLockscreen(UnityTestCase):
39 self.launch_unity()
39 self.launch_unity()
40 greeter = self.main_window.get_greeter()
40 greeter = self.main_window.get_greeter()
41
41
42− if greeter.narrowMode:
42+ if not greeter.tabletMode:
43 greeter.swipe()
43 greeter.swipe()
44 lockscreen = self._wait_for_lockscreen()
44 lockscreen = self._wait_for_lockscreen()
45 self.main_window.enter_pin_code("1234")
45 self.main_window.enter_pin_code("1234")
46− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
47 else:
46 else:
48 self._enter_prompt_passphrase("1234\n")
47 self._enter_prompt_passphrase("1234\n")
49− self.assertThat(greeter.shown, Eventually(Equals(False)))
48+ self.assertThat(greeter.shown, Eventually(Equals(False)))
50
49
51 def test_can_unlock_passphrase_screen(self):
50 def test_can_unlock_passphrase_screen(self):
52 """Must be able to unlock the passphrase entry screen."""
51 """Must be able to unlock the passphrase entry screen."""
@@ -55,14 +54,13 @@ class TestLockscreen(UnityTestCase):
55 self.launch_unity()
54 self.launch_unity()
56 greeter = self.main_window.get_greeter()
55 greeter = self.main_window.get_greeter()
57
56
58− if greeter.narrowMode:
57+ if not greeter.tabletMode:
59 greeter.swipe()
58 greeter.swipe()
60 lockscreen = self._wait_for_lockscreen()
59 lockscreen = self._wait_for_lockscreen()
61 self._enter_pin_passphrase("password")
60 self._enter_pin_passphrase("password")
62− self.assertThat(lockscreen.shown, Eventually(Equals(False)))
63 else:
61 else:
64 self._enter_prompt_passphrase("password")
62 self._enter_prompt_passphrase("password")
65− self.assertThat(greeter.shown, Eventually(Equals(False)))
63+ self.assertThat(greeter.shown, Eventually(Equals(False)))
66
64
67 def test_pin_screen_wrong_code(self):
65 def test_pin_screen_wrong_code(self):
68 """Entering the wrong pin code must not dismiss the lock screen."""
66 """Entering the wrong pin code must not dismiss the lock screen."""
@@ -70,18 +68,17 @@ class TestLockscreen(UnityTestCase):
70 self.launch_unity()
68 self.launch_unity()
71 greeter = self.main_window.get_greeter()
69 greeter = self.main_window.get_greeter()
72
70
73− if greeter.narrowMode:
71+ if not greeter.tabletMode:
74 greeter.swipe()
72 greeter.swipe()
75 lockscreen = self._wait_for_lockscreen()
73 lockscreen = self._wait_for_lockscreen()
76 self.main_window.enter_pin_code("4321")
74 self.main_window.enter_pin_code("4321")
77 pinentryField = self.main_window.get_pinentryField()
75 pinentryField = self.main_window.get_pinentryField()
78 self.assertThat(pinentryField.text, Eventually(Equals("")))
76 self.assertThat(pinentryField.text, Eventually(Equals("")))
79− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
80 else:
77 else:
81 self._enter_prompt_passphrase("4231\n")
78 self._enter_prompt_passphrase("4231\n")
82 prompt = self.main_window.get_greeter().get_prompt()
79 prompt = self.main_window.get_greeter().get_prompt()
83 self.assertThat(prompt.text, Eventually(Equals("")))
80 self.assertThat(prompt.text, Eventually(Equals("")))
84− self.assertThat(greeter.shown, Eventually(Equals(True)))
81+ self.assertThat(greeter.shown, Eventually(Equals(True)))
85
82
86 def test_passphrase_screen_wrong_password(self):
83 def test_passphrase_screen_wrong_password(self):
87 """Entering the wrong password must not dismiss the lock screen."""
84 """Entering the wrong password must not dismiss the lock screen."""
@@ -89,18 +86,17 @@ class TestLockscreen(UnityTestCase):
89 self.launch_unity()
86 self.launch_unity()
90 greeter = self.main_window.get_greeter()
87 greeter = self.main_window.get_greeter()
91
88
92− if greeter.narrowMode:
89+ if not greeter.tabletMode:
93 greeter.swipe()
90 greeter.swipe()
94 lockscreen = self._wait_for_lockscreen()
91 lockscreen = self._wait_for_lockscreen()
95 self._enter_pin_passphrase("foobar")
92 self._enter_pin_passphrase("foobar")
96 pinentryField = self.main_window.get_pinentryField()
93 pinentryField = self.main_window.get_pinentryField()
97 self.assertThat(pinentryField.text, Eventually(Equals("")))
94 self.assertThat(pinentryField.text, Eventually(Equals("")))
98− self.assertThat(lockscreen.shown, Eventually(Equals(True)))
99 else:
95 else:
100 self._enter_prompt_passphrase("foobar")
96 self._enter_prompt_passphrase("foobar")
101 prompt = self.main_window.get_greeter().get_prompt()
97 prompt = self.main_window.get_greeter().get_prompt()
102 self.assertThat(prompt.text, Eventually(Equals("")))
98 self.assertThat(prompt.text, Eventually(Equals("")))
103− self.assertThat(greeter.shown, Eventually(Equals(True)))
99+ self.assertThat(greeter.shown, Eventually(Equals(True)))
104
100
105 def _wait_for_lockscreen(self):
101 def _wait_for_lockscreen(self):
106 """Wait for the lock screen to load, and return it."""
102 """Wait for the lock screen to load, and return it."""
tests/autopilot/unity8/shell/tests/test_notifications.py+19
@@ -693,3 +693,22 @@ class EphemeralNotificationsTests(NotificationsBase):
693 self.assertThat(get_notification, Eventually(NotEquals(None)))
693 self.assertThat(get_notification, Eventually(NotEquals(None)))
694 self._assert_notification(
694 self._assert_notification(
695 get_notification(), summary, body, False, False, 1.0)
695 get_notification(), summary, body, False, False, 1.0)
696+
697+ def test_notification_helper(self):
698+ """ use the create notification script to get a notification dialog.
699+ Check that the arguments passed to the script match the fields. """
700+
701+ unity_proxy = self.launch_unity()
702+ unlock_unity(unity_proxy)
703+
704+ summary = 'Helper summary'
705+ body = 'Helper body'
706+
707+ notification = shell.create_ephemeral_notification(summary, body)
708+ notification.show()
709+
710+ notification_data = self.main_window.wait_for_notification()
711+
712+ self.assertThat(notification_data['summary'],
713+ Eventually(Equals(summary)))
714+ self.assertThat(notification_data['body'], Eventually(Equals(body)))
tests/mocks/AccountsService/AccountsService.qmltypes+3
@@ -29,5 +29,8 @@ Module {
29 Property { name: "statsWelcomeScreen"; type: "bool" }
29 Property { name: "statsWelcomeScreen"; type: "bool" }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
31 Property { name: "failedLogins"; type: "uint" }
31 Property { name: "failedLogins"; type: "uint" }
32+ Property { name: "hereEnabled"; type: "bool" }
33+ Property { name: "hereLicensePath"; type: "string" }
34+ Property { name: "hereLicensePathValid"; type: "bool"; isReadonly: true }
32 }
35 }
33}
36}
tests/mocks/LightDM/LightDM.qmltypes+33−13
@@ -10,10 +10,6 @@ Module {
10 Component {
10 Component {
11 name: "Greeter"
11 name: "Greeter"
12 prototype: "QObject"
12 prototype: "QObject"
13− exports: ["LightDM/Greeter 0.1"]
14− isCreatable: false
15− isSingleton: true
16− exportMetaObjectRevisions: [0]
17 Property { name: "active"; type: "bool" }
13 Property { name: "active"; type: "bool" }
18 Property { name: "authenticated"; type: "bool"; isReadonly: true }
14 Property { name: "authenticated"; type: "bool"; isReadonly: true }
19 Property { name: "authenticationUser"; type: "string"; isReadonly: true }
15 Property { name: "authenticationUser"; type: "string"; isReadonly: true }
@@ -62,6 +58,32 @@ Module {
62 Parameter { name: "isActive"; type: "bool" }
58 Parameter { name: "isActive"; type: "bool" }
63 }
59 }
64 }
60 }
61+ Component {
62+ name: "MockGreeter"
63+ prototype: "Greeter"
64+ exports: ["LightDM/Greeter 0.1"]
65+ isCreatable: false
66+ isSingleton: true
67+ exportMetaObjectRevisions: [0]
68+ Property { name: "mockMode"; type: "string" }
69+ Signal {
70+ name: "mockModeChanged"
71+ Parameter { name: "mode"; type: "string" }
72+ }
73+ }
74+ Component {
75+ name: "MockUsersModel"
76+ prototype: "UsersModel"
77+ exports: ["LightDM/Users 0.1"]
78+ isCreatable: false
79+ isSingleton: true
80+ exportMetaObjectRevisions: [0]
81+ Property { name: "mockMode"; type: "string" }
82+ Signal {
83+ name: "mockModeChanged"
84+ Parameter { name: "mode"; type: "string" }
85+ }
86+ }
65 Component {
87 Component {
66 name: "QAbstractProxyModel"
88 name: "QAbstractProxyModel"
67 prototype: "QAbstractItemModel"
89 prototype: "QAbstractItemModel"
@@ -86,6 +108,11 @@ Module {
86 "BackgroundPathRole": 263
108 "BackgroundPathRole": 263
87 }
109 }
88 }
110 }
111+ Property { name: "mockMode"; type: "string" }
112+ Signal {
113+ name: "mockModeChanged"
114+ Parameter { name: "mode"; type: "string" }
115+ }
89 }
116 }
90 Component {
117 Component {
91 name: "QSortFilterProxyModel"
118 name: "QSortFilterProxyModel"
@@ -114,7 +141,7 @@ Module {
114 Method { name: "invalidate" }
141 Method { name: "invalidate" }
115 }
142 }
116 Component {
143 Component {
117− name: "QSortFilterProxyModelQML"
144+ name: "UnitySortFilterProxyModelQML"
118 prototype: "QSortFilterProxyModel"
145 prototype: "QSortFilterProxyModel"
119 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
146 Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
120 Property { name: "totalCount"; type: "int"; isReadonly: true }
147 Property { name: "totalCount"; type: "int"; isReadonly: true }
@@ -240,12 +267,5 @@ Module {
240 Method { name: "nextDataSourceSlot" }
267 Method { name: "nextDataSourceSlot" }
241 Method { name: "readyForDataChangeSlot" }
268 Method { name: "readyForDataChangeSlot" }
242 }
269 }
243− Component {
270+ Component { name: "UsersModel"; prototype: "UnitySortFilterProxyModelQML" }
244− name: "UsersModel"
245− prototype: "QSortFilterProxyModelQML"
246− exports: ["LightDM/Users 0.1"]
247− isCreatable: false
248− isSingleton: true
249− exportMetaObjectRevisions: [0]
250− }
251}
271}
tests/mocks/LightDM/liblightdm/Greeter.h−2
@@ -43,8 +43,6 @@ class Q_DECL_EXPORT Greeter : public QObject
43 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
43 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
44 Q_PROPERTY(bool locked READ lockHint CONSTANT)
44 Q_PROPERTY(bool locked READ lockHint CONSTANT)
45
45
46− Q_PROPERTY(QString hostname READ hostname CONSTANT)
47−
48 Q_ENUMS(PromptType MessageType)
46 Q_ENUMS(PromptType MessageType)
49
47
50 //Mock-only API for testing purposes
48 //Mock-only API for testing purposes
tests/mocks/MeeGo/QOfono/CMakeLists.txt+1
@@ -1,6 +1,7 @@
1set(MOCK_QOFONO_SOURCES
1set(MOCK_QOFONO_SOURCES
2 plugin.cpp
2 plugin.cpp
3 MockQOfono.cpp
3 MockQOfono.cpp
4+ MockQOfonoManager.cpp
4)
5)
5
6
6add_library(MockQOfono MODULE ${MOCK_QOFONO_SOURCES})
7add_library(MockQOfono MODULE ${MOCK_QOFONO_SOURCES})
tests/mocks/MeeGo/QOfono/MockOfonoManager.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.3
18−import MeeGo.QOfono 0.2
19−
20−Item {
21− readonly property var modems: MockQOfono.modems
22− readonly property bool available: MockQOfono.available
23−}
tests/mocks/MeeGo/QOfono/MockOfonoSimManager.qml+12−2
@@ -22,21 +22,31 @@ Item {
22
22
23 property string modemPath
23 property string modemPath
24 readonly property alias present: d.present
24 readonly property alias present: d.present
25+ readonly property alias ready: d.ready
25
26
26 QtObject {
27 QtObject {
27 id: d
28 id: d
28
29
29 property bool present: false
30 property bool present: false
31+ property bool ready: false
30
32
31 function updatePresence() {
33 function updatePresence() {
32 d.present = MockQOfono.isModemPresent(simManager.modemPath)
34 d.present = MockQOfono.isModemPresent(simManager.modemPath)
35+ d.ready = MockQOfono.isModemReady(simManager.modemPath);
33 }
36 }
34 }
37 }
35
38
39+ // Simulate QOfono's asynchronous initialization
40+ Timer {
41+ id: asyncTimer
42+ interval: 1
36− onModemPathChanged: d.updatePresence()
43+ onTriggered: d.updatePresence()
44+ }
45+
46+ onModemPathChanged: asyncTimer.start()
37
47
38 Connections {
48 Connections {
39 target: MockQOfono
49 target: MockQOfono
40− onModemsChanged: d.updatePresence()
50+ onModemsChanged: asyncTimer.start()
41 }
51 }
42}
52}
tests/mocks/MeeGo/QOfono/MockQOfono.cpp+41−5
@@ -16,13 +16,31 @@
16
16
17#include "MockQOfono.h"
17#include "MockQOfono.h"
18
18
19−MockQOfono::MockQOfono(QObject *parent)
19+static MockQOfono *m_instance = nullptr;
20+
21+MockQOfono *MockQOfono::instance()
22+{
23+ if (!m_instance) {
24+ m_instance = new MockQOfono();
25+ }
26+ return m_instance;
27+}
28+
29+MockQOfono::MockQOfono()
20− : QObject(parent),
30+ : QObject(),
21 m_available(false),
31 m_available(false),
32+ m_ready(false),
22 m_modems()
33 m_modems()
23{
34{
24}
35}
25
36
37+MockQOfono::~MockQOfono()
38+{
39+ if (m_instance == this) {
40+ m_instance = nullptr;
41+ }
42+}
43+
26bool MockQOfono::available() const
44bool MockQOfono::available() const
27{
45{
28 return m_available;
46 return m_available;
@@ -34,21 +52,39 @@ void MockQOfono::setAvailable(bool available)
34 Q_EMIT availableChanged();
52 Q_EMIT availableChanged();
35}
53}
36
54
55+bool MockQOfono::ready() const
56+{
57+ return m_ready;
58+}
59+
60+void MockQOfono::setReady(bool ready)
61+{
62+ m_ready = ready;
63+ Q_EMIT readyChanged();
64+}
65+
37QStringList MockQOfono::modems() const
66QStringList MockQOfono::modems() const
38{
67{
39 return m_modems.keys();
68 return m_modems.keys();
40}
69}
41
70
42−void MockQOfono::setModems(const QStringList &modems, const QList<bool> &present)
71+void MockQOfono::setModems(const QStringList &modems, const QList<bool> &present, const QList<bool> &ready)
43{
72{
44 m_modems.clear();
73 m_modems.clear();
45 for (int i = 0; i < modems.length(); i++) {
74 for (int i = 0; i < modems.length(); i++) {
75+ QList<bool> props;
76+ props << present.value(i, true) << ready.value(i, true);
46− m_modems[modems[i]] = present[i];
77+ m_modems[modems[i]] = props;
47 }
78 }
48 Q_EMIT modemsChanged();
79 Q_EMIT modemsChanged();
49}
80}
50
81
51bool MockQOfono::isModemPresent(const QString &modem)
82bool MockQOfono::isModemPresent(const QString &modem)
52{
83{
53− return m_modems.value(modem, false);
84+ return m_modems.value(modem).value(0, false);
85+}
86+
87+bool MockQOfono::isModemReady(const QString &modem)
88+{
89+ return m_modems.value(modem).value(1, false);
54}
90}
tests/mocks/MeeGo/QOfono/MockQOfono.h+12−3
@@ -28,25 +28,34 @@ class MockQOfono : public QObject
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30 Q_PROPERTY(bool available READ available WRITE setAvailable NOTIFY availableChanged)
30 Q_PROPERTY(bool available READ available WRITE setAvailable NOTIFY availableChanged)
31+ Q_PROPERTY(bool ready READ ready WRITE setReady NOTIFY readyChanged)
31 Q_PROPERTY(QStringList modems READ modems NOTIFY modemsChanged)
32 Q_PROPERTY(QStringList modems READ modems NOTIFY modemsChanged)
32
33
33public:
34public:
34− explicit MockQOfono(QObject *parent = 0);
35+ static MockQOfono *instance();
36+ ~MockQOfono();
35
37
36 bool available() const;
38 bool available() const;
37 void setAvailable(bool available);
39 void setAvailable(bool available);
40+ bool ready() const;
41+ void setReady(bool ready);
38 QStringList modems() const;
42 QStringList modems() const;
39
43
40− Q_INVOKABLE void setModems(const QStringList &modems, const QList<bool> &present);
44+ Q_INVOKABLE void setModems(const QStringList &modems, const QList<bool> &present, const QList<bool> &ready);
41 Q_INVOKABLE bool isModemPresent(const QString &modem);
45 Q_INVOKABLE bool isModemPresent(const QString &modem);
46+ Q_INVOKABLE bool isModemReady(const QString &modem);
42
47
43Q_SIGNALS:
48Q_SIGNALS:
44 void availableChanged();
49 void availableChanged();
50+ void readyChanged();
45 void modemsChanged();
51 void modemsChanged();
46
52
47private:
53private:
54+ explicit MockQOfono();
55+
48 bool m_available;
56 bool m_available;
57+ bool m_ready;
49− QMap<QString, bool> m_modems;
58+ QMap<QString, QList<bool>> m_modems;
50};
59};
51
60
52#endif // MOCK_QOFONO_H
61#endif // MOCK_QOFONO_H
tests/mocks/MeeGo/QOfono/MockQOfonoManager.cppnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "MockQOfono.h"
18+#include "MockQOfonoManager.h"
19+#include <QTimer>
20+
21+MockQOfonoManager::MockQOfonoManager(QObject *parent)
22+ : QObject(parent),
23+ m_modemsSet(false),
24+ m_startedSet(false)
25+{
26+ connect(MockQOfono::instance(), SIGNAL(availableChanged()),
27+ this, SIGNAL(availableChanged()));
28+ connect(MockQOfono::instance(), SIGNAL(readyChanged()),
29+ this, SLOT(checkReady()));
30+ connect(MockQOfono::instance(), SIGNAL(modemsChanged()),
31+ this, SLOT(maybeModemsChanged()));
32+
33+ checkReady();
34+}
35+
36+bool MockQOfonoManager::available() const
37+{
38+ return MockQOfono::instance()->available();
39+}
40+
41+QStringList MockQOfonoManager::modems() const
42+{
43+ return m_modemsSet ? MockQOfono::instance()->modems() : QStringList();
44+}
45+
46+void MockQOfonoManager::maybeModemsChanged()
47+{
48+ if (m_modemsSet) {
49+ Q_EMIT modemsChanged();
50+ }
51+}
52+
53+void MockQOfonoManager::checkReady()
54+{
55+ if (!m_startedSet && MockQOfono::instance()->ready()) {
56+ m_startedSet = true;
57+ // Simulate QOfono's asynchronous initialization
58+ QTimer::singleShot(1, this, SLOT(setModems()));
59+ }
60+}
61+
62+void MockQOfonoManager::setModems()
63+{
64+ m_modemsSet = true;
65+ Q_EMIT modemsChanged(); // always emitted, even if new modem list is empty too
66+}
tests/mocks/MeeGo/QOfono/MockQOfonoManager.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCK_QOFONO_MANAGER_H
18+#define MOCK_QOFONO_MANAGER_H
19+
20+#include <QObject>
21+#include <QStringList>
22+
23+class MockQOfonoManager : public QObject
24+{
25+ Q_OBJECT
26+ Q_PROPERTY(bool available READ available NOTIFY availableChanged)
27+ Q_PROPERTY(QStringList modems READ modems NOTIFY modemsChanged)
28+
29+public:
30+ explicit MockQOfonoManager(QObject *parent = 0);
31+
32+ bool available() const;
33+ QStringList modems() const;
34+
35+Q_SIGNALS:
36+ void availableChanged();
37+ void modemsChanged();
38+
39+private Q_SLOTS:
40+ void maybeModemsChanged();
41+ void checkReady();
42+ void setModems();
43+
44+private:
45+ bool m_modemsSet;
46+ bool m_startedSet;
47+};
48+
49+#endif // MOCK_QOFONO_MANAGER_H
tests/mocks/MeeGo/QOfono/QOfono.qmltypesnew file+30
@@ -0,0 +1,30 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable MeeGo.QOfono 0.2 tests/mocks'
8+
9+Module {
10+ Component {
11+ name: "MockQOfono"
12+ prototype: "QObject"
13+ exports: ["MeeGo.QOfono/MockQOfono 0.2"]
14+ isCreatable: false
15+ isSingleton: true
16+ exportMetaObjectRevisions: [0]
17+ Property { name: "available"; type: "bool" }
18+ Property { name: "modems"; type: "QStringList"; isReadonly: true }
19+ Method {
20+ name: "setModems"
21+ Parameter { name: "modems"; type: "QStringList" }
22+ Parameter { name: "present"; type: "QList<bool>" }
23+ }
24+ Method {
25+ name: "isModemPresent"
26+ type: "bool"
27+ Parameter { name: "modem"; type: "string" }
28+ }
29+ }
30+}
tests/mocks/MeeGo/QOfono/plugin.cpp+3−1
@@ -16,6 +16,7 @@
16
16
17#include "plugin.h"
17#include "plugin.h"
18#include "MockQOfono.h"
18#include "MockQOfono.h"
19+#include "MockQOfonoManager.h"
19
20
20#include <QtQml>
21#include <QtQml>
21
22
@@ -23,11 +24,12 @@ static QObject *mock_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
23{
24{
24 Q_UNUSED(engine)
25 Q_UNUSED(engine)
25 Q_UNUSED(scriptEngine)
26 Q_UNUSED(scriptEngine)
26− return new MockQOfono();
27+ return MockQOfono::instance();
27}
28}
28
29
29void BackendPlugin::registerTypes(const char *uri)
30void BackendPlugin::registerTypes(const char *uri)
30{
31{
31 Q_ASSERT(uri == QLatin1String("MeeGo.QOfono"));
32 Q_ASSERT(uri == QLatin1String("MeeGo.QOfono"));
32 qmlRegisterSingletonType<MockQOfono>(uri, 0, 2, "MockQOfono", mock_provider); // just for mock
33 qmlRegisterSingletonType<MockQOfono>(uri, 0, 2, "MockQOfono", mock_provider); // just for mock
34+ qmlRegisterType<MockQOfonoManager>(uri, 0, 2, "OfonoManager");
33}
35}
tests/mocks/MeeGo/QOfono/qmldir−1
@@ -1,4 +1,3 @@
1module MeeGo.QOfono
1module MeeGo.QOfono
2plugin MockQOfono
2plugin MockQOfono
3−OfonoManager 0.2 MockOfonoManager.qml
4OfonoSimManager 0.2 MockOfonoSimManager.qml
3OfonoSimManager 0.2 MockOfonoSimManager.qml
tests/mocks/QMenuModel/QMenuModel.qmltypes+33
@@ -7,12 +7,45 @@ import QtQuick.tooling 1.1
7// 'qmlplugindump -notrelocatable QMenuModel 0.1 tests/mocks'
7// 'qmlplugindump -notrelocatable QMenuModel 0.1 tests/mocks'
8
8
9Module {
9Module {
10+ Component {
11+ name: "ActionData"
12+ prototype: "QObject"
13+ exports: ["QMenuModel/ActionData 0.1"]
14+ isCreatable: false
15+ isSingleton: true
16+ exportMetaObjectRevisions: [0]
17+ Property { name: "data"; type: "QVariant" }
18+ }
10 Component {
19 Component {
11 name: "ActionStateParser"
20 name: "ActionStateParser"
12 prototype: "QObject"
21 prototype: "QObject"
13 exports: ["QMenuModel/ActionStateParser 0.1"]
22 exports: ["QMenuModel/ActionStateParser 0.1"]
14 exportMetaObjectRevisions: [0]
23 exportMetaObjectRevisions: [0]
15 }
24 }
25+ Component {
26+ name: "DBusEnums"
27+ prototype: "QObject"
28+ exports: ["QMenuModel/DBus 0.1"]
29+ isCreatable: false
30+ exportMetaObjectRevisions: [0]
31+ Enum {
32+ name: "BusType"
33+ values: {
34+ "None": 0,
35+ "SessionBus": 1,
36+ "SystemBus": 2,
37+ "LastBusType": 3
38+ }
39+ }
40+ Enum {
41+ name: "ConnectionStatus"
42+ values: {
43+ "Disconnected": 0,
44+ "Connecting": 1,
45+ "Connected": 2
46+ }
47+ }
48+ }
16 Component {
49 Component {
17 name: "UnityMenuModel"
50 name: "UnityMenuModel"
18 prototype: "QAbstractListModel"
51 prototype: "QAbstractListModel"
tests/mocks/Ubuntu/SystemSettings/LanguagePlugin/LanguagePlugin.qmltypesnew file+10
@@ -0,0 +1,10 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Ubuntu.SystemSettings.LanguagePlugin 1.0 tests/mocks'
8+
9+Module {
10+}
tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/SecurityPrivacy.qmltypesnew file+39
@@ -0,0 +1,39 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Ubuntu.SystemSettings.SecurityPrivacy 1.0 tests/mocks'
8+
9+Module {
10+ Component {
11+ name: "MockSecurityPrivacy"
12+ prototype: "QObject"
13+ exports: [
14+ "Ubuntu.SystemSettings.SecurityPrivacy/UbuntuSecurityPrivacyPanel 1.0"
15+ ]
16+ exportMetaObjectRevisions: [0]
17+ Enum {
18+ name: "SecurityType"
19+ values: {
20+ "Swipe": 0,
21+ "Passcode": 1,
22+ "Passphrase": 2
23+ }
24+ }
25+ Signal {
26+ name: "setSecurityCalled"
27+ Parameter { name: "oldPasswd"; type: "string" }
28+ Parameter { name: "newPasswd"; type: "string" }
29+ Parameter { name: "newType"; type: "SecurityType" }
30+ }
31+ Method {
32+ name: "setSecurity"
33+ type: "string"
34+ Parameter { name: "oldPasswd"; type: "string" }
35+ Parameter { name: "newPasswd"; type: "string" }
36+ Parameter { name: "newType"; type: "SecurityType" }
37+ }
38+ }
39+}
tests/mocks/Unity/Application/Application.qmltypes+42
@@ -239,6 +239,8 @@ Module {
239 Property { name: "name"; type: "string"; isReadonly: true }
239 Property { name: "name"; type: "string"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
241 Property { name: "orientation"; type: "Qt::ScreenOrientation" }
241 Property { name: "orientation"; type: "Qt::ScreenOrientation" }
242+ Property { name: "touchPressCount"; type: "int" }
243+ Property { name: "touchReleaseCount"; type: "int" }
242 Signal {
244 Signal {
243 name: "typeChanged"
245 name: "typeChanged"
244 Parameter { type: "Type" }
246 Parameter { type: "Type" }
@@ -251,6 +253,14 @@ Module {
251 name: "liveChanged"
253 name: "liveChanged"
252 Parameter { name: "live"; type: "bool" }
254 Parameter { name: "live"; type: "bool" }
253 }
255 }
256+ Signal {
257+ name: "touchPressCountChanged"
258+ Parameter { name: "count"; type: "int" }
259+ }
260+ Signal {
261+ name: "touchReleaseCountChanged"
262+ Parameter { name: "count"; type: "int" }
263+ }
254 Signal { name: "inputMethodRequested" }
264 Signal { name: "inputMethodRequested" }
255 Signal { name: "inputMethodDismissed" }
265 Signal { name: "inputMethodDismissed" }
256 Signal { name: "deregister" }
266 Signal { name: "deregister" }
@@ -296,6 +306,10 @@ Module {
296 name: "addChildSession"
306 name: "addChildSession"
297 Parameter { name: "session"; type: "Session"; isPointer: true }
307 Parameter { name: "session"; type: "Session"; isPointer: true }
298 }
308 }
309+ Method {
310+ name: "removeChildSession"
311+ Parameter { name: "session"; type: "Session"; isPointer: true }
312+ }
299 }
313 }
300 Component {
314 Component {
301 name: "SessionManager"
315 name: "SessionManager"
@@ -345,6 +359,34 @@ Module {
345 }
359 }
346 Method { name: "inputMethodSurface"; type: "MirSurfaceItem*" }
360 Method { name: "inputMethodSurface"; type: "MirSurfaceItem*" }
347 }
361 }
362+ Component {
363+ name: "UbuntuKeyboardInfo"
364+ prototype: "QObject"
365+ exports: ["Unity.Application/UbuntuKeyboardInfo 0.1"]
366+ isCreatable: false
367+ isSingleton: true
368+ exportMetaObjectRevisions: [0]
369+ Property { name: "x"; type: "double"; isReadonly: true }
370+ Property { name: "y"; type: "double"; isReadonly: true }
371+ Property { name: "width"; type: "double"; isReadonly: true }
372+ Property { name: "height"; type: "double"; isReadonly: true }
373+ Signal {
374+ name: "xChanged"
375+ Parameter { name: "x"; type: "double" }
376+ }
377+ Signal {
378+ name: "yChanged"
379+ Parameter { name: "y"; type: "double" }
380+ }
381+ Signal {
382+ name: "widthChanged"
383+ Parameter { name: "width"; type: "double" }
384+ }
385+ Signal {
386+ name: "heightChanged"
387+ Parameter { name: "height"; type: "double" }
388+ }
389+ }
348 Component {
390 Component {
349 name: "unity::shell::application::ApplicationInfoInterface"
391 name: "unity::shell::application::ApplicationInfoInterface"
350 prototype: "QObject"
392 prototype: "QObject"
tests/mocks/Unity/Indicators/Indicators.qmltypes+7−33
@@ -29,6 +29,7 @@ Module {
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
30 exportMetaObjectRevisions: [0]
30 exportMetaObjectRevisions: [0]
31 Property { name: "count"; type: "int"; isReadonly: true }
31 Property { name: "count"; type: "int"; isReadonly: true }
32+ Property { name: "profile"; type: "string" }
32 Property { name: "modelData"; type: "QVariant" }
33 Property { name: "modelData"; type: "QVariant" }
33 Method {
34 Method {
34 name: "load"
35 name: "load"
@@ -64,16 +65,17 @@ Module {
64 exportMetaObjectRevisions: [0]
65 exportMetaObjectRevisions: [0]
65 Method {
66 Method {
66 name: "setCachedModelData"
67 name: "setCachedModelData"
67− Parameter { name: "bus"; type: "QByteArray" }
68 Parameter { name: "path"; type: "QByteArray" }
68 Parameter { name: "path"; type: "QByteArray" }
69− Parameter { name: "actions"; type: "QVariantMap" }
70 Parameter { name: "data"; type: "QVariant" }
69 Parameter { name: "data"; type: "QVariant" }
71 }
70 }
72 Method {
71 Method {
73 name: "setCachedModelData"
72 name: "setCachedModelData"
74− Parameter { name: "bus"; type: "QByteArray" }
75 Parameter { name: "path"; type: "QByteArray" }
73 Parameter { name: "path"; type: "QByteArray" }
74+ }
75+ Method {
76+ name: "getCachedModelData"
77+ type: "QVariant"
76− Parameter { name: "actions"; type: "QVariantMap" }
78+ Parameter { name: "path"; type: "QByteArray" }
77 }
79 }
78 }
80 }
79 Component {
81 Component {
@@ -105,8 +107,7 @@ Module {
105 values: {
107 values: {
106 "Identifier": 0,
108 "Identifier": 0,
107 "Position": 1,
109 "Position": 1,
108− "IndicatorProperties": 2,
110+ "IndicatorProperties": 2
109− "IsVisible": 3
110 }
111 }
111 }
112 }
112 }
113 }
@@ -182,12 +183,6 @@ Module {
182 }
183 }
183 }
184 }
184 }
185 }
185− Component {
186− name: "QAbstractProxyModel"
187− prototype: "QAbstractItemModel"
188− Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
189− }
190− Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
191 Component {
186 Component {
192 name: "SharedUnityMenuModel"
187 name: "SharedUnityMenuModel"
193 prototype: "QObject"
188 prototype: "QObject"
@@ -234,25 +229,4 @@ Module {
234 }
229 }
235 Method { name: "pop"; type: "UnityMenuModel*" }
230 Method { name: "pop"; type: "UnityMenuModel*" }
236 }
231 }
237− Component {
238− name: "VisibleIndicatorsModel"
239− prototype: "QIdentityProxyModel"
240− exports: ["Unity.Indicators/VisibleIndicatorsModel 0.1"]
241− exportMetaObjectRevisions: [0]
242− Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
243− Property { name: "visible"; type: "QVariantMap" }
244− Signal { name: "totalCountChanged" }
245− Method {
246− name: "onBeginRowInserted"
247− Parameter { type: "QModelIndex" }
248− Parameter { name: "start"; type: "int" }
249− Parameter { name: "end"; type: "int" }
250− }
251− Method {
252− name: "onRowInserted"
253− Parameter { type: "QModelIndex" }
254− Parameter { name: "start"; type: "int" }
255− Parameter { name: "end"; type: "int" }
256− }
257− }
258}
232}
tests/mocks/Unity/Launcher/Launcher.qmltypes+3
@@ -63,6 +63,7 @@ Module {
63 type: "string"
63 type: "string"
64 Parameter { name: "appId"; type: "string" }
64 Parameter { name: "appId"; type: "string" }
65 }
65 }
66+ Method { name: "emitHint" }
66 }
67 }
67 Component {
68 Component {
68 name: "MockQuickListModel"
69 name: "MockQuickListModel"
@@ -141,6 +142,8 @@ Module {
141 type: "unity::shell::application::ApplicationManagerInterface"
142 type: "unity::shell::application::ApplicationManagerInterface"
142 isPointer: true
143 isPointer: true
143 }
144 }
145+ Property { name: "onlyPinned"; type: "bool" }
146+ Signal { name: "hint" }
144 Method {
147 Method {
145 name: "move"
148 name: "move"
146 Parameter { name: "oldIndex"; type: "int" }
149 Parameter { name: "oldIndex"; type: "int" }
tests/mocks/Unity/Notifications/MockActionModel.cpp+1−1
@@ -30,7 +30,7 @@ ActionModel::ActionModel(QObject *parent) : QStringListModel(parent), p(new Acti
30ActionModel::~ActionModel() {
30ActionModel::~ActionModel() {
31}
31}
32
32
33−int ActionModel::rowCount(const QModelIndex &index) const {
33+int ActionModel::rowCount(const QModelIndex &) const {
34 return p->labels.size();
34 return p->labels.size();
35}
35}
36
36
tests/mocks/Unity/Notifications/MockNotification.cpp+4
@@ -140,6 +140,10 @@ ActionModel* MockNotification::getActions() const {
140 return p->actionsModel;
140 return p->actionsModel;
141}
141}
142
142
143+QStringList MockNotification::rawActions() const {
144+ return p->actions;
145+}
146+
143void MockNotification::setActions(const QStringList &actions) {
147void MockNotification::setActions(const QStringList &actions) {
144 if(p->actions != actions) {
148 if(p->actions != actions) {
145 p->actions = actions;
149 p->actions = actions;
tests/mocks/Unity/Notifications/MockNotification.h+2−1
@@ -38,7 +38,7 @@ class MockNotification : public QObject {
38 Q_PROPERTY(QString icon READ getIcon WRITE setIcon NOTIFY iconChanged)
38 Q_PROPERTY(QString icon READ getIcon WRITE setIcon NOTIFY iconChanged)
39 Q_PROPERTY(QString secondaryIcon READ getSecondaryIcon WRITE setSecondaryIcon NOTIFY secondaryIconChanged)
39 Q_PROPERTY(QString secondaryIcon READ getSecondaryIcon WRITE setSecondaryIcon NOTIFY secondaryIconChanged)
40 Q_PROPERTY(Type type READ getType WRITE setType NOTIFY typeChanged)
40 Q_PROPERTY(Type type READ getType WRITE setType NOTIFY typeChanged)
41− Q_PROPERTY(QStringList rawActions WRITE setActions)
41+ Q_PROPERTY(QStringList rawActions READ rawActions WRITE setActions)
42 Q_PROPERTY(ActionModel* actions READ getActions NOTIFY actionsChanged)
42 Q_PROPERTY(ActionModel* actions READ getActions NOTIFY actionsChanged)
43 Q_PROPERTY(QVariantMap hints READ getHints WRITE setHints NOTIFY hintsChanged)
43 Q_PROPERTY(QVariantMap hints READ getHints WRITE setHints NOTIFY hintsChanged)
44
44
@@ -83,6 +83,7 @@ public:
83 Type getType() const;
83 Type getType() const;
84 void setType(Type type);
84 void setType(Type type);
85 ActionModel* getActions() const;
85 ActionModel* getActions() const;
86+ QStringList rawActions() const;
86 void setActions(const QStringList &actions);
87 void setActions(const QStringList &actions);
87 QVariantMap getHints() const;
88 QVariantMap getHints() const;
88 void setHints(const QVariantMap& hints);
89 void setHints(const QVariantMap& hints);
tests/mocks/Unity/Notifications/MockNotificationModel.cpp+1−1
@@ -37,7 +37,7 @@ MockNotificationModel::MockNotificationModel(QObject *parent) : QAbstractListMod
37MockNotificationModel::~MockNotificationModel() {
37MockNotificationModel::~MockNotificationModel() {
38}
38}
39
39
40−int MockNotificationModel::rowCount(const QModelIndex &parent) const {
40+int MockNotificationModel::rowCount(const QModelIndex &) const {
41 return m_queue.size();
41 return m_queue.size();
42}
42}
43
43
tests/mocks/Unity/Notifications/Notifications.qmltypes+139
@@ -7,4 +7,143 @@ import QtQuick.tooling 1.1
7// 'qmlplugindump -notrelocatable Unity.Notifications 1.0 tests/mocks'
7// 'qmlplugindump -notrelocatable Unity.Notifications 1.0 tests/mocks'
8
8
9Module {
9Module {
10+ Component {
11+ name: "ActionModel"
12+ prototype: "QStringListModel"
13+ exports: ["Unity.Notifications/ActionModel 1.0"]
14+ exportMetaObjectRevisions: [0]
15+ Enum {
16+ name: "ActionsRoles"
17+ values: {
18+ "RoleActionLabel": 257,
19+ "RoleActionId": 258
20+ }
21+ }
22+ Property { name: "count"; type: "int"; isReadonly: true }
23+ Method {
24+ name: "data"
25+ type: "QVariant"
26+ Parameter { name: "row"; type: "int" }
27+ Parameter { name: "role"; type: "int" }
28+ }
29+ Method {
30+ name: "append"
31+ Parameter { name: "id"; type: "string" }
32+ Parameter { name: "label"; type: "string" }
33+ }
34+ }
35+ Component {
36+ name: "MockNotification"
37+ prototype: "QObject"
38+ exports: ["Unity.Notifications/Notification 1.0"]
39+ exportMetaObjectRevisions: [0]
40+ Enum {
41+ name: "Type"
42+ values: {
43+ "PlaceHolder": 0,
44+ "Confirmation": 1,
45+ "Ephemeral": 2,
46+ "Interactive": 3,
47+ "SnapDecision": 4
48+ }
49+ }
50+ Property { name: "summary"; type: "string" }
51+ Property { name: "body"; type: "string" }
52+ Property { name: "nid"; type: "int" }
53+ Property { name: "value"; type: "int" }
54+ Property { name: "icon"; type: "string" }
55+ Property { name: "secondaryIcon"; type: "string" }
56+ Property { name: "type"; type: "Type" }
57+ Property { name: "rawActions"; type: "QStringList" }
58+ Property { name: "actions"; type: "ActionModel"; isReadonly: true; isPointer: true }
59+ Property { name: "hints"; type: "QVariantMap" }
60+ Signal {
61+ name: "summaryChanged"
62+ Parameter { name: "summary"; type: "string" }
63+ }
64+ Signal {
65+ name: "bodyChanged"
66+ Parameter { name: "body"; type: "string" }
67+ }
68+ Signal {
69+ name: "idChanged"
70+ Parameter { name: "id"; type: "int" }
71+ }
72+ Signal {
73+ name: "valueChanged"
74+ Parameter { name: "value"; type: "int" }
75+ }
76+ Signal {
77+ name: "iconChanged"
78+ Parameter { name: "icon"; type: "string" }
79+ }
80+ Signal {
81+ name: "secondaryIconChanged"
82+ Parameter { name: "secondaryIcon"; type: "string" }
83+ }
84+ Signal {
85+ name: "typeChanged"
86+ Parameter { name: "type"; type: "Type" }
87+ }
88+ Signal {
89+ name: "actionsChanged"
90+ Parameter { name: "actions"; type: "QStringList" }
91+ }
92+ Signal {
93+ name: "hintsChanged"
94+ Parameter { name: "hints"; type: "QVariantMap" }
95+ }
96+ Signal {
97+ name: "dataChanged"
98+ Parameter { name: "nid"; type: "int" }
99+ }
100+ Signal {
101+ name: "completed"
102+ Parameter { name: "nid"; type: "int" }
103+ }
104+ Signal {
105+ name: "actionInvoked"
106+ Parameter { name: "action"; type: "string" }
107+ }
108+ Method {
109+ name: "invokeAction"
110+ Parameter { name: "action"; type: "string" }
111+ }
112+ Method { name: "close" }
113+ }
114+ Component {
115+ name: "MockNotificationModel"
116+ prototype: "QAbstractListModel"
117+ exports: ["Unity.Notifications/NotificationModel 1.0"]
118+ exportMetaObjectRevisions: [0]
119+ Property { name: "count"; type: "int"; isReadonly: true }
120+ Signal {
121+ name: "actionInvoked"
122+ Parameter { name: "action"; type: "string" }
123+ }
124+ Signal {
125+ name: "queueSizeChanged"
126+ Parameter { name: "newSize"; type: "int" }
127+ }
128+ Method {
129+ name: "onCompleted"
130+ Parameter { name: "id"; type: "int" }
131+ }
132+ Method {
133+ name: "append"
134+ Parameter { name: "n"; type: "MockNotification"; isPointer: true }
135+ }
136+ Method {
137+ name: "getRaw"
138+ type: "MockNotification*"
139+ Parameter { name: "notificationId"; type: "int" }
140+ }
141+ Method { name: "queued"; type: "int" }
142+ Method {
143+ name: "remove"
144+ Parameter { name: "id"; type: "int" }
145+ }
146+ Method { name: "removeSecond" }
147+ }
148+ Component { name: "QStringListModel"; prototype: "QAbstractListModel" }
10}
149}
tests/mocks/Unity/Unity.qmltypes+21
@@ -169,7 +169,18 @@ Module {
169 Parameter { name: "scope"; type: "Scope"; isPointer: true }
169 Parameter { name: "scope"; type: "Scope"; isPointer: true }
170 }
170 }
171 Method { name: "clear" }
171 Method { name: "clear" }
172+ Method { name: "clearFavorites" }
172 Method { name: "load" }
173 Method { name: "load" }
174+ Method {
175+ name: "setFavorite"
176+ Parameter { name: "scopeId"; type: "string" }
177+ Parameter { name: "favorite"; type: "bool" }
178+ }
179+ Method {
180+ name: "moveFavoriteTo"
181+ Parameter { name: "scopeId"; type: "string" }
182+ Parameter { name: "index"; type: "int" }
183+ }
173 Method {
184 Method {
174 name: "getScopeFromAll"
185 name: "getScopeFromAll"
175 type: "Scope*"
186 type: "Scope*"
@@ -443,6 +454,16 @@ Module {
443 type: "unity::shell::scopes::ScopeInterface*"
454 type: "unity::shell::scopes::ScopeInterface*"
444 Parameter { name: "scopeId"; type: "string" }
455 Parameter { name: "scopeId"; type: "string" }
445 }
456 }
457+ Method {
458+ name: "setFavorite"
459+ Parameter { name: "scopeId"; type: "string" }
460+ Parameter { name: "favorite"; type: "bool" }
461+ }
462+ Method {
463+ name: "moveFavoriteTo"
464+ Parameter { name: "scopeId"; type: "string" }
465+ Parameter { name: "index"; type: "int" }
466+ }
446 }
467 }
447 Component {
468 Component {
448 name: "unity::shell::scopes::SettingsModelInterface"
469 name: "unity::shell::scopes::SettingsModelInterface"
tests/mocks/Wizard/Wizard.qmltypes+4−3
@@ -15,6 +15,10 @@ Module {
15 isSingleton: true
15 isSingleton: true
16 exportMetaObjectRevisions: [0]
16 exportMetaObjectRevisions: [0]
17 Property { name: "wizardEnabled"; type: "bool" }
17 Property { name: "wizardEnabled"; type: "bool" }
18+ Signal {
19+ name: "updateSessionLanguageCalled"
20+ Parameter { name: "locale"; type: "string" }
21+ }
18 Method {
22 Method {
19 name: "updateSessionLanguage"
23 name: "updateSessionLanguage"
20 Parameter { name: "locale"; type: "string" }
24 Parameter { name: "locale"; type: "string" }
@@ -24,8 +28,6 @@ Module {
24 name: "PageList"
28 name: "PageList"
25 prototype: "QObject"
29 prototype: "QObject"
26 exports: ["Wizard/PageList 0.1"]
30 exports: ["Wizard/PageList 0.1"]
27− isCreatable: false
28− isSingleton: true
29 exportMetaObjectRevisions: [0]
31 exportMetaObjectRevisions: [0]
30 Property { name: "index"; type: "int"; isReadonly: true }
32 Property { name: "index"; type: "int"; isReadonly: true }
31 Property { name: "numPages"; type: "int"; isReadonly: true }
33 Property { name: "numPages"; type: "int"; isReadonly: true }
@@ -33,4 +35,3 @@ Module {
33 Method { name: "next"; type: "string" }
35 Method { name: "next"; type: "string" }
34 }
36 }
35}
37}
36−
tests/plugins/Dash/cardcreator/2.res+1
@@ -109,6 +109,7 @@ CroppedImageMinimumSourceSize {
109 }
109 }
110 anchors.topMargin: units.dp(2);
110 anchors.topMargin: units.dp(2);
111 elide: Text.ElideRight;
111 elide: Text.ElideRight;
112+ maximumLineCount: 1;
112 fontSize: "x-small";
113 fontSize: "x-small";
113 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
114 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
115 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
tests/plugins/Dash/cardcreator/3.res+1
@@ -89,6 +89,7 @@ Label {
89 }
89 }
90 anchors.topMargin: units.dp(2);
90 anchors.topMargin: units.dp(2);
91 elide: Text.ElideRight;
91 elide: Text.ElideRight;
92+ maximumLineCount: 1;
92 fontSize: "x-small";
93 fontSize: "x-small";
93 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
94 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
94 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
95 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
tests/plugins/Dash/cardcreator/4.res+1
@@ -89,6 +89,7 @@ Label {
89 }
89 }
90 anchors.topMargin: units.dp(2);
90 anchors.topMargin: units.dp(2);
91 elide: Text.ElideRight;
91 elide: Text.ElideRight;
92+ maximumLineCount: 1;
92 fontSize: "x-small";
93 fontSize: "x-small";
93 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
94 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
94 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
95 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
tests/plugins/Dash/cardcreator/5.res+1
@@ -134,6 +134,7 @@ Label {
134 }
134 }
135 anchors.topMargin: units.dp(2);
135 anchors.topMargin: units.dp(2);
136 elide: Text.ElideRight;
136 elide: Text.ElideRight;
137+ maximumLineCount: 1;
137 fontSize: "x-small";
138 fontSize: "x-small";
138 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
139 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
139 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
140 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
tests/plugins/Dash/cardcreator/6.res+2
@@ -88,6 +88,7 @@ Item {
88 }
88 }
89 anchors.topMargin: units.dp(2);
89 anchors.topMargin: units.dp(2);
90 elide: Text.ElideRight;
90 elide: Text.ElideRight;
91+ maximumLineCount: 1;
91 fontSize: "x-small";
92 fontSize: "x-small";
92 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
93 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
93 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
94 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
@@ -106,6 +107,7 @@ Item {
106 source: cardData && cardData["emblem"] || "";
107 source: cardData && cardData["emblem"] || "";
107 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
108 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
108 height: source != "" ? titleLabel.font.pixelSize : 0;
109 height: source != "" ? titleLabel.font.pixelSize : 0;
110+ width: implicitWidth > 0 && implicitHeight > 0 ? (implicitWidth / implicitHeight * height) : implicitWidth;
109 }
111 }
110
112
111 ]
113 ]
tests/plugins/Dash/cardcreator/7.res+1
@@ -110,6 +110,7 @@ CroppedImageMinimumSourceSize {
110 }
110 }
111 anchors.topMargin: units.dp(2);
111 anchors.topMargin: units.dp(2);
112 elide: Text.ElideRight;
112 elide: Text.ElideRight;
113+ maximumLineCount: 1;
113 fontSize: "x-small";
114 fontSize: "x-small";
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
116 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
tests/plugins/LightDM/CMakeLists.txt+19−4
@@ -4,17 +4,32 @@ add_executable(GreeterDBusTestExec
4 )
4 )
5qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
5qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
6
6
7+add_executable(GreeterUsersModelTestExec
8+ usersmodel.cpp
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
10+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11+ )
12+qt5_use_modules(GreeterUsersModelTestExec Core Test)
13+
7include_directories(
14include_directories(
8 ${CMAKE_CURRENT_BINARY_DIR}
15 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_SOURCE_DIR}/plugins/LightDM
16 ${CMAKE_SOURCE_DIR}/plugins/LightDM
17+ ${CMAKE_SOURCE_DIR}/plugins/Utils
10 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
18 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
11− ${LIBLIGHTDM_INCLUDE_DIRS}
12 )
19 )
13
20
14−add_dependencies(GreeterDBusTestExec MockLightDM)
21+target_link_libraries(GreeterDBusTestExec
22+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
23+ -llightdm-qt5-2
24+ )
25+
26+target_link_libraries(GreeterUsersModelTestExec
15−target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
27+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
16− -llightdm-qt5-2)
28+ -llightdm-qt5-2
29+ )
17
30
18add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
31add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
19
32
20add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
33add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
34+
35+add_binary_qml_test(GreeterUsersModel "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "LIBLIGHTDM_MOCK_MODE=full")
tests/plugins/LightDM/usersmodel.cppnew file+84
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "UsersModel.h"
18+
19+#include <QLightDM/UsersModel>
20+#include <QtTest>
21+
22+class GreeterUsersModelTest : public QObject
23+{
24+ Q_OBJECT
25+
26+private:
27+ static int findName(QAbstractItemModel *model, const QString &name)
28+ {
29+ for (int i = 0; i < model->rowCount(QModelIndex()); i++) {
30+ if (model->data(model->index(i, 0), QLightDM::UsersModel::NameRole).toString() == name) {
31+ return i;
32+ }
33+ }
34+ return -1;
35+ }
36+
37+ static QString getStringValue(QAbstractItemModel *model, const QString &name, int role)
38+ {
39+ int i = findName(model, name);
40+ return model->data(model->index(i, 0), role).toString();
41+ }
42+
43+private Q_SLOTS:
44+
45+ void init()
46+ {
47+ model = new UsersModel();
48+ QVERIFY(model);
49+ sourceModel = new QLightDM::UsersModel();
50+ QVERIFY(sourceModel);
51+ }
52+
53+ void cleanup()
54+ {
55+ delete model;
56+ delete sourceModel;
57+ }
58+
59+ void testMangleColor()
60+ {
61+ QString background = getStringValue(sourceModel, "color-background", QLightDM::UsersModel::BackgroundPathRole);
62+ QVERIFY(background == "#dd4814");
63+
64+ background = getStringValue(model, "color-background", QLightDM::UsersModel::BackgroundPathRole);
65+ QVERIFY(background == "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>");
66+ }
67+
68+ void testMangleEmptyName()
69+ {
70+ QString name = getStringValue(sourceModel, "empty-name", QLightDM::UsersModel::RealNameRole);
71+ QVERIFY(name == "");
72+
73+ name = getStringValue(model, "empty-name", QLightDM::UsersModel::RealNameRole);
74+ QVERIFY(name == "empty-name");
75+ }
76+
77+private:
78+ UsersModel *model;
79+ QLightDM::UsersModel *sourceModel;
80+};
81+
82+QTEST_MAIN(GreeterUsersModelTest)
83+
84+#include "usersmodel.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+9
@@ -20,6 +20,9 @@
20#include <QtQml/QQmlEngine>
20#include <QtQml/QQmlEngine>
21#include <QPointer>
21#include <QPointer>
22#include <private/qquickmousearea_p.h>
22#include <private/qquickmousearea_p.h>
23+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
24+ #include <private/qquickwindow_p.h>
25+#endif
23
26
24
27
25#include <DirectionalDragArea.h>
28#include <DirectionalDragArea.h>
@@ -814,10 +817,16 @@ void tst_DirectionalDragArea::sceneDistance()
814
817
815 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
818 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
816
819
820+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
821+ QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
822+#endif
817 for (int i = 0; i < totalMovementSteps; ++i) {
823 for (int i = 0; i < totalMovementSteps; ++i) {
818 touchPoint += touchMovement;
824 touchPoint += touchMovement;
819 passTime(movementTimeStepMs);
825 passTime(movementTimeStepMs);
820 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
826 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
827+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828+ wp->flushDelayedTouchEvent();
829+#endif
821 }
830 }
822
831
823 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
832 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+7
@@ -19,6 +19,9 @@
19#include <QQmlEngine>
19#include <QQmlEngine>
20#include <QQuickView>
20#include <QQuickView>
21#include <QSharedPointer>
21#include <QSharedPointer>
22+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
23+ #include <private/qquickwindow_p.h>
24+#endif
22
25
23// C++ std lib
26// C++ std lib
24#include <functional>
27#include <functional>
@@ -192,6 +195,10 @@ void tst_TouchGate::holdsEventsUntilGainsOwnership()
192
195
193 if (!ownershipAfterTouchEnd) {
196 if (!ownershipAfterTouchEnd) {
194 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
197 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
198+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
199+ QQuickWindowPrivate *wp = QQuickWindowPrivate::get(testItem->window());
200+ wp->flushDelayedTouchEvent();
201+#endif
195 // TouchGate should now open its flood gates and let testItem get all
202 // TouchGate should now open its flood gates and let testItem get all
196 // events from touch 0 produced so far
203 // events from touch 0 produced so far
197 QCOMPARE(testItem->touchEventsReceived.count(), 2);
204 QCOMPARE(testItem->touchEventsReceived.count(), 2);
tests/qmltests/CMakeLists.txt+4−3
@@ -60,10 +60,11 @@ add_qml_test(Dash/ScopeSettings ScopeSettingList)
60add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
60add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
61add_qml_test(Dash/ScopeSettings ScopeSettingString)
61add_qml_test(Dash/ScopeSettings ScopeSettingString)
62add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
62add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
63−add_qml_test(Greeter MultiGreeter)
64−add_qml_test(Greeter SingleGreeter)
65−add_qml_test(Greeter Infographics)
66add_qml_test(Greeter Clock)
63add_qml_test(Greeter Clock)
64+add_qml_test(Greeter Greeter)
65+add_qml_test(Greeter Infographics)
66+add_qml_test(Greeter NarrowView)
67+add_qml_test(Greeter WideView)
67add_qml_test(Launcher Launcher)
68add_qml_test(Launcher Launcher)
68add_qml_test(Notifications Notifications)
69add_qml_test(Notifications Notifications)
69add_qml_test(Notifications VisualSnapDecisionsQueue)
70add_qml_test(Notifications VisualSnapDecisionsQueue)
tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+1−1
@@ -196,7 +196,7 @@ Rectangle {
196 compare(args[1], "rated");
196 compare(args[1], "rated");
197 compare(args[2]["rating"], data.triggeredData["rating"]);
197 compare(args[2]["rating"], data.triggeredData["rating"]);
198 compare(args[2]["review"], data.triggeredData["review"]);
198 compare(args[2]["review"], data.triggeredData["review"]);
199− verify(args[2]["author"]); // Just verifying it exists now
199+ verify(args[2]["author"] !== undefined); // Just verifying it exists now
200 }
200 }
201 }
201 }
202}
202}
tests/qmltests/Dash/tst_Dash.qml+22
@@ -429,6 +429,28 @@ Item {
429 tryCompare(dashContentList, "currentIndex", 1)
429 tryCompare(dashContentList, "currentIndex", 1)
430 }
430 }
431
431
432+ function test_preview_no_show_manage_dash_hint() {
433+ var dashContentList = findChild(dash, "dashContentList");
434+ compare(dashContentList.currentIndex, 0);
435+ compare(dashContentList.currentItem.scopeId, "MockScope1");
436+
437+ tryCompareFunction(function() {
438+ var cardGrid = findChild(dashContentList, "dashCategory0");
439+ if (cardGrid != null) {
440+ var tile = findChild(cardGrid, "delegate0");
441+ return tile != null;
442+ }
443+ return false;
444+ },
445+ true);
446+ var tile = findChild(findChild(dashContentList, "dashCategory0"), "delegate0");
447+ waitForRendering(tile);
448+ mouseClick(tile);
449+
450+ var overviewHint = findChild(dash, "overviewHint");
451+ tryCompare(overviewHint, "opacity", 0);
452+ }
453+
432 function test_close_temp_scope_preview_opening_scope() {
454 function test_close_temp_scope_preview_opening_scope() {
433 // Show the manage dash
455 // Show the manage dash
434 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
456 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
tests/qmltests/Greeter/TestView.qmlnew file+99
@@ -0,0 +1,99 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+Item {
22+ objectName: "testView"
23+
24+ property real dragHandleLeftMargin
25+ property real launcherOffset
26+ property int currentIndex
27+ property int delayMinutes
28+ property real backgroundTopMargin
29+ property url background
30+ property bool locked
31+ property bool alphanumeric
32+ property var userModel
33+ property var infographicModel
34+ readonly property bool fullyShown: _fullyShown
35+ readonly property bool required: _required
36+
37+ property bool _fullyShown: true
38+ property bool _required: true
39+
40+ signal selected(int index)
41+ signal responded(string response)
42+ signal tease()
43+ signal emergencyCall()
44+
45+ signal _showMessageCalled(string html)
46+ signal _showPromptCalled(string text, bool isSecret, bool isDefaultPrompt)
47+ signal _showLastChanceCalled()
48+ signal _hideCalled()
49+ signal _notifyAuthenticationSucceededCalled()
50+ signal _notifyAuthenticationFailedCalled()
51+ signal _resetCalled()
52+ signal _tryToUnlockCalled(bool toTheRight)
53+
54+ function showMessage(html) {
55+ _showMessageCalled(html);
56+ }
57+
58+ function showPrompt(text, isSecret, isDefaultPrompt) {
59+ _showPromptCalled(text, isSecret, isDefaultPrompt);
60+ }
61+
62+ function showLastChance() {
63+ _showLastChanceCalled();
64+ }
65+
66+ function hide() {
67+ _hideCalled();
68+ _required = false;
69+ _fullyShown = false;
70+ }
71+
72+ function notifyAuthenticationSucceeded() {
73+ _notifyAuthenticationSucceededCalled();
74+ }
75+
76+ function notifyAuthenticationFailed() {
77+ _notifyAuthenticationFailedCalled();
78+ }
79+
80+ function reset() {
81+ _resetCalled();
82+ }
83+
84+ function tryToUnlock(toTheRight) {
85+ _tryToUnlockCalled(toTheRight);
86+ return true;
87+ }
88+
89+ Rectangle {
90+ anchors.fill: parent
91+ color: "black"
92+
93+ Label {
94+ text: "Fake view, nothing to see here"
95+ color: "white"
96+ anchors.centerIn: parent
97+ }
98+ }
99+}
tests/qmltests/Greeter/tst_Greeter.qmlnew file+472
@@ -0,0 +1,472 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import ".."
20+import "../../../qml/Greeter"
21+import Ubuntu.Components 0.1
22+import AccountsService 0.1
23+import LightDM 0.1 as LightDM
24+import Unity.Test 0.1 as UT
25+
26+Item {
27+ width: units.gu(120)
28+ height: units.gu(80)
29+
30+ property url defaultBackground: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
31+
32+ Component.onCompleted: {
33+ // set the mock mode before loading
34+ LightDM.Greeter.mockMode = "full";
35+ LightDM.Users.mockMode = "full";
36+ loader.active = true;
37+ }
38+
39+ Loader {
40+ id: loader
41+
42+ active: false
43+ anchors.fill: parent
44+
45+ property bool itemDestroyed: false
46+ sourceComponent: Component {
47+ Greeter {
48+ width: loader.width
49+ height: loader.height
50+ background: defaultBackground
51+ viewSource: Qt.resolvedUrl("TestView.qml")
52+
53+ Component.onDestruction: {
54+ loader.itemDestroyed = true;
55+ }
56+ }
57+ }
58+ }
59+
60+ SignalSpy {
61+ id: teaseSpy
62+ target: loader.item
63+ signalName: "tease"
64+ }
65+
66+ SignalSpy {
67+ id: sessionStartedSpy
68+ target: loader.item
69+ signalName: "sessionStarted"
70+ }
71+
72+ SignalSpy {
73+ id: emergencyCallSpy
74+ target: loader.item
75+ signalName: "emergencyCall"
76+ }
77+
78+ UT.UnityTestCase {
79+ id: testCase
80+ name: "Greeter"
81+ when: windowShown
82+
83+ property Item greeter: loader.status === Loader.Ready ? loader.item : null
84+ property Item view
85+
86+ SignalSpy {
87+ id: viewShowMessageSpy
88+ target: testCase.view
89+ signalName: "_showMessageCalled"
90+ }
91+
92+ SignalSpy {
93+ id: viewShowPromptSpy
94+ target: testCase.view
95+ signalName: "_showPromptCalled"
96+ }
97+
98+ SignalSpy {
99+ id: viewShowLastChanceSpy
100+ target: testCase.view
101+ signalName: "_showLastChanceCalled"
102+ }
103+
104+ SignalSpy {
105+ id: viewHideSpy
106+ target: testCase.view
107+ signalName: "_hideCalled"
108+ }
109+
110+ SignalSpy {
111+ id: viewAuthenticationSucceededSpy
112+ target: testCase.view
113+ signalName: "_notifyAuthenticationSucceededCalled"
114+ }
115+
116+ SignalSpy {
117+ id: viewAuthenticationFailedSpy
118+ target: testCase.view
119+ signalName: "_notifyAuthenticationFailedCalled"
120+ }
121+
122+ SignalSpy {
123+ id: viewResetSpy
124+ target: testCase.view
125+ signalName: "_resetCalled"
126+ }
127+
128+ SignalSpy {
129+ id: viewTryToUnlockSpy
130+ target: testCase.view
131+ signalName: "_tryToUnlockCalled"
132+ }
133+
134+ function init() {
135+ teaseSpy.clear();
136+ sessionStartedSpy.clear();
137+ emergencyCallSpy.clear();
138+ viewShowMessageSpy.clear();
139+ viewShowPromptSpy.clear();
140+ viewShowLastChanceSpy.clear();
141+ viewHideSpy.clear();
142+ viewAuthenticationSucceededSpy.clear();
143+ viewAuthenticationFailedSpy.clear();
144+ viewResetSpy.clear();
145+ viewTryToUnlockSpy.clear();
146+ tryCompare(greeter, "waiting", false);
147+ view = findChild(greeter, "testView");
148+ verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
149+ }
150+
151+ function cleanup() {
152+ loader.itemDestroyed = false;
153+ loader.active = false;
154+ tryCompare(loader, "status", Loader.Null);
155+ tryCompare(loader, "item", null);
156+ tryCompare(loader, "itemDestroyed", true);
157+ loader.active = true;
158+ tryCompare(loader, "status", Loader.Ready);
159+ removeTimeConstraintsFromDirectionalDragAreas(loader.item);
160+ }
161+
162+ function getIndexOf(name) {
163+ for (var i = 0; i < LightDM.Users.count; i++) {
164+ if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
165+ return i;
166+ }
167+ }
168+ fail("Didn't find name")
169+ return -1;
170+ }
171+
172+ function selectUser(name) {
173+ var i = getIndexOf(name);
174+ view.selected(i);
175+ verifySelected(name);
176+ return i;
177+ }
178+
179+ function verifySelected(name) {
180+ var i = getIndexOf(name);
181+ compare(view.currentIndex, i);
182+ compare(AccountsService.user, name);
183+ compare(LightDM.Greeter.authenticationUser, name);
184+ }
185+
186+ function verifyLoggedIn() {
187+ tryCompare(sessionStartedSpy, "count", 1);
188+ verify(viewAuthenticationSucceededSpy.count > 0);
189+ compare(LightDM.Greeter.authenticated, true);
190+ compare(greeter.shown, false);
191+ }
192+
193+ function test_unlockPass() {
194+ selectUser("has-password");
195+ tryCompare(viewShowPromptSpy, "count", 1);
196+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
197+ compare(viewShowPromptSpy.signalArguments[0][1], true);
198+ compare(viewShowPromptSpy.signalArguments[0][2], true);
199+
200+ view.responded("password");
201+ verifyLoggedIn();
202+ }
203+
204+ function test_unlockFail() {
205+ selectUser("has-password");
206+ tryCompare(viewShowPromptSpy, "count", 1);
207+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
208+ compare(viewShowPromptSpy.signalArguments[0][1], true);
209+ compare(viewShowPromptSpy.signalArguments[0][2], true);
210+
211+ view.responded("wr0ng p4ssw0rd");
212+ tryCompare(viewAuthenticationFailedSpy, "count", 1);
213+
214+ tryCompare(viewShowPromptSpy, "count", 2);
215+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
216+ compare(viewShowPromptSpy.signalArguments[0][1], true);
217+ compare(viewShowPromptSpy.signalArguments[0][2], true);
218+ }
219+
220+ function test_promptless() {
221+ selectUser("no-password");
222+ tryCompare(viewAuthenticationSucceededSpy, "count", 1);
223+ compare(sessionStartedSpy.count, 1);
224+ compare(viewShowPromptSpy.count, 0);
225+ compare(viewHideSpy.count, 0);
226+ compare(view.locked, false);
227+ }
228+
229+ function test_twoFactorPass() {
230+ selectUser("two-factor");
231+ tryCompare(viewShowPromptSpy, "count", 1);
232+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
233+ compare(viewShowPromptSpy.signalArguments[0][1], true);
234+ compare(viewShowPromptSpy.signalArguments[0][2], true);
235+
236+ view.responded("password");
237+ tryCompare(viewShowPromptSpy, "count", 2);
238+ compare(viewShowPromptSpy.signalArguments[1][0], "otp");
239+ compare(viewShowPromptSpy.signalArguments[1][1], false);
240+ compare(viewShowPromptSpy.signalArguments[1][2], false);
241+
242+ view.responded("otp");
243+ verifyLoggedIn();
244+ }
245+
246+ function test_twoFactorFailOnFirst() {
247+ selectUser("two-factor");
248+ tryCompare(viewShowPromptSpy, "count", 1);
249+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
250+ compare(viewShowPromptSpy.signalArguments[0][1], true);
251+ compare(viewShowPromptSpy.signalArguments[0][2], true);
252+
253+ view.responded("wr0ng p4ssw0rd");
254+ tryCompare(viewAuthenticationFailedSpy, "count", 1);
255+
256+ tryCompare(viewShowPromptSpy, "count", 2);
257+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
258+ compare(viewShowPromptSpy.signalArguments[0][1], true);
259+ compare(viewShowPromptSpy.signalArguments[0][2], true);
260+ }
261+
262+ function test_twoFactorFailOnSecond() {
263+ selectUser("two-factor");
264+ tryCompare(viewShowPromptSpy, "count", 1);
265+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
266+ compare(viewShowPromptSpy.signalArguments[0][1], true);
267+ compare(viewShowPromptSpy.signalArguments[0][2], true);
268+
269+ view.responded("password");
270+ tryCompare(viewShowPromptSpy, "count", 2);
271+ compare(viewShowPromptSpy.signalArguments[1][0], "otp");
272+ compare(viewShowPromptSpy.signalArguments[1][1], false);
273+ compare(viewShowPromptSpy.signalArguments[1][2], false);
274+
275+ view.responded("wr0ng p4ssw0rd");
276+ tryCompare(viewAuthenticationFailedSpy, "count", 1);
277+
278+ tryCompare(viewShowPromptSpy, "count", 3);
279+ compare(viewShowPromptSpy.signalArguments[0][0], "Password");
280+ compare(viewShowPromptSpy.signalArguments[0][1], true);
281+ compare(viewShowPromptSpy.signalArguments[0][2], true);
282+ }
283+
284+ function test_htmlInfoPrompt() {
285+ selectUser("html-info-prompt");
286+ tryCompare(viewShowPromptSpy, "count", 1);
287+ compare(viewShowMessageSpy.count, 1);
288+ compare(viewShowMessageSpy.signalArguments[0][0], "&lt;b&gt;&amp;&lt;/b&gt;");
289+ }
290+
291+ function test_multiInfoPrompt() {
292+ selectUser("multi-info-prompt");
293+ tryCompare(viewShowPromptSpy, "count", 1);
294+ compare(viewShowMessageSpy.count, 3);
295+ compare(viewShowMessageSpy.signalArguments[0][0], "Welcome to Unity Greeter");
296+ compare(viewShowMessageSpy.signalArguments[1][0], "<font color=\"#df382c\">This is an error</font>");
297+ compare(viewShowMessageSpy.signalArguments[2][0], "You should have seen three messages");
298+ }
299+
300+ function test_waiting() {
301+ // Make sure we unset 'waiting' on prompt
302+ selectUser("has-password");
303+ compare(greeter.waiting, true);
304+ tryCompare(greeter, "waiting", false);
305+
306+ // Make sure we unset 'waiting' on authentication
307+ selectUser("no-password");
308+ compare(greeter.waiting, true);
309+ tryCompare(greeter, "waiting", false);
310+ }
311+
312+ function test_locked() {
313+ selectUser("has-password");
314+ compare(view.locked, true);
315+
316+ LightDM.Greeter.active = false;
317+ compare(view.locked, false);
318+ LightDM.Greeter.active = true;
319+
320+ greeter.forcedUnlock = true;
321+ compare(view.locked, false);
322+ greeter.forcedUnlock = false;
323+
324+ selectUser("no-password");
325+ tryCompare(view, "locked", false);
326+ selectUser("has-password");
327+ }
328+
329+ function test_fullyShown() {
330+ compare(greeter.fullyShown, true);
331+ view.hide();
332+ compare(greeter.fullyShown, false);
333+ }
334+
335+ function test_alphanumeric() {
336+ selectUser("has-password");
337+ compare(view.alphanumeric, true);
338+ selectUser("has-pin");
339+ compare(view.alphanumeric, false);
340+ }
341+
342+ function test_background() {
343+ greeter.background = "testing";
344+ compare(view.background, Qt.resolvedUrl("testing"));
345+ }
346+
347+ function test_notifyAboutToFocusApp() {
348+ greeter.notifyAboutToFocusApp("fake-app");
349+ compare(viewTryToUnlockSpy.count, 1);
350+ compare(viewTryToUnlockSpy.signalArguments[0][0], false);
351+ }
352+
353+ function test_notifyShowingDashFromDrag() {
354+ compare(greeter.notifyShowingDashFromDrag("fake-app"), true);
355+ compare(viewTryToUnlockSpy.count, 1);
356+ compare(viewTryToUnlockSpy.signalArguments[0][0], true);
357+ }
358+
359+ function test_dragHandleLeftMargin() {
360+ compare(view.dragHandleLeftMargin, 0);
361+ greeter.dragHandleLeftMargin = 5;
362+ compare(view.dragHandleLeftMargin, 5);
363+ }
364+
365+ function test_launcherOffset() {
366+ compare(view.launcherOffset, 0);
367+ greeter.launcherOffset = 5;
368+ tryCompare(view, "launcherOffset", 5);
369+ }
370+
371+ function test_laucherOffsetAnimation() {
372+ // Our logic for smoothing launcherOffset when it suddenly goes to
373+ // zero is a bit complicated. Let's just make sure it works here.
374+
375+ launcherOffsetWatcher.target = view;
376+
377+ // should follow immediately
378+ launcherOffsetWatcher.values = [];
379+ greeter.launcherOffset = 100;
380+ compare(view.launcherOffset, 100);
381+ compare(launcherOffsetWatcher.values.length, 1);
382+
383+ // should interpolate values until it reaches 0
384+ launcherOffsetWatcher.values = [];
385+ greeter.launcherOffset = 0;
386+ tryCompare(view, "launcherOffset", 0);
387+ verify(launcherOffsetWatcher.values.length > 1);
388+ for (var i = 0; i < launcherOffsetWatcher.values.length - 1; ++i) {
389+ verify(launcherOffsetWatcher.values[i] > 0.0);
390+ verify(launcherOffsetWatcher.values[i] < 100.0);
391+ }
392+ }
393+ Connections {
394+ id: launcherOffsetWatcher
395+ property var values: []
396+ onLauncherOffsetChanged: {
397+ values.push(target.launcherOffset);
398+ }
399+ }
400+
401+ function test_backgroundTopMargin() {
402+ compare(view.backgroundTopMargin, 0);
403+ greeter.y = 5;
404+ compare(view.backgroundTopMargin, -5);
405+ }
406+
407+ function test_differentPrompt() {
408+ selectUser("different-prompt");
409+ tryCompare(viewShowPromptSpy, "count", 1);
410+ compare(viewShowPromptSpy.signalArguments[0][0], "Secret word");
411+ compare(viewShowPromptSpy.signalArguments[0][1], true);
412+ compare(viewShowPromptSpy.signalArguments[0][2], false);
413+ }
414+
415+ function test_authError() {
416+ selectUser("auth-error");
417+ tryCompare(viewAuthenticationFailedSpy, "count", 1);
418+ compare(viewShowPromptSpy.count, 0);
419+ compare(view.locked, true);
420+ }
421+
422+ function test_statsWelcomeScreen() {
423+ // Test logic in greeter that turns statsWelcomeScreen setting into infographic changes
424+ selectUser("has-password");
425+ compare(LightDM.Infographic.username, "has-password");
426+ AccountsService.statsWelcomeScreen = false;
427+ compare(LightDM.Infographic.username, "");
428+ AccountsService.statsWelcomeScreen = true;
429+ compare(LightDM.Infographic.username, "has-password");
430+ }
431+
432+ function test_dbusRequestAuthenticationUser() {
433+ selectUser("no-password");
434+ LightDM.Greeter.requestAuthenticationUser("has-password");
435+ verifySelected("has-password");
436+ }
437+
438+ function test_dbusHideGreeter() {
439+ compare(view.required, true);
440+ LightDM.Greeter.hideGreeter();
441+ compare(view.required, false);
442+ compare(greeter.required, false);
443+ }
444+
445+ function test_dbusShowGreeterFromHiddenState() {
446+ greeter.hide();
447+ compare(greeter.required, false);
448+
449+ LightDM.Greeter.showGreeter();
450+ compare(greeter.required, true);
451+ compare(greeter.fullyShown, true);
452+ view = findChild(greeter, "testView");
453+ compare(view.required, true);
454+
455+ // Can't test some of the stuff called on 'view' here because
456+ // the view was torn down and created again. So the spies missed
457+ // the good stuff while it down. See next test for more.
458+ }
459+
460+ function test_dbusShowGreeterFromShownState() {
461+ selectUser("has-password");
462+ compare(viewResetSpy.count, 1);
463+ tryCompare(viewShowPromptSpy, "count", 1);
464+
465+ viewResetSpy.clear();
466+ viewShowPromptSpy.clear();
467+
468+ LightDM.Greeter.showGreeter();
469+ compare(viewResetSpy.count, 1);
470+ }
471+ }
472+}
tests/qmltests/Greeter/tst_MultiGreeter.qmldeleted−475
@@ -1,475 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtQuick.Layouts 1.1
19−import QtTest 1.0
20−import ".."
21−import "../../../qml/Greeter"
22−import Ubuntu.Components 0.1
23−import LightDM 0.1 as LightDM
24−import Unity.Test 0.1 as UT
25−
26−Item {
27− id: root
28− width: units.gu(140)
29− height: units.gu(80)
30−
31− QtObject {
32− id: fakeInputMethod
33− property bool visible: fakeKeyboard.visible
34− property var keyboardRectangle: QtObject {
35− property real x: fakeKeyboard.x
36− property real y: fakeKeyboard.y
37− property real width: fakeKeyboard.width
38− property real height: fakeKeyboard.height
39− }
40− }
41−
42− Binding {
43− target: LightDM.Greeter
44− property: "mockMode"
45− value: "full"
46− }
47− Binding {
48− target: LightDM.Users
49− property: "mockMode"
50− value: "full"
51− }
52−
53− Greeter {
54− id: greeter
55− width: units.gu(120)
56− height: root.height
57− inputMethod: fakeInputMethod
58− locked: !LightDM.Greeter.authenticated
59− }
60−
61− Component {
62− id: greeterComponent
63− Greeter {
64− SignalSpy {
65− objectName: "selectedSpy"
66− target: parent
67− signalName: "selected"
68− }
69− }
70− }
71−
72− Rectangle {
73− id: fakeKeyboard
74− color: "green"
75− opacity: 0.7
76− anchors.bottom: root.bottom
77− width: greeter.width
78− height: greeter.height * 0.6
79− visible: keyboardVisibleCheckbox.checked
80− Text {
81− text: "Keyboard Rectangle"
82− color: "yellow"
83− font.bold: true
84− fontSizeMode: Text.Fit
85− minimumPixelSize: 10; font.pixelSize: 200
86− verticalAlignment: Text.AlignVCenter
87− x: (parent.width - width) / 2
88− y: (parent.height - height) / 2
89− width: parent.width
90− height: parent.height
91− }
92− }
93−
94− Item {
95− anchors {
96− top: root.top
97− bottom: root.bottom
98− left: greeter.right
99− right: root.right
100− }
101− RowLayout {
102− Layout.fillWidth: true
103− CheckBox {
104− id: keyboardVisibleCheckbox
105− }
106− Label { text: "Keyboard Visible"; anchors.verticalCenter: parent.verticalCenter }
107− }
108− }
109−
110− SignalSpy {
111− id: unlockSpy
112− target: greeter
113− signalName: "unlocked"
114− }
115−
116− SignalSpy {
117− id: selectionSpy
118− target: greeter
119− signalName: "selected"
120− }
121−
122− SignalSpy {
123− id: tappedSpy
124− target: greeter
125− signalName: "tapped"
126− }
127−
128− UT.UnityTestCase {
129− name: "MultiGreeter"
130− when: windowShown
131−
132− function cleanup() {
133− keyboardVisibleCheckbox.checked = false;
134− }
135−
136− function select_index(i) {
137− // We could be anywhere in list; find target index to know which direction
138− var userlist = findChild(greeter, "userList")
139− if (userlist.currentIndex == i)
140− keyClick(Qt.Key_Escape) // Reset state if we're not moving
141− while (userlist.currentIndex != i) {
142− var next = userlist.currentIndex + 1
143− if (userlist.currentIndex > i) {
144− next = userlist.currentIndex - 1
145− }
146− var account = findChild(greeter, "username"+next)
147− mouseClick(account, 1, 1)
148− tryCompare(userlist, "currentIndex", next)
149− tryCompare(userlist, "movingInternally", false)
150− }
151− }
152−
153− function select_user(name) {
154− // We could be anywhere in list; find target index to know which direction
155− for (var i = 0; i < greeter.model.count; i++) {
156− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == name) {
157− break
158− }
159− }
160− if (i == greeter.model.count) {
161− fail("Didn't find name")
162− return -1
163− }
164− select_index(i)
165− return i
166− }
167−
168− function test_properties() {
169− compare(greeter.multiUser, true)
170− compare(greeter.narrowMode, false)
171− }
172−
173− function test_cycle_data() {
174− var data = new Array()
175− for (var i = 0; i < greeter.model.count; i++) {
176− data[i] = {tag: greeter.model.data(i, LightDM.UserRoles.NameRole), uid: i }
177− }
178− return data
179− }
180−
181− function test_cycle(data) {
182− selectionSpy.clear();
183− var userList = findChild(greeter, "userList")
184− var waitForSignal = data.uid != 0 && userList.currentIndex != data.uid
185− select_index(data.uid)
186− tryCompare(userList, "currentIndex", data.uid)
187− tryCompare(greeter, "locked", data.tag !== "no-password")
188− if (waitForSignal) {
189− selectionSpy.wait()
190− tryCompare(selectionSpy, "count", 1)
191− }
192− }
193−
194− function test_unlock_password() {
195− select_user("no-password") // to guarantee a selected signal
196− unlockSpy.clear()
197− select_user("has-password")
198− var passwordInput = findChild(greeter, "passwordInput")
199− tryCompare(passwordInput, "opacity", 1)
200− mouseClick(passwordInput, 1, 1)
201− compare(unlockSpy.count, 0)
202− typeString("password")
203− keyClick(Qt.Key_Enter)
204− unlockSpy.wait()
205− }
206−
207− function test_unlock_wrong_password() {
208− select_user("no-password") // to guarantee a selected signal
209− unlockSpy.clear()
210− select_user("has-password")
211− wait(0) // spin event loop to start any pending animations
212− var passwordInput = findChild(greeter, "passwordInput")
213− tryCompare(passwordInput, "opacity", 1) // wait for opacity animation to be finished
214− mouseClick(passwordInput, 1, 1)
215− compare(unlockSpy.count, 0)
216− typeString("wr0ng p4ssw0rd")
217− keyClick(Qt.Key_Enter)
218− compare(unlockSpy.count, 0)
219− }
220−
221− function test_unlock_no_password() {
222− unlockSpy.clear()
223− select_user("no-password")
224− var passwordInput = findChild(greeter, "passwordInput")
225− tryCompare(passwordInput, "opacity", 1)
226− mouseClick(passwordInput, 1, 1)
227− unlockSpy.wait()
228− compare(unlockSpy.count, 1)
229− }
230−
231− function test_empty_name() {
232− for (var i = 0; i < greeter.model.count; i++) {
233− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == "empty-name") {
234− compare(greeter.model.data(i, LightDM.UserRoles.RealNameRole), greeter.model.data(i, LightDM.UserRoles.NameRole))
235− return
236− }
237− }
238− fail("Didn't find empty-name")
239− }
240−
241− function test_auth_error() {
242− select_user("auth-error")
243− var passwordInput = findChild(greeter, "passwordInput")
244− tryCompare(passwordInput, "placeholderText", "Retry")
245− }
246−
247− function test_different_prompt() {
248− select_user("different-prompt")
249− var passwordInput = findChild(greeter, "passwordInput")
250− tryCompare(passwordInput, "placeholderText", "Secret word")
251− }
252−
253− function test_no_response() {
254− unlockSpy.clear()
255− select_user("no-response")
256− var passwordInput = findChild(greeter, "passwordInput")
257− tryCompare(passwordInput, "opacity", 1)
258− mouseClick(passwordInput, 1, 1)
259− compare(unlockSpy.count, 0)
260− typeString("password")
261− keyClick(Qt.Key_Enter)
262− tryCompare(passwordInput, "enabled", false)
263− keyClick(Qt.Key_Escape)
264− tryCompare(passwordInput, "enabled", true)
265− compare(unlockSpy.count, 0)
266− }
267−
268− function test_two_factor_correct() {
269− unlockSpy.clear()
270− select_user("two-factor")
271− var passwordInput = findChild(greeter, "passwordInput")
272− tryCompare(passwordInput, "opacity", 1)
273− tryCompare(passwordInput, "echoMode", TextInput.Password)
274− tryCompare(passwordInput, "placeholderText", "Password")
275− mouseClick(passwordInput, 1, 1)
276− compare(unlockSpy.count, 0)
277− typeString("password")
278− keyClick(Qt.Key_Enter)
279− tryCompare(passwordInput, "echoMode", TextInput.Normal)
280− tryCompare(passwordInput, "placeholderText", "otp")
281− tryCompare(passwordInput, "enabled", true)
282− typeString("otp")
283− keyClick(Qt.Key_Enter)
284− unlockSpy.wait()
285− }
286−
287− function test_two_factor_wrong1() {
288− unlockSpy.clear()
289− select_user("two-factor")
290− var passwordInput = findChild(greeter, "passwordInput")
291− tryCompare(passwordInput, "opacity", 1)
292− tryCompare(passwordInput, "placeholderText", "Password")
293− mouseClick(passwordInput, 1, 1)
294− compare(unlockSpy.count, 0)
295− typeString("wr0ng p4ssw0rd")
296− keyClick(Qt.Key_Enter)
297− tryCompare(passwordInput, "placeholderText", "Password")
298− tryCompare(passwordInput, "enabled", true)
299− compare(unlockSpy.count, 0)
300− }
301−
302− function test_two_factor_wrong2() {
303− unlockSpy.clear()
304− select_user("two-factor")
305− var passwordInput = findChild(greeter, "passwordInput")
306− tryCompare(passwordInput, "opacity", 1)
307− tryCompare(passwordInput, "placeholderText", "Password")
308− mouseClick(passwordInput, 1, 1)
309− compare(unlockSpy.count, 0)
310− typeString("password")
311− keyClick(Qt.Key_Enter)
312− tryCompare(passwordInput, "placeholderText", "otp")
313− tryCompare(passwordInput, "enabled", true)
314− typeString("wr0ng p4ssw0rd")
315− keyClick(Qt.Key_Enter)
316− tryCompare(passwordInput, "placeholderText", "Password")
317− tryCompare(passwordInput, "enabled", true)
318− compare(unlockSpy.count, 0)
319− }
320−
321− function test_unicode() {
322− var index = select_user("unicode")
323− var label = findChild(greeter, "username"+index)
324− tryCompare(label, "text", "가나다라마")
325− }
326−
327− function test_long_name() {
328− var index = select_user("long-name")
329− var label = findChild(greeter, "username"+index)
330− tryCompare(label, "truncated", true)
331− }
332−
333− function test_info_prompt() {
334− select_user("info-prompt")
335− var label = findChild(greeter, "infoLabel")
336− tryCompare(label, "text", "Welcome to Unity Greeter")
337− tryCompare(label, "opacity", 1)
338− tryCompare(label, "clip", true)
339− tryCompareFunction(function() {return label.contentWidth > label.width;}, false) // c.f. wide-info-prompt
340− var passwordInput = findChild(greeter, "passwordInput")
341− mouseClick(passwordInput, 1, 1)
342− keyClick(Qt.Key_Escape)
343− }
344−
345− function test_info_prompt_escape() {
346− select_user("info-prompt")
347− var passwordInput = findChild(greeter, "passwordInput")
348− mouseClick(passwordInput, 1, 1)
349− keyClick(Qt.Key_Escape)
350− var label = findChild(greeter, "infoLabel")
351− tryCompare(label, "text", "Welcome to Unity Greeter")
352− tryCompare(label, "opacity", 1)
353− }
354−
355− function test_wide_info_prompt() {
356− select_user("wide-info-prompt")
357− var label = findChild(greeter, "infoLabel")
358− tryCompare(label, "clip", true)
359− tryCompareFunction(function() {return label.contentWidth > label.width;}, true)
360− }
361−
362− function test_html_info_prompt() {
363− select_user("html-info-prompt")
364− var label = findChild(greeter, "infoLabel")
365− tryCompare(label, "text", "&lt;b&gt;&amp;&lt;/b&gt;")
366− }
367−
368− function test_long_info_prompt() {
369− select_user("long-info-prompt")
370− var label = findChild(greeter, "infoLabel")
371− tryCompare(label, "text", "Welcome to Unity Greeter<br><br>We like to annoy you with super ridiculously long messages.<br>Like this one<br><br>This is the last line of a multiple line message.")
372− tryCompare(label, "textFormat", Text.StyledText) // for parsing above correctly
373− tryCompare(label, "clip", true)
374− tryCompareFunction(function() {return label.contentWidth > label.width;}, true)
375− }
376−
377− function test_multi_info_prompt() {
378− select_user("multi-info-prompt")
379− var label = findChild(greeter, "infoLabel")
380− tryCompare(label, "text", "Welcome to Unity Greeter<br><font color=\"#df382c\">This is an error</font><br>You should have seen three messages")
381− tryCompare(label, "textFormat", Text.StyledText) // for parsing above correctly
382− }
383−
384− function test_bg_color() {
385− var index = select_user("color-background")
386− compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")
387− }
388−
389− function test_bg_none() {
390− var index = select_user("no-background")
391− compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "")
392− }
393−
394− function test_tappedSignal_data() {
395− return [
396− {tag: "left", posX: units.gu(2)},
397− {tag: "right", posX: greeter.width - units.gu(2)}
398− ]
399− }
400−
401− function test_tappedSignal(data) {
402− select_user("no-password");
403− tappedSpy.clear();
404− tap(greeter, data.posX, greeter.height - units.gu(1))
405− tryCompare(tappedSpy, "count", 1)
406− }
407−
408− function test_teaseLockedUnlocked_data() {
409− return [
410− {tag: "unlocked", locked: false, narrow: false},
411− {tag: "locked", locked: true, narrow: false},
412− ];
413− }
414−
415− function test_teaseLockedUnlocked(data) {
416− tappedSpy.clear()
417− greeter.locked = data.locked;
418−
419− tap(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
420−
421− if (!data.locked || data.narrow) {
422− tappedSpy.wait()
423− tryCompare(tappedSpy, "count", 1);
424− } else {
425− // waiting 100ms to make sure nothing happens
426− wait(100);
427− compare(tappedSpy.count, 0, "Greeter teasing not disabled even though it's locked.");
428− }
429−
430− // Reset value
431− greeter.locked = false;
432− }
433−
434− function test_dbus_set_active_entry() {
435− select_user("no-password") // to guarantee a selected signal
436− selectionSpy.clear()
437− LightDM.Greeter.requestAuthenticationUser("has-password")
438−
439− selectionSpy.wait()
440− tryCompare(selectionSpy, "count", 1)
441−
442− var userlist = findChild(greeter, "userList")
443− compare(greeter.model.data(userlist.currentIndex, LightDM.UserRoles.NameRole), "has-password")
444− }
445−
446− function test_initial_selected_signal() {
447− var greeterObj = greeterComponent.createObject(this)
448− var spy = findChild(greeterObj, "selectedSpy")
449− spy.wait()
450− tryCompare(spy, "count", 1)
451− greeterObj.destroy()
452− }
453−
454− function test_login_list_not_covered_by_keyboard() {
455− var loginList = findChild(greeter, "loginLoader").item;
456− compare(loginList.height, greeter.height);
457−
458− // when the vkb shows up, loginList is moved up to remain fully uncovered
459−
460− keyboardVisibleCheckbox.checked = true;
461−
462− tryCompare(loginList, "height", greeter.height - fakeInputMethod.keyboardRectangle.height);
463− tryCompareFunction( function() {
464− var loginListRect = loginList.mapToItem(greeter, 0, 0, loginList.width, loginList.height);
465− return loginListRect.y + loginListRect.height <= fakeInputMethod.keyboardRectangle.y;
466− }, true);
467−
468− // once the vkb goes away, loginList goes back to its full height
469−
470− keyboardVisibleCheckbox.checked = false;
471−
472− tryCompare(loginList, "height", greeter.height);
473− }
474− }
475−}
tests/qmltests/Greeter/tst_NarrowView.qmlnew file+522
@@ -0,0 +1,522 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import ".."
20+import "../../../qml/Greeter"
21+import LightDM 0.1 as LightDM
22+import Ubuntu.Components 0.1
23+import Unity.Test 0.1 as UT
24+
25+Item {
26+ id: root
27+ width: units.gu(90)
28+ height: units.gu(80)
29+
30+ Row {
31+ anchors.fill: parent
32+ Loader {
33+ id: loader
34+ width: root.width - controls.width
35+ height: parent.height
36+
37+ property bool itemDestroyed: false
38+ sourceComponent: Component {
39+ NarrowView {
40+ background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
41+ userModel: LightDM.Users
42+ infographicModel: LightDM.Infographic
43+
44+ launcherOffset: parseFloat(launcherOffsetField.text)
45+ currentIndex: parseInt(currentIndexField.text, 10)
46+ delayMinutes: parseInt(delayMinutesField.text, 10)
47+ backgroundTopMargin: parseFloat(backgroundTopMarginField.text)
48+ locked: lockedCheckBox.checked
49+ alphanumeric: alphanumericCheckBox.checked
50+
51+ Component.onDestruction: {
52+ loader.itemDestroyed = true
53+ }
54+
55+ onSelected: {
56+ currentIndexField.text = index;
57+ }
58+ }
59+ }
60+ }
61+
62+ Rectangle {
63+ id: controls
64+ color: "white"
65+ width: units.gu(40)
66+ height: parent.height
67+
68+ Column {
69+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
70+ spacing: units.gu(1)
71+
72+ Row {
73+ Button {
74+ text: "Show Last Chance"
75+ onClicked: loader.item.showLastChance()
76+ }
77+ }
78+ Row {
79+ Button {
80+ text: "Hide"
81+ onClicked: loader.item.hide()
82+ }
83+ }
84+ Row {
85+ Button {
86+ text: "Reset"
87+ onClicked: loader.item.reset()
88+ }
89+ }
90+ Row {
91+ Button {
92+ text: "Show Message"
93+ onClicked: loader.item.showMessage(messageField.text)
94+ }
95+ TextField {
96+ id: messageField
97+ width: units.gu(10)
98+ text: ""
99+ }
100+ }
101+ Row {
102+ Button {
103+ text: "Show Prompt"
104+ onClicked: loader.item.showPrompt(promptField.text, isSecretCheckBox.checked, isDefaultPromptCheckBox.checked)
105+ }
106+ TextField {
107+ id: promptField
108+ width: units.gu(10)
109+ text: ""
110+ }
111+ CheckBox {
112+ id: isSecretCheckBox
113+ }
114+ Label {
115+ text: "secret"
116+ }
117+ CheckBox {
118+ id: isDefaultPromptCheckBox
119+ }
120+ Label {
121+ text: "default"
122+ }
123+ }
124+ Row {
125+ Button {
126+ text: "Authenticated"
127+ onClicked: {
128+ if (successCheckBox.checked) {
129+ loader.item.notifyAuthenticationSucceeded();
130+ } else {
131+ loader.item.notifyAuthenticationFailed();
132+ }
133+ }
134+ }
135+ CheckBox {
136+ id: successCheckBox
137+ }
138+ Label {
139+ text: "success"
140+ }
141+ }
142+ Row {
143+ Button {
144+ text: "Try To Unlock"
145+ onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
146+ }
147+ CheckBox {
148+ id: toTheRightCheckBox
149+ }
150+ Label {
151+ text: "toTheRight"
152+ }
153+ }
154+ Row {
155+ TextField {
156+ id: launcherOffsetField
157+ width: units.gu(10)
158+ text: "0"
159+ }
160+ Label {
161+ text: "launcherOffset"
162+ }
163+ }
164+ Row {
165+ TextField {
166+ id: currentIndexField
167+ width: units.gu(10)
168+ text: "0"
169+ }
170+ Label {
171+ text: "currentIndex"
172+ }
173+ }
174+ Row {
175+ TextField {
176+ id: delayMinutesField
177+ width: units.gu(10)
178+ text: "0"
179+ }
180+ Label {
181+ text: "delayMinutes"
182+ }
183+ }
184+ Row {
185+ TextField {
186+ id: backgroundTopMarginField
187+ width: units.gu(10)
188+ text: "0"
189+ }
190+ Label {
191+ text: "backgroundTopMargin"
192+ }
193+ }
194+ Row {
195+ CheckBox {
196+ id: lockedCheckBox
197+ }
198+ Label {
199+ text: "locked"
200+ }
201+ }
202+ Row {
203+ CheckBox {
204+ id: alphanumericCheckBox
205+ }
206+ Label {
207+ text: "alphanumeric"
208+ }
209+ }
210+ Row {
211+ Label {
212+ text: "selected: " + selectedSpy.count
213+ }
214+ }
215+ Row {
216+ Label {
217+ text: "responded: " + respondedSpy.count
218+ }
219+ }
220+ Row {
221+ Label {
222+ text: "teased: " + teaseSpy.count
223+ }
224+ }
225+ Row {
226+ Label {
227+ text: "emergency: " + emergencySpy.count
228+ }
229+ }
230+ Row {
231+ Button {
232+ text: "Reload View"
233+ onClicked: {
234+ loader.active = false;
235+ loader.active = true;
236+ }
237+ }
238+ }
239+ }
240+ }
241+ }
242+
243+ Binding {
244+ target: LightDM.Infographic
245+ property: "username"
246+ value: "single"
247+ }
248+
249+ SignalSpy {
250+ id: selectedSpy
251+ target: loader.item
252+ signalName: "selected"
253+ }
254+
255+ SignalSpy {
256+ id: respondedSpy
257+ target: loader.item
258+ signalName: "responded"
259+ }
260+
261+ SignalSpy {
262+ id: teaseSpy
263+ target: loader.item
264+ signalName: "tease"
265+ }
266+
267+ SignalSpy {
268+ id: emergencySpy
269+ target: loader.item
270+ signalName: "emergencyCall"
271+ }
272+
273+ SignalSpy {
274+ id: infographicDataChangedSpy
275+ target: LightDM.Infographic
276+ signalName: "dataChanged"
277+ }
278+
279+ UT.UnityTestCase {
280+ name: "NarrowView"
281+ when: windowShown
282+
283+ property Item view: loader.status === Loader.Ready ? loader.item : null
284+
285+ function init() {
286+ view.currentIndex = 0; // break binding with text field
287+
288+ selectedSpy.clear();
289+ respondedSpy.clear();
290+ teaseSpy.clear();
291+ emergencySpy.clear();
292+ infographicDataChangedSpy.clear();
293+ }
294+
295+ function cleanup() {
296+ loader.itemDestroyed = false;
297+ loader.active = false;
298+ tryCompare(loader, "status", Loader.Null);
299+ tryCompare(loader, "item", null);
300+ tryCompare(loader, "itemDestroyed", true);
301+ loader.active = true;
302+ tryCompare(loader, "status", Loader.Ready);
303+ removeTimeConstraintsFromDirectionalDragAreas(loader.item);
304+ }
305+
306+ function swipeAwayCover(toTheRight) {
307+ if (toTheRight === undefined) {
308+ toTheRight = false;
309+ }
310+
311+ tryCompare(view, "fullyShown", true);
312+ var touchY = view.height / 2;
313+ if (toTheRight) {
314+ touchFlick(view, 0, touchY, view.width, touchY);
315+ } else {
316+ touchFlick(view, view.width, touchY, 0, touchY);
317+ }
318+ var coverPage = findChild(view, "coverPage");
319+ tryCompare(coverPage, "showProgress", 0);
320+ waitForRendering(view);
321+ }
322+
323+ function enterPin(pin) {
324+ for (var i = 0; i < pin.length; ++i) {
325+ var character = pin.charAt(i);
326+ var button = findChild(view, "pinPadButton" + character);
327+ tap(button);
328+ }
329+ }
330+
331+ function test_tease_data() {
332+ return [
333+ {tag: "left", x: 0, offset: 0, count: 1},
334+ {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1},
335+ {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0},
336+ {tag: "right", x: view.width, offset: 0, count: 1},
337+ ]
338+ }
339+ function test_tease(data) {
340+ view.dragHandleLeftMargin = data.offset;
341+ tap(view, data.x, 0);
342+ compare(teaseSpy.count, data.count);
343+ }
344+
345+ function test_respondedWithPin() {
346+ view.locked = true;
347+ swipeAwayCover();
348+ enterPin("1234");
349+ compare(respondedSpy.count, 1);
350+ compare(respondedSpy.signalArguments[0][0], "1234");
351+ }
352+
353+ function test_respondedWithPassphrase() {
354+ view.locked = true;
355+ view.alphanumeric = true;
356+ swipeAwayCover();
357+ typeString("test");
358+ keyClick(Qt.Key_Enter);
359+ compare(respondedSpy.count, 1);
360+ compare(respondedSpy.signalArguments[0][0], "test");
361+ }
362+
363+ function test_respondedWithSwipe_data() {
364+ return [
365+ {tag: "left", toTheRight: false, hiddenX: -view.width},
366+ {tag: "right", toTheRight: true, hiddenX: view.width},
367+ ];
368+ }
369+ function test_respondedWithSwipe(data) {
370+ swipeAwayCover(data.toTheRight);
371+ var coverPage = findChild(view, "coverPage");
372+ compare(coverPage.x, data.hiddenX);
373+ compare(respondedSpy.count, 1);
374+ compare(respondedSpy.signalArguments[0][0], "");
375+ }
376+
377+ function test_emergencyCall() {
378+ view.locked = true;
379+ swipeAwayCover();
380+ var emergencyCallLabel = findChild(view, "emergencyCallLabel");
381+ tap(emergencyCallLabel);
382+ compare(emergencySpy.count, 1);
383+ }
384+
385+ function test_fullyShown() {
386+ tryCompare(view, "fullyShown", true);
387+ swipeAwayCover();
388+ tryCompare(view, "fullyShown", false);
389+ view.locked = true;
390+ tryCompare(view, "fullyShown", true);
391+ view.locked = false;
392+ tryCompare(view, "fullyShown", false);
393+ }
394+
395+ function test_required() {
396+ tryCompare(view, "required", true);
397+ swipeAwayCover();
398+ tryCompare(view, "required", false);
399+ view.locked = true;
400+ tryCompare(view, "required", true);
401+ view.locked = false;
402+ tryCompare(view, "required", false);
403+ }
404+
405+ function test_tryToUnlock() {
406+ var coverPage = findChild(view, "coverPage");
407+ tryCompare(coverPage, "showProgress", 1);
408+ compare(view.tryToUnlock(false), true);
409+ tryCompare(coverPage, "showProgress", 0);
410+ compare(view.tryToUnlock(false), false);
411+ }
412+
413+ /*
414+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359
415+ "User metrics can no longer be changed by double tap"
416+ */
417+ function test_doubleTapSwitchesToNextInfographic() {
418+ var infographicPrivate = findInvisibleChild(view, "infographicPrivate");
419+ verify(infographicPrivate);
420+
421+ // wait for the UI to settle down before double tapping it
422+ tryCompare(infographicPrivate, "animating", false);
423+
424+ var dataCircle = findChild(view, "dataCircle");
425+ verify(dataCircle);
426+
427+ tap(dataCircle);
428+ wait(1);
429+ tap(dataCircle);
430+
431+ tryCompare(infographicDataChangedSpy, "count", 1);
432+ }
433+
434+ function test_movesBackIntoPlaceWhenNotDraggedFarEnough() {
435+ var coverPage = findChild(view, "coverPage");
436+
437+ var dragEvaluator = findInvisibleChild(coverPage, "edgeDragEvaluator");
438+ verify(dragEvaluator);
439+
440+ // Make it easier to get a rejection/rollback. Otherwise would have to inject
441+ // a fake timer into dragEvaluator.
442+ // Afterall, we are testing if the CoverPage indeed moves back on a
443+ // rollback decision, not the drag evaluation itself.
444+ dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2;
445+
446+ // it starts as fully shown
447+ compare(coverPage.x, 0);
448+
449+ // then we drag it a bit
450+ var startX = coverPage.width - 1;
451+ var touchY = coverPage.height / 2;
452+ var dragXDelta = -(dragEvaluator.minDragDistance * 0.3);
453+ touchFlick(coverPage,
454+ startX , touchY, // start pos
455+ startX + dragXDelta, touchY, // end pos
456+ true /* beginTouch */, false /* endTouch */);
457+
458+ // which should make it move a bit
459+ tryCompareFunction(function() {return coverPage.x < 0;}, true);
460+
461+ // then we release it
462+ touchRelease(coverPage, startX + dragXDelta, touchY);
463+
464+ // which should make it move back into its original position as it didn't move
465+ // far enough to have it hidden
466+ tryCompare(coverPage, "x", 0);
467+ }
468+
469+ function test_dragToHide_data() {
470+ return [
471+ {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1, hiddenX: -view.width},
472+ {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95, hiddenX: view.width},
473+ ];
474+ }
475+ function test_dragToHide(data) {
476+ var coverPage = findChild(view, "coverPage");
477+ compare(coverPage.x, 0);
478+ compare(coverPage.visible, true);
479+ compare(coverPage.shown, true);
480+ compare(coverPage.showProgress, 1);
481+ compare(view.fullyShown, true);
482+
483+ touchFlick(view,
484+ data.startX, view.height / 2, // start pos
485+ data.endX, view.height / 2); // end pos
486+
487+ tryCompare(coverPage, "x", data.hiddenX);
488+ tryCompare(coverPage, "visible", false);
489+ tryCompare(coverPage, "shown", false);
490+ tryCompare(coverPage, "showProgress", 0);
491+ compare(view.fullyShown, false);
492+ }
493+
494+ function test_hiddenViewRemainsHiddenAfterResize_data() {
495+ return [
496+ {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1},
497+ {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95},
498+ ];
499+ }
500+ function test_hiddenViewRemainsHiddenAfterResize(data) {
501+ touchFlick(view,
502+ data.startX, view.height / 2, // start pos
503+ data.endX, view.height / 2); // end pos
504+
505+ var coverPage = findChild(view, "coverPage");
506+ tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
507+ tryCompare(coverPage, "visible", false);
508+ tryCompare(coverPage, "shown", false);
509+ tryCompare(coverPage, "showProgress", 0);
510+
511+ // flip dimensions to simulate an orientation change
512+ view.width = loader.height;
513+ view.height = loader.width;
514+
515+ // All properties should remain consistent
516+ tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
517+ tryCompare(coverPage, "visible", false);
518+ tryCompare(coverPage, "shown", false);
519+ tryCompare(coverPage, "showProgress", 0);
520+ }
521+ }
522+}
tests/qmltests/Greeter/tst_SingleGreeter.qmldeleted−244
@@ -1,244 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import ".."
20−import "../../../qml/Greeter"
21−import AccountsService 0.1
22−import LightDM 0.1 as LightDM
23−import Ubuntu.Components 0.1
24−import Unity.Test 0.1 as UT
25−
26−Rectangle {
27− color: "darkblue"
28− width: units.gu(60)
29− height: units.gu(80)
30−
31− Button {
32− anchors.centerIn: parent
33− text: "Show Greeter"
34− onClicked: {
35− if (greeterLoader.item)
36− greeterLoader.item.show();
37− }
38− }
39−
40− Loader {
41− id: greeterLoader
42− anchors.fill: parent
43−
44− property bool itemDestroyed: false
45−
46− sourceComponent: Component {
47− Greeter {
48− width: greeterLoader.width
49− height: greeterLoader.height
50−
51− Component.onDestruction: {
52− greeterLoader.itemDestroyed = true;
53− }
54− SignalSpy {
55− objectName: "selectedSpy"
56− target: parent
57− signalName: "selected"
58− }
59− }
60− }
61− }
62−
63− SignalSpy {
64− id: unlockSpy
65− target: greeterLoader.item
66− signalName: "unlocked"
67− }
68−
69− SignalSpy {
70− id: tappedSpy
71− target: greeterLoader.item
72− signalName: "tapped"
73− }
74−
75− SignalSpy {
76− id: infographicDataChangedSpy
77− target: LightDM.Infographic
78− signalName: "dataChanged"
79− }
80−
81− UT.UnityTestCase {
82− name: "SingleGreeter"
83− when: windowShown
84−
85− property Greeter greeter: greeterLoader.item
86−
87− function cleanup() {
88− AccountsService.statsWelcomeScreen = true
89−
90− // force a reload so that we get a fresh Greeter for the next test
91− greeterLoader.itemDestroyed = false;
92− greeterLoader.active = false;
93− tryCompare(greeterLoader, "itemDestroyed", true);
94−
95− unlockSpy.clear();
96− tappedSpy.clear();
97−
98− greeterLoader.active = true;
99− tryCompare(greeterLoader, "status", Loader.Ready);
100− removeTimeConstraintsFromDirectionalDragAreas(greeterLoader.item);
101− }
102−
103− function test_properties() {
104− compare(greeter.multiUser, false)
105− compare(greeter.narrowMode, true)
106− }
107−
108− function test_teasingArea_data() {
109− return [
110− {tag: "left", posX: units.gu(2), leftPressed: true, rightPressed: false},
111− {tag: "right", posX: greeter.width - units.gu(2), leftPressed: false, rightPressed: true}
112− ]
113− }
114−
115− function test_teasingArea(data) {
116− tappedSpy.clear()
117− tap(greeter, data.posX, greeter.height - units.gu(1))
118− tappedSpy.wait()
119− tryCompare(tappedSpy, "count", 1)
120− }
121−
122− function test_statsWelcomeScreen() {
123− // Test logic in greeter that turns statsWelcomeScreen setting into infographic changes
124− compare(AccountsService.statsWelcomeScreen, true)
125− tryCompare(LightDM.Infographic, "username", "single")
126− AccountsService.statsWelcomeScreen = false
127− tryCompare(LightDM.Infographic, "username", "")
128− AccountsService.statsWelcomeScreen = true
129− tryCompare(LightDM.Infographic, "username", "single")
130− }
131−
132− function test_initial_selected_signal() {
133− var selectedSpy = findChild(greeter, "selectedSpy");
134− selectedSpy.wait();
135− tryCompare(selectedSpy, "count", 1);
136− }
137−
138− /*
139− Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359
140− "User metrics can no longer be changed by double tap"
141− */
142− function test_doubleTapSwitchesToNextInfographic() {
143− infographicDataChangedSpy.clear();
144−
145− var infographicPrivate = findInvisibleChild(greeter, "infographicPrivate");
146− verify(infographicPrivate);
147−
148− // wait for the UI to settle down before double tapping it
149− tryCompare(infographicPrivate, "animating", false);
150−
151− var dataCircle = findChild(greeter, "dataCircle");
152− verify(dataCircle);
153−
154− tap(dataCircle);
155− wait(1);
156− tap(dataCircle);
157−
158− tryCompare(infographicDataChangedSpy, "count", 1);
159− }
160−
161− function test_movesBackIntoPlaceWhenNotDraggedFarEnough() {
162−
163− var dragEvaluator = findInvisibleChild(greeter, "edgeDragEvaluator");
164− verify(dragEvaluator);
165−
166− // Make it easier to get a rejection/rollback. Otherwise would have to inject
167− // a fake timer into dragEvaluator.
168− // Afterall, we are testing if the Greeter indeed moves back on a
169− // rollback decision, not the drag evaluation itself.
170− dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2;
171−
172− // it starts as fully shown
173− compare(greeter.x, 0);
174−
175− // then we drag it a bit
176− var startX = greeter.width - 1;
177− var touchY = greeter.height / 2;
178− var dragXDelta = -(dragEvaluator.minDragDistance * 0.3);
179− touchFlick(greeter,
180− startX , touchY, // start pos
181− startX + dragXDelta, touchY, // end pos
182− true /* beginTouch */, false /* endTouch */);
183−
184− // which should make it move a bit
185− tryCompareFunction(function(){return greeter.x < 0;}, true);
186−
187− // then we release it
188− touchRelease(greeter, startX + dragXDelta, touchY);
189−
190− // which should make it move back into its original position as it didn't move
191− // far enough to have it hidden
192− tryCompare(greeter, "x", 0);
193− }
194−
195− function test_dragToHide_data() {
196− return [
197− {tag: "left", startX: greeter.width * 0.95, endX: greeter.width * 0.1, hiddenX: -greeter.width},
198− {tag: "right", startX: greeter.width * 0.1, endX: greeter.width * 0.95, hiddenX: greeter.width},
199− ];
200− }
201− function test_dragToHide(data) {
202− compare(greeter.x, 0);
203− compare(greeter.visible, true);
204− compare(greeter.shown, true);
205− compare(greeter.showProgress, 1);
206−
207− touchFlick(greeter,
208− data.startX, greeter.height / 2, // start pos
209− data.endX, greeter.height / 2); // end pos
210−
211− tryCompare(greeter, "x", data.hiddenX);
212− tryCompare(greeter, "visible", false);
213− tryCompare(greeter, "shown", false);
214− tryCompare(greeter, "showProgress", 0);
215− }
216−
217− function test_hiddenGreeterRemainsHiddenAfterResize_data() {
218− return [
219− {tag: "left", startX: greeter.width * 0.95, endX: greeter.width * 0.1},
220− {tag: "right", startX: greeter.width * 0.1, endX: greeter.width * 0.95},
221− ];
222− }
223− function test_hiddenGreeterRemainsHiddenAfterResize(data) {
224− touchFlick(greeter,
225− data.startX, greeter.height / 2, // start pos
226− data.endX, greeter.height / 2); // end pos
227−
228− tryCompare(greeter, "x", data.tag == "left" ? -greeter.width : greeter.width);
229− tryCompare(greeter, "visible", false);
230− tryCompare(greeter, "shown", false);
231− tryCompare(greeter, "showProgress", 0);
232−
233− // flip dimensions to simulate an orientation change
234− greeter.width = greeterLoader.height;
235− greeter.height = greeterLoader.width;
236−
237− // All properties should remain consistent
238− tryCompare(greeter, "x", data.tag == "left" ? -greeter.width : greeter.width);
239− tryCompare(greeter, "visible", false);
240− tryCompare(greeter, "shown", false);
241− tryCompare(greeter, "showProgress", 0);
242− }
243− }
244−}
tests/qmltests/Greeter/tst_WideView.qmlnew file+519
@@ -0,0 +1,519 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import ".."
20+import "../../../qml/Greeter"
21+import LightDM 0.1 as LightDM
22+import Ubuntu.Components 0.1
23+import Unity.Test 0.1 as UT
24+
25+Item {
26+ id: root
27+ width: units.gu(120)
28+ height: units.gu(80)
29+
30+ Binding {
31+ target: LightDM.Users
32+ property: "mockMode"
33+ value: "full"
34+ }
35+
36+ Row {
37+ anchors.fill: parent
38+ Loader {
39+ id: loader
40+ width: root.width - controls.width
41+ height: parent.height
42+
43+ property bool itemDestroyed: false
44+ sourceComponent: Component {
45+ WideView {
46+ id: view
47+
48+ background: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
49+ userModel: LightDM.Users
50+ infographicModel: LightDM.Infographic
51+
52+ launcherOffset: parseFloat(launcherOffsetField.text)
53+ currentIndex: parseInt(currentIndexField.text, 10)
54+ delayMinutes: parseInt(delayMinutesField.text, 10)
55+ backgroundTopMargin: parseFloat(backgroundTopMarginField.text)
56+ locked: lockedCheckBox.checked
57+ inputMethod: fakeInputMethod
58+
59+ Component.onDestruction: {
60+ loader.itemDestroyed = true
61+ }
62+
63+ onSelected: {
64+ currentIndexField.text = index;
65+ }
66+
67+ QtObject {
68+ id: fakeInputMethod
69+ property bool visible: fakeKeyboard.visible
70+ property var keyboardRectangle: QtObject {
71+ property real x: fakeKeyboard.x
72+ property real y: fakeKeyboard.y
73+ property real width: fakeKeyboard.width
74+ property real height: fakeKeyboard.height
75+ }
76+ }
77+
78+ Rectangle {
79+ id: fakeKeyboard
80+ color: "green"
81+ opacity: 0.7
82+ anchors.bottom: view.bottom
83+ width: view.width
84+ height: view.height * 0.6
85+ visible: keyboardVisibleCheckBox.checked
86+ Text {
87+ text: "Keyboard Rectangle"
88+ color: "yellow"
89+ font.bold: true
90+ fontSizeMode: Text.Fit
91+ minimumPixelSize: 10; font.pixelSize: 200
92+ verticalAlignment: Text.AlignVCenter
93+ x: (parent.width - width) / 2
94+ y: (parent.height - height) / 2
95+ width: parent.width
96+ height: parent.height
97+ }
98+ }
99+
100+ }
101+ }
102+ }
103+
104+ Rectangle {
105+ id: controls
106+ color: "white"
107+ width: units.gu(40)
108+ height: parent.height
109+
110+ Column {
111+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
112+ spacing: units.gu(1)
113+
114+ Row {
115+ Button {
116+ text: "Show Last Chance"
117+ onClicked: loader.item.showLastChance()
118+ }
119+ }
120+ Row {
121+ Button {
122+ text: "Hide"
123+ onClicked: loader.item.hide()
124+ }
125+ }
126+ Row {
127+ Button {
128+ text: "Reset"
129+ onClicked: loader.item.reset()
130+ }
131+ }
132+ Row {
133+ Button {
134+ text: "Show Message"
135+ onClicked: loader.item.showMessage(messageField.text)
136+ }
137+ TextField {
138+ id: messageField
139+ width: units.gu(10)
140+ text: ""
141+ }
142+ }
143+ Row {
144+ Button {
145+ text: "Show Prompt"
146+ onClicked: loader.item.showPrompt(promptField.text, isSecretCheckBox.checked, isDefaultPromptCheckBox.checked)
147+ }
148+ TextField {
149+ id: promptField
150+ width: units.gu(10)
151+ text: ""
152+ }
153+ CheckBox {
154+ id: isSecretCheckBox
155+ }
156+ Label {
157+ text: "secret"
158+ }
159+ CheckBox {
160+ id: isDefaultPromptCheckBox
161+ }
162+ Label {
163+ text: "default"
164+ }
165+ }
166+ Row {
167+ Button {
168+ text: "Authenticated"
169+ onClicked: {
170+ if (successCheckBox.checked) {
171+ loader.item.notifyAuthenticationSucceeded();
172+ } else {
173+ loader.item.notifyAuthenticationFailed();
174+ }
175+ }
176+ }
177+ CheckBox {
178+ id: successCheckBox
179+ }
180+ Label {
181+ text: "success"
182+ }
183+ }
184+ Row {
185+ Button {
186+ text: "Try To Unlock"
187+ onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
188+ }
189+ CheckBox {
190+ id: toTheRightCheckBox
191+ }
192+ Label {
193+ text: "toTheRight"
194+ }
195+ }
196+ Row {
197+ TextField {
198+ id: launcherOffsetField
199+ width: units.gu(10)
200+ text: "0"
201+ }
202+ Label {
203+ text: "launcherOffset"
204+ }
205+ }
206+ Row {
207+ TextField {
208+ id: currentIndexField
209+ width: units.gu(10)
210+ text: "0"
211+ }
212+ Label {
213+ text: "currentIndex"
214+ }
215+ }
216+ Row {
217+ TextField {
218+ id: delayMinutesField
219+ width: units.gu(10)
220+ text: "0"
221+ }
222+ Label {
223+ text: "delayMinutes"
224+ }
225+ }
226+ Row {
227+ TextField {
228+ id: backgroundTopMarginField
229+ width: units.gu(10)
230+ text: "0"
231+ }
232+ Label {
233+ text: "backgroundTopMargin"
234+ }
235+ }
236+ Row {
237+ CheckBox {
238+ id: lockedCheckBox
239+ }
240+ Label {
241+ text: "locked"
242+ }
243+ }
244+ Row {
245+ Label {
246+ text: "selected: " + selectedSpy.count
247+ }
248+ }
249+ Row {
250+ Label {
251+ text: "responded: " + respondedSpy.count
252+ }
253+ }
254+ Row {
255+ Label {
256+ text: "teased: " + teaseSpy.count
257+ }
258+ }
259+ Row {
260+ Label {
261+ text: "emergency: " + emergencySpy.count
262+ }
263+ }
264+ Row {
265+ Button {
266+ text: "Reload View"
267+ onClicked: {
268+ loader.active = false;
269+ loader.active = true;
270+ }
271+ }
272+ }
273+ Row {
274+ CheckBox {
275+ id: keyboardVisibleCheckBox
276+ }
277+ Label {
278+ text: "Keyboard Visible"
279+ }
280+ }
281+ }
282+ }
283+ }
284+
285+ SignalSpy {
286+ id: selectedSpy
287+ target: loader.item
288+ signalName: "selected"
289+ }
290+
291+ SignalSpy {
292+ id: respondedSpy
293+ target: loader.item
294+ signalName: "responded"
295+ }
296+
297+ SignalSpy {
298+ id: teaseSpy
299+ target: loader.item
300+ signalName: "tease"
301+ }
302+
303+ SignalSpy {
304+ id: emergencySpy
305+ target: loader.item
306+ signalName: "emergencyCall"
307+ }
308+
309+ UT.UnityTestCase {
310+ name: "WideView"
311+ when: windowShown
312+
313+ property Item view: loader.status === Loader.Ready ? loader.item : null
314+
315+ function init() {
316+ view.currentIndex = 0; // break binding with text field
317+ selectedSpy.clear();
318+ respondedSpy.clear();
319+ teaseSpy.clear();
320+ emergencySpy.clear();
321+ }
322+
323+ function cleanup() {
324+ keyboardVisibleCheckBox.checked = false;
325+
326+ loader.itemDestroyed = false;
327+ loader.active = false;
328+ tryCompare(loader, "status", Loader.Null);
329+ tryCompare(loader, "item", null);
330+ tryCompare(loader, "itemDestroyed", true);
331+ loader.active = true;
332+ tryCompare(loader, "status", Loader.Ready);
333+ removeTimeConstraintsFromDirectionalDragAreas(loader.item);
334+ }
335+
336+ function getIndexOf(name) {
337+ for (var i = 0; i < LightDM.Users.count; i++) {
338+ if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
339+ return i;
340+ }
341+ }
342+ fail("Didn't find name")
343+ return -1;
344+ }
345+
346+ function selectUser(name) {
347+ var i = getIndexOf(name);
348+ view.currentIndex = i;
349+ return i;
350+ }
351+
352+ function swipeAwayCover() {
353+ tryCompare(view, "fullyShown", true);
354+ var touchY = view.height / 2;
355+ touchFlick(view, view.width, touchY, 0, touchY);
356+ var coverPage = findChild(view, "coverPage");
357+ tryCompare(coverPage, "showProgress", 0);
358+ waitForRendering(view);
359+ }
360+
361+ function test_tease_data() {
362+ return [
363+ {tag: "locked", x: 0, offset: 0, count: 0, locked: true},
364+ {tag: "left", x: 0, offset: 0, count: 1, locked: false},
365+ {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1, locked: false},
366+ {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0, locked: false},
367+ {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
368+ ]
369+ }
370+ function test_tease(data) {
371+ view.locked = data.locked;
372+ view.dragHandleLeftMargin = data.offset;
373+ tap(view, data.x, 0);
374+ compare(teaseSpy.count, data.count);
375+ }
376+
377+ function test_selected() {
378+ var delegate = findChild(view, "username2");
379+ tap(delegate);
380+ compare(selectedSpy.count, 1);
381+ compare(selectedSpy.signalArguments[0][0], 2);
382+ compare(view.currentIndex, 0); // confirm we didn't change
383+ }
384+
385+ function test_respondedWithPassword() {
386+ view.locked = true;
387+ view.showPrompt("Prompt", true, true);
388+ var passwordInput = findChild(view, "passwordInput");
389+ compare(passwordInput.placeholderText, "Prompt");
390+ compare(passwordInput.echoMode, TextInput.Password);
391+ tap(passwordInput);
392+ typeString("password");
393+ keyClick(Qt.Key_Enter);
394+ compare(respondedSpy.count, 1);
395+ compare(respondedSpy.signalArguments[0][0], "password");
396+ }
397+
398+ function test_respondedWithNonSecret() {
399+ view.locked = true;
400+ view.showPrompt("otp", false, false);
401+ var passwordInput = findChild(view, "passwordInput");
402+ compare(passwordInput.placeholderText, "otp");
403+ compare(passwordInput.echoMode, TextInput.Normal);
404+ tap(passwordInput);
405+ typeString("foo");
406+ keyClick(Qt.Key_Enter);
407+ compare(respondedSpy.count, 1);
408+ compare(respondedSpy.signalArguments[0][0], "foo");
409+ }
410+
411+ function test_respondedWithSwipe() {
412+ swipeAwayCover();
413+ compare(respondedSpy.count, 1);
414+ compare(respondedSpy.signalArguments[0][0], "");
415+ }
416+
417+ function test_fullyShown() {
418+ tryCompare(view, "fullyShown", true);
419+ swipeAwayCover();
420+ tryCompare(view, "fullyShown", false);
421+ }
422+
423+ function test_required() {
424+ tryCompare(view, "required", true);
425+ swipeAwayCover();
426+ tryCompare(view, "required", false);
427+ }
428+
429+ function test_showMessage() {
430+ view.showMessage("Welcome to Unity Greeter");
431+ view.showMessage("<font color=\"#df382c\">This is an error</font>");
432+ view.showMessage("You should have seen three messages and this is a really long message too. wow so long much length");
433+ var infoLabel = findChild(view, "infoLabel");
434+ compare(infoLabel.text, "Welcome to Unity Greeter<br><font color=\"#df382c\">This is an error</font><br>You should have seen three messages and this is a really long message too. wow so long much length");
435+ compare(infoLabel.textFormat, Text.StyledText);
436+ compare(infoLabel.clip, true);
437+ verify(infoLabel.contentWidth > infoLabel.width);
438+ verify(infoLabel.opacity < 1);
439+ tryCompare(infoLabel, "opacity", 1);
440+ }
441+
442+ // Escape is used to reset the authentication, especially if PAM is unresponsive
443+ function test_escape() {
444+ view.currentIndex = 1;
445+ view.locked = true;
446+ view.showPrompt("Prompt", true, true);
447+ var passwordInput = findChild(view, "passwordInput");
448+ tap(passwordInput);
449+ compare(passwordInput.focus, true);
450+ compare(passwordInput.enabled, true);
451+
452+ typeString("password");
453+ keyClick(Qt.Key_Enter);
454+ compare(passwordInput.focus, false);
455+ compare(passwordInput.enabled, false);
456+
457+ compare(selectedSpy.count, 0);
458+ keyClick(Qt.Key_Escape);
459+ compare(selectedSpy.count, 1);
460+ compare(selectedSpy.signalArguments[0][0], 1);
461+
462+ view.reset();
463+ compare(passwordInput.focus, false);
464+ compare(passwordInput.enabled, true);
465+ }
466+
467+ function test_unicode() {
468+ var index = selectUser("unicode");
469+ var label = findChild(view, "username" + index);
470+ tryCompare(label, "text", "가나다라마");
471+ }
472+
473+ function test_longName() {
474+ var index = selectUser("long-name");
475+ var label = findChild(view, "username" + index);
476+ tryCompare(label, "truncated", true);
477+ }
478+
479+ function test_promptless() {
480+ var passwordInput = findChild(view, "passwordInput");
481+
482+ view.locked = true;
483+ compare(passwordInput.placeholderText, "Retry");
484+ tap(passwordInput);
485+ compare(respondedSpy.count, 0);
486+ compare(selectedSpy.count, 1);
487+ compare(selectedSpy.signalArguments[0][0], 0);
488+ selectedSpy.clear();
489+
490+ view.locked = false;
491+ compare(passwordInput.placeholderText, "Tap to unlock");
492+ tap(passwordInput);
493+ compare(selectedSpy.count, 0);
494+ compare(respondedSpy.count, 1);
495+ compare(respondedSpy.signalArguments[0][0], "");
496+ }
497+
498+ function test_loginListNotCoveredByKeyboard() {
499+ var loginList = findChild(view, "loginList");
500+ compare(loginList.height, view.height);
501+
502+ // when the vkb shows up, loginList is moved up to remain fully uncovered
503+
504+ keyboardVisibleCheckBox.checked = true;
505+
506+ tryCompare(loginList, "height", view.height - view.inputMethod.keyboardRectangle.height);
507+ tryCompareFunction( function() {
508+ var loginListRect = loginList.mapToItem(view, 0, 0, loginList.width, loginList.height);
509+ return loginListRect.y + loginListRect.height <= view.inputMethod.keyboardRectangle.y;
510+ }, true);
511+
512+ // once the vkb goes away, loginList goes back to its full height
513+
514+ keyboardVisibleCheckBox.checked = false;
515+
516+ tryCompare(loginList, "height", view.height);
517+ }
518+ }
519+}
tests/qmltests/Tutorial/tst_Tutorial.qml+5−4
@@ -112,9 +112,9 @@ Item {
112
112
113 function init() {
113 function init() {
114 tryCompare(shell, "enabled", true); // enabled by greeter when ready
114 tryCompare(shell, "enabled", true); // enabled by greeter when ready
115− swipeAwayGreeter();
116 AccountsService.demoEdges = false;
115 AccountsService.demoEdges = false;
117 AccountsService.demoEdges = true;
116 AccountsService.demoEdges = true;
117+ swipeAwayGreeter();
118 }
118 }
119
119
120 function cleanup() {
120 function cleanup() {
@@ -151,13 +151,14 @@ Item {
151 }
151 }
152
152
153 function swipeAwayGreeter() {
153 function swipeAwayGreeter() {
154− var greeter = findChild(shell, "greeter");
154+ var coverPage = findChild(shell, "coverPage");
155− tryCompare(greeter, "showProgress", 1);
155+ tryCompare(coverPage, "showProgress", 1);
156
156
157 touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
157 touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
158
158
159 // wait until the animation has finished
159 // wait until the animation has finished
160+ var greeter = findChild(shell, "greeter");
160− tryCompare(greeter, "showProgress", 0);
161+ tryCompare(greeter, "required", false);
161 waitForRendering(greeter);
162 waitForRendering(greeter);
162 }
163 }
163
164
tests/qmltests/Wizard/tst_Wizard.qml+48−5
@@ -73,8 +73,9 @@ Item {
73 AccountsService.hereEnabled = false;
73 AccountsService.hereEnabled = false;
74 AccountsService.hereLicensePath = Qt.resolvedUrl("licenses");
74 AccountsService.hereLicensePath = Qt.resolvedUrl("licenses");
75 i18n.language = "en";
75 i18n.language = "en";
76− MockQOfono.setModems(["sim1"], [false]);
76+ MockQOfono.setModems(["sim1"], [false], []);
77 MockQOfono.available = true;
77 MockQOfono.available = true;
78+ MockQOfono.ready = true;
78 System.wizardEnabled = true;
79 System.wizardEnabled = true;
79
80
80 updateSessionLanguageSpy.clear();
81 updateSessionLanguageSpy.clear();
@@ -140,6 +141,19 @@ Item {
140 tryCompareFunction(function() { return stack.currentPage.objectName; }, name);
141 tryCompareFunction(function() { return stack.currentPage.objectName; }, name);
141 tryCompare(stack.currentPage, "opacity", 1.0);
142 tryCompare(stack.currentPage, "opacity", 1.0);
142 tryCompare(stack.currentPage, "enabled", true);
143 tryCompare(stack.currentPage, "enabled", true);
144+ tryCompare(stack.currentPage, "skipValid", true);
145+ tryCompare(stack.currentPage, "skip", false);
146+ waitForRendering(stack.currentPage);
147+ return stack.currentPage;
148+ }
149+
150+ function verifyPageIsBlocked(name) {
151+ var pages = findChild(wizard, "wizardPages");
152+ var stack = findChild(pages, "pageStack");
153+ // don't simply call tryCompare here, because stack.currentPage will be swapped out itself
154+ tryCompareFunction(function() { return stack.currentPage.objectName; }, name);
155+ tryCompare(stack.currentPage, "enabled", false);
156+ tryCompare(stack.currentPage, "skipValid", false);
143 waitForRendering(stack.currentPage);
157 waitForRendering(stack.currentPage);
144 return stack.currentPage;
158 return stack.currentPage;
145 }
159 }
@@ -223,25 +237,54 @@ Item {
223 }
237 }
224
238
225 function test_simNoModemsSkip() {
239 function test_simNoModemsSkip() {
226− MockQOfono.setModems([], []);
240+ MockQOfono.setModems([], [], []);
227 goToPage("passwdPage", true);
241 goToPage("passwdPage", true);
228 }
242 }
229
243
230 function test_simFirstSkip() {
244 function test_simFirstSkip() {
231− MockQOfono.setModems(["a", "b"], [true, false]);
245+ MockQOfono.setModems(["a", "b"], [true, false], []);
232 goToPage("passwdPage", true);
246 goToPage("passwdPage", true);
233 }
247 }
234
248
235 function test_simSecondSkip() {
249 function test_simSecondSkip() {
236− MockQOfono.setModems(["a", "b"], [false, true]);
250+ MockQOfono.setModems(["a", "b"], [false, true], []);
237 goToPage("passwdPage", true);
251 goToPage("passwdPage", true);
238 }
252 }
239
253
240 function test_simBothSkip() {
254 function test_simBothSkip() {
241− MockQOfono.setModems(["a", "b"], [true, true]);
255+ MockQOfono.setModems(["a", "b"], [true, true], []);
242 goToPage("passwdPage", true);
256 goToPage("passwdPage", true);
243 }
257 }
244
258
259+ function test_simWaitOnManagerAsync() {
260+ MockQOfono.ready = false;
261+ MockQOfono.setModems(["a"], [false], []);
262+
263+ // Go to SIM page, which will be waiting for skip to be valid
264+ var page = goToPage("languagePage");
265+ tap(findChild(page, "forwardButton"));
266+ verifyPageIsBlocked("simPage");
267+
268+ // Now release QOfono from blocking the page
269+ MockQOfono.ready = true;
270+
271+ waitForPage("simPage");
272+ }
273+
274+ function test_simWaitOnCardAsync() {
275+ MockQOfono.setModems(["a"], [false], [false]);
276+
277+ // Go to SIM page, which will be waiting for skip to be valid
278+ var page = goToPage("languagePage");
279+ tap(findChild(page, "forwardButton"));
280+ verifyPageIsBlocked("simPage");
281+
282+ // Now release QOfono from blocking the page
283+ MockQOfono.setModems(["a"], [false], [true]);
284+
285+ waitForPage("simPage");
286+ }
287+
245 function enterPasscode(passcode) {
288 function enterPasscode(passcode) {
246 for (var i = 0; i < passcode.length; ++i) {
289 for (var i = 0; i < passcode.length; ++i) {
247 var character = passcode.charAt(i);
290 var character = passcode.charAt(i);
tests/qmltests/tst_Shell.qml+65−43
@@ -19,9 +19,11 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import QtTest 1.0
21import QtTest 1.0
22+import AccountsService 0.1
22import GSettings 1.0
23import GSettings 1.0
23import LightDM 0.1 as LightDM
24import LightDM 0.1 as LightDM
24import Ubuntu.Components 1.1
25import Ubuntu.Components 1.1
26+import Ubuntu.Components.ListItems 1.0 as ListItem
25import Ubuntu.Telephony 0.1 as Telephony
27import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Application 0.1
28import Unity.Application 0.1
27import Unity.Connectivity 0.1
29import Unity.Connectivity 0.1
@@ -80,7 +82,7 @@ Item {
80 }
82 }
81
83
82 Rectangle {
84 Rectangle {
83− color: "white"
85+ color: "darkgrey"
84 width: units.gu(30)
86 width: units.gu(30)
85 height: shellLoader.height
87 height: shellLoader.height
86
88
@@ -98,11 +100,23 @@ Item {
98
100
99 var greeter = testCase.findChild(shellLoader.item, "greeter");
101 var greeter = testCase.findChild(shellLoader.item, "greeter");
100 if (!greeter.shown) {
102 if (!greeter.shown) {
101− greeter.show();
103+ LightDM.Greeter.showGreeter();
102 }
104 }
103 }
105 }
104 }
106 }
105 }
107 }
108+ ListItem.ItemSelector {
109+ anchors { left: parent.left; right: parent.right }
110+ activeFocusOnPress: false
111+ text: "LightDM mock mode"
112+ model: ["single", "single-passphrase", "single-pin"]
113+ onSelectedIndexChanged: {
114+ shellLoader.active = false;
115+ LightDM.Greeter.mockMode = model[selectedIndex];
116+ LightDM.Users.mockMode = model[selectedIndex];
117+ shellLoader.active = true;
118+ }
119+ }
106 }
120 }
107 }
121 }
108 }
122 }
@@ -284,11 +298,13 @@ Item {
284
298
285 function test_leftEdgeDrag_data() {
299 function test_leftEdgeDrag_data() {
286 return [
300 return [
287− {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
301+ {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false},
288− {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
302+ {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false},
289− {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false},
303+ {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false, greeterShown: false},
290− {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
304+ {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false},
305+ {tag: "small swipe with greeter", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false, greeterShown: true},
306+ {tag: "long swipe with greeter", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: true},
291− {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash", launcherHides: false}
307+ {tag: "swipe over dash", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash", launcherHides: false, greeterShown: false},
292 ];
308 ];
293 }
309 }
294
310
@@ -299,6 +315,12 @@ Item {
299 ApplicationManager.focusApplication(data.focusedApp)
315 ApplicationManager.focusApplication(data.focusedApp)
300 waitUntilApplicationWindowIsFullyVisible();
316 waitUntilApplicationWindowIsFullyVisible();
301
317
318+ var greeter = findChild(shell, "greeter");
319+ if (data.greeterShown) {
320+ LightDM.Greeter.showGreeter();
321+ tryCompare(greeter, "fullyShown", true);
322+ }
323+
302 if (data.revealLauncher) {
324 if (data.revealLauncher) {
303 dragLauncherIntoView();
325 dragLauncherIntoView();
304 }
326 }
@@ -306,8 +328,10 @@ Item {
306 swipeFromLeftEdge(data.swipeLength);
328 swipeFromLeftEdge(data.swipeLength);
307 if (data.appHides) {
329 if (data.appHides) {
308 waitUntilDashIsFocused();
330 waitUntilDashIsFocused();
331+ tryCompare(greeter, "shown", false);
309 } else {
332 } else {
310 waitUntilApplicationWindowIsFullyVisible();
333 waitUntilApplicationWindowIsFullyVisible();
334+ compare(greeter.fullyShown, data.greeterShown);
311 }
335 }
312
336
313 var launcher = findChild(shell, "launcherPanel");
337 var launcher = findChild(shell, "launcherPanel");
@@ -335,20 +359,20 @@ Item {
335 // Suspend while call is active...
359 // Suspend while call is active...
336 callManager.foregroundCall = phoneCall;
360 callManager.foregroundCall = phoneCall;
337 Powerd.status = Powerd.Off;
361 Powerd.status = Powerd.Off;
338− tryCompare(greeter, "showProgress", 0);
362+ tryCompare(greeter, "shown", false);
339
363
340 // Now try again after ending call
364 // Now try again after ending call
341 callManager.foregroundCall = null;
365 callManager.foregroundCall = null;
342 Powerd.status = Powerd.On;
366 Powerd.status = Powerd.On;
343 Powerd.status = Powerd.Off;
367 Powerd.status = Powerd.Off;
344− tryCompare(greeter, "showProgress", 1);
368+ tryCompare(greeter, "fullyShown", true);
345
369
346 tryCompare(ApplicationManager, "suspended", true);
370 tryCompare(ApplicationManager, "suspended", true);
347 compare(mainApp.state, ApplicationInfoInterface.Suspended);
371 compare(mainApp.state, ApplicationInfoInterface.Suspended);
348
372
349 // And wake up
373 // And wake up
350 Powerd.status = Powerd.On;
374 Powerd.status = Powerd.On;
351− tryCompare(greeter, "showProgress", 1);
375+ tryCompare(greeter, "fullyShown", true);
352
376
353 // Swipe away greeter to focus app
377 // Swipe away greeter to focus app
354 swipeAwayGreeter();
378 swipeAwayGreeter();
@@ -359,14 +383,14 @@ Item {
359
383
360 function swipeAwayGreeter() {
384 function swipeAwayGreeter() {
361 var greeter = findChild(shell, "greeter");
385 var greeter = findChild(shell, "greeter");
362− tryCompare(greeter, "showProgress", 1);
386+ tryCompare(greeter, "fullyShown", true);
363
387
364 var touchX = shell.width - (shell.edgeSize / 2);
388 var touchX = shell.width - (shell.edgeSize / 2);
365 var touchY = shell.height / 2;
389 var touchY = shell.height / 2;
366 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
390 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
367
391
368 // wait until the animation has finished
392 // wait until the animation has finished
369− tryCompare(greeter, "showProgress", 0);
393+ tryCompare(greeter, "shown", false);
370 waitForRendering(greeter);
394 waitForRendering(greeter);
371 }
395 }
372
396
@@ -608,12 +632,12 @@ Item {
608
632
609 waitUntilDashIsFocused();
633 waitUntilDashIsFocused();
610
634
611− greeter.show();
635+ LightDM.Greeter.showGreeter();
612− tryCompare(greeter, "showProgress", 1);
636+ tryCompare(greeter, "fullyShown", true);
613
637
614 // The main point of this test
638 // The main point of this test
615 ApplicationManager.requestFocusApplication("dialer-app");
639 ApplicationManager.requestFocusApplication("dialer-app");
616− tryCompare(greeter, "showProgress", 0);
640+ tryCompare(greeter, "shown", false);
617 waitForRendering(greeter);
641 waitForRendering(greeter);
618 }
642 }
619
643
@@ -631,24 +655,22 @@ Item {
631
655
632 function test_showAndHideGreeterDBusCalls() {
656 function test_showAndHideGreeterDBusCalls() {
633 var greeter = findChild(shell, "greeter")
657 var greeter = findChild(shell, "greeter")
634− tryCompare(greeter, "showProgress", 0)
658+ tryCompare(greeter, "shown", false)
635 waitForRendering(greeter);
659 waitForRendering(greeter);
636 LightDM.Greeter.showGreeter()
660 LightDM.Greeter.showGreeter()
637 waitForRendering(greeter)
661 waitForRendering(greeter)
638− tryCompare(greeter, "showProgress", 1)
662+ tryCompare(greeter, "fullyShown", true)
639 LightDM.Greeter.hideGreeter()
663 LightDM.Greeter.hideGreeter()
640− tryCompare(greeter, "showProgress", 0)
664+ tryCompare(greeter, "shown", false)
641 }
665 }
642
666
643 function test_login() {
667 function test_login() {
644− sessionSpy.clear()
645−
646 var greeter = findChild(shell, "greeter")
668 var greeter = findChild(shell, "greeter")
647 waitForRendering(greeter)
669 waitForRendering(greeter)
648− greeter.show()
670+ LightDM.Greeter.showGreeter();
649− tryCompare(greeter, "showProgress", 1)
671+ tryCompare(greeter, "fullyShown", true)
650
672
651− tryCompare(sessionSpy, "count", 0)
673+ sessionSpy.clear();
652 swipeAwayGreeter()
674 swipeAwayGreeter()
653 tryCompare(sessionSpy, "count", 1)
675 tryCompare(sessionSpy, "count", 1)
654 }
676 }
@@ -689,7 +711,8 @@ Item {
689
711
690 function test_unlockedProperties() {
712 function test_unlockedProperties() {
691 // Confirm that various properties have the correct values when unlocked
713 // Confirm that various properties have the correct values when unlocked
714+ var greeter = findChild(shell, "greeter");
692− tryCompare(shell, "locked", false)
715+ tryCompare(greeter, "locked", false);
693
716
694 var launcher = findChild(shell, "launcher")
717 var launcher = findChild(shell, "launcher")
695 tryCompare(launcher, "available", true)
718 tryCompare(launcher, "available", true)
@@ -702,25 +725,6 @@ Item {
702 tryCompare(unlockAllModemsSpy, "count", 1)
725 tryCompare(unlockAllModemsSpy, "count", 1)
703 }
726 }
704
727
705− function test_leftEdgeDragOnGreeter_data() {
706− return [
707− {tag: "short swipe", targetX: shell.width / 3, unlocked: false},
708− {tag: "long swipe", targetX: shell.width / 3 * 2, unlocked: true}
709− ]
710− }
711−
712− function test_leftEdgeDragOnGreeter(data) {
713− var greeter = findChild(shell, "greeter");
714− greeter.show();
715− tryCompare(greeter, "showProgress", 1);
716−
717− var touchStartX = 2;
718− var touchStartY = shell.height / 2;
719− touchFlick(shell, touchStartX, touchStartY, data.targetX, touchStartY);
720−
721− tryCompare(greeter, "showProgress", data.unlocked ? 0 : 1);
722− }
723−
724 function test_tapOnRightEdgeReachesApplicationSurface() {
728 function test_tapOnRightEdgeReachesApplicationSurface() {
725 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
729 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
726 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
730 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
@@ -830,5 +834,23 @@ Item {
830 var launcherPanel = findChild(shell, "launcherPanel");
834 var launcherPanel = findChild(shell, "launcherPanel");
831 tryCompare(launcherPanel, "x", -launcherPanel.width);
835 tryCompare(launcherPanel, "x", -launcherPanel.width);
832 }
836 }
837+
838+ function test_background_data() {
839+ return [
840+ {tag: "color", accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"), gsettings: "", output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
841+ {tag: "empty", accounts: "", gsettings: "", output: shell.defaultBackground},
842+ {tag: "as-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: "", output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
843+ {tag: "gs-specified", accounts: "", gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
844+ {tag: "both-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
845+ {tag: "invalid-as", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
846+ {tag: "invalid-both", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"), output: shell.defaultBackground},
847+ ]
848+ }
849+ function test_background(data) {
850+ AccountsService.backgroundFile = data.accounts;
851+ var backgroundSettings = findInvisibleChild(shell, "backgroundSettings");
852+ backgroundSettings.pictureUri = data.gsettings;
853+ tryCompare(shell, "background", data.output);
854+ }
833 }
855 }
834}
856}
tests/qmltests/tst_ShellWithPin.qml+97−48
@@ -74,7 +74,6 @@ Item {
74 Component.onDestruction: {
74 Component.onDestruction: {
75 shellLoader.itemDestroyed = true
75 shellLoader.itemDestroyed = true
76 }
76 }
77− maxFailedLogins: maxRetriesTextField.text
78 }
77 }
79 }
78 }
80 }
79 }
@@ -90,15 +89,7 @@ Item {
90 Button {
89 Button {
91 anchors { left: parent.left; right: parent.right }
90 anchors { left: parent.left; right: parent.right }
92 text: "Show Greeter"
91 text: "Show Greeter"
93− onClicked: {
92+ onClicked: LightDM.Greeter.showGreeter()
94− if (shellLoader.status !== Loader.Ready)
95− return
96−
97− var greeter = testCase.findChild(shellLoader.item, "greeter")
98− if (!greeter.shown) {
99− greeter.show()
100− }
101− }
102 }
93 }
103
94
104 Label {
95 Label {
@@ -108,6 +99,10 @@ Item {
108 TextField {
99 TextField {
109 id: maxRetriesTextField
100 id: maxRetriesTextField
110 text: "-1"
101 text: "-1"
102+ onTextChanged: {
103+ var greeter = testCase.findChild(shellLoader.item, "greeter");
104+ greeter.maxFailedLogins = text;
105+ }
111 }
106 }
112 }
107 }
113 }
108 }
@@ -144,11 +139,12 @@ Item {
144
139
145 function init() {
140 function init() {
146 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
141 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
147− sessionSpy.target = findChild(shell, "greeter")
142+ var greeter = findChild(shell, "greeter");
143+ sessionSpy.target = greeter;
148− swipeAwayGreeter()
144+ swipeAwayGreeter(true);
149 waitForLockscreen()
145 waitForLockscreen()
150− shell.failedLoginsDelayAttempts = -1
146+ greeter.failedLoginsDelayAttempts = -1;
151− maxRetriesTextField.text = "-1"
147+ greeter.maxFailedLogins = -1;
152 }
148 }
153
149
154 function cleanup() {
150 function cleanup() {
@@ -192,17 +188,21 @@ Item {
192 compare(ApplicationManager.count, 1)
188 compare(ApplicationManager.count, 1)
193 }
189 }
194
190
195− function swipeAwayGreeter() {
191+ function swipeAwayGreeter(waitForCoverPage) {
196 var greeter = findChild(shell, "greeter");
192 var greeter = findChild(shell, "greeter");
197 waitForRendering(greeter)
193 waitForRendering(greeter)
194+ var coverPage = findChild(shell, "coverPage");
198− tryCompare(greeter, "showProgress", 1);
195+ tryCompare(coverPage, "showProgress", 1);
199
196
200 var touchX = shell.width - (shell.edgeSize / 2);
197 var touchX = shell.width - (shell.edgeSize / 2);
201 var touchY = shell.height / 2;
198 var touchY = shell.height / 2;
202 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
199 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
203
200
201+ if (waitForCoverPage) {
204− // wait until the animation has finished
202+ // wait until the animation has finished
203+ var coverPage = findChild(shell, "coverPage");
205− tryCompare(greeter, "showProgress", 0);
204+ tryCompare(coverPage, "showProgress", 0);
205+ }
206 }
206 }
207
207
208 function waitForLockscreen() {
208 function waitForLockscreen() {
@@ -213,19 +213,17 @@ Item {
213 }
213 }
214
214
215 function enterPin(pin) {
215 function enterPin(pin) {
216− var inputField = findChild(shell, "pinentryField")
217 for (var i = 0; i < pin.length; ++i) {
216 for (var i = 0; i < pin.length; ++i) {
218 var character = pin.charAt(i)
217 var character = pin.charAt(i)
219 var button = findChild(shell, "pinPadButton" + character)
218 var button = findChild(shell, "pinPadButton" + character)
219+ tryCompare(button, "enabled", true);
220 tap(button)
220 tap(button)
221 }
221 }
222 }
222 }
223
223
224 function confirmLockedApp(app) {
224 function confirmLockedApp(app) {
225 var greeter = findChild(shell, "greeter")
225 var greeter = findChild(shell, "greeter")
226− var lockscreen = findChild(shell, "lockscreen")
227 tryCompare(greeter, "shown", false)
226 tryCompare(greeter, "shown", false)
228− tryCompare(lockscreen, "shown", false)
229 tryCompare(greeter, "hasLockedApp", true)
227 tryCompare(greeter, "hasLockedApp", true)
230 tryCompare(greeter, "lockedApp", app)
228 tryCompare(greeter, "lockedApp", app)
231 tryCompare(LightDM.Greeter, "active", true)
229 tryCompare(LightDM.Greeter, "active", true)
@@ -264,13 +262,16 @@ Item {
264 AccountsService.demoEdges = true
262 AccountsService.demoEdges = true
265 tryCompare(lockscreen, "shown", false)
263 tryCompare(lockscreen, "shown", false)
266
264
265+ var greeter = findChild(shell, "greeter");
267− swipeAwayGreeter()
266+ swipeAwayGreeter(false);
267+ tryCompare(greeter, "shown", false);
268 tryCompare(sessionSpy, "count", 1)
268 tryCompare(sessionSpy, "count", 1)
269
269
270 // Lockscreen is only hidden by the edge demo, so if we turn that
270 // Lockscreen is only hidden by the edge demo, so if we turn that
271 // off and show greeter again, lockscreen should appear
271 // off and show greeter again, lockscreen should appear
272 AccountsService.demoEdges = false
272 AccountsService.demoEdges = false
273 LightDM.Greeter.showGreeter()
273 LightDM.Greeter.showGreeter()
274+ lockscreen = findChild(shell, "lockscreen");
274 tryCompare(lockscreen, "shown", true)
275 tryCompare(lockscreen, "shown", true)
275 }
276 }
276
277
@@ -288,18 +289,16 @@ Item {
288
289
289 function test_emergencyCall() {
290 function test_emergencyCall() {
290 var greeter = findChild(shell, "greeter")
291 var greeter = findChild(shell, "greeter")
291− var lockscreen = findChild(shell, "lockscreen")
292− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
293 var panel = findChild(shell, "panel")
292 var panel = findChild(shell, "panel")
294 var indicators = findChild(shell, "indicators")
293 var indicators = findChild(shell, "indicators")
295 var launcher = findChild(shell, "launcher")
294 var launcher = findChild(shell, "launcher")
296 var stage = findChild(shell, "stage")
295 var stage = findChild(shell, "stage")
297
296
298− tap(emergencyButton)
297+ tap(findChild(greeter, "emergencyCallLabel"));
299
298
300 tryCompare(greeter, "lockedApp", "dialer-app")
299 tryCompare(greeter, "lockedApp", "dialer-app")
301 tryCompare(greeter, "hasLockedApp", true)
300 tryCompare(greeter, "hasLockedApp", true)
302− tryCompare(lockscreen, "shown", false)
301+ tryCompare(greeter, "shown", false);
303 tryCompare(panel, "fullscreenMode", true)
302 tryCompare(panel, "fullscreenMode", true)
304 tryCompare(indicators, "available", false)
303 tryCompare(indicators, "available", false)
305 tryCompare(launcher, "available", false)
304 tryCompare(launcher, "available", false)
@@ -312,7 +311,7 @@ Item {
312 tryCompare(greeter, "shown", true)
311 tryCompare(greeter, "shown", true)
313 tryCompare(greeter, "lockedApp", "")
312 tryCompare(greeter, "lockedApp", "")
314 tryCompare(greeter, "hasLockedApp", false)
313 tryCompare(greeter, "hasLockedApp", false)
315− tryCompare(lockscreen, "shown", true)
314+ tryCompare(greeter, "fullyShown", true);
316 tryCompare(panel, "fullscreenMode", false)
315 tryCompare(panel, "fullscreenMode", false)
317 tryCompare(indicators, "available", true)
316 tryCompare(indicators, "available", true)
318 tryCompare(launcher, "available", true)
317 tryCompare(launcher, "available", true)
@@ -320,23 +319,25 @@ Item {
320 }
319 }
321
320
322 function test_emergencyCallCrash() {
321 function test_emergencyCallCrash() {
323− var lockscreen = findChild(shell, "lockscreen")
322+ var greeter = findChild(shell, "greeter");
324− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
323+ var emergencyButton = findChild(greeter, "emergencyCallLabel");
325 tap(emergencyButton)
324 tap(emergencyButton)
326
325
327− tryCompare(lockscreen, "shown", false)
326+ tryCompare(greeter, "shown", false);
328 killApps() // kill dialer-app, as if it crashed
327 killApps() // kill dialer-app, as if it crashed
328+ tryCompare(greeter, "shown", true);
329− tryCompare(lockscreen, "shown", true)
329+ tryCompare(findChild(greeter, "lockscreen"), "shown", true);
330+ tryCompare(findChild(greeter, "coverPage"), "shown", false);
330 }
331 }
331
332
332 function test_emergencyCallAppLaunch() {
333 function test_emergencyCallAppLaunch() {
333− var lockscreen = findChild(shell, "lockscreen")
334+ var greeter = findChild(shell, "greeter");
334− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
335+ var emergencyButton = findChild(greeter, "emergencyCallLabel");
335 tap(emergencyButton)
336 tap(emergencyButton)
336
337
337− tryCompare(lockscreen, "shown", false)
338+ tryCompare(greeter, "shown", false);
338 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
339 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
339− tryCompare(lockscreen, "shown", true)
340+ tryCompare(greeter, "shown", true);
340 }
341 }
341
342
342 function test_failedLoginsCount() {
343 function test_failedLoginsCount() {
@@ -350,7 +351,8 @@ Item {
350 }
351 }
351
352
352 function test_wrongEntries() {
353 function test_wrongEntries() {
354+ var greeter = findChild(shell, "greeter");
353− shell.failedLoginsDelayAttempts = 3
355+ greeter.failedLoginsDelayAttempts = 3;
354
356
355 var placeHolder = findChild(shell, "wrongNoticeLabel")
357 var placeHolder = findChild(shell, "wrongNoticeLabel")
356 tryCompare(placeHolder, "text", "")
358 tryCompare(placeHolder, "text", "")
@@ -364,7 +366,7 @@ Item {
364 var lockscreen = findChild(shell, "lockscreen")
366 var lockscreen = findChild(shell, "lockscreen")
365 tryCompare(lockscreen, "delayMinutes", 0)
367 tryCompare(lockscreen, "delayMinutes", 0)
366 enterPin("1111")
368 enterPin("1111")
367− tryCompare(lockscreen, "delayMinutes", shell.failedLoginsDelayMinutes)
369+ tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
368 }
370 }
369
371
370 function test_factoryReset() {
372 function test_factoryReset() {
@@ -410,7 +412,7 @@ Item {
410 // And when we kill the app, we go back to locked tablet mode
412 // And when we kill the app, we go back to locked tablet mode
411 killApps()
413 killApps()
412 var greeter = findChild(shell, "greeter")
414 var greeter = findChild(shell, "greeter")
413− tryCompare(greeter, "showProgress", 1)
415+ tryCompare(greeter, "fullyShown", true)
414 tryCompare(shell, "sideStageEnabled", true)
416 tryCompare(shell, "sideStageEnabled", true)
415 tryCompare(applicationsDisplayLoader, "tabletMode", true)
417 tryCompare(applicationsDisplayLoader, "tabletMode", true)
416 }
418 }
@@ -453,11 +455,11 @@ Item {
453 // - Should be back in normal dialer
455 // - Should be back in normal dialer
454 // (we've had a bug where we locked screen in this case)
456 // (we've had a bug where we locked screen in this case)
455
457
456− var lockscreen = findChild(shell, "lockscreen");
458+ var greeter = findChild(shell, "greeter");
457 var panel = findChild(shell, "panel");
459 var panel = findChild(shell, "panel");
458
460
459 enterPin("1234");
461 enterPin("1234");
460− tryCompare(lockscreen, "shown", false);
462+ tryCompare(greeter, "shown", false);
461 tryCompare(LightDM.Greeter, "active", false);
463 tryCompare(LightDM.Greeter, "active", false);
462
464
463 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
465 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
@@ -472,10 +474,32 @@ Item {
472 ApplicationManager.requestFocusApplication("dialer-app");
474 ApplicationManager.requestFocusApplication("dialer-app");
473
475
474 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
476 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
475− tryCompare(lockscreen, "shown", false);
477+ tryCompare(greeter, "shown", false);
476 tryCompare(LightDM.Greeter, "active", false);
478 tryCompare(LightDM.Greeter, "active", false);
477 }
479 }
478
480
481+ function test_focusRequestedHidesCoverPage() {
482+ LightDM.Greeter.showGreeter();
483+
484+ var app = ApplicationManager.startApplication("gallery-app");
485+ // wait until the app is fully loaded (ie, real surface replaces splash screen)
486+ tryCompareFunction(function() { return app.session !== null && app.session.surface !== null }, true);
487+
488+ // New app hides coverPage?
489+ var greeter = findChild(shell, "greeter");
490+ var coverPage = testCase.findChild(shell, "coverPage");
491+ tryCompare(coverPage, "showProgress", 0);
492+ tryCompare(greeter, "fullyShown", true);
493+
494+ LightDM.Greeter.showGreeter();
495+ tryCompare(coverPage, "showProgress", 1);
496+
497+ // Make sure focusing same app triggers same behavior
498+ ApplicationManager.requestFocusApplication("gallery-app");
499+ tryCompare(coverPage, "showProgress", 0);
500+ tryCompare(greeter, "fullyShown", true);
501+ }
502+
479 function test_suspend() {
503 function test_suspend() {
480 var greeter = findChild(shell, "greeter");
504 var greeter = findChild(shell, "greeter");
481
505
@@ -487,10 +511,10 @@ Item {
487
511
488 // And wake up
512 // And wake up
489 Powerd.status = Powerd.On;
513 Powerd.status = Powerd.On;
490− tryCompare(greeter, "showProgress", 1);
514+ tryCompare(greeter, "fullyShown", true);
491
515
492 // Swipe away greeter to focus app
516 // Swipe away greeter to focus app
493− swipeAwayGreeter();
517+ swipeAwayGreeter(true);
494
518
495 // We have a lockscreen, make sure we're still suspended
519 // We have a lockscreen, make sure we're still suspended
496 tryCompare(ApplicationManager, "suspended", true);
520 tryCompare(ApplicationManager, "suspended", true);
@@ -539,9 +563,9 @@ Item {
539 only after its idle timeout is triggered.
563 only after its idle timeout is triggered.
540 */
564 */
541 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
565 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
566+ LightDM.Greeter.showGreeter();
542− var greeter = testCase.findChild(shell, "greeter")
567+ var coverPage = testCase.findChild(shell, "coverPage");
543− greeter.show();
544− tryCompare(greeter, "showProgress", 1);
568+ tryCompare(coverPage, "showProgress", 1);
545
569
546 var launcher = testCase.findChild(shell, "launcher")
570 var launcher = testCase.findChild(shell, "launcher")
547 {
571 {
@@ -562,12 +586,12 @@ Item {
562 tryCompare(launcherPanel, "x", 0);
586 tryCompare(launcherPanel, "x", 0);
563
587
564 // Greeter should be displaced
588 // Greeter should be displaced
565− tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
589+ tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
566
590
567 touchRelease(shell, toX, shell.height * 0.5);
591 touchRelease(shell, toX, shell.height * 0.5);
568
592
569 // Upon release the greeter should have slid back into full view
593 // Upon release the greeter should have slid back into full view
570− tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x === 0; }, true);
594+ tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x === 0; }, true);
571
595
572 // And the launcher should stay fully shown
596 // And the launcher should stay fully shown
573 for (var i = 0; i < 10; ++i) {
597 for (var i = 0; i < 10; ++i) {
@@ -575,5 +599,30 @@ Item {
575 compare(launcherPanel.x, 0);
599 compare(launcherPanel.x, 0);
576 }
600 }
577 }
601 }
602+
603+ function test_longLeftEdgeDrags() {
604+ var coverPage = findChild(shell, "coverPage");
605+ var lockscreen = findChild(shell, "lockscreen");
606+ var launcher = findChild(shell, "launcherPanel");
607+ ApplicationManager.startApplication("gallery-app");
608+ tryCompare(ApplicationManager, "focusedApplicationId", "gallery-app");
609+
610+ // Show greeter
611+ LightDM.Greeter.showGreeter();
612+ tryCompare(coverPage, "showProgress", 1);
613+
614+ // Swipe cover page away
615+ touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
616+ tryCompare(launcher, "x", -launcher.width);
617+ tryCompare(coverPage, "showProgress", 0);
618+ compare(lockscreen.shown, true);
619+ compare(ApplicationManager.focusedApplicationId, "gallery-app");
620+
621+ // Now attempt a swipe on lockscreen
622+ touchFlick(shell, 2, shell.height / 2, units.gu(27), shell.height / 2);
623+ tryCompare(launcher, "x", 0);
624+ compare(lockscreen.shown, true);
625+ compare(ApplicationManager.focusedApplicationId, "gallery-app");
626+ }
578 }
627 }
579}
628}
tests/qmltests/tst_TabletShell.qml+50−60
@@ -28,9 +28,10 @@ import Powerd 0.1
28
28
29import "../../qml"
29import "../../qml"
30
30
31−Row {
31+Item {
32 id: root
32 id: root
33− spacing: 0
33+ width: units.gu(120)
34+ height: units.gu(80)
34
35
35 Component.onCompleted: {
36 Component.onCompleted: {
36 // must set the mock mode before loading the Shell
37 // must set the mock mode before loading the Shell
@@ -55,49 +56,52 @@ Row {
55 }
56 }
56 }
57 }
57
58
59+ Row {
60+ anchors.fill: parent
58− Loader {
61+ Loader {
59− id: shellLoader
62+ id: shellLoader
60
63
61− active: false
64+ active: false
62− width: units.gu(100)
65+ width: units.gu(100)
63− height: units.gu(80)
66+ height: parent.height
64
67
65− property bool itemDestroyed: false
68+ property bool itemDestroyed: false
66− sourceComponent: Component {
69+ sourceComponent: Component {
67− Shell {
70+ Shell {
68− property string indicatorProfile: "phone"
71+ property string indicatorProfile: "phone"
69
72
70− Component.onDestruction: {
73+ Component.onDestruction: {
71− shellLoader.itemDestroyed = true
74+ shellLoader.itemDestroyed = true
75+ }
72 }
76 }
73 }
77 }
74 }
78 }
75− }
76
79
77− Rectangle {
80+ Rectangle {
78− color: "white"
81+ color: "white"
79− width: units.gu(20)
82+ width: units.gu(20)
80− height: shellLoader.height
83+ height: parent.height
81−
84+
82− Column {
85+ Column {
83− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
86+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
84− spacing: units.gu(1)
87+ spacing: units.gu(1)
85− Button {
88+ Button {
86− text: "Show Greeter"
89+ text: "Show Greeter"
87− onClicked: {
90+ onClicked: {
88− if (shellLoader.status !== Loader.Ready)
91+ if (shellLoader.status !== Loader.Ready)
89− return
92+ return
90−
93+
91− var greeter = testCase.findChild(shellLoader.item, "greeter")
94+ var greeter = testCase.findChild(shellLoader.item, "greeter")
92− if (!greeter.shown) {
95+ if (!greeter.shown) {
93− greeter.show()
96+ greeter.show()
97+ }
94 }
98 }
95 }
99 }
96− }
97− Button {
100+ Button {
98− text: "Demo edges"
101+ text: "Demo edges"
99− onClicked: {
102+ onClicked: {
100− AccountsService.demoEdges = true
103+ AccountsService.demoEdges = true;
104+ }
101 }
105 }
102 }
106 }
103 }
107 }
@@ -133,6 +137,8 @@ Row {
133
137
134 function init() {
138 function init() {
135 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
139 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
140+ var userList = findChild(shell, "userList");
141+ tryCompare(userList, "movingInternally", false);
136 sessionSpy.clear()
142 sessionSpy.clear()
137 sessionSpy.target = findChild(shell, "greeter")
143 sessionSpy.target = findChild(shell, "greeter")
138 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
144 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
@@ -188,8 +194,7 @@ Row {
188 if (userlist.currentIndex > i) {
194 if (userlist.currentIndex > i) {
189 next = userlist.currentIndex - 1
195 next = userlist.currentIndex - 1
190 }
196 }
191− var account = findChild(greeter, "username"+next)
197+ tap(findChild(greeter, "username"+next));
192− mouseClick(account, 1, 1)
193 tryCompare(userlist, "currentIndex", next)
198 tryCompare(userlist, "currentIndex", next)
194 tryCompare(userlist, "movingInternally", false)
199 tryCompare(userlist, "movingInternally", false)
195 }
200 }
@@ -197,13 +202,12 @@ Row {
197
202
198 function selectUser(name) {
203 function selectUser(name) {
199 // Find index of user with the right name
204 // Find index of user with the right name
200− var greeter = findChild(shell, "greeter")
201− for (var i = 0; i < greeter.model.count; i++) {
205+ for (var i = 0; i < LightDM.Users.count; i++) {
202− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == name) {
206+ if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
203 break
207 break
204 }
208 }
205 }
209 }
206− if (i == greeter.model.count) {
210+ if (i == LightDM.Users.count) {
207 fail("Didn't find name")
211 fail("Didn't find name")
208 return -1
212 return -1
209 }
213 }
@@ -213,7 +217,7 @@ Row {
213
217
214 function clickPasswordInput(isButton) {
218 function clickPasswordInput(isButton) {
215 var greeter = findChild(shell, "greeter")
219 var greeter = findChild(shell, "greeter")
216− tryCompare(greeter, "showProgress", 1)
220+ tryCompare(greeter, "fullyShown", true);
217
221
218 var passwordMouseArea = findChild(shell, "passwordMouseArea")
222 var passwordMouseArea = findChild(shell, "passwordMouseArea")
219 tryCompare(passwordMouseArea, "enabled", isButton)
223 tryCompare(passwordMouseArea, "enabled", isButton)
@@ -223,9 +227,9 @@ Row {
223 }
227 }
224
228
225 function confirmLoggedIn(loggedIn) {
229 function confirmLoggedIn(loggedIn) {
226− var greeterWrapper = findChild(shell, "greeterWrapper")
230+ var greeter = findChild(shell, "greeter");
227− tryCompare(greeterWrapper, "showProgress", loggedIn ? 0 : 1)
231+ tryCompare(greeter, "shown", loggedIn ? false : true);
228− tryCompare(sessionSpy, "count", loggedIn ? 1 : 0)
232+ verify(loggedIn ? sessionSpy.count > 0 : sessionSpy.count === 0);
229 }
233 }
230
234
231 function swipeFromLeftEdge(swipeLength) {
235 function swipeFromLeftEdge(swipeLength) {
@@ -234,20 +238,6 @@ Row {
234 touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY)
238 touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY)
235 }
239 }
236
240
237− function test_noLockscreen() {
238− selectUser("has-password")
239− var lockscreen = findChild(shell, "lockscreen")
240− tryCompare(lockscreen, "shown", false)
241− }
242−
243− function test_showAndHideGreeterDBusCalls() {
244− var greeter = findChild(shell, "greeter")
245− LightDM.Greeter.hideGreeter()
246− tryCompare(greeter, "showProgress", 0)
247− LightDM.Greeter.showGreeter()
248− tryCompare(greeter, "showProgress", 1)
249− }
250−
251 function test_login_data() {
241 function test_login_data() {
252 return [
242 return [
253 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},
243 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},
tests/utils/modules/Unity/Test/CMakeLists.txt+1
@@ -4,6 +4,7 @@ add_definitions(-DQT_NO_KEYWORDS)
4
4
5include_directories(
5include_directories(
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
7 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
8 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
8)
9)
9
10
tests/utils/modules/Unity/Test/Test.qmltypes+1
@@ -21,6 +21,7 @@ Module {
21 Parameter { type: "string" }
21 Parameter { type: "string" }
22 }
22 }
23 Method { name: "touchEvent"; type: "TouchEventSequenceWrapper*" }
23 Method { name: "touchEvent"; type: "TouchEventSequenceWrapper*" }
24+ Method { name: "ensureTouchRegistryInstalled" }
24 }
25 }
25 Component {
26 Component {
26 name: "TouchEventSequenceWrapper"
27 name: "TouchEventSequenceWrapper"
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.cpp+10−1
@@ -15,15 +15,24 @@
15 */
15 */
16
16
17#include "TouchEventSequenceWrapper.h"
17#include "TouchEventSequenceWrapper.h"
18+#include <private/qquickwindow_p.h>
19+
18−TouchEventSequenceWrapper::TouchEventSequenceWrapper(QTest::QTouchEventSequence eventSequence)
20+TouchEventSequenceWrapper::TouchEventSequenceWrapper(QTest::QTouchEventSequence eventSequence, QQuickItem *item)
19 : QObject(0)
21 : QObject(0)
20 , m_eventSequence(eventSequence)
22 , m_eventSequence(eventSequence)
23+ , m_item(item)
21{
24{
22}
25}
23
26
24void TouchEventSequenceWrapper::commit(bool processEvents)
27void TouchEventSequenceWrapper::commit(bool processEvents)
25{
28{
26 m_eventSequence.commit(processEvents);
29 m_eventSequence.commit(processEvents);
30+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
31+ if (m_item->window()) {
32+ QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_item->window());
33+ wp->flushDelayedTouchEvent();
34+ }
35+#endif
27}
36}
28
37
29void TouchEventSequenceWrapper::move(int touchId, int x, int y)
38void TouchEventSequenceWrapper::move(int touchId, int x, int y)
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+4−1
@@ -21,11 +21,13 @@
21#include <QtTest/QTest>
21#include <QtTest/QTest>
22#include <QtQml/qqml.h>
22#include <QtQml/qqml.h>
23
23
24+class QQuickItem;
25+
24class TouchEventSequenceWrapper : public QObject
26class TouchEventSequenceWrapper : public QObject
25{
27{
26 Q_OBJECT
28 Q_OBJECT
27public:
29public:
28− TouchEventSequenceWrapper(QTest::QTouchEventSequence eventSequence);
30+ TouchEventSequenceWrapper(QTest::QTouchEventSequence eventSequence, QQuickItem *item);
29
31
30 Q_INVOKABLE void commit(bool processEvents = true);
32 Q_INVOKABLE void commit(bool processEvents = true);
31 Q_INVOKABLE void move(int touchId, int x, int y);
33 Q_INVOKABLE void move(int touchId, int x, int y);
@@ -35,6 +37,7 @@ public:
35
37
36private:
38private:
37 QTest::QTouchEventSequence m_eventSequence;
39 QTest::QTouchEventSequence m_eventSequence;
40+ QQuickItem *m_item;
38};
41};
39
42
40QML_DECLARE_TYPE(TouchEventSequenceWrapper)
43QML_DECLARE_TYPE(TouchEventSequenceWrapper)
tests/utils/modules/Unity/Test/UnityTestCase.qml+11−11
@@ -221,8 +221,8 @@ TestCase {
221 }
221 }
222 }
222 }
223
223
224− function touchEvent() {
224+ function touchEvent(item) {
225− return UT.Util.touchEvent()
225+ return UT.Util.touchEvent(item)
226 }
226 }
227
227
228 // speed is in pixels/second
228 // speed is in pixels/second
@@ -255,7 +255,7 @@ TestCase {
255 if (beginTouch) {
255 if (beginTouch) {
256 fakeDateTime.currentTimeMs += timeStep
256 fakeDateTime.currentTimeMs += timeStep
257
257
258− var event = touchEvent()
258+ var event = touchEvent(item)
259 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
259 event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
260 event.commit()
260 event.commit()
261 }
261 }
@@ -265,19 +265,19 @@ TestCase {
265 // Avoid any rounding errors by making the last move be at precisely
265 // Avoid any rounding errors by making the last move be at precisely
266 // the point specified
266 // the point specified
267 wait(iterations / speed)
267 wait(iterations / speed)
268− var event = touchEvent()
268+ var event = touchEvent(item)
269 event.move(0 /* touchId */, rootTo.x, rootTo.y)
269 event.move(0 /* touchId */, rootTo.x, rootTo.y)
270 event.commit()
270 event.commit()
271 } else {
271 } else {
272 wait(iterations / speed)
272 wait(iterations / speed)
273− var event = touchEvent()
273+ var event = touchEvent(item)
274 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
274 event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
275 event.commit()
275 event.commit()
276 }
276 }
277 }
277 }
278 if (endTouch) {
278 if (endTouch) {
279 fakeDateTime.currentTimeMs += timeStep
279 fakeDateTime.currentTimeMs += timeStep
280− var event = touchEvent()
280+ var event = touchEvent(item)
281 event.release(0 /* touchId */, rootTo.x, rootTo.y)
281 event.release(0 /* touchId */, rootTo.x, rootTo.y)
282 event.commit()
282 event.commit()
283 }
283 }
@@ -287,7 +287,7 @@ TestCase {
287 // Make sure the item is rendered
287 // Make sure the item is rendered
288 waitForRendering(item);
288 waitForRendering(item);
289
289
290− var event1 = touchEvent();
290+ var event1 = touchEvent(item);
291 // first finger
291 // first finger
292 event1.press(0, x1Start, y1Start);
292 event1.press(0, x1Start, y1Start);
293 event1.commit();
293 event1.commit();
@@ -320,7 +320,7 @@ TestCase {
320 var root = fetchRootItem(item)
320 var root = fetchRootItem(item)
321 var rootPoint = item.mapToItem(root, x, y)
321 var rootPoint = item.mapToItem(root, x, y)
322
322
323− var event = touchEvent()
323+ var event = touchEvent(item)
324 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
324 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
325 event.commit()
325 event.commit()
326 }
326 }
@@ -329,7 +329,7 @@ TestCase {
329 var root = fetchRootItem(item)
329 var root = fetchRootItem(item)
330 var rootPoint = item.mapToItem(root, x, y)
330 var rootPoint = item.mapToItem(root, x, y)
331
331
332− var event = touchEvent()
332+ var event = touchEvent(item)
333 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
333 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
334 event.commit()
334 event.commit()
335 }
335 }
@@ -347,11 +347,11 @@ TestCase {
347 var root = fetchRootItem(item)
347 var root = fetchRootItem(item)
348 var rootPoint = item.mapToItem(root, x, y)
348 var rootPoint = item.mapToItem(root, x, y)
349
349
350− var event = touchEvent()
350+ var event = touchEvent(item)
351 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
351 event.press(0 /* touchId */, rootPoint.x, rootPoint.y)
352 event.commit()
352 event.commit()
353
353
354− event = touchEvent()
354+ event = touchEvent(item)
355 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
355 event.release(0 /* touchId */, rootPoint.x, rootPoint.y)
356 event.commit()
356 event.commit()
357 }
357 }
tests/utils/modules/Unity/Test/testutil.cpp+2−2
@@ -55,14 +55,14 @@ TestUtil::isInstanceOf(QObject *obj, QString name)
55 return result;
55 return result;
56}
56}
57
57
58−TouchEventSequenceWrapper *TestUtil::touchEvent()
58+TouchEventSequenceWrapper *TestUtil::touchEvent(QQuickItem *item)
59{
59{
60 ensureTargetWindow();
60 ensureTargetWindow();
61 ensureTouchDevice();
61 ensureTouchDevice();
62 ensureTouchRegistryInstalled();
62 ensureTouchRegistryInstalled();
63
63
64 return new TouchEventSequenceWrapper(
64 return new TouchEventSequenceWrapper(
65− QTest::touchEvent(m_targetWindow, m_touchDevice, /* autoCommit */ false));
65+ QTest::touchEvent(m_targetWindow, m_touchDevice, /* autoCommit */ false), item);
66}
66}
67
67
68void TestUtil::ensureTargetWindow()
68void TestUtil::ensureTargetWindow()
tests/utils/modules/Unity/Test/testutil.h+1−1
@@ -33,7 +33,7 @@ public:
33 ~TestUtil();
33 ~TestUtil();
34
34
35 Q_INVOKABLE bool isInstanceOf(QObject*, QString);
35 Q_INVOKABLE bool isInstanceOf(QObject*, QString);
36− Q_INVOKABLE TouchEventSequenceWrapper *touchEvent();
36+ Q_INVOKABLE TouchEventSequenceWrapper *touchEvent(QQuickItem *item);
37 Q_INVOKABLE void ensureTouchRegistryInstalled();
37 Q_INVOKABLE void ensureTouchRegistryInstalled();
38
38
39private:
39private: