on xenial help sign in
Separate greeter into WideGreeter and NarrowGreeter classes
93b318cMichael Terry
qml/Greeter/GreeterContent.qml → qml/Greeter/CoverPage.qmlrenamed+50−94
@@ -21,35 +21,50 @@ import Ubuntu.Components 1.1
21import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
22import "../Components"
22import "../Components"
23
23
24−Item {
24+Showable {
25 id: root
25 id: root
26− anchors.fill: parent
27
26
27+ property real launcherOffset
28+ property alias background: greeterBackground.source
29+ property real backgroundTopMargin
28− property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
30+ property bool ready: greeterBackground.source == "" || greeterBackground.status == Image.Ready || greeterBackground.status == Image.Error
31+ property int currentIndex
32+ property bool draggable: true
33+
34+ property alias infographics: infographics
35+
36+ readonly property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
29
37
30− signal selected(int uid)
31− signal unlocked(int uid)
32 signal tease()
38 signal tease()
33
39
34− function tryToUnlock() {
40+ function hideRight() {
35− if (loginLoader.item) {
41+ if (shown) {
36− loginLoader.item.tryToUnlock()
42+ hideAnimation = d.rightHideAnimation;
43+ hide();
37 }
44 }
38 }
45 }
39
46
40− function reset() {
47+ QtObject {
41− if (loginLoader.item) {
48+ id: d
42− loginLoader.item.reset()
49+
43− }
50+ property var showAnimation: StandardAnimation { property: "dragOffset"; to: 0; duration: UbuntuAnimation.FastDuration }
51+ property var leftHideAnimation: StandardAnimation { property: "dragOffset"; to: -width }
52+ property var rightHideAnimation: StandardAnimation { property: "dragOffset"; to: width }
44 }
53 }
45
54
55+ property real dragOffset
56+ x: launcherOffset + dragOffset
57+
58+ showAnimation: d.showAnimation
59+ hideAnimation: d.leftHideAnimation
60+
46 onTease: showLabelAnimation.start()
61 onTease: showLabelAnimation.start()
47
62
48 // Bi-directional revealer
63 // Bi-directional revealer
49 DraggingArea {
64 DraggingArea {
50 id: dragHandle
65 id: dragHandle
51 anchors.fill: parent
66 anchors.fill: parent
52− enabled: greeter.narrowMode || !greeter.locked
67+ enabled: draggable
53 orientation: Qt.Horizontal
68 orientation: Qt.Horizontal
54 propagateComposedEvents: true
69 propagateComposedEvents: true
55
70
@@ -60,7 +75,7 @@ Item {
60 }
75 }
61
76
62 function maybeTease() {
77 function maybeTease() {
63− if (!greeter.locked || greeter.narrowMode) {
78+ if (enabled) {
64 root.tease();
79 root.tease();
65 }
80 }
66 }
81 }
@@ -70,30 +85,30 @@ Item {
70 onPressAndHold: {} // eat event, but no need to tease, as drag will cover it
85 onPressAndHold: {} // eat event, but no need to tease, as drag will cover it
71
86
72 onDragEnd: {
87 onDragEnd: {
73− if (greeter.dragOffset > 0 && rightEvaluator.shouldAutoComplete()) {
88+ if (root.dragOffset > 0 && rightEvaluator.shouldAutoComplete()) {
74− greeter.hideRight()
89+ root.hideRight();
75− } else if (greeter.dragOffset < 0 && leftEvaluator.shouldAutoComplete()) {
90+ } else if (root.dragOffset < 0 && leftEvaluator.shouldAutoComplete()) {
76− greeter.hide();
91+ root.hide();
77 } else {
92 } else {
78− greeter.show(); // undo drag
93+ root.show(); // undo drag
79 }
94 }
80 }
95 }
81
96
82 onDragValueChanged: {
97 onDragValueChanged: {
83 // dragValue is kept as a "step" value since we do this adjusting on the fly
98 // dragValue is kept as a "step" value since we do this adjusting on the fly
84− greeter.dragOffset += dragValue;
99+ root.dragOffset += dragValue;
85 }
100 }
86
101
87 EdgeDragEvaluator {
102 EdgeDragEvaluator {
88 id: rightEvaluator
103 id: rightEvaluator
89− trackedPosition: dragHandle.dragValue + greeter.dragOffset
104+ trackedPosition: dragHandle.dragValue + root.dragOffset
90 maxDragDistance: root.width
105 maxDragDistance: root.width
91 direction: Direction.Rightwards
106 direction: Direction.Rightwards
92 }
107 }
93
108
94 EdgeDragEvaluator {
109 EdgeDragEvaluator {
95 id: leftEvaluator
110 id: leftEvaluator
96− trackedPosition: dragHandle.dragValue + greeter.dragOffset
111+ trackedPosition: dragHandle.dragValue + root.dragOffset
97 maxDragDistance: root.width
112 maxDragDistance: root.width
98 direction: Direction.Leftwards
113 direction: Direction.Leftwards
99 }
114 }
@@ -113,17 +128,16 @@ Item {
113 }
128 }
114
129
115 CrossFadeImage {
130 CrossFadeImage {
116− id: background
131+ id: greeterBackground
117 objectName: "greeterBackground"
132 objectName: "greeterBackground"
118 anchors {
133 anchors {
119 fill: parent
134 fill: parent
120− topMargin: backgroundTopMargin
135+ topMargin: root.backgroundTopMargin
121 }
136 }
122 fillMode: Image.PreserveAspectCrop
137 fillMode: Image.PreserveAspectCrop
123 // Limit how much memory we'll reserve for this image
138 // Limit how much memory we'll reserve for this image
124 sourceSize.height: height
139 sourceSize.height: height
125 sourceSize.width: width
140 sourceSize.width: width
126− source: greeter.background
127 }
141 }
128
142
129 Rectangle {
143 Rectangle {
@@ -132,91 +146,33 @@ Item {
132 opacity: 0.4
146 opacity: 0.4
133 }
147 }
134
148
135− Loader {
136− id: loginLoader
137− objectName: "loginLoader"
138− anchors {
139− left: parent.left
140− leftMargin: Math.min(parent.width * 0.16, units.gu(20))
141− verticalCenter: parent.verticalCenter
142− }
143− width: units.gu(29)
144− height: parent.height
145−
146− // TODO: Once we have a system API for determining which mode we are
147− // in, tablet/phone/desktop, that should be used instead of narrowMode.
148− source: greeter.narrowMode ? "" : "LoginList.qml"
149−
150− onLoaded: {
151− item.currentIndex = greeterContentLoader.currentIndex;
152− }
153−
154− Binding {
155− target: loginLoader.item
156− property: "model"
157− value: greeterContentLoader.model
158− }
159−
160− Connections {
161− target: loginLoader.item
162−
163− onSelected: {
164− root.selected(uid);
165− }
166−
167− onUnlocked: {
168− root.unlocked(uid);
169− }
170−
171− onCurrentIndexChanged: {
172− if (greeterContentLoader.currentIndex !== loginLoader.item.currentIndex) {
173− greeterContentLoader.currentIndex = loginLoader.item.currentIndex;
174− }
175− }
176− }
177− }
178−
179 Infographics {
149 Infographics {
180 id: infographics
150 id: infographics
181 objectName: "infographics"
151 objectName: "infographics"
182− height: narrowMode ? parent.height : 0.75 * parent.height
152+ height: parent.height
183− model: greeterContentLoader.infographicModel
153+ model: LightDM.Infographic
184
154
185− property string selectedUser
155+ property string selectedUser: LightDM.Users.data(root.currentIndex, LightDM.UserRoles.NameRole)
186− property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : ""
187− onInfographicUserChanged: greeterContentLoader.infographicModel.username = infographicUser
188
156
189 Component.onCompleted: {
157 Component.onCompleted: {
190− selectedUser = greeterContentLoader.model.data(greeterContentLoader.currentIndex, LightDM.UserRoles.NameRole)
191− greeterContentLoader.infographicModel.username = infographicUser
192− greeterContentLoader.infographicModel.readyForDataChange()
158+ LightDM.Infographic.readyForDataChange();
193 }
159 }
194
160
195− Connections {
161+ Binding {
196− target: root
162+ target: LightDM.Infographic
197− onSelected: infographics.selectedUser = greeterContentLoader.model.data(uid, LightDM.UserRoles.NameRole)
163+ property: "username"
164+ value: AccountsService.statsWelcomeScreen ? infographics.selectedUser : ""
198 }
165 }
199
166
200 Connections {
167 Connections {
201 target: i18n
168 target: i18n
202− onLanguageChanged: greeterContentLoader.infographicModel.readyForDataChange()
169+ onLanguageChanged: LightDM.Infographic.readyForDataChange()
203 }
170 }
204
171
205 anchors {
172 anchors {
206 verticalCenter: parent.verticalCenter
173 verticalCenter: parent.verticalCenter
207− left: narrowMode ? root.left : loginLoader.right
174+ left: parent.left
208− right: root.right
209− }
210− }
211−
212− Clock {
213− id: clock
214− visible: narrowMode
215−
216− anchors {
217− top: parent.top
175+ right: parent.right
218− topMargin: units.gu(2)
219− horizontalCenter: parent.horizontalCenter
220 }
176 }
221 }
177 }
222
178
qml/Greeter/Greeter.qml+181−252
@@ -24,358 +24,287 @@ import "../Components"
24
24
25Showable {
25Showable {
26 id: greeter
26 id: greeter
27− created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
27+ created: loader.status == Loader.Ready
28
28
29 property url background
29 property url background
30
30
31 // How far to offset the top greeter layer during a launcher left-drag
31 // How far to offset the top greeter layer during a launcher left-drag
32 property real launcherOffset
32 property real launcherOffset
33
33
34− // 1 when fully shown and 0 when fully hidden
34+ readonly property bool active: shown || hasLockedApp
35− readonly property real showProgress: MathUtils.clamp((width - Math.abs(greeterContentLoader.x)) / width, 0, 1)
36− readonly property bool fullyShown: showProgress === 1 || lockscreen.shown
35+ readonly property bool fullyShown: loader.item ? loader.item.fullyShown : false
37− readonly property bool semiShown: shown || lockscreen.shown
38
36
39 // True when the greeter is waiting for PAM or other setup process
37 // True when the greeter is waiting for PAM or other setup process
40− property bool waiting: true
38+ readonly property alias waiting: d.waiting
41
39
42 property string lockedApp: ""
40 property string lockedApp: ""
43− property bool hasLockedApp: lockedApp !== ""
41+ readonly property bool hasLockedApp: lockedApp !== ""
44
42
45− showAnimation: d.showAnimation
46− hideAnimation: d.leftHideAnimation
47−
48− readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
49 property bool forcedUnlock
43 property bool forcedUnlock
50− onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
44+ readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
51
45
52 property bool tabletMode
46 property bool tabletMode
53− readonly property bool narrowMode: !multiUser && !tabletMode
47+
54− readonly property bool multiUser: LightDM.Users.count > 1
48+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
55−
49+ property int failedLoginsDelayAttempts: 7 // number of failed logins
56− property real dragOffset
50+ property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
57−
58− // We define a proxy and "is valid" property for launcherOffset because of
59− // a quirk in Qml. We only want this animation to fire if we are reset
60− // back to zero (on a release of the drag). But by defining a Behavior,
61− // we delay the property from reaching zero until it's too late. So we set
62− // a proxy bound to launcherOffset, which lets us see the target value of
63− // zero as we also slowly adjust the proxy down to zero. But Qml will send
64− // change notifications in declaration order. So unless we define the
65− // proxy first, we need a little "is valid" property defined above the
66− // proxy, so we know when to enable the proxy behavior. Phew!
67− readonly property bool launcherOffsetValid: launcherOffset > 0
68− property real launcherOffsetProxy: shown ? launcherOffset : 0
69− Behavior on launcherOffsetProxy {
70− enabled: !launcherOffsetValid
71− StandardAnimation {}
72− }
73
51
74 signal tease()
52 signal tease()
75 signal sessionStarted()
53 signal sessionStarted()
76 signal emergencyCall()
54 signal emergencyCall()
77
55
78− function hideRight() {
79− if (shown) {
80− hideAnimation = d.rightHideAnimation;
81− hide()
82− }
83− }
84−
85 function notifyAppFocused(appId) {
56 function notifyAppFocused(appId) {
86− if (narrowMode) {
87− if (hasLockedApp) {
88− if (appId === lockedApp) {
89− lockscreen.hide(); // show locked app
90− } else {
91− startUnlock(); // show lockscreen if necessary
92− }
93− }
94− hide();
95− } else {
96− if (LightDM.Greeter.active) {
57+ if (!active) {
97− startUnlock();
58+ return;
98− }
99− }
100− }
101−
102− // Do we need these next two functions, really?
103− function notifyFocusChanged(appId) {
104− if (hasLockedApp && lockedApp !== appId) {
105− startUnlock();
106− }
107− }
108−
109− function notifyAppAdded(appId) {
110− if (shown && appId != "unity8-dash") {
111− startUnlock();
112− }
113− if (narrowMode && hasLockedApp && appId === lockedApp) {
114− lockscreen.hide(); // show locked app
115 }
59 }
116− }
117
60
118− function notifyAboutToStartApp(appId) {
119 if (hasLockedApp) {
61 if (hasLockedApp) {
120− startUnlock();
62+ if (appId === lockedApp) {
121− }
63+ hide(); // show locked app
122− }
123−
124− function startUnlock() {
125− if (narrowMode) {
126− if (!LightDM.Greeter.authenticated) {
127− lockscreen.maybeShow();
128− }
129− hide();
130− } else {
64+ } else {
131− show();
65+ show();
132− if (greeterContentLoader.item) {
66+ d.startUnlock(false);
133− greeterContentLoader.item.tryToUnlock();
134 }
67 }
68+ } else if (appId !== "unity8-dash") { // dash isn't started by user
69+ d.startUnlock(false);
135 }
70 }
136 }
71 }
137
72
138− onRequiredChanged: {
73+ function notifyAboutToFocusApp(appId) {
139− // Reset hide animation to default once we're finished with it
74+ if (!active) {
140− if (required) {
75+ return;
141− // Reset hide animation so that a hide() call is reliably left
142− hideAnimation = d.leftHideAnimation;
143 }
76 }
144− }
145−
146− onShownChanged: {
147− if (shown) {
148− waiting = true;
149
77
150− if (narrowMode) {
78+ // A hint that we're about to focus an app. This way we can look
151− d.selectUser(greeterContentLoader.currentIndex);
79+ // a little more responsive, rather than waiting for the above
152− } else {
80+ // notifyAppFocused call. We also need this in case we have a locked
153− d.reset();
81+ // app, in order to show lockscreen instead of new app.
154− }
82+ d.startUnlock(false);
155− lockedApp = "";
156− forceActiveFocus();
157− }
158 }
83 }
159
84
160− onFullyShownChanged: {
85+ function notifyShowingDashFromDrag() {
161− // Wait until the greeter is completely covering lockscreen before resetting it.
86+ if (!active) {
162− if (narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
87+ return;
163− lockscreen.reset();
164− lockscreen.maybeShow();
165 }
88 }
166− }
167
89
168− onShowProgressChanged: {
90+ // This is a just a glorified notifyAboutToFocusApp(), but it does one
169− if (showProgress === 0) {
91+ // other thing: it hides any cover pages to the RIGHT, because the user
170− if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || forcedUnlock) {
92+ // just came from a launcher drag starting on the left.
171− d.login();
93+ d.startUnlock(true);
172− } else if (narrowMode) {
173− lockscreen.clear(false); // to reset focus if necessary
174− }
175− }
176 }
94 }
177
95
178 QtObject {
96 QtObject {
179 id: d
97 id: d
180
98
181− property var showAnimation: StandardAnimation { property: "dragOffset"; to: 0; duration: UbuntuAnimation.FastDuration }
99+ readonly property bool multiUser: LightDM.Users.count > 1
182− property var leftHideAnimation: StandardAnimation { property: "dragOffset"; to: -width }
100+ property int currentIndex
183− property var rightHideAnimation: StandardAnimation { property: "dragOffset"; to: width }
101+ property int delayMinutes
102+ property bool waiting
103+
104+ // We define a proxy and "is valid" property for launcherOffset because of
105+ // a quirk in Qml. We only want this animation to fire if we are reset
106+ // back to zero (on a release of the drag). But by defining a Behavior,
107+ // we delay the property from reaching zero until it's too late. So we set
108+ // a proxy bound to launcherOffset, which lets us see the target value of
109+ // zero as we also slowly adjust the proxy down to zero. But Qml will send
110+ // change notifications in declaration order. So unless we define the
111+ // proxy first, we need a little "is valid" property defined above the
112+ // proxy, so we know when to enable the proxy behavior. Phew!
113+ readonly property bool launcherOffsetValid: launcherOffset > 0
114+ property real launcherOffsetProxy: shown ? launcherOffset : 0
115+ Behavior on launcherOffsetProxy {
116+ enabled: !d.launcherOffsetValid
117+ StandardAnimation {}
118+ }
184
119
185 function selectUser(uid) {
120 function selectUser(uid) {
186− // Update launcher items for new user
121+ currentIndex = uid;
187 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
122 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
188 AccountsService.user = user;
123 AccountsService.user = user;
189 LauncherModel.setUser(user);
124 LauncherModel.setUser(user);
190 LightDM.Greeter.authenticate(user); // always resets auth state
125 LightDM.Greeter.authenticate(user); // always resets auth state
191 }
126 }
192
127
193− function reset() {
194− if (greeterContentLoader.item) {
195− greeterContentLoader.item.reset()
196− }
197− }
198−
199 function login() {
128 function login() {
200 enabled = false;
129 enabled = false;
201 if (LightDM.Greeter.startSessionSync()) {
130 if (LightDM.Greeter.startSessionSync()) {
202 sessionStarted();
131 sessionStarted();
203− hide();
132+ loader.item.authenticated(true);
204− lockscreen.hide();
133+ } else {
134+ loader.item.authenticated(false);
205 }
135 }
206 enabled = true;
136 enabled = true;
207 }
137 }
138+
139+ function startUnlock(toTheRight) {
140+ if (loader.item) {
141+ loader.item.tryToUnlock(toTheRight);
142+ }
143+ }
208 }
144 }
209
145
210− Lockscreen {
146+ onForcedUnlockChanged: {
211− id: lockscreen
147+ if (forcedUnlock && shown) {
212− objectName: "lockscreen"
148+ // pretend we were just authenticated
149+ loader.item.authenticated(true);
150+ }
151+ }
213
152
214− shown: false
153+ onRequiredChanged: {
215− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
216− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
217− anchors.fill: parent
218− visible: required
154+ if (required) {
219− background: greeter.background
155+ d.waiting = true;
220− darkenBackground: 0.4
156+ lockedApp = "";
221− alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
222− minPinLength: 4
223− maxPinLength: 4
224−
225− property string promptText
226− infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
227− alphaNumeric ? i18n.tr("Enter passphrase") :
228− i18n.tr("Enter passcode")
229− errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
230− alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
231− i18n.tr("Please re-enter") :
232− i18n.tr("Sorry, incorrect passcode")
233−
234− // FIXME: We *should* show emergency dialer if there is a SIM present,
235− // regardless of whether the side stage is enabled. But right now,
236− // the assumption is that narrow screens are phones which have SIMs
237− // and wider screens are tablets which don't. When we do allow this
238− // on devices with a side stage and a SIM, work should be done to
239− // ensure that the main stage is disabled while the dialer is present
240− // in the side stage. See the FIXME in the stage loader in Shell.qml.
241− showEmergencyCallButton: !greeter.tabletMode
242−
243− onEntered: LightDM.Greeter.respond(passphrase);
244− onCancel: greeter.show()
245− onEmergencyCall: greeter.emergencyCall()
246−
247− onShownChanged: if (shown) greeter.lockedApp = ""
248−
249− function maybeShow() {
250− if (!greeter.forcedUnlock) {
251− show();
252− }
253 }
157 }
158+ }
254
159
255− Timer {
160+ Timer {
256− id: forcedDelayTimer
161+ id: forcedDelayTimer
257− interval: 1000 * 60
162+ interval: 1000 * 60
258− onTriggered: {
163+ onTriggered: {
259− if (lockscreen.delayMinutes > 0) {
164+ if (d.delayMinutes > 0) {
260− lockscreen.delayMinutes -= 1
165+ d.delayMinutes -= 1;
261− if (lockscreen.delayMinutes > 0) {
166+ if (d.delayMinutes > 0) {
262− start() // go again
167+ start(); // go again
263− }
264 }
168 }
265 }
169 }
266 }
170 }
267 }
171 }
268
172
269− Rectangle {
270− anchors.fill: parent
271− color: "black"
272− opacity: greeter.showProgress * 0.8
273− }
274−
275 Loader {
173 Loader {
276− id: greeterContentLoader
174+ id: loader
277− objectName: "greeterContentLoader"
175+ objectName: "loader"
278−
279− x: launcherOffsetProxy + dragOffset
280− width: parent.width
281− height: parent.height
282
176
283− property var model: LightDM.Users
177+ anchors.fill: parent
284− property int currentIndex: 0
285− property var infographicModel: LightDM.Infographic
286− readonly property int backgroundTopMargin: -greeter.y
287
178
288− source: (greeter.required || lockscreen.required) ? "GreeterContent.qml" : ""
179+ source: greeter.required ? ((!d.multiUser && !tabletMode) ? "NarrowGreeter.qml"
180+ : "WideGreeter.qml")
181+ : ""
289
182
290 onLoaded: {
183 onLoaded: {
184+ loader.item.reset();
185+ greeter.lockedApp = "";
186+ greeter.forceActiveFocus();
291− d.selectUser(currentIndex);
187+ d.selectUser(d.currentIndex);
292 }
188 }
293
189
294 Connections {
190 Connections {
295− target: greeterContentLoader.item
191+ target: loader.item
296−
297 onSelected: {
192 onSelected: {
298− d.selectUser(uid);
193+ d.selectUser(index);
299− greeterContentLoader.currentIndex = uid;
194+ }
195+ onUnlocked: {
196+ d.login()
300 }
197 }
301− onUnlocked: greeter.hide()
302 onTease: greeter.tease()
198 onTease: greeter.tease()
199+ onRequiredChanged: {
200+ if (!loader.item.required) {
201+ greeter.hide();
202+ }
203+ }
204+ }
205+
206+ Binding {
207+ target: loader.item
208+ property: "backgroundTopMargin"
209+ value: -greeter.y
210+ }
211+
212+ Binding {
213+ target: loader.item
214+ property: "launcherOffset"
215+ value: d.launcherOffsetProxy
216+ }
217+
218+ Binding {
219+ target: loader.item
220+ property: "delayMinutes"
221+ value: d.delayMinutes
303 }
222 }
304 }
223 }
305
224
306 Connections {
225 Connections {
307 target: LightDM.Greeter
226 target: LightDM.Greeter
308
227
309− onShowGreeter: greeter.show()
228+ onShowGreeter: {
310− onHideGreeter: d.login()
229+ greeter.show();
230+ loader.item.reset();
231+ d.selectUser(d.currentIndex);
232+ }
311
233
312− onShowPrompt: {
234+ onHideGreeter: {
313− waiting = false;
235+ d.login();
236+ loader.item.hide();
237+ }
314
238
239+ onShowMessage: {
315 if (!LightDM.Greeter.active) {
240 if (!LightDM.Greeter.active) {
316 return; // could happen if hideGreeter() comes in before we prompt
241 return; // could happen if hideGreeter() comes in before we prompt
317 }
242 }
318
243
319− if (greeter.narrowMode) {
244+ // inefficient, but we only rarely deal with messages
320− lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
245+ var html = text.replace(/&/g, "&amp;")
321− lockscreen.maybeShow();
246+ .replace(/</g, "&lt;")
247+ .replace(/>/g, "&gt;")
248+ .replace(/\n/g, "<br>");
249+ if (isError) {
250+ html = "<font color=\"#df382c\">" + html + "</font>";
322 }
251 }
252+
253+ loader.item.showMessage(html);
323 }
254 }
324
255
325− onPromptlessChanged: {
256+ onShowPrompt: {
257+ d.waiting = false;
258+
326 if (!LightDM.Greeter.active) {
259 if (!LightDM.Greeter.active) {
327 return; // could happen if hideGreeter() comes in before we prompt
260 return; // could happen if hideGreeter() comes in before we prompt
328 }
261 }
329− if (greeter.narrowMode) {
262+
330− if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
263+ loader.item.showPrompt(text, isSecret, isDefaultPrompt);
331− lockscreen.hide()
332− } else {
333− lockscreen.reset();
334− lockscreen.maybeShow();
335− }
336− }
337 }
264 }
338
265
339 onAuthenticationComplete: {
266 onAuthenticationComplete: {
340− waiting = false;
267+ d.waiting = false;
341− if (LightDM.Greeter.authenticated) {
342− AccountsService.failedLogins = 0
343− }
344− // Else only penalize user for a failed login if they actually were
345− // prompted for a password. We do this below after the promptless
346− // early exit.
347−
348− if (LightDM.Greeter.promptless) {
349− return;
350− }
351
268
352 if (LightDM.Greeter.authenticated) {
269 if (LightDM.Greeter.authenticated) {
270+ AccountsService.failedLogins = 0;
353 d.login();
271 d.login();
272+ if (!LightDM.Greeter.promptless) {
273+ loader.item.hide();
274+ }
354 } else {
275 } else {
276+ if (!LightDM.Greeter.promptless) {
355− AccountsService.failedLogins++
277+ AccountsService.failedLogins++;
356−
278+ }
357− if (greeter.narrowMode) {
279+
280+ // Check if we should initiate a factory reset
358− if (maxFailedLogins >= 2) { // require at least a warning
281+ if (maxFailedLogins >= 2) { // require at least a warning
359− if (AccountsService.failedLogins === maxFailedLogins - 1) {
282+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
360− var title = lockscreen.alphaNumeric ?
283+ loader.item.showLastChance();
361− i18n.tr("Sorry, incorrect passphrase.") :
362− i18n.tr("Sorry, incorrect passcode.")
363− var text = i18n.tr("This will be your last attempt.") + " " +
364− (lockscreen.alphaNumeric ?
365− i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
366− i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
367− lockscreen.showInfoPopup(title, text)
368− } else if (AccountsService.failedLogins >= maxFailedLogins) {
284+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
369− SystemImage.factoryReset() // Ouch!
285+ SystemImage.factoryReset(); // Ouch!
370− }
371− }
372− if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
373− lockscreen.delayMinutes = failedLoginsDelayMinutes
374− forcedDelayTimer.start()
375 }
286 }
287+ }
288+
289+ // Check if we should initiate a forced login delay
290+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
291+ d.delayMinutes = failedLoginsDelayMinutes;
292+ forcedDelayTimer.start();
293+ }
294+
295+ loader.item.authenticated(false);
296+ if (!LightDM.Greeter.promptless) {
297+ d.selectUser(d.currentIndex);
298+ }
299+ }
300+ }
376
301
377− lockscreen.clear(true);
302+ onRequestAuthenticationUser: {
303+ // Find index for requested user, if it exists
304+ for (var i = 0; i < LightDM.Users.count; i++) {
305+ if (user === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
378− d.selectUser(greeterContentLoader.currentIndex);
306+ d.selectUser(i);
307+ return;
379 }
308 }
380 }
309 }
381 }
310 }
@@ -384,6 +313,6 @@ Showable {
384 Binding {
313 Binding {
385 target: LightDM.Greeter
314 target: LightDM.Greeter
386 property: "active"
315 property: "active"
387− value: greeter.semiShown || greeter.hasLockedApp
316+ value: greeter.active
388 }
317 }
389}
318}
qml/Greeter/LoginList.qml+39−58
@@ -33,25 +33,49 @@ Item {
33 property bool wasPrompted: false
33 property bool wasPrompted: false
34
34
35 signal selected(int uid)
35 signal selected(int uid)
36− signal unlocked(int uid)
36+ signal unlocked()
37
37
38 function tryToUnlock() {
38 function tryToUnlock() {
39 if (LightDM.Greeter.promptless) {
39 if (LightDM.Greeter.promptless) {
40 if (LightDM.Greeter.authenticated) {
40 if (LightDM.Greeter.authenticated) {
41− root.unlocked(userList.currentIndex)
41+ root.unlocked()
42 } else {
42 } else {
43 root.resetAuthentication()
43 root.resetAuthentication()
44+ root.selected(currentIndex);
44 }
45 }
45 } else {
46 } else {
46 passwordInput.forceActiveFocus()
47 passwordInput.forceActiveFocus()
47 }
48 }
48 }
49 }
49
50
51+ function showMessage(html) {
52+ infoLabel.text = html;
53+ }
54+
55+ function showPrompt(text, isSecret, isDefaultPrompt) {
56+ passwordInput.text = "";
57+ passwordInput.promptText = text;
58+ passwordInput.enabled = true;
59+ passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal;
60+ if (wasPrompted) // stay in text field if second prompt
61+ passwordInput.focus = true;
62+ wasPrompted = true;
63+ }
64+
65+ function showError() {
66+ wrongPasswordAnimation.start();
67+ root.resetAuthentication();
68+ passwordInput.focus = true;
69+ }
70+
50 function reset() {
71 function reset() {
51− root.resetAuthentication()
72+ root.resetAuthentication();
52 }
73 }
53
74
75+ Keys.onEscapePressed: {
54− Keys.onEscapePressed: root.resetAuthentication()
76+ root.resetAuthentication();
77+ selected(currentIndex);
78+ }
55
79
56 Rectangle {
80 Rectangle {
57 id: highlightItem
81 id: highlightItem
@@ -85,6 +109,7 @@ Item {
85 onCurrentIndexChanged: {
109 onCurrentIndexChanged: {
86 if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
110 if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
87 root.resetAuthentication();
111 root.resetAuthentication();
112+ root.selected(currentIndex);
88 }
113 }
89 }
114 }
90
115
@@ -204,6 +229,11 @@ Item {
204 width: parent.width - anchors.margins * 2
229 width: parent.width - anchors.margins * 2
205 opacity: userList.movingInternally ? 0 : 1
230 opacity: userList.movingInternally ? 0 : 1
206
231
232+ property string promptText
233+ placeholderText: LightDM.Greeter.promptless ? (LightDM.Greeter.authenticated ? i18n.tr("Tap to unlock")
234+ : i18n.tr("Retry"))
235+ : promptText
236+
207 Behavior on opacity {
237 Behavior on opacity {
208 NumberAnimation { duration: 100 }
238 NumberAnimation { duration: 100 }
209 }
239 }
@@ -215,7 +245,10 @@ Item {
215 enabled = false;
245 enabled = false;
216 LightDM.Greeter.respond(text);
246 LightDM.Greeter.respond(text);
217 }
247 }
248+ Keys.onEscapePressed: {
218− Keys.onEscapePressed: root.resetAuthentication()
249+ root.resetAuthentication();
250+ root.selected(currentIndex);
251+ }
219
252
220 Image {
253 Image {
221 anchors {
254 anchors {
@@ -256,62 +289,10 @@ Item {
256 return;
289 return;
257 }
290 }
258 infoLabel.text = "";
291 infoLabel.text = "";
259− passwordInput.placeholderText = "";
292+ passwordInput.promptText = "";
260 passwordInput.text = "";
293 passwordInput.text = "";
261 passwordInput.focus = false;
294 passwordInput.focus = false;
262 passwordInput.enabled = true;
295 passwordInput.enabled = true;
263 root.wasPrompted = false;
296 root.wasPrompted = false;
264− selected(currentIndex);
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 }
297 }
317}
298}
qml/Greeter/NarrowGreeter.qmlnew file+169
@@ -0,0 +1,169 @@
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 AccountsService 0.1
19+import LightDM 0.1 as LightDM
20+import Ubuntu.Components 1.1
21+import "../Components"
22+
23+Item {
24+ id: root
25+
26+ property alias launcherOffset: coverPage.launcherOffset
27+ property int currentIndex // unused
28+ property alias delayMinutes: lockscreen.delayMinutes
29+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
30+ property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
31+ property bool required: coverPage.required || lockscreen.required
32+
33+ signal selected(int index)
34+ signal unlocked()
35+ signal tease()
36+
37+ function showMessage(html) {
38+ // TODO
39+ }
40+
41+ function showPrompt(text, isSecret, isDefaultPrompt) {
42+ lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
43+ lockscreen.maybeShow();
44+ }
45+
46+ function showLastChance() {
47+ var title = lockscreen.alphaNumeric ?
48+ i18n.tr("Sorry, incorrect passphrase.") :
49+ i18n.tr("Sorry, incorrect passcode.");
50+ var text = i18n.tr("This will be your last attempt.") + " " +
51+ (lockscreen.alphaNumeric ?
52+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
53+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."));
54+ lockscreen.showInfoPopup(title, text);
55+ }
56+
57+ function hide() {
58+ lockscreen.hide();
59+ coverPage.hide();
60+ }
61+
62+ function authenticated(success) {
63+ if (success) {
64+ lockscreen.hide();
65+ } else {
66+ lockscreen.clear(true);
67+ }
68+ }
69+
70+ function reset() {
71+ coverPage.show();
72+ }
73+
74+ function tryToUnlock(toTheRight) {
75+ if (!LightDM.Greeter.authenticated) {
76+ lockscreen.maybeShow();
77+ }
78+ if (toTheRight) {
79+ coverPage.hideRight();
80+ } else {
81+ coverPage.hide();
82+ }
83+ }
84+
85+ QtObject {
86+ id: d
87+ }
88+
89+ Lockscreen {
90+ id: lockscreen
91+ objectName: "lockscreen"
92+
93+ shown: false
94+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
95+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
96+ anchors.fill: parent
97+ visible: required
98+ background: greeter.background
99+ darkenBackground: 0.4
100+ alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
101+ minPinLength: 4
102+ maxPinLength: 4
103+
104+ property string promptText
105+ infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
106+ alphaNumeric ? i18n.tr("Enter passphrase") :
107+ i18n.tr("Enter passcode")
108+ errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
109+ alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
110+ i18n.tr("Please re-enter") :
111+ i18n.tr("Sorry, incorrect passcode")
112+
113+ // FIXME: We *should* show emergency dialer if there is a SIM present,
114+ // regardless of whether the side stage is enabled. But right now,
115+ // the assumption is that narrow screens are phones which have SIMs
116+ // and wider screens are tablets which don't. When we do allow this
117+ // on devices with a side stage and a SIM, work should be done to
118+ // ensure that the main stage is disabled while the dialer is present
119+ // in the side stage. See the FIXME in the stage loader in Shell.qml.
120+ showEmergencyCallButton: !greeter.tabletMode
121+
122+ onEntered: LightDM.Greeter.respond(passphrase)
123+ onCancel: coverPage.show()
124+ onEmergencyCall: greeter.emergencyCall()
125+
126+ function maybeShow() {
127+ if (!greeter.forcedUnlock) {
128+ show();
129+ }
130+ }
131+ }
132+
133+ Rectangle {
134+ anchors.fill: parent
135+ color: "black"
136+ opacity: coverPage.showProgress * 0.8
137+ }
138+
139+ CoverPage {
140+ id: coverPage
141+ objectName: "coverPage"
142+ height: parent.height
143+ width: parent.width
144+ background: greeter.background
145+ onTease: root.tease()
146+
147+ onShowProgressChanged: {
148+ if (showProgress === 1) {
149+ lockscreen.reset();
150+ }
151+
152+ if (showProgress === 0) {
153+ if (greeter.locked) {
154+ lockscreen.clear(false); // to reset focus if necessary
155+ } else {
156+ root.unlocked();
157+ }
158+ }
159+ }
160+
161+ Clock {
162+ anchors {
163+ top: parent.top
164+ topMargin: units.gu(2)
165+ horizontalCenter: parent.horizontalCenter
166+ }
167+ }
168+ }
169+}
qml/Greeter/WideGreeter.qmlnew file+123
@@ -0,0 +1,123 @@
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 LightDM 0.1 as LightDM
19+import Ubuntu.Components 1.1
20+
21+Item {
22+ id: root
23+
24+ property real launcherOffset // unused
25+ property alias currentIndex: loginList.currentIndex
26+ property int delayMinutes // TODO
27+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
28+ property bool fullyShown: coverPage.showProgress === 1
29+ property bool required: coverPage.required
30+
31+ signal selected(int index)
32+ signal unlocked()
33+ signal tease()
34+
35+ function showMessage(html) {
36+ loginList.showMessage(html);
37+ }
38+
39+ function showPrompt(text, isSecret, isDefaultPrompt) {
40+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
41+ }
42+
43+ function showLastChance() {
44+ // TODO
45+ }
46+
47+ function hide() {
48+ coverPage.hide();
49+ }
50+
51+ function authenticated(success) {
52+ if (!success) {
53+ loginList.showError();
54+ }
55+ }
56+
57+ function reset() {
58+ loginList.reset();
59+ }
60+
61+ function tryToUnlock(toTheRight) {
62+ if (LightDM.Greeter.authenticated) {
63+ if (toTheRight) {
64+ coverPage.hideRight();
65+ } else {
66+ coverPage.hide();
67+ }
68+ } else {
69+ coverPage.show();
70+ loginList.tryToUnlock();
71+ }
72+ }
73+
74+ QtObject {
75+ id: d
76+ }
77+
78+ Rectangle {
79+ anchors.fill: parent
80+ color: "black"
81+ opacity: coverPage.showProgress * 0.8
82+ }
83+
84+ CoverPage {
85+ id: coverPage
86+ objectName: "coverPage"
87+ height: parent.height
88+ width: parent.width
89+ background: greeter.background
90+ draggable: !greeter.locked
91+
92+ infographics {
93+ height: 0.75 * parent.height
94+ anchors.leftMargin: loginList.x + loginList.width
95+ }
96+
97+ onTease: root.tease()
98+
99+ onShowProgressChanged: {
100+ if (showProgress === 0 && !greeter.locked) {
101+ root.unlocked();
102+ }
103+ }
104+ }
105+
106+ LoginList {
107+ id: loginList
108+ objectName: "loginList"
109+
110+ anchors {
111+ left: parent.left
112+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
113+ verticalCenter: parent.verticalCenter
114+ }
115+ width: units.gu(29)
116+ height: parent.height
117+
118+ model: LightDM.Users
119+
120+ onSelected: root.selected(uid)
121+ onUnlocked: coverPage.hide()
122+ }
123+}
qml/Shell.qml+14−27
@@ -59,15 +59,9 @@ Item {
59 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
59 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
60 readonly property real panelHeight: panel.panelHeight
60 readonly property real panelHeight: panel.panelHeight
61
61
62− readonly property alias locked: greeter.locked
63−
64 property bool sideStageEnabled: shell.width >= units.gu(100)
62 property bool sideStageEnabled: shell.width >= units.gu(100)
65 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
63 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
66
64
67− property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
68− property int failedLoginsDelayAttempts: 7 // number of failed logins
69− property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
70−
71 property int orientation
65 property int orientation
72 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
73 onDeviceOrientationAngleChanged: {
67 onDeviceOrientationAngleChanged: {
@@ -94,7 +88,7 @@ Item {
94 }
88 }
95
89
96 function startLockedApp(app) {
90 function startLockedApp(app) {
97− if (shell.locked) {
91+ if (greeter.locked) {
98 greeter.lockedApp = app;
92 greeter.lockedApp = app;
99 }
93 }
100 shell.activateApplication(app);
94 shell.activateApplication(app);
@@ -214,7 +208,7 @@ Item {
214 }
208 }
215
209
216 onFocusedApplicationIdChanged: {
210 onFocusedApplicationIdChanged: {
217− greeter.notifyFocusChanged(ApplicationManager.focusedApplicationId);
211+ greeter.notifyAppFocused(ApplicationManager.focusedApplicationId);
218 panel.indicators.hide();
212 panel.indicators.hide();
219 }
213 }
220
214
@@ -227,7 +221,7 @@ Item {
227 wizard.hide();
221 wizard.hide();
228 }
222 }
229
223
230− greeter.notifyAppAdded(appId);
224+ greeter.notifyAppFocused(appId);
231 launcher.hide();
225 launcher.hide();
232 }
226 }
233 }
227 }
@@ -267,7 +261,7 @@ Item {
267 Binding {
261 Binding {
268 target: applicationsDisplayLoader.item
262 target: applicationsDisplayLoader.item
269 property: "interactive"
263 property: "interactive"
270− value: edgeDemo.stagesEnabled && !greeter.fullyShown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
264+ value: edgeDemo.stagesEnabled && !greeter.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
271 }
265 }
272 Binding {
266 Binding {
273 target: applicationsDisplayLoader.item
267 target: applicationsDisplayLoader.item
@@ -329,7 +323,7 @@ Item {
329
323
330 hides: [launcher, panel.indicators]
324 hides: [launcher, panel.indicators]
331 tabletMode: shell.sideStageEnabled
325 tabletMode: shell.sideStageEnabled
332− launcherOffset: (narrowMode && showProgress > 0) ? launcher.progress : 0
326+ launcherOffset: launcher.progress
333 forcedUnlock: edgeDemo.running
327 forcedUnlock: edgeDemo.running
334 background: shell.background
328 background: shell.background
335
329
@@ -346,7 +340,7 @@ Item {
346 Binding {
340 Binding {
347 target: ApplicationManager
341 target: ApplicationManager
348 property: "suspended"
342 property: "suspended"
349− value: greeter.fullyShown
343+ value: greeter.shown
350 }
344 }
351 }
345 }
352
346
@@ -355,7 +349,7 @@ Item {
355 target: callManager
349 target: callManager
356
350
357 onHasCallsChanged: {
351 onHasCallsChanged: {
358− if (shell.locked && callManager.hasCalls) {
352+ if (greeter.locked && callManager.hasCalls) {
359 // We just received an incoming call while locked. The
353 // We just received an incoming call while locked. The
360 // indicator will have already launched dialer-app for us, but
354 // indicator will have already launched dialer-app for us, but
361 // there is a race between "hasCalls" changing and the dialer
355 // there is a race between "hasCalls" changing and the dialer
@@ -386,9 +380,7 @@ Item {
386 return
380 return
387 }
381 }
388
382
389− if (LightDM.Greeter.active) {
383+ greeter.notifyAboutToFocusApp("unity8-dash");
390− greeter.startUnlock()
391− }
392
384
393 var animate = !LightDM.Greeter.active && !stages.shown
385 var animate = !LightDM.Greeter.active && !stages.shown
394 dash.setCurrentScope(0, animate, false)
386 dash.setCurrentScope(0, animate, false)
@@ -396,13 +388,8 @@ Item {
396 }
388 }
397
389
398 function showDash() {
390 function showDash() {
399− if (greeter.hasLockedApp || // just in case user gets here
391+ if (greeter.active) {
400− (!greeter.narrowMode && shell.locked)) {
392+ greeter.notifyShowingDashFromDrag();
401− return
402− }
403−
404− if (greeter.shown) {
405− greeter.hideRight();
406 launcher.fadeOut();
393 launcher.fadeOut();
407 }
394 }
408
395
@@ -424,7 +411,7 @@ Item {
424 anchors.fill: parent //because this draws indicator menus
411 anchors.fill: parent //because this draws indicator menus
425 indicators {
412 indicators {
426 hides: [launcher]
413 hides: [launcher]
427− available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
414+ available: edgeDemo.panelEnabled && (!greeter.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
428 contentEnabled: edgeDemo.panelContentEnabled
415 contentEnabled: edgeDemo.panelContentEnabled
429 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
416 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
430
417
@@ -440,7 +427,7 @@ Item {
440 Component.onCompleted: initialise(indicatorProfile)
427 Component.onCompleted: initialise(indicatorProfile)
441 }
428 }
442 callHint {
429 callHint {
443− greeterShown: greeter.fullyShown
430+ greeterShown: greeter.shown
444 }
431 }
445
432
446 property bool topmostApplicationIsFullscreen:
433 property bool topmostApplicationIsFullscreen:
@@ -461,7 +448,7 @@ Item {
461 anchors.bottom: parent.bottom
448 anchors.bottom: parent.bottom
462 width: parent.width
449 width: parent.width
463 dragAreaWidth: shell.edgeSize
450 dragAreaWidth: shell.edgeSize
464− available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
451+ available: edgeDemo.launcherEnabled && (!greeter.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
465
452
466 onShowDashHome: showHome()
453 onShowDashHome: showHome()
467 onDash: showDash()
454 onDash: showDash()
@@ -472,7 +459,7 @@ Item {
472 }
459 }
473 onLauncherApplicationSelected: {
460 onLauncherApplicationSelected: {
474 if (!edgeDemo.running) {
461 if (!edgeDemo.running) {
475− greeter.notifyAboutToStartApp(appId);
462+ greeter.notifyAboutToFocusApp(appId);
476 shell.activateApplication(appId)
463 shell.activateApplication(appId)
477 }
464 }
478 }
465 }
tests/mocks/libusermetrics/UserMetrics.cpp+8
@@ -26,6 +26,7 @@
26
26
27#include <functional>
27#include <functional>
28#include <random>
28#include <random>
29+#include <QDebug>
29
30
30using namespace UserMetricsOutput;
31using namespace UserMetricsOutput;
31
32
@@ -289,6 +290,7 @@ void UserMetricsPrivate::generateFakeData()
289 new UserMetricsData("<b>52km</b> travelled", first, firstMonth,
290 new UserMetricsData("<b>52km</b> travelled", first, firstMonth,
290 ninth, secondMonth, this));
291 ninth, secondMonth, this));
291 m_fakeData.insert("single", data);
292 m_fakeData.insert("single", data);
293+ m_fakeData.insert("has-pin", data);
292 }
294 }
293
295
294 {
296 {
@@ -304,6 +306,7 @@ void UserMetricsPrivate::generateFakeData()
304 new UserMetricsData("<b>33</b> messages today", second,
306 new UserMetricsData("<b>33</b> messages today", second,
305 firstMonth, eighth, secondMonth, this));
307 firstMonth, eighth, secondMonth, this));
306 m_fakeData.insert("single", data);
308 m_fakeData.insert("single", data);
309+ m_fakeData.insert("has-pin", data);
307 }
310 }
308
311
309 {
312 {
@@ -319,6 +322,7 @@ void UserMetricsPrivate::generateFakeData()
319 new UserMetricsData("<b>19</b> minutes talk time", eighth,
322 new UserMetricsData("<b>19</b> minutes talk time", eighth,
320 firstMonth, second, secondMonth, this));
323 firstMonth, second, secondMonth, this));
321 m_fakeData.insert("single", data);
324 m_fakeData.insert("single", data);
325+ m_fakeData.insert("has-pin", data);
322 // Also use same data for some tablet users
326 // Also use same data for some tablet users
323 m_fakeData.insert("has-password", data);
327 m_fakeData.insert("has-password", data);
324 m_fakeData.insert("no-password", data);
328 m_fakeData.insert("no-password", data);
@@ -386,6 +390,10 @@ void UserMetricsPrivate::nextFakeData()
386 if (m_dataIndex == m_fakeData.constEnd() || m_dataIndex.key() != m_username)
390 if (m_dataIndex == m_fakeData.constEnd() || m_dataIndex.key() != m_username)
387 {
391 {
388 m_dataIndex = m_fakeData.constFind(m_username);
392 m_dataIndex = m_fakeData.constFind(m_username);
393+ if (m_dataIndex == m_fakeData.constEnd())
394+ {
395+ m_dataIndex = m_fakeData.constFind("");
396+ }
389 }
397 }
390
398
391 loadFakeData();
399 loadFakeData();
tests/qmltests/Dash/tst_DashContent.qml+1−1
@@ -71,7 +71,7 @@ Item {
71 scopeLoadedSpy.clear();
71 scopeLoadedSpy.clear();
72 scopesModel.load();
72 scopesModel.load();
73 tryCompare(scopeLoadedSpy, "count", 6, 15000);
73 tryCompare(scopeLoadedSpy, "count", 6, 15000);
74− tryCompare(scopesModel, "loaded", true);
74+ tryCompare(scopesModel, "loaded", true, 1000, typeof scopesModel.loaded);
75 tryCompareFunction(function() {
75 tryCompareFunction(function() {
76 var mockScope1Loader = findChild(shell, "scopeLoader0");
76 var mockScope1Loader = findChild(shell, "scopeLoader0");
77 return mockScope1Loader && mockScope1Loader.item != null; },
77 return mockScope1Loader && mockScope1Loader.item != null; },
tests/qmltests/Greeter/tst_MultiGreeter.qml+12−11
@@ -29,6 +29,7 @@ Item {
29 Greeter {
29 Greeter {
30 id: greeter
30 id: greeter
31 anchors.fill: parent
31 anchors.fill: parent
32+ tabletMode: true
32 }
33 }
33
34
34 Component {
35 Component {
@@ -83,12 +84,12 @@ Item {
83
84
84 function select_user(name) {
85 function select_user(name) {
85 // We could be anywhere in list; find target index to know which direction
86 // We could be anywhere in list; find target index to know which direction
86− for (var i = 0; i < greeter.model.count; i++) {
87+ for (var i = 0; i < LightDM.Users.count; i++) {
87− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == name) {
88+ if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
88 break
89 break
89 }
90 }
90 }
91 }
91− if (i == greeter.model.count) {
92+ if (i == LightDM.Users.count) {
92 fail("Didn't find name")
93 fail("Didn't find name")
93 return -1
94 return -1
94 }
95 }
@@ -103,8 +104,8 @@ Item {
103
104
104 function test_cycle_data() {
105 function test_cycle_data() {
105 var data = new Array()
106 var data = new Array()
106− for (var i = 0; i < greeter.model.count; i++) {
107+ for (var i = 0; i < LightDM.Users.count; i++) {
107− data[i] = {tag: greeter.model.data(i, LightDM.UserRoles.NameRole), uid: i }
108+ data[i] = {tag: LightDM.Users.data(i, LightDM.UserRoles.NameRole), uid: i }
108 }
109 }
109 return data
110 return data
110 }
111 }
@@ -160,9 +161,9 @@ Item {
160 }
161 }
161
162
162 function test_empty_name() {
163 function test_empty_name() {
163− for (var i = 0; i < greeter.model.count; i++) {
164+ for (var i = 0; i < LightDM.Users.count; i++) {
164− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == "empty-name") {
165+ if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == "empty-name") {
165− compare(greeter.model.data(i, LightDM.UserRoles.RealNameRole), greeter.model.data(i, LightDM.UserRoles.NameRole))
166+ compare(LightDM.Users.data(i, LightDM.UserRoles.RealNameRole), LightDM.Users.data(i, LightDM.UserRoles.NameRole))
166 return
167 return
167 }
168 }
168 }
169 }
@@ -314,12 +315,12 @@ Item {
314
315
315 function test_bg_color() {
316 function test_bg_color() {
316 var index = select_user("color-background")
317 var index = select_user("color-background")
317− compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")
318+ compare(LightDM.Users.data(index, LightDM.UserRoles.BackgroundPathRole), "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")
318 }
319 }
319
320
320 function test_bg_none() {
321 function test_bg_none() {
321 var index = select_user("no-background")
322 var index = select_user("no-background")
322− compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "")
323+ compare(LightDM.Users.data(index, LightDM.UserRoles.BackgroundPathRole), "")
323 }
324 }
324
325
325 function test_teasingArea_data() {
326 function test_teasingArea_data() {
@@ -371,7 +372,7 @@ Item {
371 tryCompare(selectionSpy, "count", 1)
372 tryCompare(selectionSpy, "count", 1)
372
373
373 var userlist = findChild(greeter, "userList")
374 var userlist = findChild(greeter, "userList")
374− compare(greeter.model.data(userlist.currentIndex, LightDM.UserRoles.NameRole), "has-password")
375+ compare(LightDM.Users.data(userlist.currentIndex, LightDM.UserRoles.NameRole), "has-password")
375 }
376 }
376
377
377 function test_initial_selected_signal() {
378 function test_initial_selected_signal() {
tests/qmltests/tst_Shell.qml+24−21
@@ -87,7 +87,7 @@ Item {
87
87
88 var greeter = testCase.findChild(shellLoader.item, "greeter");
88 var greeter = testCase.findChild(shellLoader.item, "greeter");
89 if (!greeter.shown) {
89 if (!greeter.shown) {
90− greeter.show();
90+ LightDM.Greeter.showGreeter();
91 }
91 }
92 }
92 }
93 }
93 }
@@ -323,20 +323,20 @@ Item {
323 // Suspend while call is active...
323 // Suspend while call is active...
324 callManager.foregroundCall = phoneCall;
324 callManager.foregroundCall = phoneCall;
325 Powerd.status = Powerd.Off;
325 Powerd.status = Powerd.Off;
326− tryCompare(greeter, "showProgress", 0);
326+ tryCompare(greeter, "shown", false);
327
327
328 // Now try again after ending call
328 // Now try again after ending call
329 callManager.foregroundCall = null;
329 callManager.foregroundCall = null;
330 Powerd.status = Powerd.On;
330 Powerd.status = Powerd.On;
331 Powerd.status = Powerd.Off;
331 Powerd.status = Powerd.Off;
332− tryCompare(greeter, "showProgress", 1);
332+ tryCompare(greeter, "fullyShown", true);
333
333
334 tryCompare(ApplicationManager, "suspended", true);
334 tryCompare(ApplicationManager, "suspended", true);
335 compare(mainApp.state, ApplicationInfoInterface.Suspended);
335 compare(mainApp.state, ApplicationInfoInterface.Suspended);
336
336
337 // And wake up
337 // And wake up
338 Powerd.status = Powerd.On;
338 Powerd.status = Powerd.On;
339− tryCompare(greeter, "showProgress", 1);
339+ tryCompare(greeter, "fullyShown", true);
340
340
341 // Swipe away greeter to focus app
341 // Swipe away greeter to focus app
342 swipeAwayGreeter();
342 swipeAwayGreeter();
@@ -347,14 +347,14 @@ Item {
347
347
348 function swipeAwayGreeter() {
348 function swipeAwayGreeter() {
349 var greeter = findChild(shell, "greeter");
349 var greeter = findChild(shell, "greeter");
350− tryCompare(greeter, "showProgress", 1);
350+ tryCompare(greeter, "fullyShown", true);
351
351
352 var touchX = shell.width - (shell.edgeSize / 2);
352 var touchX = shell.width - (shell.edgeSize / 2);
353 var touchY = shell.height / 2;
353 var touchY = shell.height / 2;
354 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
354 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
355
355
356 // wait until the animation has finished
356 // wait until the animation has finished
357− tryCompare(greeter, "showProgress", 0);
357+ tryCompare(greeter, "shown", false);
358 waitForRendering(greeter);
358 waitForRendering(greeter);
359 }
359 }
360
360
@@ -559,12 +559,12 @@ Item {
559
559
560 waitUntilDashIsFocused();
560 waitUntilDashIsFocused();
561
561
562− greeter.show();
562+ LightDM.Greeter.showGreeter();
563− tryCompare(greeter, "showProgress", 1);
563+ tryCompare(greeter, "fullyShown", true);
564
564
565 // The main point of this test
565 // The main point of this test
566 ApplicationManager.requestFocusApplication("dialer-app");
566 ApplicationManager.requestFocusApplication("dialer-app");
567− tryCompare(greeter, "showProgress", 0);
567+ tryCompare(greeter, "shown", false);
568 waitForRendering(greeter);
568 waitForRendering(greeter);
569 }
569 }
570
570
@@ -582,24 +582,22 @@ Item {
582
582
583 function test_showAndHideGreeterDBusCalls() {
583 function test_showAndHideGreeterDBusCalls() {
584 var greeter = findChild(shell, "greeter")
584 var greeter = findChild(shell, "greeter")
585− tryCompare(greeter, "showProgress", 0)
585+ tryCompare(greeter, "shown", false)
586 waitForRendering(greeter);
586 waitForRendering(greeter);
587 LightDM.Greeter.showGreeter()
587 LightDM.Greeter.showGreeter()
588 waitForRendering(greeter)
588 waitForRendering(greeter)
589− tryCompare(greeter, "showProgress", 1)
589+ tryCompare(greeter, "fullyShown", true)
590 LightDM.Greeter.hideGreeter()
590 LightDM.Greeter.hideGreeter()
591− tryCompare(greeter, "showProgress", 0)
591+ tryCompare(greeter, "shown", false)
592 }
592 }
593
593
594 function test_login() {
594 function test_login() {
595− sessionSpy.clear()
596−
597 var greeter = findChild(shell, "greeter")
595 var greeter = findChild(shell, "greeter")
598 waitForRendering(greeter)
596 waitForRendering(greeter)
599− greeter.show()
597+ LightDM.Greeter.showGreeter();
600− tryCompare(greeter, "showProgress", 1)
598+ tryCompare(greeter, "fullyShown", true)
601
599
602− tryCompare(sessionSpy, "count", 0)
600+ sessionSpy.clear();
603 swipeAwayGreeter()
601 swipeAwayGreeter()
604 tryCompare(sessionSpy, "count", 1)
602 tryCompare(sessionSpy, "count", 1)
605 }
603 }
@@ -640,7 +638,8 @@ Item {
640
638
641 function test_unlockedProperties() {
639 function test_unlockedProperties() {
642 // Confirm that various properties have the correct values when unlocked
640 // Confirm that various properties have the correct values when unlocked
641+ var greeter = findChild(shell, "greeter");
643− tryCompare(shell, "locked", false)
642+ tryCompare(greeter, "locked", false);
644
643
645 var launcher = findChild(shell, "launcher")
644 var launcher = findChild(shell, "launcher")
646 tryCompare(launcher, "available", true)
645 tryCompare(launcher, "available", true)
@@ -662,14 +661,18 @@ Item {
662
661
663 function test_leftEdgeDragOnGreeter(data) {
662 function test_leftEdgeDragOnGreeter(data) {
664 var greeter = findChild(shell, "greeter");
663 var greeter = findChild(shell, "greeter");
665− greeter.show();
664+ LightDM.Greeter.showGreeter();
666− tryCompare(greeter, "showProgress", 1);
665+ tryCompare(greeter, "fullyShown", true);
667
666
668 var touchStartX = 2;
667 var touchStartX = 2;
669 var touchStartY = shell.height / 2;
668 var touchStartY = shell.height / 2;
670 touchFlick(shell, touchStartX, touchStartY, data.targetX, touchStartY);
669 touchFlick(shell, touchStartX, touchStartY, data.targetX, touchStartY);
671
670
672− tryCompare(greeter, "showProgress", data.unlocked ? 0 : 1);
671+ if (data.unlocked) {
672+ tryCompare(greeter, "shown", false);
673+ } else {
674+ tryCompare(greeter, "fullyShown", true);
675+ }
673 }
676 }
674
677
675 function test_tapOnRightEdgeReachesApplicationSurface() {
678 function test_tapOnRightEdgeReachesApplicationSurface() {
tests/qmltests/tst_ShellWithPin.qml+42−35
@@ -66,7 +66,6 @@ Item {
66 Component.onDestruction: {
66 Component.onDestruction: {
67 shellLoader.itemDestroyed = true
67 shellLoader.itemDestroyed = true
68 }
68 }
69− maxFailedLogins: maxRetriesTextField.text
70 }
69 }
71 }
70 }
72 }
71 }
@@ -100,6 +99,10 @@ Item {
100 TextField {
99 TextField {
101 id: maxRetriesTextField
100 id: maxRetriesTextField
102 text: "-1"
101 text: "-1"
102+ onTextChanged: {
103+ var greeter = testCase.findChild(shellLoader.item, "greeter");
104+ greeter.maxFailedLogins = text;
105+ }
103 }
106 }
104 }
107 }
105 }
108 }
@@ -130,11 +133,12 @@ Item {
130
133
131 function init() {
134 function init() {
132 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
135 tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
133− sessionSpy.target = findChild(shell, "greeter")
136+ var greeter = findChild(shell, "greeter");
137+ sessionSpy.target = greeter;
134− swipeAwayGreeter()
138+ swipeAwayGreeter(true);
135 waitForLockscreen()
139 waitForLockscreen()
136− shell.failedLoginsDelayAttempts = -1
140+ greeter.failedLoginsDelayAttempts = -1;
137− maxRetriesTextField.text = "-1"
141+ greeter.maxFailedLogins = -1;
138 }
142 }
139
143
140 function cleanup() {
144 function cleanup() {
@@ -178,17 +182,20 @@ Item {
178 compare(ApplicationManager.count, 1)
182 compare(ApplicationManager.count, 1)
179 }
183 }
180
184
181− function swipeAwayGreeter() {
185+ function swipeAwayGreeter(waitForCoverPage) {
182 var greeter = findChild(shell, "greeter");
186 var greeter = findChild(shell, "greeter");
183 waitForRendering(greeter)
187 waitForRendering(greeter)
184− tryCompare(greeter, "showProgress", 1);
188+ tryCompare(greeter, "fullyShown", true);
185
189
186 var touchX = shell.width - (shell.edgeSize / 2);
190 var touchX = shell.width - (shell.edgeSize / 2);
187 var touchY = shell.height / 2;
191 var touchY = shell.height / 2;
188 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
192 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
189
193
194+ if (waitForCoverPage) {
190− // wait until the animation has finished
195+ // wait until the animation has finished
196+ var coverPage = findChild(shell, "coverPage");
191− tryCompare(greeter, "showProgress", 0);
197+ tryCompare(coverPage, "showProgress", 0);
198+ }
192 }
199 }
193
200
194 function waitForLockscreen() {
201 function waitForLockscreen() {
@@ -209,9 +216,7 @@ Item {
209
216
210 function confirmLockedApp(app) {
217 function confirmLockedApp(app) {
211 var greeter = findChild(shell, "greeter")
218 var greeter = findChild(shell, "greeter")
212− var lockscreen = findChild(shell, "lockscreen")
213 tryCompare(greeter, "shown", false)
219 tryCompare(greeter, "shown", false)
214− tryCompare(lockscreen, "shown", false)
215 tryCompare(greeter, "hasLockedApp", true)
220 tryCompare(greeter, "hasLockedApp", true)
216 tryCompare(greeter, "lockedApp", app)
221 tryCompare(greeter, "lockedApp", app)
217 tryCompare(LightDM.Greeter, "active", true)
222 tryCompare(LightDM.Greeter, "active", true)
@@ -234,13 +239,16 @@ Item {
234 AccountsService.demoEdges = true
239 AccountsService.demoEdges = true
235 tryCompare(lockscreen, "shown", false)
240 tryCompare(lockscreen, "shown", false)
236
241
242+ var greeter = findChild(shell, "greeter");
237− swipeAwayGreeter()
243+ swipeAwayGreeter(false);
244+ tryCompare(greeter, "shown", false);
238 tryCompare(sessionSpy, "count", 1)
245 tryCompare(sessionSpy, "count", 1)
239
246
240 // Lockscreen is only hidden by the edge demo, so if we turn that
247 // Lockscreen is only hidden by the edge demo, so if we turn that
241 // off and show greeter again, lockscreen should appear
248 // off and show greeter again, lockscreen should appear
242 AccountsService.demoEdges = false
249 AccountsService.demoEdges = false
243 LightDM.Greeter.showGreeter()
250 LightDM.Greeter.showGreeter()
251+ lockscreen = findChild(shell, "lockscreen");
244 tryCompare(lockscreen, "shown", true)
252 tryCompare(lockscreen, "shown", true)
245 }
253 }
246
254
@@ -258,18 +266,16 @@ Item {
258
266
259 function test_emergencyCall() {
267 function test_emergencyCall() {
260 var greeter = findChild(shell, "greeter")
268 var greeter = findChild(shell, "greeter")
261− var lockscreen = findChild(shell, "lockscreen")
262− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
263 var panel = findChild(shell, "panel")
269 var panel = findChild(shell, "panel")
264 var indicators = findChild(shell, "indicators")
270 var indicators = findChild(shell, "indicators")
265 var launcher = findChild(shell, "launcher")
271 var launcher = findChild(shell, "launcher")
266 var stage = findChild(shell, "stage")
272 var stage = findChild(shell, "stage")
267
273
268− tap(emergencyButton)
274+ tap(findChild(greeter, "emergencyCallLabel"));
269
275
270 tryCompare(greeter, "lockedApp", "dialer-app")
276 tryCompare(greeter, "lockedApp", "dialer-app")
271 tryCompare(greeter, "hasLockedApp", true)
277 tryCompare(greeter, "hasLockedApp", true)
272− tryCompare(lockscreen, "shown", false)
278+ tryCompare(greeter, "shown", false);
273 tryCompare(panel, "fullscreenMode", true)
279 tryCompare(panel, "fullscreenMode", true)
274 tryCompare(indicators, "available", false)
280 tryCompare(indicators, "available", false)
275 tryCompare(launcher, "available", false)
281 tryCompare(launcher, "available", false)
@@ -282,7 +288,7 @@ Item {
282 tryCompare(greeter, "shown", true)
288 tryCompare(greeter, "shown", true)
283 tryCompare(greeter, "lockedApp", "")
289 tryCompare(greeter, "lockedApp", "")
284 tryCompare(greeter, "hasLockedApp", false)
290 tryCompare(greeter, "hasLockedApp", false)
285− tryCompare(lockscreen, "shown", true)
291+ tryCompare(greeter, "fullyShown", true);
286 tryCompare(panel, "fullscreenMode", false)
292 tryCompare(panel, "fullscreenMode", false)
287 tryCompare(indicators, "available", true)
293 tryCompare(indicators, "available", true)
288 tryCompare(launcher, "available", true)
294 tryCompare(launcher, "available", true)
@@ -290,23 +296,25 @@ Item {
290 }
296 }
291
297
292 function test_emergencyCallCrash() {
298 function test_emergencyCallCrash() {
293− var lockscreen = findChild(shell, "lockscreen")
299+ var greeter = findChild(shell, "greeter");
294− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
300+ var emergencyButton = findChild(greeter, "emergencyCallLabel");
295 tap(emergencyButton)
301 tap(emergencyButton)
296
302
297− tryCompare(lockscreen, "shown", false)
303+ tryCompare(greeter, "shown", false);
298 killApps() // kill dialer-app, as if it crashed
304 killApps() // kill dialer-app, as if it crashed
305+ tryCompare(greeter, "shown", true);
299− tryCompare(lockscreen, "shown", true)
306+ tryCompare(findChild(greeter, "lockscreen"), "shown", true);
307+ tryCompare(findChild(greeter, "coverPage"), "shown", false);
300 }
308 }
301
309
302 function test_emergencyCallAppLaunch() {
310 function test_emergencyCallAppLaunch() {
303− var lockscreen = findChild(shell, "lockscreen")
311+ var greeter = findChild(shell, "greeter");
304− var emergencyButton = findChild(lockscreen, "emergencyCallLabel")
312+ var emergencyButton = findChild(greeter, "emergencyCallLabel");
305 tap(emergencyButton)
313 tap(emergencyButton)
306
314
307− tryCompare(lockscreen, "shown", false)
315+ tryCompare(greeter, "shown", false);
308 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
316 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
309− tryCompare(lockscreen, "shown", true)
317+ tryCompare(greeter, "shown", true);
310 }
318 }
311
319
312 function test_failedLoginsCount() {
320 function test_failedLoginsCount() {
@@ -320,7 +328,8 @@ Item {
320 }
328 }
321
329
322 function test_wrongEntries() {
330 function test_wrongEntries() {
331+ var greeter = findChild(shell, "greeter");
323− shell.failedLoginsDelayAttempts = 3
332+ greeter.failedLoginsDelayAttempts = 3;
324
333
325 var placeHolder = findChild(shell, "wrongNoticeLabel")
334 var placeHolder = findChild(shell, "wrongNoticeLabel")
326 tryCompare(placeHolder, "text", "")
335 tryCompare(placeHolder, "text", "")
@@ -334,7 +343,7 @@ Item {
334 var lockscreen = findChild(shell, "lockscreen")
343 var lockscreen = findChild(shell, "lockscreen")
335 tryCompare(lockscreen, "delayMinutes", 0)
344 tryCompare(lockscreen, "delayMinutes", 0)
336 enterPin("1111")
345 enterPin("1111")
337− tryCompare(lockscreen, "delayMinutes", shell.failedLoginsDelayMinutes)
346+ tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
338 }
347 }
339
348
340 function test_factoryReset() {
349 function test_factoryReset() {
@@ -380,7 +389,7 @@ Item {
380 // And when we kill the app, we go back to locked tablet mode
389 // And when we kill the app, we go back to locked tablet mode
381 killApps()
390 killApps()
382 var greeter = findChild(shell, "greeter")
391 var greeter = findChild(shell, "greeter")
383− tryCompare(greeter, "showProgress", 1)
392+ tryCompare(greeter, "fullyShown", true)
384 tryCompare(shell, "sideStageEnabled", true)
393 tryCompare(shell, "sideStageEnabled", true)
385 tryCompare(applicationsDisplayLoader, "tabletMode", true)
394 tryCompare(applicationsDisplayLoader, "tabletMode", true)
386 }
395 }
@@ -401,7 +410,6 @@ Item {
401 var lockscreen = findChild(shell, "lockscreen");
410 var lockscreen = findChild(shell, "lockscreen");
402
411
403 lockscreen.emergencyCall();
412 lockscreen.emergencyCall();
404−console.log("MIKE first locked app");
405 confirmLockedApp("dialer-app");
413 confirmLockedApp("dialer-app");
406 callManager.foregroundCall = phoneCall;
414 callManager.foregroundCall = phoneCall;
407
415
@@ -412,7 +420,6 @@ console.log("MIKE first locked app");
412 // simulate a callHint press, the real thing requires dialer: url support
420 // simulate a callHint press, the real thing requires dialer: url support
413 ApplicationManager.requestFocusApplication("dialer-app");
421 ApplicationManager.requestFocusApplication("dialer-app");
414
422
415−console.log("MIKE second locked app");
416 confirmLockedApp("dialer-app");
423 confirmLockedApp("dialer-app");
417 }
424 }
418
425
@@ -425,11 +432,11 @@ console.log("MIKE second locked app");
425 // - Should be back in normal dialer
432 // - Should be back in normal dialer
426 // (we've had a bug where we locked screen in this case)
433 // (we've had a bug where we locked screen in this case)
427
434
428− var lockscreen = findChild(shell, "lockscreen");
435+ var greeter = findChild(shell, "greeter");
429 var panel = findChild(shell, "panel");
436 var panel = findChild(shell, "panel");
430
437
431 enterPin("1234");
438 enterPin("1234");
432− tryCompare(lockscreen, "shown", false);
439+ tryCompare(greeter, "shown", false);
433 tryCompare(LightDM.Greeter, "active", false);
440 tryCompare(LightDM.Greeter, "active", false);
434
441
435 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
442 ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
@@ -444,7 +451,7 @@ console.log("MIKE second locked app");
444 ApplicationManager.requestFocusApplication("dialer-app");
451 ApplicationManager.requestFocusApplication("dialer-app");
445
452
446 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
453 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
447− tryCompare(lockscreen, "shown", false);
454+ tryCompare(greeter, "shown", false);
448 tryCompare(LightDM.Greeter, "active", false);
455 tryCompare(LightDM.Greeter, "active", false);
449 }
456 }
450
457
@@ -459,10 +466,10 @@ console.log("MIKE second locked app");
459
466
460 // And wake up
467 // And wake up
461 Powerd.status = Powerd.On;
468 Powerd.status = Powerd.On;
462− tryCompare(greeter, "showProgress", 1);
469+ tryCompare(greeter, "fullyShown", true);
463
470
464 // Swipe away greeter to focus app
471 // Swipe away greeter to focus app
465− swipeAwayGreeter();
472+ swipeAwayGreeter(true);
466
473
467 // We have a lockscreen, make sure we're still suspended
474 // We have a lockscreen, make sure we're still suspended
468 tryCompare(ApplicationManager, "suspended", true);
475 tryCompare(ApplicationManager, "suspended", true);
tests/qmltests/tst_TabletShell.qml+45−42
@@ -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 QtObject {
36 QtObject {
36 id: applicationArguments
37 id: applicationArguments
@@ -48,43 +49,45 @@ Row {
48 }
49 }
49 }
50 }
50
51
52+ Row {
53+ anchors.fill: parent
51− Loader {
54+ Loader {
52− id: shellLoader
55+ id: shellLoader
56+ width: units.gu(100)
57+ height: parent.height
53
58
54− width: units.gu(100)
59+ property bool itemDestroyed: false
55− height: units.gu(80)
60+ sourceComponent: Component {
61+ Shell {
62+ property string indicatorProfile: "phone"
56
63
57− property bool itemDestroyed: false
58− sourceComponent: Component {
59− Shell {
60− property string indicatorProfile: "phone"
61−
62− Component.onDestruction: {
64+ Component.onDestruction: {
63− shellLoader.itemDestroyed = true
65+ shellLoader.itemDestroyed = true
66+ }
64 }
67 }
65 }
68 }
66 }
69 }
67− }
68
70
69− Rectangle {
71+ Rectangle {
70− color: "white"
72+ color: "white"
71− width: units.gu(20)
73+ width: units.gu(20)
72− height: shellLoader.height
74+ height: parent.height
73−
75+
74− Column {
76+ Column {
75− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
77+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
76− spacing: units.gu(1)
78+ spacing: units.gu(1)
77− Row {
79+ Row {
78− anchors { left: parent.left; right: parent.right }
80+ anchors { left: parent.left; right: parent.right }
79− Button {
81+ Button {
80− text: "Show Greeter"
82+ text: "Show Greeter"
81− onClicked: {
83+ onClicked: {
82− if (shellLoader.status !== Loader.Ready)
84+ if (shellLoader.status !== Loader.Ready)
83− return
85+ return
84−
86+
85− var greeter = testCase.findChild(shellLoader.item, "greeter")
87+ var greeter = testCase.findChild(shellLoader.item, "greeter")
86− if (!greeter.shown) {
88+ if (!greeter.shown) {
87− greeter.show()
89+ greeter.show()
90+ }
88 }
91 }
89 }
92 }
90 }
93 }
@@ -187,12 +190,12 @@ Row {
187 function selectUser(name) {
190 function selectUser(name) {
188 // Find index of user with the right name
191 // Find index of user with the right name
189 var greeter = findChild(shell, "greeterContentLoader")
192 var greeter = findChild(shell, "greeterContentLoader")
190− for (var i = 0; i < greeter.model.count; i++) {
193+ for (var i = 0; i < LightDM.Users.count; i++) {
191− if (greeter.model.data(i, LightDM.UserRoles.NameRole) == name) {
194+ if (LightDM.Users.data(i, LightDM.UserRoles.NameRole) == name) {
192 break
195 break
193 }
196 }
194 }
197 }
195− if (i == greeter.model.count) {
198+ if (i == LightDM.Users.count) {
196 fail("Didn't find name")
199 fail("Didn't find name")
197 return -1
200 return -1
198 }
201 }
@@ -202,7 +205,7 @@ Row {
202
205
203 function clickPasswordInput(isButton) {
206 function clickPasswordInput(isButton) {
204 var greeter = findChild(shell, "greeter")
207 var greeter = findChild(shell, "greeter")
205− tryCompare(greeter, "showProgress", 1)
208+ tryCompare(greeter, "fullyShown", true);
206
209
207 var passwordMouseArea = findChild(shell, "passwordMouseArea")
210 var passwordMouseArea = findChild(shell, "passwordMouseArea")
208 tryCompare(passwordMouseArea, "enabled", isButton)
211 tryCompare(passwordMouseArea, "enabled", isButton)
@@ -213,8 +216,8 @@ Row {
213
216
214 function confirmLoggedIn(loggedIn) {
217 function confirmLoggedIn(loggedIn) {
215 var greeter = findChild(shell, "greeter");
218 var greeter = findChild(shell, "greeter");
216− tryCompare(greeter, "showProgress", loggedIn ? 0 : 1);
219+ tryCompare(greeter, "shown", loggedIn ? false : true);
217− tryCompare(sessionSpy, "count", loggedIn ? 1 : 0)
220+ verify(loggedIn ? sessionSpy.count > 0 : sessionSpy.count === 0);
218 }
221 }
219
222
220 function swipeFromLeftEdge(swipeLength) {
223 function swipeFromLeftEdge(swipeLength) {
@@ -226,15 +229,15 @@ Row {
226 function test_noLockscreen() {
229 function test_noLockscreen() {
227 selectUser("has-password")
230 selectUser("has-password")
228 var lockscreen = findChild(shell, "lockscreen")
231 var lockscreen = findChild(shell, "lockscreen")
229− tryCompare(lockscreen, "shown", false)
232+ compare(lockscreen, null);
230 }
233 }
231
234
232 function test_showAndHideGreeterDBusCalls() {
235 function test_showAndHideGreeterDBusCalls() {
233 var greeter = findChild(shell, "greeter")
236 var greeter = findChild(shell, "greeter")
234 LightDM.Greeter.hideGreeter()
237 LightDM.Greeter.hideGreeter()
235− tryCompare(greeter, "showProgress", 0)
238+ tryCompare(greeter, "shown", false);
236 LightDM.Greeter.showGreeter()
239 LightDM.Greeter.showGreeter()
237− tryCompare(greeter, "showProgress", 1)
240+ tryCompare(greeter, "fullyShown", true);
238 }
241 }
239
242
240 function test_login_data() {
243 function test_login_data() {