Merge lp:~mterry/unity8/greeter-refactor into lp:unity8
- greeter-refactor
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Daniel d'Andrada |
Approved revision: | 1507 |
Merged at revision: | 1622 |
Proposed branch: | lp:~mterry/unity8/greeter-refactor |
Merge into: | lp:unity8 |
Prerequisite: | lp:~mterry/unity8/tutorial-new-screens |
Diff against target: |
5614 lines (+2954/-1668) 28 files modified
cmake/modules/QmlTest.cmake (+1/-1) qml/Components/DelayedLockscreen.qml (+3/-1) qml/Components/PassphraseLockscreen.qml (+2/-3) qml/Components/PinLockscreen.qml (+3/-4) qml/Greeter/CoverPage.qml (+163/-110) qml/Greeter/Greeter.qml (+358/-186) qml/Greeter/Infographics.qml (+1/-0) qml/Greeter/LoginList.qml (+70/-88) qml/Greeter/NarrowView.qml (+170/-0) qml/Greeter/WideView.qml (+134/-0) qml/Shell.qml (+78/-364) tests/autopilot/unity8/shell/emulators/greeter.py (+19/-3) tests/autopilot/unity8/shell/emulators/main_window.py (+0/-6) tests/autopilot/unity8/shell/tests/__init__.py (+2/-3) tests/autopilot/unity8/shell/tests/test_lock_screen.py (+8/-12) tests/plugins/LightDM/CMakeLists.txt (+21/-6) tests/plugins/LightDM/usersmodel.cpp (+84/-0) tests/qmltests/CMakeLists.txt (+4/-3) tests/qmltests/Greeter/TestView.qml (+99/-0) tests/qmltests/Greeter/tst_Greeter.qml (+472/-0) tests/qmltests/Greeter/tst_MultiGreeter.qml (+0/-475) tests/qmltests/Greeter/tst_NarrowView.qml (+522/-0) tests/qmltests/Greeter/tst_SingleGreeter.qml (+0/-244) tests/qmltests/Greeter/tst_WideView.qml (+519/-0) tests/qmltests/Tutorial/tst_Tutorial.qml (+5/-4) tests/qmltests/tst_Shell.qml (+65/-43) tests/qmltests/tst_ShellWithPin.qml (+97/-48) tests/qmltests/tst_TabletShell.qml (+54/-64) |
To merge this branch: | bzr merge lp:~mterry/unity8/greeter-refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Zanetti (community) | Approve | ||
Daniel d'Andrada (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Review via email: mp+248829@code.launchpad.net |
This proposal supersedes a proposal from 2015-01-29.
Commit message
Refactor the greeter code to be more compartmented and isolatable.
This will help make it an optional piece of the shell when we allow a split greeter/shell.
My driving goals were:
- Remove as much logic from Shell.qml as possible. Right now, there is so much Greeter logic spread all throughout Shell.qml in different bits. For example, the Lockscreen widget sitting there instead of in qml/Greeter/.
- Clean up how we load the two different views (tablet & phone). Right now we have some widgets always present but sometimes hidden (Lockscreen) and Loaders within Loaders (LoginList inside GreeterContent). I wanted just two top-level view widgets that could be separately loaded depending on which mode we are in.
- Toward that end, I wanted to consolidate all the Greeter logic out of Shell and out of those two view classes to avoid duplication. So I made the main Greeter entry point widget handle all the login logic and interactions with LightDM. It controls the views, who always ask it to do things on their behalf. Classic MVC stuff.
- I separated out some of the widgetry from the previous Greeter class into a CoverPage class that both NarrowView and WideView use. This class now holds the drag handle for sliding the greeter away and the infographic.
- Now that each piece of the greeter has a much more brightly defined boundary, testing them in isolation is much easier. I've added tst_WideView, tst_NarrowView, and tst_Greeter files. I've moved existing tests around a bit and added some more. But it should be much more clear where a prospective test belongs now (logic tests in Greeter, visual tests in the respective View).
- I also moved some tests we had in qmluitests/ that were purely testing the LightDM plugin into their own plugin test file.
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
No.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.
* If you changed the UI, has there been a design review?
Not applicable.
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1476
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
About the commit message: Mind doing the "short summary on first line, empty line, rest of the explanation" format?
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
"""
1174 + * Copyright (C) 2014 Canonical, Ltd.
"""
We're past that year, man. :)
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
OK, copied "Description" into "Commit Message" and updated years. :)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1478
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1480
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1481
http://
Executed test runs:
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1481
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
Text conflict in qml/Greeter/
Text conflict in qml/Greeter/
Text conflict in qml/Shell.qml
Text conflict in tests/plugins/
Text conflict in tests/qmltests/
Contents conflict in tests/qmltests/
Text conflict in tests/qmltests/
7 conflicts encountered.
Michael Terry (mterry) wrote : | # |
I rebased this MP on tutorial-
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1484
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1485. By Michael Terry
-
Fix tutorial autopilot test with new greeter refactor
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1485
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1486. By Michael Terry
-
Fix autopilot harder
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1486
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1487. By Michael Terry
-
Wait until userlist settles at beginning of TabletShell test
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1487
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1487
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
There seems to be a badly resolved merge conflict:
qml/Components/
Daniel d'Andrada (dandrader) wrote : | # |
Please update the copyright year of qml/Components/
Daniel d'Andrada (dandrader) wrote : | # |
I run "make tryGreeter" and got a black screen on the left side. clicking the "show greeter" button has no visible effect. Is that expected?
Daniel d'Andrada (dandrader) wrote : | # |
"""
=== modified file 'qml/Components
--- qml/Components/
+++ qml/Components/
@@ -98,6 +98,11 @@ Item {
// has oddly sized password characters that don't scale right)
+ onFocusChanged: {
+ // Within our FocusScope, we always want to have focus
+ focus = true;
+ }
+
// simulate being disabled, but without removing OSK focus
"""
This looks like a desperate hack. Setting "focus: true" should suffice (along with a FocusScope surrounding the offender in the worst case, if that makes sense). Do you know who is taking away the focus from this item?
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1487
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1488. By Michael Terry
-
Small cleanups
- 1489. By Michael Terry
-
Make fake TestView more obviously intentionally-blank
Michael Terry (mterry) wrote : | # |
> There seems to be a badly resolved merge conflict:
> qml/Components/
Fixed, whoops!
> Please update the copyright year of qml/Components/
Fixed.
> I run "make tryGreeter" and got a black screen on the left side.
> clicking the "show greeter" button has no visible effect. Is that expected?
Yes. tst_Greeter.qml only tests the "controller" side of things -- the glue between the user visible views and AccountsService
If you want to play with a view class, use tryWideView and tryNarrowView. If you want to play with the Greeter + a view, use tryShell, tryShellWithPin, or tryTabletShell.
> This looks like a desperate hack. Setting "focus: true" should suffice
Yeah... You got me. It didn't suffice, and I couldn't tell why focus was being stolen. All I could tell was that activeFocus was moving to the greeter's loader itself. But not in a situation that seemingly would require it to move (e.g. it wasn't being reloaded nor was a widget disabled as far as I could tell).
Michael Terry (mterry) wrote : | # |
(Well, I guess it's not surprising that the loader got activeFocus, I think that was just the side effect of the entry's focus being false. What was surprising was that the entry object's focus property was being set to false in the first place. Wasn't sure why.)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1489
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1490. By Michael Terry
-
Restore behavior animating LoginList height
- 1491. By Michael Terry
-
Merge from tutorial-
new-screens
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1490
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1490
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1491
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
"""
Connections {
target: ApplicationManager
}
}
}
}
"""
All those greeter.
A focus request, as it name says, it's just a request. It will be processed by the window manager part of unity8 (eg PhoneStage or TabletStage) which will finally change the app that is focused, causing ApplicationMana
The same goes for the onApplicationAdded case.
Can't we have greeter.
Daniel d'Andrada (dandrader) wrote : | # |
>
> A focus request, as it name says, it's just a request. It will be processed by
> the window manager part of unity8 (eg PhoneStage or TabletStage) which will
> finally change the app that is focused, causing
> ApplicationMana
> request you will be calling greeter.
> you're assuming that a request will always be successful and are telling
> greeter that a given app now has focus before it actually happens.
Unless you wanna do something also when ApplicationMana
Daniel d'Andrada (dandrader) wrote : | # |
In Shell.qml:
"""
"""
This line is way too long.
- 1492. By Michael Terry
-
Shorten a couple long lines
- 1493. By Michael Terry
-
Consolidate app-switching logic
- 1494. By Michael Terry
-
Add test and comment for the relied-upon behavior of emitting a focus id changed signal even when we focus the same app
Daniel d'Andrada (dandrader) wrote : | # |
run "make tryShell"
Start a left edge drag, up to 1/3 of the display width (to that you have the greeter move), then release it.
Expected outcome:
Greeter slides back into place, in an animated fashion
Actual outcome:
Greeter jumps immediately back into place. There's not animation at all.
Michael Terry (mterry) wrote : | # |
> This line is way too long.
Fixed, plus another similar line.
> All those greeter.
Well, you are right. Back when I originally added "emergency dialer" support, I tested and the focusChanged signal was not emitted in all cases (like if it was already focused) and not everything went through the requestFocus path (some things were just focused without a request, by the shell). So I had redundant bits in each signal handler.
But not 100% redundant. Each behaved slightly different. In this branch, I've consolidated all that down to one function call per signal handler, and I felt pretty good about that. But you're right, the whole thing looks silly from someone that isn't in the weeds of it.
So stepping back, I retested. And nowadays, we seem to always properly requestFocus, not to mention that a focusChanged signal is emitted in all cases now. So all those signal handlers can consolidate down to one, when focusChanged is received.
So I've done that. Thanks for the kick in the pants! :) I've also added a test to cover the specific case of a re-focus on an already focused app from the greeter. We didn't test that precise case before, but since that one depends on special behavior of re-emitting a changed signal even when nothing changed, a test & comment seemed prudent.
Daniel d'Andrada (dandrader) wrote : | # |
> run "make tryShell"
>
> Start a left edge drag, up to 1/3 of the display width (to that you have the
> greeter move), then release it.
>
> Expected outcome:
> Greeter slides back into place, in an animated fashion
>
> Actual outcome:
> Greeter jumps immediately back into place. There's not animation at all.
Wonder if we could have a qml regression test for that, that checks that the greeter.x is changed to a couple of intermediate values before finally reaching zero.
- 1495. By Michael Terry
-
Animate letting go of the coverPage
Daniel d'Andrada (dandrader) wrote : | # |
1- make tryShellWithPin
2- swipe away the cover page
3- press "Show greeter" button
You get those warnings:
"""
qml/Components/
qml/Components/
"""
Michael Terry (mterry) wrote : | # |
The animation when letting go of the coverPage while making a left drag is fixed too.
Michael Terry (mterry) wrote : | # |
Oh, let me see about a test...
Daniel d'Andrada (dandrader) wrote : | # |
> 1- make tryShellWithPin
> 2- swipe away the cover page
> 3- press "Show greeter" button
>
> You get those warnings:
> """
> qml/Components/
> null
> qml/Components/
> 'horizontalCenter' of null
> """
It also happens in trunk, but it would be good to have it fixed here
Daniel d'Andrada (dandrader) wrote : | # |
1- run make tryShell
A bunch of new warnings show up:
"""
qml/Greeter/
qml/Greeter/
qml/Greeter/
qml/Greeter/
qml/Greeter/
qml/Greeter/
"""
then when you swipe away the greeter and press the "show greeter" button, you get all the warnings above plus this one:
"""
qml/Components/
"""
- 1496. By Michael Terry
-
Stop some warnings from being printed
Michael Terry (mterry) wrote : | # |
OK, warnings fixed.
- 1497. By Michael Terry
-
Add tiny test to confirm that we don't just jump to launcherOffset = 0
Michael Terry (mterry) wrote : | # |
And a test added for the animation of the coverPage being let go.
Albert Astals Cid (aacid) wrote : | # |
Text conflict in qml/Greeter/
1 conflicts encountered.
Daniel d'Andrada (dandrader) wrote : | # |
This launcherOffsetProxy thing in Greeter.qml is rather messy. Have you tried using a SmoothedAnimation, like that? http://
Seems to work fine.
Daniel d'Andrada (dandrader) wrote : | # |
On 10/02/15 18:16, Michael Terry wrote:
>> This looks like a desperate hack. Setting "focus: true" should suffice
> Yeah... You got me. It didn't suffice, and I couldn't tell why focus was being stolen. All I could tell was that activeFocus was moving to the greeter's loader itself. But not in a situation that seemingly would require it to move (e.g. it wasn't being reloaded nor was a widget disabled as far as I could tell).
The latest unity8 now has a tool to help you find out who's stealing
your focus. See tests/uqmlscene
You can use it to debug focus with "make tryFoo" by changing "#define
UQMLSCENE_
If you can only reproduce the issue on the device then you will have to
use ActiveFocusLogger in src/main.cpp. Just see how it's done in
tests/uqmlscene
This gotta be clarified and fixed.
Daniel d'Andrada (dandrader) wrote : | # |
> This launcherOffsetProxy thing in Greeter.qml is rather messy. Have you tried
> using a SmoothedAnimation, like that? http://
>
> Seems to work fine.
This seems perfect: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1494
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1498. By Michael Terry
-
Merge from trunk
Michael Terry (mterry) wrote : | # |
> This seems perfect: http://
As I said on IRC:
"""
We don't want [the Behavior animation] when the user is driving the abrupt changes
Then we want to be immediately responsive
It makes the phone look laggy if we trail their finger
[Your latest patch is] still a noticable delay when jittering the mouse/finger. It's better for sure. But still noticable.
It feels clear to me that rather than speeding up a behavior so fast as to not be noticable in that case, we just want to disable it...
I understand it's more code, but it feels more correct
"""
So I'm still pushing back on this request. I'm happy to bow to more pressure, if we want a third opinion.
I am looking into the focus issue.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1497
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1499. By Michael Terry
-
Add unused tst_ShellWithPa
ssphrase. qml file, to allow tryShellWithPas sphrase - 1500. By Michael Terry
-
Remove (unneeded?) focus hack
Michael Terry (mterry) wrote : | # |
Uh, so I guess the focus issue just doesn't exist anymore? Removing that code seems to have no effect. I can't reproduce the bug I was trying to fix (which occurred when entering a bad passphrase).
So I've taken the hack out entirely.
I've also added a bare-bones tst_ShellWithPa
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1498
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1500
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
> I've also added a bare-bones tst_ShellWithPa
> passphrase shell, which I just realized is hard to do without the benefit of
> "./run.sh -k" now.
Now that the lightdm mock libs are unified we no longer need separate test files for different lightdm mock modes.
We can have them all under tst_Shell, like this:
http://
Daniel d'Andrada (dandrader) wrote : | # |
I'm getting some test failures:
FAIL! : qmltestrunner:
Actual ():
Expected (): visible
Loc: [/home/
FAIL! : qmltestrunner:
Actual ():
Expected (): visible
Loc: [/home/
FAIL! : qmltestrunner:
Actual ():
Expected (): visible
Loc: [/home/
FAIL! : qmltestrunner:
Actual (): -64
Expected (): 0
Loc: [/home/
Daniel d'Andrada (dandrader) wrote : | # |
In Greeter.qml:
"""
loader.
"""
Please name it like a function, instead of like a boolean property.
I would suggest:
onAuthenticatio
eg:
"""
function authenticated(
if (success) {
} else {
}
}
"""
Would be:
"""
function onAuthenticatio
function onAuthenticatio
"""
Daniel d'Andrada (dandrader) wrote : | # |
You have this both in Greeter.qml and CoverPage.qml:
"""
* Copyright (C) 2013 Canonical, Ltd.
"""
Please update.
Daniel d'Andrada (dandrader) wrote : | # |
In Greeter.qml
"""
d.startUnlock(
"""
This forces the reader to jump to the function definition to figure out what the heck that booelan means, hurting code readability.
Please at least add an inline comment saying what this boolean is about, like:
"""
d.startUnlock(false /* toTheRight */);
"""
Daniel d'Andrada (dandrader) wrote : | # |
Both in NarrowView.qml and in WideView.qml\
"""
QtObject {
id: d
}
"""
The intention is good, but right now it's just dead code.
Daniel d'Andrada (dandrader) wrote : | # |
In CoverPager.qml:
"""
property bool ready: greeterBackgrou
"""
This line is too long.
Daniel d'Andrada (dandrader) wrote : | # |
In tst_WideView.qml
"""
Binding {
target: LightDM.Users
property: "mockMode"
value: "full"
}
"""
Shouldn't you also set LightDM.
Daniel d'Andrada (dandrader) wrote : | # |
> In tst_WideView.qml
>
> """
> Binding {
> target: LightDM.Users
> property: "mockMode"
> value: "full"
> }
> """
>
> Shouldn't you also set LightDM.
> seing it being done anywhere in this file.
Hmm, maybe it doesn't matter as LightDM.Greeter doesn't seem to be used by WideView anyway.
Daniel d'Andrada (dandrader) wrote : | # |
In tst_Greeter.qml
"""
* Copyright 2013 Canonical Ltd.
"""
Daniel d'Andrada (dandrader) wrote : | # |
tst_Greeter.
Suggestion:
http://
Daniel d'Andrada (dandrader) wrote : | # |
In tests/plugins/
"""
* Copyright (C) 2013 Canonical, Ltd.
"""
Daniel d'Andrada (dandrader) wrote : | # |
I think that would be all. Tested manually on N4 and N10 and didn't spot any obvious regressions.
- 1501. By Michael Terry
-
Merge from trunk
- 1502. By Michael Terry
-
Fix some review nits
- 1503. By Michael Terry
-
More nits
- 1504. By Michael Terry
-
More nits
- 1505. By Michael Terry
-
Fix shell tests, by reverting a small cleanup I tried to make
Michael Terry (mterry) wrote : | # |
> """
> QtObject {
> id: d
> }
> """
>
> The intention is good, but right now it's just dead code.
I tend to use QtObject 'd' blocks like that as barriers between the "public" code and the implementation code. So they are useful as a visual separator, even if empty. But alright, removed.
> In tst_Greeter.qml [and usersmodel.cpp, Greeter.qml, and CoverPage.qml]
>
> """
> * Copyright 2013 Canonical Ltd.
> """
Fixed.
> """
> property bool ready: greeterBackgrou
> """
>
> This line is too long.
Fixed by removing it. It's not actually used anywhere now with this refactor.
> tst_Greeter.
Fair enough, used your version. Thanks!
> Now that the lightdm mock libs are unified we no longer need separate test files for different lightdm mock modes.
Fair enough, used your version. And added a line for "single-pin", though I didn't take the further step of consolidating tst_Shell.qml and tst_ShellWithPi
> Shouldn't you also set LightDM.
> seing it being done anywhere in this file.
>
> Hmm, maybe it doesn't matter as LightDM.Greeter doesn't seem to be used by WideView anyway.
That's why I didn't have it. The views should never need the LightDM.Greeter object, they should get everything from the Greeter.qml class.
> Please at least add an inline comment saying what this boolean is about, like:
>
> """
> d.startUnlock(false /* toTheRight */);
> """
Done.
> """
> loader.
> """
>
> Please name it like a function, instead of like a boolean property.
Fair, especially since LightDM.Greeter is already a property that exists in and around this code.
I've changed it to notifyAuthentic
> I'm getting some test failures:
> [snip]
Fixed. I got overzealous in my consolidation of app-switching code in r1493.
=================
OK, that should be all your comments addressed.
Michael Terry (mterry) wrote : | # |
"Fair, especially since LightDM.
is what I meant to say.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1505
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1505
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Have to remove this line from tests/qmltests/
"""
add_qml_test(. ShellWithPassph
"""
- 1506. By Michael Terry
-
Whoops, remove reference to ShellWithPassphrase
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1506
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
Yup, thanks. :P Fixed
- 1507. By Michael Terry
-
Don't enable backspace pin button when not supposed to
Daniel d'Andrada (dandrader) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes.
* Did CI run pass? If not, please explain why.
Unrelated, known, failures due to the move to qt 5.4 (I think)
* Did you make sure that the branch does not contain spurious tags?
Yes
- 1508. By Michael Terry
-
Simplify behavior launcher/
greeter/ dash when doing a long-left-drag; now we never show dash while locked
Michael Zanetti (mzanetti) wrote : | # |
Approving changed behavior in the last commit. Works fine in my opinion now. Also had a discussion with design about it and we're good.
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes.
* Did CI run pass? If not, please explain why.
Unrelated, known, failures due to the move to qt 5.4 (I think)
* Did you make sure that the branch does not contain spurious tags?
Yes
Vesa Rautiainen (vesar) wrote : | # |
> Approving changed behavior in the last commit. Works fine in my opinion now.
> Also had a discussion with design about it and we're good.
>
>
> * Did you perform an exploratory manual test run of the code change and any
> related functionality?
> Yes.
>
> * Did CI run pass? If not, please explain why.
> Unrelated, known, failures due to the move to qt 5.4 (I think)
>
> * Did you make sure that the branch does not contain spurious tags?
> Yes
Just to confirm here that Michael and I we had a chat about lockscreen blocking the long left edge swipe behaviour and that's all fine for design.
Preview Diff
1 | === modified file 'cmake/modules/QmlTest.cmake' |
2 | --- cmake/modules/QmlTest.cmake 2014-12-11 15:48:35 +0000 |
3 | +++ cmake/modules/QmlTest.cmake 2015-02-23 15:44:00 +0000 |
4 | @@ -28,7 +28,7 @@ |
5 | |
6 | set(qmlscene_exe ${CMAKE_BINARY_DIR}/tests/uqmlscene/uqmlscene) |
7 | |
8 | -set(test_env UNITY_TESTING=1) |
9 | +set(test_env LC_ALL=C UNITY_TESTING=1) |
10 | |
11 | macro(add_manual_qml_test SUBPATH COMPONENT_NAME) |
12 | set(options NO_ADD_TEST NO_TARGETS) |
13 | |
14 | === modified file 'qml/Components/DelayedLockscreen.qml' |
15 | --- qml/Components/DelayedLockscreen.qml 2014-09-16 00:00:06 +0000 |
16 | +++ qml/Components/DelayedLockscreen.qml 2015-02-23 15:44:00 +0000 |
17 | @@ -20,11 +20,13 @@ |
18 | |
19 | Item { |
20 | id: root |
21 | - anchors.fill: parent |
22 | |
23 | property int delayMinutes |
24 | property bool alphaNumeric |
25 | |
26 | + signal entered(string passphrase) // unused |
27 | + signal cancel() // unused |
28 | + |
29 | function clear(playAnimation) {} |
30 | |
31 | Column { |
32 | |
33 | === modified file 'qml/Components/PassphraseLockscreen.qml' |
34 | --- qml/Components/PassphraseLockscreen.qml 2014-10-23 13:23:07 +0000 |
35 | +++ qml/Components/PassphraseLockscreen.qml 2015-02-23 15:44:00 +0000 |
36 | @@ -1,5 +1,5 @@ |
37 | /* |
38 | - * Copyright (C) 2013 Canonical, Ltd. |
39 | + * Copyright (C) 2013,2014,2015 Canonical, Ltd. |
40 | * |
41 | * This program is free software; you can redistribute it and/or modify |
42 | * it under the terms of the GNU General Public License as published by |
43 | @@ -20,8 +20,7 @@ |
44 | |
45 | Item { |
46 | id: root |
47 | - anchors.top: parent.top |
48 | - anchors.topMargin: units.gu(4) |
49 | + y: units.gu(4) |
50 | height: shakeContainer.height |
51 | |
52 | property string infoText |
53 | |
54 | === modified file 'qml/Components/PinLockscreen.qml' |
55 | --- qml/Components/PinLockscreen.qml 2014-10-23 13:23:07 +0000 |
56 | +++ qml/Components/PinLockscreen.qml 2015-02-23 15:44:00 +0000 |
57 | @@ -21,9 +21,7 @@ |
58 | |
59 | Column { |
60 | id: root |
61 | - anchors.top: parent.top |
62 | - anchors.topMargin: units.gu(4) |
63 | - anchors.horizontalCenter: parent.horizontalCenter |
64 | + y: units.gu(4) |
65 | spacing: units.gu(2) |
66 | |
67 | property string infoText |
68 | @@ -123,6 +121,7 @@ |
69 | objectName: "backspaceIcon" |
70 | anchors { right: parent.right; top: parent.top; bottom: parent.bottom } |
71 | width: height |
72 | + enabled: root.entryEnabled |
73 | |
74 | Icon { |
75 | anchors.fill: parent |
76 | @@ -130,7 +129,7 @@ |
77 | color: "#f3f3e7" |
78 | } |
79 | |
80 | - opacity: (pinentryField.text.length && !pinentryField.incorrectOverride) > 0 ? 1 : 0 |
81 | + opacity: (pinentryField.text.length > 0 && !pinentryField.incorrectOverride) ? 1 : 0 |
82 | |
83 | Behavior on opacity { |
84 | UbuntuNumberAnimation {} |
85 | |
86 | === renamed file 'qml/Greeter/GreeterContent.qml' => 'qml/Greeter/CoverPage.qml' |
87 | --- qml/Greeter/GreeterContent.qml 2015-02-11 16:04:13 +0000 |
88 | +++ qml/Greeter/CoverPage.qml 2015-02-23 15:44:00 +0000 |
89 | @@ -1,5 +1,5 @@ |
90 | /* |
91 | - * Copyright (C) 2013 Canonical, Ltd. |
92 | + * Copyright (C) 2013,2014,2015 Canonical, Ltd. |
93 | * |
94 | * This program is free software; you can redistribute it and/or modify |
95 | * it under the terms of the GNU General Public License as published by |
96 | @@ -14,34 +14,48 @@ |
97 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
98 | */ |
99 | |
100 | -import QtQuick 2.0 |
101 | -import AccountsService 0.1 |
102 | -import Ubuntu.Components 0.1 |
103 | -import LightDM 0.1 as LightDM |
104 | +import QtQuick 2.3 |
105 | +import Ubuntu.Components 1.1 |
106 | +import Ubuntu.Gestures 0.1 |
107 | import "../Components" |
108 | |
109 | -Item { |
110 | +Showable { |
111 | id: root |
112 | - anchors.fill: parent |
113 | - |
114 | - property var inputMethod |
115 | - |
116 | - property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error |
117 | - |
118 | - signal selected(int uid) |
119 | - signal unlocked(int uid) |
120 | - |
121 | - function tryToUnlock() { |
122 | - if (loginLoader.item) { |
123 | - loginLoader.item.tryToUnlock() |
124 | - } |
125 | - } |
126 | - |
127 | - function reset() { |
128 | - if (loginLoader.item) { |
129 | - loginLoader.item.reset() |
130 | - } |
131 | - } |
132 | + |
133 | + property real dragHandleLeftMargin |
134 | + property real launcherOffset |
135 | + property alias background: greeterBackground.source |
136 | + property real backgroundTopMargin |
137 | + property var infographicModel |
138 | + property bool draggable: true |
139 | + |
140 | + property alias infographics: infographics |
141 | + |
142 | + readonly property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1) |
143 | + |
144 | + signal tease() |
145 | + |
146 | + function hideRight() { |
147 | + d.forceRightOnNextHideAnimation = true; |
148 | + hide(); |
149 | + } |
150 | + |
151 | + QtObject { |
152 | + id: d |
153 | + property bool forceRightOnNextHideAnimation: false |
154 | + } |
155 | + |
156 | + prepareToHide: function () { |
157 | + hideTranslation.from = root.x + translation.x |
158 | + hideTranslation.to = root.x > 0 || d.forceRightOnNextHideAnimation ? root.width : -root.width; |
159 | + d.forceRightOnNextHideAnimation = false; |
160 | + } |
161 | + |
162 | + // We don't directly bind "x" because that's owned by the DragHandle. So |
163 | + // instead, we can get a little extra horizontal push by using transforms. |
164 | + transform: Translate { id: translation; x: root.draggable ? launcherOffset : 0 } |
165 | + |
166 | + MouseArea { anchors.fill: parent; } |
167 | |
168 | Rectangle { |
169 | // In case background fails to load |
170 | @@ -51,17 +65,16 @@ |
171 | } |
172 | |
173 | CrossFadeImage { |
174 | - id: background |
175 | + id: greeterBackground |
176 | objectName: "greeterBackground" |
177 | anchors { |
178 | fill: parent |
179 | - topMargin: backgroundTopMargin |
180 | + topMargin: root.backgroundTopMargin |
181 | } |
182 | fillMode: Image.PreserveAspectCrop |
183 | // Limit how much memory we'll reserve for this image |
184 | sourceSize.height: height |
185 | sourceSize.width: width |
186 | - source: greeter.background |
187 | } |
188 | |
189 | Rectangle { |
190 | @@ -70,94 +83,134 @@ |
191 | opacity: 0.4 |
192 | } |
193 | |
194 | - Loader { |
195 | - id: loginLoader |
196 | - objectName: "loginLoader" |
197 | - anchors { |
198 | - left: parent.left |
199 | - leftMargin: Math.min(parent.width * 0.16, units.gu(20)) |
200 | - top: parent.top |
201 | - } |
202 | - width: units.gu(29) |
203 | - height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height |
204 | - : parent.height |
205 | - Behavior on height { UbuntuNumberAnimation {} } |
206 | - |
207 | - // TODO: Once we have a system API for determining which mode we are |
208 | - // in, tablet/phone/desktop, that should be used instead of narrowMode. |
209 | - source: greeter.narrowMode ? "" : "LoginList.qml" |
210 | - |
211 | - onLoaded: { |
212 | - item.currentIndex = greeterContentLoader.currentIndex; |
213 | - } |
214 | - |
215 | - Binding { |
216 | - target: loginLoader.item |
217 | - property: "model" |
218 | - value: greeterContentLoader.model |
219 | - } |
220 | - |
221 | - Connections { |
222 | - target: loginLoader.item |
223 | - |
224 | - onSelected: { |
225 | - root.selected(uid); |
226 | - } |
227 | - |
228 | - onUnlocked: { |
229 | - root.unlocked(uid); |
230 | - } |
231 | - |
232 | - onCurrentIndexChanged: { |
233 | - if (greeterContentLoader.currentIndex !== loginLoader.item.currentIndex) { |
234 | - greeterContentLoader.currentIndex = loginLoader.item.currentIndex; |
235 | - } |
236 | - } |
237 | - } |
238 | - } |
239 | - |
240 | Infographics { |
241 | id: infographics |
242 | objectName: "infographics" |
243 | - height: narrowMode ? parent.height : 0.75 * parent.height |
244 | - model: greeterContentLoader.infographicModel |
245 | + height: parent.height |
246 | + model: root.infographicModel |
247 | clip: true // clip large data bubbles |
248 | |
249 | - property string selectedUser |
250 | - property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : "" |
251 | - onInfographicUserChanged: greeterContentLoader.infographicModel.username = infographicUser |
252 | - |
253 | - Component.onCompleted: { |
254 | - selectedUser = greeterContentLoader.model.data(greeterContentLoader.currentIndex, LightDM.UserRoles.NameRole) |
255 | - greeterContentLoader.infographicModel.username = infographicUser |
256 | - greeterContentLoader.infographicModel.readyForDataChange() |
257 | - } |
258 | - |
259 | - Connections { |
260 | - target: root |
261 | - onSelected: infographics.selectedUser = greeterContentLoader.model.data(uid, LightDM.UserRoles.NameRole) |
262 | - } |
263 | - |
264 | - Connections { |
265 | - target: i18n |
266 | - onLanguageChanged: greeterContentLoader.infographicModel.readyForDataChange() |
267 | - } |
268 | - |
269 | anchors { |
270 | verticalCenter: parent.verticalCenter |
271 | - left: narrowMode ? root.left : loginLoader.right |
272 | - right: root.right |
273 | - } |
274 | - } |
275 | - |
276 | - Clock { |
277 | - id: clock |
278 | - visible: narrowMode |
279 | - |
280 | - anchors { |
281 | - top: parent.top |
282 | - topMargin: units.gu(2) |
283 | - horizontalCenter: parent.horizontalCenter |
284 | + left: parent.left |
285 | + right: parent.right |
286 | + } |
287 | + } |
288 | + |
289 | + Label { |
290 | + id: swipeHint |
291 | + property real baseOpacity: 0.5 |
292 | + opacity: 0.0 |
293 | + anchors.horizontalCenter: parent.horizontalCenter |
294 | + anchors.bottom: parent.bottom |
295 | + anchors.bottomMargin: units.gu(5) |
296 | + text: "《 " + i18n.tr("Unlock") + " 》" |
297 | + color: "white" |
298 | + font.weight: Font.Light |
299 | + |
300 | + SequentialAnimation on opacity { |
301 | + id: showLabelAnimation |
302 | + running: false |
303 | + loops: 2 |
304 | + |
305 | + StandardAnimation { |
306 | + from: 0.0 |
307 | + to: swipeHint.baseOpacity |
308 | + duration: UbuntuAnimation.SleepyDuration |
309 | + } |
310 | + PauseAnimation { duration: UbuntuAnimation.BriskDuration } |
311 | + StandardAnimation { |
312 | + from: swipeHint.baseOpacity |
313 | + to: 0.0 |
314 | + duration: UbuntuAnimation.SleepyDuration |
315 | + } |
316 | + } |
317 | + } |
318 | + |
319 | + DragHandle { |
320 | + id: dragHandle |
321 | + anchors.fill: parent |
322 | + anchors.leftMargin: root.dragHandleLeftMargin |
323 | + enabled: root.draggable |
324 | + direction: Direction.Horizontal |
325 | + |
326 | + onDraggingChanged: { |
327 | + if (dragging) { |
328 | + root.tease(); |
329 | + showLabelAnimation.start(); |
330 | + } |
331 | + } |
332 | + } |
333 | + |
334 | + // right side shadow |
335 | + Image { |
336 | + anchors.left: parent.right |
337 | + anchors.top: parent.top |
338 | + anchors.bottom: parent.bottom |
339 | + fillMode: Image.Tile |
340 | + source: "../graphics/dropshadow_right.png" |
341 | + } |
342 | + |
343 | + // left side shadow |
344 | + Image { |
345 | + anchors.right: parent.left |
346 | + anchors.top: parent.top |
347 | + anchors.bottom: parent.bottom |
348 | + fillMode: Image.Tile |
349 | + source: "../graphics/dropshadow_left.png" |
350 | + } |
351 | + |
352 | + Binding { |
353 | + id: positionLock |
354 | + |
355 | + property bool enabled: false |
356 | + onEnabledChanged: { |
357 | + if (enabled === __enabled) { |
358 | + return; |
359 | + } |
360 | + |
361 | + if (enabled) { |
362 | + if (root.x > 0) { |
363 | + value = Qt.binding(function() { return root.width; }) |
364 | + } else { |
365 | + value = Qt.binding(function() { return -root.width; }) |
366 | + } |
367 | + } |
368 | + |
369 | + __enabled = enabled; |
370 | + } |
371 | + |
372 | + property bool __enabled: false |
373 | + |
374 | + target: root |
375 | + when: __enabled |
376 | + property: "x" |
377 | + } |
378 | + |
379 | + hideAnimation: SequentialAnimation { |
380 | + id: hideAnimation |
381 | + objectName: "hideAnimation" |
382 | + property var target // unused, here to silence Showable warning |
383 | + StandardAnimation { |
384 | + id: hideTranslation |
385 | + property: "x" |
386 | + target: root |
387 | + } |
388 | + PropertyAction { target: root; property: "visible"; value: false } |
389 | + PropertyAction { target: positionLock; property: "enabled"; value: true } |
390 | + } |
391 | + |
392 | + showAnimation: SequentialAnimation { |
393 | + id: showAnimation |
394 | + objectName: "showAnimation" |
395 | + property var target // unused, here to silence Showable warning |
396 | + PropertyAction { target: root; property: "visible"; value: true } |
397 | + PropertyAction { target: positionLock; property: "enabled"; value: false } |
398 | + StandardAnimation { |
399 | + property: "x" |
400 | + target: root |
401 | + to: 0 |
402 | + duration: UbuntuAnimation.FastDuration |
403 | } |
404 | } |
405 | } |
406 | |
407 | === modified file 'qml/Greeter/Greeter.qml' |
408 | --- qml/Greeter/Greeter.qml 2015-01-23 19:47:37 +0000 |
409 | +++ qml/Greeter/Greeter.qml 2015-02-23 15:44:00 +0000 |
410 | @@ -1,5 +1,5 @@ |
411 | /* |
412 | - * Copyright (C) 2013 Canonical, Ltd. |
413 | + * Copyright (C) 2013,2014,2015 Canonical, Ltd. |
414 | * |
415 | * This program is free software; you can redistribute it and/or modify |
416 | * it under the terms of the GNU General Public License as published by |
417 | @@ -14,66 +14,185 @@ |
418 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
419 | */ |
420 | |
421 | -import QtQuick 2.0 |
422 | -import Ubuntu.Components 0.1 |
423 | -import Ubuntu.Gestures 0.1 |
424 | +import QtQuick 2.3 |
425 | +import AccountsService 0.1 |
426 | import LightDM 0.1 as LightDM |
427 | +import Ubuntu.Components 1.1 |
428 | +import Ubuntu.SystemImage 0.1 |
429 | +import Unity.Connectivity 0.1 |
430 | +import Unity.Launcher 0.1 |
431 | import "../Components" |
432 | |
433 | Showable { |
434 | - id: greeter |
435 | - enabled: shown |
436 | - created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready |
437 | + id: root |
438 | + created: loader.status == Loader.Ready |
439 | |
440 | property real dragHandleLeftMargin: 0 |
441 | - property alias dragging: dragHandle.dragging |
442 | |
443 | property url background |
444 | |
445 | - // so that it can be replaced in tests with a mock object |
446 | - property var inputMethod: Qt.inputMethod |
447 | - |
448 | - prepareToHide: function () { |
449 | - hideTranslation.to = greeter.x > 0 || d.forceRightOnNextHideAnimation ? greeter.width : -greeter.width; |
450 | - d.forceRightOnNextHideAnimation = false; |
451 | + // How far to offset the top greeter layer during a launcher left-drag |
452 | + property real launcherOffset |
453 | + |
454 | + readonly property bool active: shown || hasLockedApp |
455 | + readonly property bool fullyShown: loader.item ? loader.item.fullyShown : false |
456 | + |
457 | + // True when the greeter is waiting for PAM or other setup process |
458 | + readonly property alias waiting: d.waiting |
459 | + |
460 | + property string lockedApp: "" |
461 | + readonly property bool hasLockedApp: lockedApp !== "" |
462 | + |
463 | + property bool forcedUnlock |
464 | + readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock |
465 | + |
466 | + property bool tabletMode |
467 | + property url viewSource // only used for testing |
468 | + |
469 | + property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future |
470 | + property int failedLoginsDelayAttempts: 7 // number of failed logins |
471 | + property int failedLoginsDelayMinutes: 5 // minutes of forced waiting |
472 | + |
473 | + signal tease() |
474 | + signal sessionStarted() |
475 | + signal emergencyCall() |
476 | + |
477 | + function forceShow() { |
478 | + showNow(); |
479 | + loader.item.reset(); |
480 | + } |
481 | + |
482 | + function notifyAppFocused(appId) { |
483 | + if (!active) { |
484 | + return; |
485 | + } |
486 | + |
487 | + if (hasLockedApp) { |
488 | + if (appId === lockedApp) { |
489 | + hide(); // show locked app |
490 | + } else { |
491 | + show(); |
492 | + d.startUnlock(false /* toTheRight */); |
493 | + } |
494 | + } else if (appId !== "unity8-dash") { // dash isn't started by user |
495 | + d.startUnlock(false /* toTheRight */); |
496 | + } |
497 | + } |
498 | + |
499 | + function notifyAboutToFocusApp(appId) { |
500 | + if (!active) { |
501 | + return; |
502 | + } |
503 | + |
504 | + // A hint that we're about to focus an app. This way we can look |
505 | + // a little more responsive, rather than waiting for the above |
506 | + // notifyAppFocused call. We also need this in case we have a locked |
507 | + // app, in order to show lockscreen instead of new app. |
508 | + d.startUnlock(false /* toTheRight */); |
509 | + } |
510 | + |
511 | + // This is a just a glorified notifyAboutToFocusApp(), but it does one |
512 | + // other thing: it hides any cover pages to the RIGHT, because the user |
513 | + // just came from a launcher drag starting on the left. |
514 | + // It also returns a boolean value, indicating whether there was a visual |
515 | + // change or not (the shell only wants to hide the launcher if there was |
516 | + // a change). |
517 | + function notifyShowingDashFromDrag() { |
518 | + if (!active) { |
519 | + return false; |
520 | + } |
521 | + |
522 | + return d.startUnlock(true /* toTheRight */); |
523 | } |
524 | |
525 | QtObject { |
526 | id: d |
527 | - property bool forceRightOnNextHideAnimation: false |
528 | - } |
529 | - |
530 | - property bool loadContent: required |
531 | - |
532 | - // 1 when fully shown and 0 when fully hidden |
533 | - property real showProgress: visible ? MathUtils.clamp((width - Math.abs(x)) / width, 0, 1) : 0 |
534 | - |
535 | - property alias model: greeterContentLoader.model |
536 | - property bool locked: true |
537 | - |
538 | - readonly property bool narrowMode: !multiUser && height > width |
539 | - readonly property bool multiUser: LightDM.Users.count > 1 |
540 | - |
541 | - readonly property int currentIndex: greeterContentLoader.currentIndex |
542 | - |
543 | - signal selected(int uid) |
544 | - signal unlocked(int uid) |
545 | - signal tapped() |
546 | - |
547 | - function hideRight() { |
548 | - d.forceRightOnNextHideAnimation = true; |
549 | - hide(); |
550 | - } |
551 | - |
552 | - function tryToUnlock() { |
553 | - if (created) { |
554 | - greeterContentLoader.item.tryToUnlock() |
555 | - } |
556 | - } |
557 | - |
558 | - function reset() { |
559 | - if (created) { |
560 | - greeterContentLoader.item.reset() |
561 | + |
562 | + readonly property bool multiUser: LightDM.Users.count > 1 |
563 | + property int currentIndex |
564 | + property int delayMinutes |
565 | + property bool waiting |
566 | + |
567 | + // We want 'launcherOffset' to animate down to zero. But not to animate |
568 | + // while being dragged. So ideally we change this only when the user |
569 | + // lets go and launcherOffset drops to zero. But we need to wait for |
570 | + // the behavior to be enabled first. So we cache the last known good |
571 | + // launcherOffset value to cover us during that brief gap between |
572 | + // release and the behavior turning on. |
573 | + property real lastKnownPositiveOffset // set in a launcherOffsetChanged below |
574 | + property real launcherOffsetProxy: (shown && !launcherOffsetProxyBehavior.enabled) ? lastKnownPositiveOffset : 0 |
575 | + Behavior on launcherOffsetProxy { |
576 | + id: launcherOffsetProxyBehavior |
577 | + enabled: launcherOffset === 0 |
578 | + UbuntuNumberAnimation {} |
579 | + } |
580 | + |
581 | + function selectUser(uid, reset) { |
582 | + d.waiting = true; |
583 | + if (reset) { |
584 | + loader.item.reset(); |
585 | + } |
586 | + currentIndex = uid; |
587 | + var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole); |
588 | + AccountsService.user = user; |
589 | + LauncherModel.setUser(user); |
590 | + LightDM.Greeter.authenticate(user); // always resets auth state |
591 | + } |
592 | + |
593 | + function login() { |
594 | + enabled = false; |
595 | + if (LightDM.Greeter.startSessionSync()) { |
596 | + sessionStarted(); |
597 | + loader.item.notifyAuthenticationSucceeded(); |
598 | + } else { |
599 | + loader.item.notifyAuthenticationFailed(); |
600 | + } |
601 | + enabled = true; |
602 | + } |
603 | + |
604 | + function startUnlock(toTheRight) { |
605 | + if (loader.item) { |
606 | + return loader.item.tryToUnlock(toTheRight); |
607 | + } else { |
608 | + return false; |
609 | + } |
610 | + } |
611 | + } |
612 | + |
613 | + onLauncherOffsetChanged: { |
614 | + if (launcherOffset > 0) { |
615 | + d.lastKnownPositiveOffset = launcherOffset; |
616 | + } |
617 | + } |
618 | + |
619 | + onForcedUnlockChanged: { |
620 | + if (forcedUnlock && shown) { |
621 | + // pretend we were just authenticated |
622 | + loader.item.notifyAuthenticationSucceeded(); |
623 | + } |
624 | + } |
625 | + |
626 | + onRequiredChanged: { |
627 | + if (required) { |
628 | + d.waiting = true; |
629 | + lockedApp = ""; |
630 | + } |
631 | + } |
632 | + |
633 | + Component.onCompleted: { |
634 | + Connectivity.unlockAllModems(); |
635 | + } |
636 | + |
637 | + Timer { |
638 | + id: forcedDelayTimer |
639 | + interval: 1000 * 60 |
640 | + onTriggered: { |
641 | + if (d.delayMinutes > 0) { |
642 | + d.delayMinutes -= 1; |
643 | + if (d.delayMinutes > 0) { |
644 | + start(); // go again |
645 | + } |
646 | + } |
647 | } |
648 | } |
649 | |
650 | @@ -82,152 +201,205 @@ |
651 | MouseArea { anchors.fill: parent } |
652 | |
653 | Loader { |
654 | - id: greeterContentLoader |
655 | - objectName: "greeterContentLoader" |
656 | + id: loader |
657 | + objectName: "loader" |
658 | + |
659 | anchors.fill: parent |
660 | - property var model: LightDM.Users |
661 | - property int currentIndex: 0 |
662 | - property var infographicModel: LightDM.Infographic |
663 | - readonly property int backgroundTopMargin: -greeter.y |
664 | |
665 | - source: loadContent ? "GreeterContent.qml" : "" |
666 | + active: root.required |
667 | + source: root.viewSource.toString() ? root.viewSource : |
668 | + (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml" |
669 | |
670 | onLoaded: { |
671 | - selected(currentIndex); |
672 | + root.lockedApp = ""; |
673 | + root.forceActiveFocus(); |
674 | + d.selectUser(d.currentIndex, true); |
675 | + LightDM.Infographic.readyForDataChange(); |
676 | } |
677 | |
678 | Connections { |
679 | - target: greeterContentLoader.item |
680 | - |
681 | + target: loader.item |
682 | onSelected: { |
683 | - greeter.selected(uid); |
684 | - greeterContentLoader.currentIndex = uid; |
685 | - } |
686 | - onUnlocked: greeter.unlocked(uid); |
687 | - } |
688 | - Binding { |
689 | - target: greeterContentLoader.item |
690 | - property: "inputMethod" |
691 | - value: greeter.inputMethod |
692 | - } |
693 | - } |
694 | - |
695 | - DragHandle { |
696 | - id: dragHandle |
697 | - anchors.fill: parent |
698 | - anchors.leftMargin: greeter.dragHandleLeftMargin |
699 | - enabled: (greeter.narrowMode || !greeter.locked) && greeter.enabled && greeter.shown |
700 | - direction: Direction.Horizontal |
701 | - |
702 | - onTapped: { |
703 | - greeter.tapped(); |
704 | - showLabelAnimation.start(); |
705 | - } |
706 | - |
707 | - onDraggingChanged: { |
708 | - if (dragging) { |
709 | - showLabelAnimation.start(); |
710 | - } |
711 | - } |
712 | - } |
713 | - |
714 | - Label { |
715 | - id: swipeHint |
716 | - property real baseOpacity: 0.5 |
717 | - opacity: 0.0 |
718 | - anchors.horizontalCenter: parent.horizontalCenter |
719 | - anchors.bottom: parent.bottom |
720 | - anchors.bottomMargin: units.gu(5) |
721 | - text: "《 " + i18n.tr("Unlock") + " 》" |
722 | - color: "white" |
723 | - font.weight: Font.Light |
724 | - |
725 | - SequentialAnimation on opacity { |
726 | - id: showLabelAnimation |
727 | - running: false |
728 | - loops: 2 |
729 | - |
730 | - StandardAnimation { |
731 | - from: 0.0 |
732 | - to: swipeHint.baseOpacity |
733 | - duration: UbuntuAnimation.SleepyDuration |
734 | - } |
735 | - PauseAnimation { duration: UbuntuAnimation.BriskDuration } |
736 | - StandardAnimation { |
737 | - from: swipeHint.baseOpacity |
738 | - to: 0.0 |
739 | - duration: UbuntuAnimation.SleepyDuration |
740 | - } |
741 | - } |
742 | - } |
743 | - |
744 | - // right side shadow |
745 | - Image { |
746 | - anchors.left: parent.right |
747 | - anchors.top: parent.top |
748 | - anchors.bottom: parent.bottom |
749 | - fillMode: Image.Tile |
750 | - source: "../graphics/dropshadow_right.png" |
751 | - } |
752 | - |
753 | - // left side shadow |
754 | - Image { |
755 | - anchors.right: parent.left |
756 | - anchors.top: parent.top |
757 | - anchors.bottom: parent.bottom |
758 | - fillMode: Image.Tile |
759 | - source: "../graphics/dropshadow_left.png" |
760 | - } |
761 | - |
762 | - Binding { |
763 | - id: positionLock |
764 | - |
765 | - property bool enabled: false |
766 | - onEnabledChanged: { |
767 | - if (enabled === __enabled) { |
768 | - return; |
769 | - } |
770 | - |
771 | - if (enabled) { |
772 | - if (greeter.x > 0) { |
773 | - value = Qt.binding(function() { return greeter.width; }) |
774 | + d.selectUser(index, true); |
775 | + } |
776 | + onResponded: { |
777 | + if (root.locked) { |
778 | + LightDM.Greeter.respond(response); |
779 | } else { |
780 | - value = Qt.binding(function() { return -greeter.width; }) |
781 | - } |
782 | - } |
783 | - |
784 | - __enabled = enabled; |
785 | - } |
786 | - |
787 | - property bool __enabled: false |
788 | - |
789 | - target: greeter |
790 | - when: __enabled |
791 | - property: "x" |
792 | - } |
793 | - |
794 | - hideAnimation: SequentialAnimation { |
795 | - id: hideAnimation |
796 | - objectName: "hideAnimation" |
797 | - StandardAnimation { |
798 | - id: hideTranslation |
799 | - property: "x" |
800 | - target: greeter |
801 | - } |
802 | - PropertyAction { target: greeter; property: "visible"; value: false } |
803 | - PropertyAction { target: positionLock; property: "enabled"; value: true } |
804 | - } |
805 | - |
806 | - showAnimation: SequentialAnimation { |
807 | - id: showAnimation |
808 | - objectName: "showAnimation" |
809 | - PropertyAction { target: greeter; property: "visible"; value: true } |
810 | - PropertyAction { target: positionLock; property: "enabled"; value: false } |
811 | - StandardAnimation { |
812 | - property: "x" |
813 | - target: greeter |
814 | - to: 0 |
815 | - duration: UbuntuAnimation.FastDuration |
816 | - } |
817 | + if (LightDM.Greeter.active && !LightDM.Greeter.authenticated) { // could happen if forcedUnlock |
818 | + d.login(); |
819 | + } |
820 | + loader.item.hide(); |
821 | + } |
822 | + } |
823 | + onTease: root.tease() |
824 | + onEmergencyCall: root.emergencyCall() |
825 | + onRequiredChanged: { |
826 | + if (!loader.item.required) { |
827 | + root.hide(); |
828 | + } |
829 | + } |
830 | + } |
831 | + |
832 | + Binding { |
833 | + target: loader.item |
834 | + property: "backgroundTopMargin" |
835 | + value: -root.y |
836 | + } |
837 | + |
838 | + Binding { |
839 | + target: loader.item |
840 | + property: "launcherOffset" |
841 | + value: d.launcherOffsetProxy |
842 | + } |
843 | + |
844 | + Binding { |
845 | + target: loader.item |
846 | + property: "dragHandleLeftMargin" |
847 | + value: root.dragHandleLeftMargin |
848 | + } |
849 | + |
850 | + Binding { |
851 | + target: loader.item |
852 | + property: "delayMinutes" |
853 | + value: d.delayMinutes |
854 | + } |
855 | + |
856 | + Binding { |
857 | + target: loader.item |
858 | + property: "background" |
859 | + value: root.background |
860 | + } |
861 | + |
862 | + Binding { |
863 | + target: loader.item |
864 | + property: "locked" |
865 | + value: root.locked |
866 | + } |
867 | + |
868 | + Binding { |
869 | + target: loader.item |
870 | + property: "alphanumeric" |
871 | + value: AccountsService.passwordDisplayHint === AccountsService.Keyboard |
872 | + } |
873 | + |
874 | + Binding { |
875 | + target: loader.item |
876 | + property: "currentIndex" |
877 | + value: d.currentIndex |
878 | + } |
879 | + |
880 | + Binding { |
881 | + target: loader.item |
882 | + property: "userModel" |
883 | + value: LightDM.Users |
884 | + } |
885 | + |
886 | + Binding { |
887 | + target: loader.item |
888 | + property: "infographicModel" |
889 | + value: LightDM.Infographic |
890 | + } |
891 | + } |
892 | + |
893 | + Connections { |
894 | + target: LightDM.Greeter |
895 | + |
896 | + onShowGreeter: root.forceShow() |
897 | + |
898 | + onHideGreeter: { |
899 | + d.login(); |
900 | + loader.item.hide(); |
901 | + } |
902 | + |
903 | + onShowMessage: { |
904 | + if (!LightDM.Greeter.active) { |
905 | + return; // could happen if hideGreeter() comes in before we prompt |
906 | + } |
907 | + |
908 | + // inefficient, but we only rarely deal with messages |
909 | + var html = text.replace(/&/g, "&") |
910 | + .replace(/</g, "<") |
911 | + .replace(/>/g, ">") |
912 | + .replace(/\n/g, "<br>"); |
913 | + if (isError) { |
914 | + html = "<font color=\"#df382c\">" + html + "</font>"; |
915 | + } |
916 | + |
917 | + loader.item.showMessage(html); |
918 | + } |
919 | + |
920 | + onShowPrompt: { |
921 | + d.waiting = false; |
922 | + |
923 | + if (!LightDM.Greeter.active) { |
924 | + return; // could happen if hideGreeter() comes in before we prompt |
925 | + } |
926 | + |
927 | + loader.item.showPrompt(text, isSecret, isDefaultPrompt); |
928 | + } |
929 | + |
930 | + onAuthenticationComplete: { |
931 | + d.waiting = false; |
932 | + |
933 | + if (LightDM.Greeter.authenticated) { |
934 | + AccountsService.failedLogins = 0; |
935 | + d.login(); |
936 | + if (!LightDM.Greeter.promptless) { |
937 | + loader.item.hide(); |
938 | + } |
939 | + } else { |
940 | + if (!LightDM.Greeter.promptless) { |
941 | + AccountsService.failedLogins++; |
942 | + } |
943 | + |
944 | + // Check if we should initiate a factory reset |
945 | + if (maxFailedLogins >= 2) { // require at least a warning |
946 | + if (AccountsService.failedLogins === maxFailedLogins - 1) { |
947 | + loader.item.showLastChance(); |
948 | + } else if (AccountsService.failedLogins >= maxFailedLogins) { |
949 | + SystemImage.factoryReset(); // Ouch! |
950 | + } |
951 | + } |
952 | + |
953 | + // Check if we should initiate a forced login delay |
954 | + if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) { |
955 | + d.delayMinutes = failedLoginsDelayMinutes; |
956 | + forcedDelayTimer.start(); |
957 | + } |
958 | + |
959 | + loader.item.notifyAuthenticationFailed(); |
960 | + if (!LightDM.Greeter.promptless) { |
961 | + d.selectUser(d.currentIndex, false); |
962 | + } |
963 | + } |
964 | + } |
965 | + |
966 | + onRequestAuthenticationUser: { |
967 | + // Find index for requested user, if it exists |
968 | + for (var i = 0; i < LightDM.Users.count; i++) { |
969 | + if (user === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) { |
970 | + d.selectUser(i, true); |
971 | + return; |
972 | + } |
973 | + } |
974 | + } |
975 | + } |
976 | + |
977 | + Binding { |
978 | + target: LightDM.Greeter |
979 | + property: "active" |
980 | + value: root.active |
981 | + } |
982 | + |
983 | + Binding { |
984 | + target: LightDM.Infographic |
985 | + property: "username" |
986 | + value: AccountsService.statsWelcomeScreen ? LightDM.Users.data(d.currentIndex, LightDM.UserRoles.NameRole) : "" |
987 | + } |
988 | + |
989 | + Connections { |
990 | + target: i18n |
991 | + onLanguageChanged: LightDM.Infographic.readyForDataChange() |
992 | } |
993 | } |
994 | |
995 | === modified file 'qml/Greeter/Infographics.qml' |
996 | --- qml/Greeter/Infographics.qml 2015-01-09 10:41:28 +0000 |
997 | +++ qml/Greeter/Infographics.qml 2015-02-23 15:44:00 +0000 |
998 | @@ -38,6 +38,7 @@ |
999 | |
1000 | Connections { |
1001 | target: model |
1002 | + ignoreUnknownSignals: model === undefined |
1003 | |
1004 | onDataAboutToAppear: startHideAnimation() // hide "no data" label |
1005 | onDataAppeared: startShowAnimation() |
1006 | |
1007 | === modified file 'qml/Greeter/LoginList.qml' |
1008 | --- qml/Greeter/LoginList.qml 2014-11-04 12:52:49 +0000 |
1009 | +++ qml/Greeter/LoginList.qml 2015-02-23 15:44:00 +0000 |
1010 | @@ -14,44 +14,76 @@ |
1011 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1012 | */ |
1013 | |
1014 | -import QtQuick 2.0 |
1015 | -import Ubuntu.Components 0.1 |
1016 | -import LightDM 0.1 as LightDM |
1017 | +import QtQuick 2.3 |
1018 | +import Ubuntu.Components 1.1 |
1019 | import "../Components" |
1020 | |
1021 | Item { |
1022 | id: root |
1023 | |
1024 | - property alias userList: userList |
1025 | property alias model: userList.model |
1026 | - property alias currentIndex: userList.currentIndex |
1027 | + property int currentIndex |
1028 | + property bool locked |
1029 | |
1030 | readonly property int numAboveBelow: 4 |
1031 | readonly property int cellHeight: units.gu(5) |
1032 | readonly property int highlightedHeight: units.gu(10) |
1033 | readonly property int moveDuration: 200 |
1034 | + readonly property string currentUser: userList.currentItem.username |
1035 | property bool wasPrompted: false |
1036 | |
1037 | - signal selected(int uid) |
1038 | - signal unlocked(int uid) |
1039 | + signal selected(int index) |
1040 | + signal responded(string response) |
1041 | |
1042 | function tryToUnlock() { |
1043 | - if (LightDM.Greeter.promptless) { |
1044 | - if (LightDM.Greeter.authenticated) { |
1045 | - root.unlocked(userList.currentIndex) |
1046 | + if (wasPrompted) { |
1047 | + passwordInput.forceActiveFocus(); |
1048 | + } else { |
1049 | + if (root.locked) { |
1050 | + root.selected(currentIndex); |
1051 | } else { |
1052 | - root.resetAuthentication() |
1053 | + root.responded(""); |
1054 | } |
1055 | + } |
1056 | + } |
1057 | + |
1058 | + function showMessage(html) { |
1059 | + if (infoLabel.text === "") { |
1060 | + infoLabel.text = html; |
1061 | } else { |
1062 | - passwordInput.forceActiveFocus() |
1063 | + infoLabel.text += "<br>" + html; |
1064 | + } |
1065 | + } |
1066 | + |
1067 | + function showPrompt(text, isSecret, isDefaultPrompt) { |
1068 | + passwordInput.text = ""; |
1069 | + passwordInput.promptText = text; |
1070 | + passwordInput.enabled = true; |
1071 | + passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal; |
1072 | + if (wasPrompted) // stay in text field if second prompt |
1073 | + passwordInput.focus = true; |
1074 | + wasPrompted = true; |
1075 | + } |
1076 | + |
1077 | + function showError() { |
1078 | + wrongPasswordAnimation.start(); |
1079 | + root.resetAuthentication(); |
1080 | + if (wasPrompted) { |
1081 | + passwordInput.focus = true; |
1082 | } |
1083 | } |
1084 | |
1085 | function reset() { |
1086 | - root.resetAuthentication() |
1087 | - } |
1088 | - |
1089 | - Keys.onEscapePressed: root.resetAuthentication() |
1090 | + root.resetAuthentication(); |
1091 | + } |
1092 | + |
1093 | + Keys.onEscapePressed: { |
1094 | + selected(currentIndex); |
1095 | + } |
1096 | + |
1097 | + onCurrentIndexChanged: { |
1098 | + userList.currentIndex = currentIndex; |
1099 | + } |
1100 | |
1101 | Rectangle { |
1102 | id: highlightItem |
1103 | @@ -81,26 +113,24 @@ |
1104 | flickDeceleration: 10000 |
1105 | |
1106 | readonly property bool movingInternally: moveTimer.running || userList.moving |
1107 | - |
1108 | - onCurrentIndexChanged: { |
1109 | - if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) { |
1110 | - root.resetAuthentication(); |
1111 | - } |
1112 | - } |
1113 | - |
1114 | onMovingInternallyChanged: { |
1115 | - // Only emit the selected signal once we stop moving to avoid frequent background changes |
1116 | if (!movingInternally) { |
1117 | - root.selected(userList.currentIndex); |
1118 | + root.selected(currentIndex); |
1119 | } |
1120 | } |
1121 | |
1122 | + onCurrentIndexChanged: { |
1123 | + root.resetAuthentication(); |
1124 | + moveTimer.start(); |
1125 | + } |
1126 | + |
1127 | delegate: Item { |
1128 | width: parent.width |
1129 | height: root.cellHeight |
1130 | |
1131 | readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex) |
1132 | readonly property int belowOffset: root.highlightedHeight - root.cellHeight |
1133 | + readonly property string username: name |
1134 | |
1135 | opacity: { |
1136 | // The goal here is to make names less and less opaque as they |
1137 | @@ -148,11 +178,8 @@ |
1138 | topMargin: parent.belowHighlight ? parent.belowOffset : 0 |
1139 | } |
1140 | height: parent.height |
1141 | - enabled: userList.currentIndex !== index |
1142 | - onClicked: { |
1143 | - moveTimer.start(); |
1144 | - userList.currentIndex = index; |
1145 | - } |
1146 | + enabled: userList.currentIndex !== index && parent.opacity > 0 |
1147 | + onClicked: root.selected(index) |
1148 | |
1149 | Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } } |
1150 | } |
1151 | @@ -204,6 +231,11 @@ |
1152 | width: parent.width - anchors.margins * 2 |
1153 | opacity: userList.movingInternally ? 0 : 1 |
1154 | |
1155 | + property string promptText |
1156 | + placeholderText: root.wasPrompted ? promptText |
1157 | + : (root.locked ? i18n.tr("Retry") |
1158 | + : i18n.tr("Tap to unlock")) |
1159 | + |
1160 | Behavior on opacity { |
1161 | NumberAnimation { duration: 100 } |
1162 | } |
1163 | @@ -213,9 +245,11 @@ |
1164 | return; |
1165 | root.focus = true; // so that it can handle Escape presses for us |
1166 | enabled = false; |
1167 | - LightDM.Greeter.respond(text); |
1168 | - } |
1169 | - Keys.onEscapePressed: root.resetAuthentication() |
1170 | + root.responded(text); |
1171 | + } |
1172 | + Keys.onEscapePressed: { |
1173 | + root.selected(currentIndex); |
1174 | + } |
1175 | |
1176 | Image { |
1177 | anchors { |
1178 | @@ -223,7 +257,7 @@ |
1179 | rightMargin: units.gu(2) |
1180 | verticalCenter: parent.verticalCenter |
1181 | } |
1182 | - visible: LightDM.Greeter.promptless |
1183 | + visible: !root.wasPrompted |
1184 | source: "graphics/icon_arrow.png" |
1185 | } |
1186 | |
1187 | @@ -247,7 +281,7 @@ |
1188 | id: passwordMouseArea |
1189 | objectName: "passwordMouseArea" |
1190 | anchors.fill: passwordInput |
1191 | - enabled: LightDM.Greeter.promptless |
1192 | + enabled: !root.wasPrompted |
1193 | onClicked: root.tryToUnlock() |
1194 | } |
1195 | |
1196 | @@ -256,62 +290,10 @@ |
1197 | return; |
1198 | } |
1199 | infoLabel.text = ""; |
1200 | - passwordInput.placeholderText = ""; |
1201 | + passwordInput.promptText = ""; |
1202 | passwordInput.text = ""; |
1203 | passwordInput.focus = false; |
1204 | passwordInput.enabled = true; |
1205 | root.wasPrompted = false; |
1206 | - LightDM.Greeter.authenticate(userList.model.data(currentIndex, LightDM.UserRoles.NameRole)); |
1207 | - } |
1208 | - |
1209 | - Connections { |
1210 | - target: LightDM.Greeter |
1211 | - |
1212 | - onShowMessage: { |
1213 | - // inefficient, but we only rarely deal with messages |
1214 | - var html = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br>") |
1215 | - if (isError) |
1216 | - html = "<font color=\"#df382c\">" + html + "</font>" |
1217 | - if (infoLabel.text == "") |
1218 | - infoLabel.text = html |
1219 | - else |
1220 | - infoLabel.text = infoLabel.text + "<br>" + html |
1221 | - } |
1222 | - |
1223 | - onShowPrompt: { |
1224 | - passwordInput.text = ""; |
1225 | - passwordInput.placeholderText = text; |
1226 | - passwordInput.enabled = true; |
1227 | - passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal; |
1228 | - if (root.wasPrompted) // stay in text field if second prompt |
1229 | - passwordInput.focus = true; |
1230 | - root.wasPrompted = true; |
1231 | - } |
1232 | - |
1233 | - onAuthenticationComplete: { |
1234 | - if (LightDM.Greeter.promptless) { |
1235 | - passwordInput.placeholderText = LightDM.Greeter.authenticated ? "Tap to unlock" : "Retry"; |
1236 | - return; |
1237 | - } |
1238 | - if (LightDM.Greeter.authenticated) { |
1239 | - root.unlocked(userList.currentIndex); |
1240 | - } else { |
1241 | - wrongPasswordAnimation.start(); |
1242 | - root.resetAuthentication(); |
1243 | - passwordInput.focus = true; |
1244 | - } |
1245 | - passwordInput.text = ""; |
1246 | - } |
1247 | - |
1248 | - onRequestAuthenticationUser: { |
1249 | - // Find index for requested user, if it exists |
1250 | - for (var i = 0; i < userList.model.count; i++) { |
1251 | - if (user == userList.model.data(i, LightDM.UserRoles.NameRole)) { |
1252 | - moveTimer.start(); |
1253 | - userList.currentIndex = i; |
1254 | - return; |
1255 | - } |
1256 | - } |
1257 | - } |
1258 | } |
1259 | } |
1260 | |
1261 | === added file 'qml/Greeter/NarrowView.qml' |
1262 | --- qml/Greeter/NarrowView.qml 1970-01-01 00:00:00 +0000 |
1263 | +++ qml/Greeter/NarrowView.qml 2015-02-23 15:44:00 +0000 |
1264 | @@ -0,0 +1,170 @@ |
1265 | +/* |
1266 | + * Copyright (C) 2015 Canonical, Ltd. |
1267 | + * |
1268 | + * This program is free software; you can redistribute it and/or modify |
1269 | + * it under the terms of the GNU General Public License as published by |
1270 | + * the Free Software Foundation; version 3. |
1271 | + * |
1272 | + * This program is distributed in the hope that it will be useful, |
1273 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1274 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1275 | + * GNU General Public License for more details. |
1276 | + * |
1277 | + * You should have received a copy of the GNU General Public License |
1278 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1279 | + */ |
1280 | + |
1281 | +import QtQuick 2.3 |
1282 | +import Ubuntu.Components 1.1 |
1283 | +import "../Components" |
1284 | + |
1285 | +FocusScope { |
1286 | + id: root |
1287 | + |
1288 | + property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin |
1289 | + property alias launcherOffset: coverPage.launcherOffset |
1290 | + property int currentIndex // unused |
1291 | + property alias delayMinutes: lockscreen.delayMinutes |
1292 | + property alias backgroundTopMargin: coverPage.backgroundTopMargin |
1293 | + property url background |
1294 | + property bool locked |
1295 | + property bool alphanumeric |
1296 | + property var userModel // unused |
1297 | + property alias infographicModel: coverPage.infographicModel |
1298 | + readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown |
1299 | + readonly property bool required: coverPage.required || lockscreen.required |
1300 | + |
1301 | + signal selected(int index) // unused |
1302 | + signal responded(string response) |
1303 | + signal tease() |
1304 | + signal emergencyCall() |
1305 | + |
1306 | + function showMessage(html) { |
1307 | + // TODO |
1308 | + } |
1309 | + |
1310 | + function showPrompt(text, isSecret, isDefaultPrompt) { |
1311 | + lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase(); |
1312 | + lockscreen.maybeShow(); |
1313 | + } |
1314 | + |
1315 | + function showLastChance() { |
1316 | + var title = lockscreen.alphaNumeric ? |
1317 | + i18n.tr("Sorry, incorrect passphrase.") : |
1318 | + i18n.tr("Sorry, incorrect passcode."); |
1319 | + var text = i18n.tr("This will be your last attempt.") + " " + |
1320 | + (lockscreen.alphaNumeric ? |
1321 | + i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") : |
1322 | + i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.")); |
1323 | + lockscreen.showInfoPopup(title, text); |
1324 | + } |
1325 | + |
1326 | + function hide() { |
1327 | + lockscreen.hide(); |
1328 | + coverPage.hide(); |
1329 | + } |
1330 | + |
1331 | + function notifyAuthenticationSucceeded() { |
1332 | + lockscreen.hide(); |
1333 | + } |
1334 | + |
1335 | + function notifyAuthenticationFailed() { |
1336 | + lockscreen.clear(true); |
1337 | + } |
1338 | + |
1339 | + function reset() { |
1340 | + coverPage.show(); |
1341 | + } |
1342 | + |
1343 | + function tryToUnlock(toTheRight) { |
1344 | + var coverChanged = coverPage.shown; |
1345 | + lockscreen.maybeShow(); |
1346 | + if (toTheRight) { |
1347 | + coverPage.hideRight(); |
1348 | + } else { |
1349 | + coverPage.hide(); |
1350 | + } |
1351 | + return coverChanged; |
1352 | + } |
1353 | + |
1354 | + onLockedChanged: { |
1355 | + if (locked) { |
1356 | + lockscreen.maybeShow(); |
1357 | + } else { |
1358 | + lockscreen.hide(); |
1359 | + } |
1360 | + } |
1361 | + |
1362 | + Lockscreen { |
1363 | + id: lockscreen |
1364 | + objectName: "lockscreen" |
1365 | + |
1366 | + shown: false |
1367 | + showAnimation: StandardAnimation { property: "opacity"; to: 1 } |
1368 | + hideAnimation: StandardAnimation { property: "opacity"; to: 0 } |
1369 | + anchors.fill: parent |
1370 | + visible: required |
1371 | + enabled: !coverPage.shown |
1372 | + background: root.background |
1373 | + darkenBackground: 0.4 |
1374 | + alphaNumeric: root.alphanumeric |
1375 | + minPinLength: 4 |
1376 | + maxPinLength: 4 |
1377 | + |
1378 | + property string promptText |
1379 | + infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) : |
1380 | + alphaNumeric ? i18n.tr("Enter passphrase") : |
1381 | + i18n.tr("Enter passcode") |
1382 | + errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) : |
1383 | + alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" + |
1384 | + i18n.tr("Please re-enter") : |
1385 | + i18n.tr("Sorry, incorrect passcode") |
1386 | + |
1387 | + onEntered: root.responded(passphrase) |
1388 | + onCancel: coverPage.show() |
1389 | + onEmergencyCall: root.emergencyCall() |
1390 | + |
1391 | + function maybeShow() { |
1392 | + if (root.locked && !shown) { |
1393 | + showNow(); |
1394 | + } |
1395 | + } |
1396 | + } |
1397 | + |
1398 | + Rectangle { |
1399 | + anchors.fill: parent |
1400 | + color: "black" |
1401 | + opacity: coverPage.showProgress * 0.8 |
1402 | + } |
1403 | + |
1404 | + CoverPage { |
1405 | + id: coverPage |
1406 | + objectName: "coverPage" |
1407 | + height: parent.height |
1408 | + width: parent.width |
1409 | + background: root.background |
1410 | + onTease: root.tease() |
1411 | + |
1412 | + onShowProgressChanged: { |
1413 | + if (showProgress === 1) { |
1414 | + lockscreen.reset(); |
1415 | + } |
1416 | + |
1417 | + if (showProgress === 0) { |
1418 | + if (root.locked) { |
1419 | + lockscreen.clear(false); // to reset focus if necessary |
1420 | + } else { |
1421 | + root.responded(""); |
1422 | + } |
1423 | + } |
1424 | + } |
1425 | + |
1426 | + Clock { |
1427 | + anchors { |
1428 | + top: parent.top |
1429 | + topMargin: units.gu(2) |
1430 | + horizontalCenter: parent.horizontalCenter |
1431 | + } |
1432 | + } |
1433 | + } |
1434 | +} |
1435 | |
1436 | === added file 'qml/Greeter/WideView.qml' |
1437 | --- qml/Greeter/WideView.qml 1970-01-01 00:00:00 +0000 |
1438 | +++ qml/Greeter/WideView.qml 2015-02-23 15:44:00 +0000 |
1439 | @@ -0,0 +1,134 @@ |
1440 | +/* |
1441 | + * Copyright (C) 2015 Canonical, Ltd. |
1442 | + * |
1443 | + * This program is free software; you can redistribute it and/or modify |
1444 | + * it under the terms of the GNU General Public License as published by |
1445 | + * the Free Software Foundation; version 3. |
1446 | + * |
1447 | + * This program is distributed in the hope that it will be useful, |
1448 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1449 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1450 | + * GNU General Public License for more details. |
1451 | + * |
1452 | + * You should have received a copy of the GNU General Public License |
1453 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1454 | + */ |
1455 | + |
1456 | +import QtQuick 2.3 |
1457 | +import Ubuntu.Components 1.1 |
1458 | + |
1459 | +FocusScope { |
1460 | + id: root |
1461 | + |
1462 | + property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin |
1463 | + property alias launcherOffset: coverPage.launcherOffset |
1464 | + property alias currentIndex: loginList.currentIndex |
1465 | + property int delayMinutes // TODO |
1466 | + property alias backgroundTopMargin: coverPage.backgroundTopMargin |
1467 | + property alias background: coverPage.background |
1468 | + property bool locked |
1469 | + property bool alphanumeric // unused |
1470 | + property alias userModel: loginList.model |
1471 | + property alias infographicModel: coverPage.infographicModel |
1472 | + readonly property bool fullyShown: coverPage.showProgress === 1 |
1473 | + readonly property bool required: coverPage.required |
1474 | + |
1475 | + // so that it can be replaced in tests with a mock object |
1476 | + property var inputMethod: Qt.inputMethod |
1477 | + |
1478 | + signal selected(int index) |
1479 | + signal responded(string response) |
1480 | + signal tease() |
1481 | + signal emergencyCall() // unused |
1482 | + |
1483 | + function showMessage(html) { |
1484 | + loginList.showMessage(html); |
1485 | + } |
1486 | + |
1487 | + function showPrompt(text, isSecret, isDefaultPrompt) { |
1488 | + loginList.showPrompt(text, isSecret, isDefaultPrompt); |
1489 | + } |
1490 | + |
1491 | + function showLastChance() { |
1492 | + // TODO |
1493 | + } |
1494 | + |
1495 | + function hide() { |
1496 | + coverPage.hide(); |
1497 | + } |
1498 | + |
1499 | + function notifyAuthenticationSucceeded() { |
1500 | + // Nothing needed |
1501 | + } |
1502 | + |
1503 | + function notifyAuthenticationFailed() { |
1504 | + loginList.showError(); |
1505 | + } |
1506 | + |
1507 | + function reset() { |
1508 | + loginList.reset(); |
1509 | + } |
1510 | + |
1511 | + function tryToUnlock(toTheRight) { |
1512 | + if (root.locked) { |
1513 | + coverPage.show(); |
1514 | + loginList.tryToUnlock(); |
1515 | + return false; |
1516 | + } else { |
1517 | + var coverChanged = coverPage.shown; |
1518 | + if (toTheRight) { |
1519 | + coverPage.hideRight(); |
1520 | + } else { |
1521 | + coverPage.hide(); |
1522 | + } |
1523 | + return coverChanged; |
1524 | + } |
1525 | + } |
1526 | + |
1527 | + Rectangle { |
1528 | + anchors.fill: parent |
1529 | + color: "black" |
1530 | + opacity: coverPage.showProgress * 0.8 |
1531 | + } |
1532 | + |
1533 | + CoverPage { |
1534 | + id: coverPage |
1535 | + objectName: "coverPage" |
1536 | + height: parent.height |
1537 | + width: parent.width |
1538 | + draggable: !root.locked |
1539 | + |
1540 | + infographics { |
1541 | + height: 0.75 * parent.height |
1542 | + anchors.leftMargin: loginList.x + loginList.width |
1543 | + } |
1544 | + |
1545 | + onTease: root.tease() |
1546 | + |
1547 | + onShowProgressChanged: { |
1548 | + if (showProgress === 0 && !root.locked) { |
1549 | + root.responded(""); |
1550 | + } |
1551 | + } |
1552 | + |
1553 | + LoginList { |
1554 | + id: loginList |
1555 | + objectName: "loginList" |
1556 | + |
1557 | + anchors { |
1558 | + left: parent.left |
1559 | + leftMargin: Math.min(parent.width * 0.16, units.gu(20)) |
1560 | + top: parent.top |
1561 | + } |
1562 | + width: units.gu(29) |
1563 | + height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height |
1564 | + : parent.height |
1565 | + Behavior on height { UbuntuNumberAnimation {} } |
1566 | + |
1567 | + locked: root.locked |
1568 | + |
1569 | + onSelected: root.selected(index) |
1570 | + onResponded: root.responded(response) |
1571 | + } |
1572 | + } |
1573 | +} |
1574 | |
1575 | === modified file 'qml/Shell.qml' |
1576 | --- qml/Shell.qml 2015-02-11 17:12:49 +0000 |
1577 | +++ qml/Shell.qml 2015-02-23 15:44:00 +0000 |
1578 | @@ -22,9 +22,7 @@ |
1579 | import Ubuntu.Components 0.1 |
1580 | import Ubuntu.Components.Popups 1.0 |
1581 | import Ubuntu.Gestures 0.1 |
1582 | -import Ubuntu.SystemImage 0.1 |
1583 | import Ubuntu.Telephony 0.1 as Telephony |
1584 | -import Unity.Connectivity 0.1 |
1585 | import Unity.Launcher 0.1 |
1586 | import Utils 0.1 |
1587 | import LightDM 0.1 as LightDM |
1588 | @@ -46,9 +44,9 @@ |
1589 | Item { |
1590 | id: shell |
1591 | |
1592 | - // Disable everything so that user can't swipe greeter or launcher until |
1593 | - // we get first prompt/authenticate, which will re-enable the shell. |
1594 | - enabled: false |
1595 | + // Disable everything while greeter is waiting, so that the user can't swipe |
1596 | + // the greeter or launcher until we know whether the session is locked. |
1597 | + enabled: !greeter.waiting |
1598 | |
1599 | // this is only here to select the width / height of the window if not running fullscreen |
1600 | property bool tablet: false |
1601 | @@ -61,18 +59,9 @@ |
1602 | : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground |
1603 | readonly property real panelHeight: panel.panelHeight |
1604 | |
1605 | - readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock |
1606 | - readonly property alias hasLockedApp: greeter.hasLockedApp |
1607 | - readonly property bool forcedUnlock: tutorial.running |
1608 | - onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide() |
1609 | - |
1610 | property bool sideStageEnabled: shell.width >= units.gu(100) |
1611 | readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId |
1612 | |
1613 | - property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future |
1614 | - property int failedLoginsDelayAttempts: 7 // number of failed logins |
1615 | - property int failedLoginsDelayMinutes: 5 // minutes of forced waiting |
1616 | - |
1617 | property int orientation |
1618 | readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
1619 | onDeviceOrientationAngleChanged: { |
1620 | @@ -99,7 +88,7 @@ |
1621 | } |
1622 | |
1623 | function startLockedApp(app) { |
1624 | - if (shell.locked) { |
1625 | + if (greeter.locked) { |
1626 | greeter.lockedApp = app; |
1627 | } |
1628 | shell.activateApplication(app); |
1629 | @@ -117,6 +106,7 @@ |
1630 | |
1631 | GSettings { |
1632 | id: backgroundSettings |
1633 | + objectName: "backgroundSettings" |
1634 | schema.id: "org.gnome.desktop.background" |
1635 | } |
1636 | |
1637 | @@ -222,53 +212,32 @@ |
1638 | |
1639 | Connections { |
1640 | target: ApplicationManager |
1641 | - onFocusRequested: { |
1642 | - if (greeter.narrowMode) { |
1643 | - if (appId === "dialer-app" && callManager.hasCalls && shell.locked) { |
1644 | - // If we are in the middle of a call, make dialer lockedApp and show it. |
1645 | - // This can happen if user backs out of dialer back to greeter, then |
1646 | - // launches dialer again. |
1647 | - greeter.lockedApp = appId; |
1648 | - } |
1649 | - if (greeter.hasLockedApp) { |
1650 | - if (appId === greeter.lockedApp) { |
1651 | - lockscreen.hide() // show locked app |
1652 | - } else { |
1653 | - greeter.startUnlock() // show lockscreen if necessary |
1654 | - } |
1655 | - } |
1656 | - greeter.hide(); |
1657 | - } else { |
1658 | - if (LightDM.Greeter.active) { |
1659 | - greeter.startUnlock() |
1660 | - } |
1661 | - } |
1662 | - } |
1663 | |
1664 | + // This signal is also fired when we try to focus the current app |
1665 | + // again. We rely on this! |
1666 | onFocusedApplicationIdChanged: { |
1667 | - if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) { |
1668 | - greeter.startUnlock() |
1669 | - } |
1670 | - panel.indicators.hide(); |
1671 | - } |
1672 | - |
1673 | - onApplicationAdded: { |
1674 | - if (appId != "unity8-dash") { |
1675 | - if (greeter.shown) { |
1676 | - greeter.startUnlock(); |
1677 | - } |
1678 | - |
1679 | + var appId = ApplicationManager.focusedApplicationId; |
1680 | + |
1681 | + if (tutorial.running && appId != "unity8-dash") { |
1682 | // If this happens on first boot, we may be in edge |
1683 | // tutorial or wizard while receiving a call. But a call |
1684 | // is more important than wizard so just bail out of those. |
1685 | - if (tutorial.running) { |
1686 | - tutorial.finish(); |
1687 | - wizard.hide(); |
1688 | - } |
1689 | - } |
1690 | - if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) { |
1691 | - lockscreen.hide() // show locked app |
1692 | - } |
1693 | + tutorial.finish(); |
1694 | + wizard.hide(); |
1695 | + } |
1696 | + |
1697 | + if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) { |
1698 | + // If we are in the middle of a call, make dialer lockedApp and show it. |
1699 | + // This can happen if user backs out of dialer back to greeter, then |
1700 | + // launches dialer again. |
1701 | + greeter.lockedApp = appId; |
1702 | + } |
1703 | + greeter.notifyAppFocused(appId); |
1704 | + |
1705 | + panel.indicators.hide(); |
1706 | + } |
1707 | + |
1708 | + onApplicationAdded: { |
1709 | launcher.hide(); |
1710 | } |
1711 | } |
1712 | @@ -292,7 +261,6 @@ |
1713 | |
1714 | property bool interactive: tutorial.spreadEnabled |
1715 | && !greeter.shown |
1716 | - && !lockscreen.shown |
1717 | && panel.indicators.fullyClosed |
1718 | && launcher.progress == 0 |
1719 | && !notifications.useModal |
1720 | @@ -328,7 +296,7 @@ |
1721 | Binding { |
1722 | target: applicationsDisplayLoader.item |
1723 | property: "inverseProgress" |
1724 | - value: launcher.progress |
1725 | + value: greeter.locked ? 0 : launcher.progress |
1726 | } |
1727 | Binding { |
1728 | target: applicationsDisplayLoader.item |
1729 | @@ -379,299 +347,50 @@ |
1730 | } |
1731 | } |
1732 | |
1733 | - Lockscreen { |
1734 | - id: lockscreen |
1735 | - objectName: "lockscreen" |
1736 | + Greeter { |
1737 | + id: greeter |
1738 | + objectName: "greeter" |
1739 | |
1740 | hides: [launcher, panel.indicators] |
1741 | - shown: false |
1742 | - enabled: true |
1743 | - showAnimation: StandardAnimation { property: "opacity"; to: 1 } |
1744 | - hideAnimation: StandardAnimation { property: "opacity"; to: 0 } |
1745 | - y: panel.panelHeight |
1746 | - visible: required |
1747 | - width: parent.width |
1748 | - height: parent.height - panel.panelHeight |
1749 | + tabletMode: shell.sideStageEnabled |
1750 | + launcherOffset: launcher.progress |
1751 | + forcedUnlock: tutorial.running |
1752 | background: shell.background |
1753 | - darkenBackground: 0.4 |
1754 | - alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard |
1755 | - minPinLength: 4 |
1756 | - maxPinLength: 4 |
1757 | - |
1758 | - property string promptText |
1759 | - infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) : |
1760 | - alphaNumeric ? i18n.tr("Enter passphrase") : |
1761 | - i18n.tr("Enter passcode") |
1762 | - errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) : |
1763 | - alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" + |
1764 | - i18n.tr("Please re-enter") : |
1765 | - i18n.tr("Sorry, incorrect passcode") |
1766 | - |
1767 | - // FIXME: We *should* show emergency dialer if there is a SIM present, |
1768 | - // regardless of whether the side stage is enabled. But right now, |
1769 | - // the assumption is that narrow screens are phones which have SIMs |
1770 | - // and wider screens are tablets which don't. When we do allow this |
1771 | - // on devices with a side stage and a SIM, work should be done to |
1772 | - // ensure that the main stage is disabled while the dialer is present |
1773 | - // in the side stage. See the FIXME in the stage loader in this file. |
1774 | - showEmergencyCallButton: !shell.sideStageEnabled |
1775 | - |
1776 | - onEntered: LightDM.Greeter.respond(passphrase); |
1777 | - onCancel: greeter.show() |
1778 | + |
1779 | + anchors.fill: parent |
1780 | + anchors.topMargin: panel.panelHeight |
1781 | + |
1782 | + // avoid overlapping with Launcher's edge drag area |
1783 | + // FIXME: Fix TouchRegistry & friends and remove this workaround |
1784 | + // Issue involves launcher's DDA getting disabled on a long |
1785 | + // left-edge drag |
1786 | + dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0 |
1787 | + |
1788 | + onSessionStarted: { |
1789 | + launcher.hide(); |
1790 | + } |
1791 | + |
1792 | + onTease: { |
1793 | + if (!tutorial.running) { |
1794 | + launcher.tease(); |
1795 | + } |
1796 | + } |
1797 | + |
1798 | onEmergencyCall: startLockedApp("dialer-app") |
1799 | |
1800 | - onShownChanged: if (shown) greeter.lockedApp = "" |
1801 | - |
1802 | - function maybeShow() { |
1803 | - if (!shell.forcedUnlock && !shown) { |
1804 | - showNow(); |
1805 | - } |
1806 | - } |
1807 | - |
1808 | Timer { |
1809 | - id: forcedDelayTimer |
1810 | - interval: 1000 * 60 |
1811 | + // See powerConnection for why this is useful |
1812 | + id: showGreeterDelayed |
1813 | + interval: 1 |
1814 | onTriggered: { |
1815 | - if (lockscreen.delayMinutes > 0) { |
1816 | - lockscreen.delayMinutes -= 1 |
1817 | - if (lockscreen.delayMinutes > 0) { |
1818 | - start() // go again |
1819 | - } |
1820 | - } |
1821 | - } |
1822 | - } |
1823 | - |
1824 | - Component.onCompleted: { |
1825 | - if (greeter.narrowMode) { |
1826 | - LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole)) |
1827 | - } |
1828 | - } |
1829 | - } |
1830 | - |
1831 | - Connections { |
1832 | - target: LightDM.Greeter |
1833 | - |
1834 | - onShowGreeter: greeter.show() |
1835 | - onHideGreeter: greeter.login() |
1836 | - |
1837 | - onShowPrompt: { |
1838 | - shell.enabled = true; |
1839 | - if (!LightDM.Greeter.active) { |
1840 | - return; // could happen if hideGreeter() comes in before we prompt |
1841 | - } |
1842 | - if (greeter.narrowMode) { |
1843 | - lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase(); |
1844 | - lockscreen.maybeShow(); |
1845 | - } |
1846 | - } |
1847 | - |
1848 | - onPromptlessChanged: { |
1849 | - if (!LightDM.Greeter.active) { |
1850 | - return; // could happen if hideGreeter() comes in before we prompt |
1851 | - } |
1852 | - if (greeter.narrowMode) { |
1853 | - if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) { |
1854 | - lockscreen.hide() |
1855 | - } else { |
1856 | - lockscreen.reset(); |
1857 | - lockscreen.maybeShow(); |
1858 | - } |
1859 | - } |
1860 | - } |
1861 | - |
1862 | - onAuthenticationComplete: { |
1863 | - shell.enabled = true; |
1864 | - if (LightDM.Greeter.authenticated) { |
1865 | - AccountsService.failedLogins = 0 |
1866 | - } |
1867 | - // Else only penalize user for a failed login if they actually were |
1868 | - // prompted for a password. We do this below after the promptless |
1869 | - // early exit. |
1870 | - |
1871 | - if (LightDM.Greeter.promptless) { |
1872 | - return; |
1873 | - } |
1874 | - |
1875 | - if (LightDM.Greeter.authenticated) { |
1876 | - greeter.login(); |
1877 | - } else { |
1878 | - AccountsService.failedLogins++ |
1879 | - if (maxFailedLogins >= 2) { // require at least a warning |
1880 | - if (AccountsService.failedLogins === maxFailedLogins - 1) { |
1881 | - var title = lockscreen.alphaNumeric ? |
1882 | - i18n.tr("Sorry, incorrect passphrase.") : |
1883 | - i18n.tr("Sorry, incorrect passcode.") |
1884 | - var text = i18n.tr("This will be your last attempt.") + " " + |
1885 | - (lockscreen.alphaNumeric ? |
1886 | - i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") : |
1887 | - i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.")) |
1888 | - lockscreen.showInfoPopup(title, text) |
1889 | - } else if (AccountsService.failedLogins >= maxFailedLogins) { |
1890 | - SystemImage.factoryReset() // Ouch! |
1891 | - } |
1892 | - } |
1893 | - if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) { |
1894 | - lockscreen.delayMinutes = failedLoginsDelayMinutes |
1895 | - forcedDelayTimer.start() |
1896 | - } |
1897 | - |
1898 | - lockscreen.clear(true); |
1899 | - if (greeter.narrowMode) { |
1900 | - LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole)) |
1901 | - } |
1902 | - } |
1903 | - } |
1904 | - } |
1905 | - |
1906 | - Binding { |
1907 | - target: LightDM.Greeter |
1908 | - property: "active" |
1909 | - value: greeter.shown || lockscreen.shown || greeter.hasLockedApp |
1910 | - } |
1911 | - |
1912 | - Rectangle { |
1913 | - anchors.fill: parent |
1914 | - color: "black" |
1915 | - opacity: greeterWrapper.showProgress * 0.8 |
1916 | - } |
1917 | - |
1918 | - Item { |
1919 | - // Just a tiny wrapper to adjust greeter's x without messing with its own dragging |
1920 | - id: greeterWrapper |
1921 | - objectName: "greeterWrapper" |
1922 | - x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0 |
1923 | - y: panel.panelHeight |
1924 | - width: parent.width |
1925 | - height: parent.height - panel.panelHeight |
1926 | - |
1927 | - Behavior on x { |
1928 | - enabled: !launcher.dashSwipe |
1929 | - StandardAnimation {} |
1930 | - } |
1931 | - |
1932 | - property bool fullyShown: showProgress === 1.0 |
1933 | - onFullyShownChanged: { |
1934 | - // Wait until the greeter is completely covering lockscreen before resetting it. |
1935 | - if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) { |
1936 | - lockscreen.reset(); |
1937 | - lockscreen.maybeShow(); |
1938 | - } |
1939 | - } |
1940 | - |
1941 | - readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1) |
1942 | - onShowProgressChanged: { |
1943 | - if (showProgress === 0) { |
1944 | - if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) { |
1945 | - greeter.login() |
1946 | - } else if (greeter.narrowMode) { |
1947 | - lockscreen.clear(false) // to reset focus if necessary |
1948 | - } |
1949 | - } |
1950 | - } |
1951 | - |
1952 | - Greeter { |
1953 | - id: greeter |
1954 | - objectName: "greeter" |
1955 | - |
1956 | - signal sessionStarted() // helpful for tests |
1957 | - |
1958 | - property string lockedApp: "" |
1959 | - property bool hasLockedApp: lockedApp !== "" |
1960 | - |
1961 | - hides: [launcher, panel.indicators] |
1962 | - loadContent: required || lockscreen.required // keeps content in memory for quick show() |
1963 | - |
1964 | - locked: shell.locked |
1965 | - |
1966 | - background: shell.background |
1967 | - |
1968 | - width: parent.width |
1969 | - height: parent.height |
1970 | - |
1971 | - |
1972 | - // avoid overlapping with Launcher's edge drag area |
1973 | - // FIXME: Fix TouchRegistry & friends and remove this workaround |
1974 | - // Issue involves launcher's DDA getting disabled on a long |
1975 | - // left-edge drag |
1976 | - dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0 |
1977 | - |
1978 | - function startUnlock() { |
1979 | - if (narrowMode) { |
1980 | - if (!LightDM.Greeter.authenticated) { |
1981 | - lockscreen.maybeShow() |
1982 | - } |
1983 | - hide() |
1984 | - } else { |
1985 | - show() |
1986 | - tryToUnlock() |
1987 | - } |
1988 | - } |
1989 | - |
1990 | - function login() { |
1991 | - enabled = false; |
1992 | - if (LightDM.Greeter.startSessionSync()) { |
1993 | - sessionStarted(); |
1994 | - greeter.hide(); |
1995 | - lockscreen.hide(); |
1996 | - launcher.hide(); |
1997 | - } |
1998 | - enabled = true; |
1999 | - } |
2000 | - |
2001 | - Timer { |
2002 | - // See powerConnection for why this is useful |
2003 | - id: showGreeterDelayed |
2004 | - interval: 1 |
2005 | - onTriggered: { |
2006 | - greeter.showNow(); |
2007 | - } |
2008 | - } |
2009 | - |
2010 | - onShownChanged: { |
2011 | - if (shown) { |
2012 | - // Disable everything so that user can't swipe greeter or |
2013 | - // launcher until we get the next prompt/authenticate, which |
2014 | - // will re-enable the shell. |
2015 | - shell.enabled = false; |
2016 | - |
2017 | - if (greeter.narrowMode) { |
2018 | - LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole)); |
2019 | - } else { |
2020 | - reset() |
2021 | - } |
2022 | - greeter.lockedApp = ""; |
2023 | - greeter.forceActiveFocus(); |
2024 | - } |
2025 | - } |
2026 | - |
2027 | - Component.onCompleted: { |
2028 | - Connectivity.unlockAllModems() |
2029 | - } |
2030 | - |
2031 | - onUnlocked: greeter.hide() |
2032 | - onSelected: { |
2033 | - // Update launcher items for new user |
2034 | - var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole); |
2035 | - AccountsService.user = user; |
2036 | - LauncherModel.setUser(user); |
2037 | - } |
2038 | - |
2039 | - onTapped: { |
2040 | - if (!tutorial.running) { |
2041 | - launcher.tease(); |
2042 | - } |
2043 | - } |
2044 | - onDraggingChanged: { |
2045 | - if (dragging && !tutorial.running) { |
2046 | - launcher.tease(); |
2047 | - } |
2048 | - } |
2049 | - |
2050 | - Binding { |
2051 | - target: ApplicationManager |
2052 | - property: "suspended" |
2053 | - value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown |
2054 | - } |
2055 | + greeter.forceShow(); |
2056 | + } |
2057 | + } |
2058 | + |
2059 | + Binding { |
2060 | + target: ApplicationManager |
2061 | + property: "suspended" |
2062 | + value: greeter.shown |
2063 | } |
2064 | } |
2065 | |
2066 | @@ -680,7 +399,7 @@ |
2067 | target: callManager |
2068 | |
2069 | onHasCallsChanged: { |
2070 | - if (shell.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") { |
2071 | + if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") { |
2072 | // We just received an incoming call while locked. The |
2073 | // indicator will have already launched dialer-app for us, but |
2074 | // there is a race between "hasCalls" changing and the dialer |
2075 | @@ -720,9 +439,7 @@ |
2076 | return |
2077 | } |
2078 | |
2079 | - if (LightDM.Greeter.active) { |
2080 | - greeter.startUnlock() |
2081 | - } |
2082 | + greeter.notifyAboutToFocusApp("unity8-dash"); |
2083 | |
2084 | var animate = !LightDM.Greeter.active && !stages.shown |
2085 | dash.setCurrentScope(0, animate, false) |
2086 | @@ -730,17 +447,11 @@ |
2087 | } |
2088 | |
2089 | function showDash() { |
2090 | - if (greeter.hasLockedApp || // just in case user gets here |
2091 | - (!greeter.narrowMode && shell.locked)) { |
2092 | - return |
2093 | - } |
2094 | - |
2095 | - if (greeter.shown) { |
2096 | - greeter.hideRight(); |
2097 | + if (greeter.notifyShowingDashFromDrag()) { |
2098 | launcher.fadeOut(); |
2099 | } |
2100 | |
2101 | - if (ApplicationManager.focusedApplicationId != "unity8-dash") { |
2102 | + if (!greeter.locked && ApplicationManager.focusedApplicationId != "unity8-dash") { |
2103 | ApplicationManager.requestFocusApplication("unity8-dash") |
2104 | launcher.fadeOut(); |
2105 | } |
2106 | @@ -758,7 +469,9 @@ |
2107 | anchors.fill: parent //because this draws indicator menus |
2108 | indicators { |
2109 | hides: [launcher] |
2110 | - available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp |
2111 | + available: tutorial.panelEnabled |
2112 | + && (!greeter.locked || AccountsService.enableIndicatorsWhileLocked) |
2113 | + && !greeter.hasLockedApp |
2114 | contentEnabled: tutorial.panelContentEnabled |
2115 | width: parent.width > units.gu(60) ? units.gu(40) : parent.width |
2116 | |
2117 | @@ -772,7 +485,7 @@ |
2118 | } |
2119 | } |
2120 | callHint { |
2121 | - greeterShown: greeter.shown || lockscreen.shown |
2122 | + greeterShown: greeter.shown |
2123 | } |
2124 | |
2125 | property bool topmostApplicationIsFullscreen: |
2126 | @@ -794,7 +507,9 @@ |
2127 | anchors.bottom: parent.bottom |
2128 | width: parent.width |
2129 | dragAreaWidth: shell.edgeSize |
2130 | - available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp |
2131 | + available: tutorial.launcherEnabled |
2132 | + && (!greeter.locked || AccountsService.enableLauncherWhileLocked) |
2133 | + && !greeter.hasLockedApp |
2134 | inverted: usageModeSettings.usageMode === "Staged" |
2135 | shadeBackground: !tutorial.running |
2136 | |
2137 | @@ -806,11 +521,10 @@ |
2138 | } |
2139 | } |
2140 | onLauncherApplicationSelected: { |
2141 | - if (greeter.hasLockedApp) { |
2142 | - greeter.startUnlock() |
2143 | - } |
2144 | - if (!tutorial.running) |
2145 | + if (!tutorial.running) { |
2146 | + greeter.notifyAboutToFocusApp(appId); |
2147 | shell.activateApplication(appId) |
2148 | + } |
2149 | } |
2150 | onShownChanged: { |
2151 | if (shown) { |
2152 | |
2153 | === modified file 'tests/autopilot/unity8/shell/emulators/greeter.py' |
2154 | --- tests/autopilot/unity8/shell/emulators/greeter.py 2014-09-08 10:33:50 +0000 |
2155 | +++ tests/autopilot/unity8/shell/emulators/greeter.py 2015-02-23 15:44:00 +0000 |
2156 | @@ -19,6 +19,7 @@ |
2157 | |
2158 | import ubuntuuitoolkit |
2159 | |
2160 | +from autopilot.utilities import sleep |
2161 | from unity8.shell.emulators import UnityEmulatorBase |
2162 | |
2163 | |
2164 | @@ -26,12 +27,27 @@ |
2165 | """An emulator that understands the greeter screen.""" |
2166 | |
2167 | def wait_swiped_away(self): |
2168 | - self.showProgress.wait_for(0) |
2169 | + # We have to be careful here, because coverPage can go away at any time |
2170 | + # if there isn't a lockscreen behind it (it hides completely, then |
2171 | + # the greeter disposes it). But if there *is* a lockscreen, then we |
2172 | + # need a different check, by looking at its showProgress. So make our |
2173 | + # own wait_for loop and check both possibilities. |
2174 | + for i in range(10): |
2175 | + if not self.required: |
2176 | + return |
2177 | + coverPage = self.select_single(objectName='coverPage') |
2178 | + if coverPage.showProgress == 0: |
2179 | + return |
2180 | + sleep(1) |
2181 | + |
2182 | + raise AssertionError("Greeter cover page still up after 10s") |
2183 | + |
2184 | |
2185 | def swipe(self): |
2186 | """Swipe the greeter screen away.""" |
2187 | - self.created.wait_for(True) |
2188 | - self.showProgress.wait_for(1) |
2189 | + self.waiting.wait_for(False) |
2190 | + coverPage = self.select_single(objectName='coverPage') |
2191 | + coverPage.showProgress.wait_for(1) |
2192 | |
2193 | rect = self.globalRect |
2194 | start_x = rect[0] + rect[2] - 3 |
2195 | |
2196 | === modified file 'tests/autopilot/unity8/shell/emulators/main_window.py' |
2197 | --- tests/autopilot/unity8/shell/emulators/main_window.py 2015-01-21 04:11:36 +0000 |
2198 | +++ tests/autopilot/unity8/shell/emulators/main_window.py 2015-02-23 15:44:00 +0000 |
2199 | @@ -35,12 +35,6 @@ |
2200 | def get_greeter(self): |
2201 | return self.select_single(Greeter) |
2202 | |
2203 | - def get_greeter_content_loader(self): |
2204 | - return self.wait_select_single( |
2205 | - "QQuickLoader", |
2206 | - objectName="greeterContentLoader" |
2207 | - ) |
2208 | - |
2209 | def get_login_loader(self): |
2210 | return self.select_single("QQuickLoader", objectName="loginLoader") |
2211 | |
2212 | |
2213 | === modified file 'tests/autopilot/unity8/shell/tests/__init__.py' |
2214 | --- tests/autopilot/unity8/shell/tests/__init__.py 2015-01-21 13:50:14 +0000 |
2215 | +++ tests/autopilot/unity8/shell/tests/__init__.py 2015-02-23 15:44:00 +0000 |
2216 | @@ -375,9 +375,8 @@ |
2217 | self._proxy = None |
2218 | |
2219 | def wait_for_unity(self): |
2220 | - greeter_content_loader = self.main_window.wait_select_single( |
2221 | - objectName='greeterContentLoader') |
2222 | - greeter_content_loader.progress.wait_for(1) |
2223 | + greeter = self.main_window.wait_select_single(objectName='greeter') |
2224 | + greeter.waiting.wait_for(False) |
2225 | |
2226 | def get_dash(self): |
2227 | pid = process_helpers.get_job_pid('unity8-dash') |
2228 | |
2229 | === modified file 'tests/autopilot/unity8/shell/tests/test_lock_screen.py' |
2230 | --- tests/autopilot/unity8/shell/tests/test_lock_screen.py 2015-01-21 13:50:14 +0000 |
2231 | +++ tests/autopilot/unity8/shell/tests/test_lock_screen.py 2015-02-23 15:44:00 +0000 |
2232 | @@ -47,14 +47,13 @@ |
2233 | self.launch_unity() |
2234 | greeter = self.main_window.get_greeter() |
2235 | |
2236 | - if greeter.narrowMode: |
2237 | + if not greeter.tabletMode: |
2238 | greeter.swipe() |
2239 | lockscreen = self._wait_for_lockscreen() |
2240 | self.main_window.enter_pin_code("1234") |
2241 | - self.assertThat(lockscreen.shown, Eventually(Equals(False))) |
2242 | else: |
2243 | self._enter_prompt_passphrase("1234\n") |
2244 | - self.assertThat(greeter.shown, Eventually(Equals(False))) |
2245 | + self.assertThat(greeter.shown, Eventually(Equals(False))) |
2246 | |
2247 | def test_can_unlock_passphrase_screen(self): |
2248 | """Must be able to unlock the passphrase entry screen.""" |
2249 | @@ -63,14 +62,13 @@ |
2250 | self.launch_unity() |
2251 | greeter = self.main_window.get_greeter() |
2252 | |
2253 | - if greeter.narrowMode: |
2254 | + if not greeter.tabletMode: |
2255 | greeter.swipe() |
2256 | lockscreen = self._wait_for_lockscreen() |
2257 | self._enter_pin_passphrase("password") |
2258 | - self.assertThat(lockscreen.shown, Eventually(Equals(False))) |
2259 | else: |
2260 | self._enter_prompt_passphrase("password") |
2261 | - self.assertThat(greeter.shown, Eventually(Equals(False))) |
2262 | + self.assertThat(greeter.shown, Eventually(Equals(False))) |
2263 | |
2264 | def test_pin_screen_wrong_code(self): |
2265 | """Entering the wrong pin code must not dismiss the lock screen.""" |
2266 | @@ -78,18 +76,17 @@ |
2267 | self.launch_unity() |
2268 | greeter = self.main_window.get_greeter() |
2269 | |
2270 | - if greeter.narrowMode: |
2271 | + if not greeter.tabletMode: |
2272 | greeter.swipe() |
2273 | lockscreen = self._wait_for_lockscreen() |
2274 | self.main_window.enter_pin_code("4321") |
2275 | pinentryField = self.main_window.get_pinentryField() |
2276 | self.assertThat(pinentryField.text, Eventually(Equals(""))) |
2277 | - self.assertThat(lockscreen.shown, Eventually(Equals(True))) |
2278 | else: |
2279 | self._enter_prompt_passphrase("4231\n") |
2280 | prompt = self.main_window.get_greeter().get_prompt() |
2281 | self.assertThat(prompt.text, Eventually(Equals(""))) |
2282 | - self.assertThat(greeter.shown, Eventually(Equals(True))) |
2283 | + self.assertThat(greeter.shown, Eventually(Equals(True))) |
2284 | |
2285 | def test_passphrase_screen_wrong_password(self): |
2286 | """Entering the wrong password must not dismiss the lock screen.""" |
2287 | @@ -97,18 +94,17 @@ |
2288 | self.launch_unity() |
2289 | greeter = self.main_window.get_greeter() |
2290 | |
2291 | - if greeter.narrowMode: |
2292 | + if not greeter.tabletMode: |
2293 | greeter.swipe() |
2294 | lockscreen = self._wait_for_lockscreen() |
2295 | self._enter_pin_passphrase("foobar") |
2296 | pinentryField = self.main_window.get_pinentryField() |
2297 | self.assertThat(pinentryField.text, Eventually(Equals(""))) |
2298 | - self.assertThat(lockscreen.shown, Eventually(Equals(True))) |
2299 | else: |
2300 | self._enter_prompt_passphrase("foobar") |
2301 | prompt = self.main_window.get_greeter().get_prompt() |
2302 | self.assertThat(prompt.text, Eventually(Equals(""))) |
2303 | - self.assertThat(greeter.shown, Eventually(Equals(True))) |
2304 | + self.assertThat(greeter.shown, Eventually(Equals(True))) |
2305 | |
2306 | def _wait_for_lockscreen(self): |
2307 | """Wait for the lock screen to load, and return it.""" |
2308 | |
2309 | === modified file 'tests/plugins/LightDM/CMakeLists.txt' |
2310 | --- tests/plugins/LightDM/CMakeLists.txt 2015-01-27 15:50:02 +0000 |
2311 | +++ tests/plugins/LightDM/CMakeLists.txt 2015-02-23 15:44:00 +0000 |
2312 | @@ -4,17 +4,32 @@ |
2313 | ) |
2314 | qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test) |
2315 | |
2316 | +add_executable(GreeterUsersModelTestExec |
2317 | + usersmodel.cpp |
2318 | + ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp |
2319 | + ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp |
2320 | + ) |
2321 | +qt5_use_modules(GreeterUsersModelTestExec Core Test) |
2322 | + |
2323 | include_directories( |
2324 | ${CMAKE_CURRENT_BINARY_DIR} |
2325 | ${CMAKE_SOURCE_DIR}/plugins/LightDM |
2326 | + ${CMAKE_SOURCE_DIR}/plugins/Utils |
2327 | ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM |
2328 | - ${LIBLIGHTDM_INCLUDE_DIRS} |
2329 | - ) |
2330 | - |
2331 | -add_dependencies(GreeterDBusTestExec MockLightDM) |
2332 | -target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm |
2333 | - -llightdm-qt5-2) |
2334 | + ) |
2335 | + |
2336 | +target_link_libraries(GreeterDBusTestExec |
2337 | + -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm |
2338 | + -llightdm-qt5-2 |
2339 | + ) |
2340 | + |
2341 | +target_link_libraries(GreeterUsersModelTestExec |
2342 | + -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm |
2343 | + -llightdm-qt5-2 |
2344 | + ) |
2345 | |
2346 | add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") |
2347 | |
2348 | add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks") |
2349 | + |
2350 | +add_binary_qml_test(GreeterUsersModel "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "LIBLIGHTDM_MOCK_MODE=full") |
2351 | |
2352 | === added file 'tests/plugins/LightDM/usersmodel.cpp' |
2353 | --- tests/plugins/LightDM/usersmodel.cpp 1970-01-01 00:00:00 +0000 |
2354 | +++ tests/plugins/LightDM/usersmodel.cpp 2015-02-23 15:44:00 +0000 |
2355 | @@ -0,0 +1,84 @@ |
2356 | +/* |
2357 | + * Copyright (C) 2015 Canonical, Ltd. |
2358 | + * |
2359 | + * This program is free software; you can redistribute it and/or modify |
2360 | + * it under the terms of the GNU General Public License as published by |
2361 | + * the Free Software Foundation; version 3. |
2362 | + * |
2363 | + * This program is distributed in the hope that it will be useful, |
2364 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2365 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2366 | + * GNU General Public License for more details. |
2367 | + * |
2368 | + * You should have received a copy of the GNU General Public License |
2369 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2370 | + */ |
2371 | + |
2372 | +#include "UsersModel.h" |
2373 | + |
2374 | +#include <QLightDM/UsersModel> |
2375 | +#include <QtTest> |
2376 | + |
2377 | +class GreeterUsersModelTest : public QObject |
2378 | +{ |
2379 | + Q_OBJECT |
2380 | + |
2381 | +private: |
2382 | + static int findName(QAbstractItemModel *model, const QString &name) |
2383 | + { |
2384 | + for (int i = 0; i < model->rowCount(QModelIndex()); i++) { |
2385 | + if (model->data(model->index(i, 0), QLightDM::UsersModel::NameRole).toString() == name) { |
2386 | + return i; |
2387 | + } |
2388 | + } |
2389 | + return -1; |
2390 | + } |
2391 | + |
2392 | + static QString getStringValue(QAbstractItemModel *model, const QString &name, int role) |
2393 | + { |
2394 | + int i = findName(model, name); |
2395 | + return model->data(model->index(i, 0), role).toString(); |
2396 | + } |
2397 | + |
2398 | +private Q_SLOTS: |
2399 | + |
2400 | + void init() |
2401 | + { |
2402 | + model = new UsersModel(); |
2403 | + QVERIFY(model); |
2404 | + sourceModel = new QLightDM::UsersModel(); |
2405 | + QVERIFY(sourceModel); |
2406 | + } |
2407 | + |
2408 | + void cleanup() |
2409 | + { |
2410 | + delete model; |
2411 | + delete sourceModel; |
2412 | + } |
2413 | + |
2414 | + void testMangleColor() |
2415 | + { |
2416 | + QString background = getStringValue(sourceModel, "color-background", QLightDM::UsersModel::BackgroundPathRole); |
2417 | + QVERIFY(background == "#dd4814"); |
2418 | + |
2419 | + background = getStringValue(model, "color-background", QLightDM::UsersModel::BackgroundPathRole); |
2420 | + QVERIFY(background == "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"); |
2421 | + } |
2422 | + |
2423 | + void testMangleEmptyName() |
2424 | + { |
2425 | + QString name = getStringValue(sourceModel, "empty-name", QLightDM::UsersModel::RealNameRole); |
2426 | + QVERIFY(name == ""); |
2427 | + |
2428 | + name = getStringValue(model, "empty-name", QLightDM::UsersModel::RealNameRole); |
2429 | + QVERIFY(name == "empty-name"); |
2430 | + } |
2431 | + |
2432 | +private: |
2433 | + UsersModel *model; |
2434 | + QLightDM::UsersModel *sourceModel; |
2435 | +}; |
2436 | + |
2437 | +QTEST_MAIN(GreeterUsersModelTest) |
2438 | + |
2439 | +#include "usersmodel.moc" |
2440 | |
2441 | === modified file 'tests/qmltests/CMakeLists.txt' |
2442 | --- tests/qmltests/CMakeLists.txt 2015-02-11 17:11:41 +0000 |
2443 | +++ tests/qmltests/CMakeLists.txt 2015-02-23 15:44:00 +0000 |
2444 | @@ -60,10 +60,11 @@ |
2445 | add_qml_test(Dash/ScopeSettings ScopeSettingNumber) |
2446 | add_qml_test(Dash/ScopeSettings ScopeSettingString) |
2447 | add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory) |
2448 | -add_qml_test(Greeter MultiGreeter) |
2449 | -add_qml_test(Greeter SingleGreeter) |
2450 | +add_qml_test(Greeter Clock) |
2451 | +add_qml_test(Greeter Greeter) |
2452 | add_qml_test(Greeter Infographics) |
2453 | -add_qml_test(Greeter Clock) |
2454 | +add_qml_test(Greeter NarrowView) |
2455 | +add_qml_test(Greeter WideView) |
2456 | add_qml_test(Launcher Launcher) |
2457 | add_qml_test(Notifications Notifications) |
2458 | add_qml_test(Notifications VisualSnapDecisionsQueue) |
2459 | |
2460 | === added file 'tests/qmltests/Greeter/TestView.qml' |
2461 | --- tests/qmltests/Greeter/TestView.qml 1970-01-01 00:00:00 +0000 |
2462 | +++ tests/qmltests/Greeter/TestView.qml 2015-02-23 15:44:00 +0000 |
2463 | @@ -0,0 +1,99 @@ |
2464 | +/* |
2465 | + * Copyright (C) 2015 Canonical, Ltd. |
2466 | + * |
2467 | + * This program is free software; you can redistribute it and/or modify |
2468 | + * it under the terms of the GNU General Public License as published by |
2469 | + * the Free Software Foundation; version 3. |
2470 | + * |
2471 | + * This program is distributed in the hope that it will be useful, |
2472 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2473 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2474 | + * GNU General Public License for more details. |
2475 | + * |
2476 | + * You should have received a copy of the GNU General Public License |
2477 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2478 | + */ |
2479 | + |
2480 | +import QtQuick 2.3 |
2481 | +import Ubuntu.Components 1.1 |
2482 | +import "../Components" |
2483 | + |
2484 | +Item { |
2485 | + objectName: "testView" |
2486 | + |
2487 | + property real dragHandleLeftMargin |
2488 | + property real launcherOffset |
2489 | + property int currentIndex |
2490 | + property int delayMinutes |
2491 | + property real backgroundTopMargin |
2492 | + property url background |
2493 | + property bool locked |
2494 | + property bool alphanumeric |
2495 | + property var userModel |
2496 | + property var infographicModel |
2497 | + readonly property bool fullyShown: _fullyShown |
2498 | + readonly property bool required: _required |
2499 | + |
2500 | + property bool _fullyShown: true |
2501 | + property bool _required: true |
2502 | + |
2503 | + signal selected(int index) |
2504 | + signal responded(string response) |
2505 | + signal tease() |
2506 | + signal emergencyCall() |
2507 | + |
2508 | + signal _showMessageCalled(string html) |
2509 | + signal _showPromptCalled(string text, bool isSecret, bool isDefaultPrompt) |
2510 | + signal _showLastChanceCalled() |
2511 | + signal _hideCalled() |
2512 | + signal _notifyAuthenticationSucceededCalled() |
2513 | + signal _notifyAuthenticationFailedCalled() |
2514 | + signal _resetCalled() |
2515 | + signal _tryToUnlockCalled(bool toTheRight) |
2516 | + |
2517 | + function showMessage(html) { |
2518 | + _showMessageCalled(html); |
2519 | + } |
2520 | + |
2521 | + function showPrompt(text, isSecret, isDefaultPrompt) { |
2522 | + _showPromptCalled(text, isSecret, isDefaultPrompt); |
2523 | + } |
2524 | + |
2525 | + function showLastChance() { |
2526 | + _showLastChanceCalled(); |
2527 | + } |
2528 | + |
2529 | + function hide() { |
2530 | + _hideCalled(); |
2531 | + _required = false; |
2532 | + _fullyShown = false; |
2533 | + } |
2534 | + |
2535 | + function notifyAuthenticationSucceeded() { |
2536 | + _notifyAuthenticationSucceededCalled(); |
2537 | + } |
2538 | + |
2539 | + function notifyAuthenticationFailed() { |
2540 | + _notifyAuthenticationFailedCalled(); |
2541 | + } |
2542 | + |
2543 | + function reset() { |
2544 | + _resetCalled(); |
2545 | + } |
2546 | + |
2547 | + function tryToUnlock(toTheRight) { |
2548 | + _tryToUnlockCalled(toTheRight); |
2549 | + return true; |
2550 | + } |
2551 | + |
2552 | + Rectangle { |
2553 | + anchors.fill: parent |
2554 | + color: "black" |
2555 | + |
2556 | + Label { |
2557 | + text: "Fake view, nothing to see here" |
2558 | + color: "white" |
2559 | + anchors.centerIn: parent |
2560 | + } |
2561 | + } |
2562 | +} |
2563 | |
2564 | === added file 'tests/qmltests/Greeter/tst_Greeter.qml' |
2565 | --- tests/qmltests/Greeter/tst_Greeter.qml 1970-01-01 00:00:00 +0000 |
2566 | +++ tests/qmltests/Greeter/tst_Greeter.qml 2015-02-23 15:44:00 +0000 |
2567 | @@ -0,0 +1,472 @@ |
2568 | +/* |
2569 | + * Copyright 2015 Canonical Ltd. |
2570 | + * |
2571 | + * This program is free software; you can redistribute it and/or modify |
2572 | + * it under the terms of the GNU General Public License as published by |
2573 | + * the Free Software Foundation; version 3. |
2574 | + * |
2575 | + * This program is distributed in the hope that it will be useful, |
2576 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2577 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2578 | + * GNU General Public License for more details. |
2579 | + * |
2580 | + * You should have received a copy of the GNU General Public License |
2581 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2582 | + */ |
2583 | + |
2584 | +import QtQuick 2.0 |
2585 | +import QtTest 1.0 |
2586 | +import ".." |
2587 | +import "../../../qml/Greeter" |
2588 | +import Ubuntu.Components 0.1 |
2589 | +import AccountsService 0.1 |
2590 | +import LightDM 0.1 as LightDM |
2591 | +import Unity.Test 0.1 as UT |
2592 | + |
2593 | +Item { |
2594 | + width: units.gu(120) |
2595 | + height: units.gu(80) |
2596 | + |
2597 | + property url defaultBackground: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg") |
2598 | + |
2599 | + Component.onCompleted: { |
2600 | + // set the mock mode before loading |
2601 | + LightDM.Greeter.mockMode = "full"; |
2602 | + LightDM.Users.mockMode = "full"; |
2603 | + loader.active = true; |
2604 | + } |
2605 | + |
2606 | + Loader { |
2607 | + id: loader |
2608 | + |
2609 | + active: false |
2610 | + anchors.fill: parent |
2611 | + |
2612 | + property bool itemDestroyed: false |
2613 | + sourceComponent: Component { |
2614 | + Greeter { |
2615 | + width: loader.width |
2616 | + height: loader.height |
2617 | + background: defaultBackground |
2618 | + viewSource: Qt.resolvedUrl("TestView.qml") |
2619 | + |
2620 | + Component.onDestruction: { |
2621 | + loader.itemDestroyed = true; |
2622 | + } |
2623 | + } |
2624 | + } |
2625 | + } |
2626 | + |
2627 | + SignalSpy { |
2628 | + id: teaseSpy |
2629 | + target: loader.item |
2630 | + signalName: "tease" |
2631 | + } |
2632 | + |
2633 | + SignalSpy { |
2634 | + id: sessionStartedSpy |
2635 | + target: loader.item |
2636 | + signalName: "sessionStarted" |
2637 | + } |
2638 | + |
2639 | + SignalSpy { |
2640 | + id: emergencyCallSpy |
2641 | + target: loader.item |
2642 | + signalName: "emergencyCall" |
2643 | + } |
2644 | + |
2645 | + UT.UnityTestCase { |
2646 | + id: testCase |
2647 | + name: "Greeter" |
2648 | + when: windowShown |
2649 | + |
2650 | + property Item greeter: loader.status === Loader.Ready ? loader.item : null |
2651 | + property Item view |
2652 | + |
2653 | + SignalSpy { |
2654 | + id: viewShowMessageSpy |
2655 | + target: testCase.view |
2656 | + signalName: "_showMessageCalled" |
2657 | + } |
2658 | + |
2659 | + SignalSpy { |
2660 | + id: viewShowPromptSpy |
2661 | + target: testCase.view |
2662 | + signalName: "_showPromptCalled" |
2663 | + } |
2664 | + |
2665 | + SignalSpy { |
2666 | + id: viewShowLastChanceSpy |
2667 | + target: testCase.view |
2668 | + signalName: "_showLastChanceCalled" |
2669 | + } |
2670 | + |
2671 | + SignalSpy { |
2672 | + id: viewHideSpy |
2673 | + target: testCase.view |
2674 | + signalName: "_hideCalled" |
2675 | + } |
2676 | + |
2677 | + SignalSpy { |
2678 | + id: viewAuthenticationSucceededSpy |
2679 | + target: testCase.view |
2680 | + signalName: "_notifyAuthenticationSucceededCalled" |
2681 | + } |
2682 | + |
2683 | + SignalSpy { |
2684 | + id: viewAuthenticationFailedSpy |
2685 | + target: testCase.view |
2686 | + signalName: "_notifyAuthenticationFailedCalled" |
2687 | + } |
2688 | + |
2689 | + SignalSpy { |
2690 | + id: viewResetSpy |
2691 | + target: testCase.view |
2692 | + signalName: "_resetCalled" |
2693 | + } |
2694 | + |
2695 | + SignalSpy { |
2696 | + id: viewTryToUnlockSpy |
2697 | + target: testCase.view |
2698 | + signalName: "_tryToUnlockCalled" |
2699 | + } |
2700 | + |
2701 | + function init() { |
2702 | + teaseSpy.clear(); |
2703 | + sessionStartedSpy.clear(); |
2704 | + emergencyCallSpy.clear(); |
2705 | + viewShowMessageSpy.clear(); |
2706 | + viewShowPromptSpy.clear(); |
2707 | + viewShowLastChanceSpy.clear(); |
2708 | + viewHideSpy.clear(); |
2709 | + viewAuthenticationSucceededSpy.clear(); |
2710 | + viewAuthenticationFailedSpy.clear(); |
2711 | + viewResetSpy.clear(); |
2712 | + viewTryToUnlockSpy.clear(); |
2713 | + tryCompare(greeter, "waiting", false); |
2714 | + view = findChild(greeter, "testView"); |
2715 | + verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole)); |
2716 | + } |
2717 | + |
2718 | + function cleanup() { |
2719 | + loader.itemDestroyed = false; |
2720 | + loader.active = false; |
2721 | + tryCompare(loader, "status", Loader.Null); |
2722 | + tryCompare(loader, "item", null); |
2723 | + tryCompare(loader, "itemDestroyed", true); |
2724 | + loader.active = true; |
2725 | + tryCompare(loader, "status", Loader.Ready); |
2726 | + removeTimeConstraintsFromDirectionalDragAreas(loader.item); |
2727 | + } |
2728 | + |
2729 | + function getIndexOf(name) { |
2730 | + for (var i = 0; i < LightDM.Users.count; i++) { |
2731 | + if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) { |
2732 | + return i; |
2733 | + } |
2734 | + } |
2735 | + fail("Didn't find name") |
2736 | + return -1; |
2737 | + } |
2738 | + |
2739 | + function selectUser(name) { |
2740 | + var i = getIndexOf(name); |
2741 | + view.selected(i); |
2742 | + verifySelected(name); |
2743 | + return i; |
2744 | + } |
2745 | + |
2746 | + function verifySelected(name) { |
2747 | + var i = getIndexOf(name); |
2748 | + compare(view.currentIndex, i); |
2749 | + compare(AccountsService.user, name); |
2750 | + compare(LightDM.Greeter.authenticationUser, name); |
2751 | + } |
2752 | + |
2753 | + function verifyLoggedIn() { |
2754 | + tryCompare(sessionStartedSpy, "count", 1); |
2755 | + verify(viewAuthenticationSucceededSpy.count > 0); |
2756 | + compare(LightDM.Greeter.authenticated, true); |
2757 | + compare(greeter.shown, false); |
2758 | + } |
2759 | + |
2760 | + function test_unlockPass() { |
2761 | + selectUser("has-password"); |
2762 | + tryCompare(viewShowPromptSpy, "count", 1); |
2763 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2764 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2765 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2766 | + |
2767 | + view.responded("password"); |
2768 | + verifyLoggedIn(); |
2769 | + } |
2770 | + |
2771 | + function test_unlockFail() { |
2772 | + selectUser("has-password"); |
2773 | + tryCompare(viewShowPromptSpy, "count", 1); |
2774 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2775 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2776 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2777 | + |
2778 | + view.responded("wr0ng p4ssw0rd"); |
2779 | + tryCompare(viewAuthenticationFailedSpy, "count", 1); |
2780 | + |
2781 | + tryCompare(viewShowPromptSpy, "count", 2); |
2782 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2783 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2784 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2785 | + } |
2786 | + |
2787 | + function test_promptless() { |
2788 | + selectUser("no-password"); |
2789 | + tryCompare(viewAuthenticationSucceededSpy, "count", 1); |
2790 | + compare(sessionStartedSpy.count, 1); |
2791 | + compare(viewShowPromptSpy.count, 0); |
2792 | + compare(viewHideSpy.count, 0); |
2793 | + compare(view.locked, false); |
2794 | + } |
2795 | + |
2796 | + function test_twoFactorPass() { |
2797 | + selectUser("two-factor"); |
2798 | + tryCompare(viewShowPromptSpy, "count", 1); |
2799 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2800 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2801 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2802 | + |
2803 | + view.responded("password"); |
2804 | + tryCompare(viewShowPromptSpy, "count", 2); |
2805 | + compare(viewShowPromptSpy.signalArguments[1][0], "otp"); |
2806 | + compare(viewShowPromptSpy.signalArguments[1][1], false); |
2807 | + compare(viewShowPromptSpy.signalArguments[1][2], false); |
2808 | + |
2809 | + view.responded("otp"); |
2810 | + verifyLoggedIn(); |
2811 | + } |
2812 | + |
2813 | + function test_twoFactorFailOnFirst() { |
2814 | + selectUser("two-factor"); |
2815 | + tryCompare(viewShowPromptSpy, "count", 1); |
2816 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2817 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2818 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2819 | + |
2820 | + view.responded("wr0ng p4ssw0rd"); |
2821 | + tryCompare(viewAuthenticationFailedSpy, "count", 1); |
2822 | + |
2823 | + tryCompare(viewShowPromptSpy, "count", 2); |
2824 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2825 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2826 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2827 | + } |
2828 | + |
2829 | + function test_twoFactorFailOnSecond() { |
2830 | + selectUser("two-factor"); |
2831 | + tryCompare(viewShowPromptSpy, "count", 1); |
2832 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2833 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2834 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2835 | + |
2836 | + view.responded("password"); |
2837 | + tryCompare(viewShowPromptSpy, "count", 2); |
2838 | + compare(viewShowPromptSpy.signalArguments[1][0], "otp"); |
2839 | + compare(viewShowPromptSpy.signalArguments[1][1], false); |
2840 | + compare(viewShowPromptSpy.signalArguments[1][2], false); |
2841 | + |
2842 | + view.responded("wr0ng p4ssw0rd"); |
2843 | + tryCompare(viewAuthenticationFailedSpy, "count", 1); |
2844 | + |
2845 | + tryCompare(viewShowPromptSpy, "count", 3); |
2846 | + compare(viewShowPromptSpy.signalArguments[0][0], "Password"); |
2847 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2848 | + compare(viewShowPromptSpy.signalArguments[0][2], true); |
2849 | + } |
2850 | + |
2851 | + function test_htmlInfoPrompt() { |
2852 | + selectUser("html-info-prompt"); |
2853 | + tryCompare(viewShowPromptSpy, "count", 1); |
2854 | + compare(viewShowMessageSpy.count, 1); |
2855 | + compare(viewShowMessageSpy.signalArguments[0][0], "<b>&</b>"); |
2856 | + } |
2857 | + |
2858 | + function test_multiInfoPrompt() { |
2859 | + selectUser("multi-info-prompt"); |
2860 | + tryCompare(viewShowPromptSpy, "count", 1); |
2861 | + compare(viewShowMessageSpy.count, 3); |
2862 | + compare(viewShowMessageSpy.signalArguments[0][0], "Welcome to Unity Greeter"); |
2863 | + compare(viewShowMessageSpy.signalArguments[1][0], "<font color=\"#df382c\">This is an error</font>"); |
2864 | + compare(viewShowMessageSpy.signalArguments[2][0], "You should have seen three messages"); |
2865 | + } |
2866 | + |
2867 | + function test_waiting() { |
2868 | + // Make sure we unset 'waiting' on prompt |
2869 | + selectUser("has-password"); |
2870 | + compare(greeter.waiting, true); |
2871 | + tryCompare(greeter, "waiting", false); |
2872 | + |
2873 | + // Make sure we unset 'waiting' on authentication |
2874 | + selectUser("no-password"); |
2875 | + compare(greeter.waiting, true); |
2876 | + tryCompare(greeter, "waiting", false); |
2877 | + } |
2878 | + |
2879 | + function test_locked() { |
2880 | + selectUser("has-password"); |
2881 | + compare(view.locked, true); |
2882 | + |
2883 | + LightDM.Greeter.active = false; |
2884 | + compare(view.locked, false); |
2885 | + LightDM.Greeter.active = true; |
2886 | + |
2887 | + greeter.forcedUnlock = true; |
2888 | + compare(view.locked, false); |
2889 | + greeter.forcedUnlock = false; |
2890 | + |
2891 | + selectUser("no-password"); |
2892 | + tryCompare(view, "locked", false); |
2893 | + selectUser("has-password"); |
2894 | + } |
2895 | + |
2896 | + function test_fullyShown() { |
2897 | + compare(greeter.fullyShown, true); |
2898 | + view.hide(); |
2899 | + compare(greeter.fullyShown, false); |
2900 | + } |
2901 | + |
2902 | + function test_alphanumeric() { |
2903 | + selectUser("has-password"); |
2904 | + compare(view.alphanumeric, true); |
2905 | + selectUser("has-pin"); |
2906 | + compare(view.alphanumeric, false); |
2907 | + } |
2908 | + |
2909 | + function test_background() { |
2910 | + greeter.background = "testing"; |
2911 | + compare(view.background, Qt.resolvedUrl("testing")); |
2912 | + } |
2913 | + |
2914 | + function test_notifyAboutToFocusApp() { |
2915 | + greeter.notifyAboutToFocusApp("fake-app"); |
2916 | + compare(viewTryToUnlockSpy.count, 1); |
2917 | + compare(viewTryToUnlockSpy.signalArguments[0][0], false); |
2918 | + } |
2919 | + |
2920 | + function test_notifyShowingDashFromDrag() { |
2921 | + compare(greeter.notifyShowingDashFromDrag("fake-app"), true); |
2922 | + compare(viewTryToUnlockSpy.count, 1); |
2923 | + compare(viewTryToUnlockSpy.signalArguments[0][0], true); |
2924 | + } |
2925 | + |
2926 | + function test_dragHandleLeftMargin() { |
2927 | + compare(view.dragHandleLeftMargin, 0); |
2928 | + greeter.dragHandleLeftMargin = 5; |
2929 | + compare(view.dragHandleLeftMargin, 5); |
2930 | + } |
2931 | + |
2932 | + function test_launcherOffset() { |
2933 | + compare(view.launcherOffset, 0); |
2934 | + greeter.launcherOffset = 5; |
2935 | + tryCompare(view, "launcherOffset", 5); |
2936 | + } |
2937 | + |
2938 | + function test_laucherOffsetAnimation() { |
2939 | + // Our logic for smoothing launcherOffset when it suddenly goes to |
2940 | + // zero is a bit complicated. Let's just make sure it works here. |
2941 | + |
2942 | + launcherOffsetWatcher.target = view; |
2943 | + |
2944 | + // should follow immediately |
2945 | + launcherOffsetWatcher.values = []; |
2946 | + greeter.launcherOffset = 100; |
2947 | + compare(view.launcherOffset, 100); |
2948 | + compare(launcherOffsetWatcher.values.length, 1); |
2949 | + |
2950 | + // should interpolate values until it reaches 0 |
2951 | + launcherOffsetWatcher.values = []; |
2952 | + greeter.launcherOffset = 0; |
2953 | + tryCompare(view, "launcherOffset", 0); |
2954 | + verify(launcherOffsetWatcher.values.length > 1); |
2955 | + for (var i = 0; i < launcherOffsetWatcher.values.length - 1; ++i) { |
2956 | + verify(launcherOffsetWatcher.values[i] > 0.0); |
2957 | + verify(launcherOffsetWatcher.values[i] < 100.0); |
2958 | + } |
2959 | + } |
2960 | + Connections { |
2961 | + id: launcherOffsetWatcher |
2962 | + property var values: [] |
2963 | + onLauncherOffsetChanged: { |
2964 | + values.push(target.launcherOffset); |
2965 | + } |
2966 | + } |
2967 | + |
2968 | + function test_backgroundTopMargin() { |
2969 | + compare(view.backgroundTopMargin, 0); |
2970 | + greeter.y = 5; |
2971 | + compare(view.backgroundTopMargin, -5); |
2972 | + } |
2973 | + |
2974 | + function test_differentPrompt() { |
2975 | + selectUser("different-prompt"); |
2976 | + tryCompare(viewShowPromptSpy, "count", 1); |
2977 | + compare(viewShowPromptSpy.signalArguments[0][0], "Secret word"); |
2978 | + compare(viewShowPromptSpy.signalArguments[0][1], true); |
2979 | + compare(viewShowPromptSpy.signalArguments[0][2], false); |
2980 | + } |
2981 | + |
2982 | + function test_authError() { |
2983 | + selectUser("auth-error"); |
2984 | + tryCompare(viewAuthenticationFailedSpy, "count", 1); |
2985 | + compare(viewShowPromptSpy.count, 0); |
2986 | + compare(view.locked, true); |
2987 | + } |
2988 | + |
2989 | + function test_statsWelcomeScreen() { |
2990 | + // Test logic in greeter that turns statsWelcomeScreen setting into infographic changes |
2991 | + selectUser("has-password"); |
2992 | + compare(LightDM.Infographic.username, "has-password"); |
2993 | + AccountsService.statsWelcomeScreen = false; |
2994 | + compare(LightDM.Infographic.username, ""); |
2995 | + AccountsService.statsWelcomeScreen = true; |
2996 | + compare(LightDM.Infographic.username, "has-password"); |
2997 | + } |
2998 | + |
2999 | + function test_dbusRequestAuthenticationUser() { |
3000 | + selectUser("no-password"); |
3001 | + LightDM.Greeter.requestAuthenticationUser("has-password"); |
3002 | + verifySelected("has-password"); |
3003 | + } |
3004 | + |
3005 | + function test_dbusHideGreeter() { |
3006 | + compare(view.required, true); |
3007 | + LightDM.Greeter.hideGreeter(); |
3008 | + compare(view.required, false); |
3009 | + compare(greeter.required, false); |
3010 | + } |
3011 | + |
3012 | + function test_dbusShowGreeterFromHiddenState() { |
3013 | + greeter.hide(); |
3014 | + compare(greeter.required, false); |
3015 | + |
3016 | + LightDM.Greeter.showGreeter(); |
3017 | + compare(greeter.required, true); |
3018 | + compare(greeter.fullyShown, true); |
3019 | + view = findChild(greeter, "testView"); |
3020 | + compare(view.required, true); |
3021 | + |
3022 | + // Can't test some of the stuff called on 'view' here because |
3023 | + // the view was torn down and created again. So the spies missed |
3024 | + // the good stuff while it down. See next test for more. |
3025 | + } |
3026 | + |
3027 | + function test_dbusShowGreeterFromShownState() { |
3028 | + selectUser("has-password"); |
3029 | + compare(viewResetSpy.count, 1); |
3030 | + tryCompare(viewShowPromptSpy, "count", 1); |
3031 | + |
3032 | + viewResetSpy.clear(); |
3033 | + viewShowPromptSpy.clear(); |
3034 | + |
3035 | + LightDM.Greeter.showGreeter(); |
3036 | + compare(viewResetSpy.count, 1); |
3037 | + } |
3038 | + } |
3039 | +} |
3040 | |
3041 | === removed file 'tests/qmltests/Greeter/tst_MultiGreeter.qml' |
3042 | --- tests/qmltests/Greeter/tst_MultiGreeter.qml 2015-02-02 14:28:32 +0000 |
3043 | +++ tests/qmltests/Greeter/tst_MultiGreeter.qml 1970-01-01 00:00:00 +0000 |
3044 | @@ -1,475 +0,0 @@ |
3045 | -/* |
3046 | - * Copyright 2013 Canonical Ltd. |
3047 | - * |
3048 | - * This program is free software; you can redistribute it and/or modify |
3049 | - * it under the terms of the GNU General Public License as published by |
3050 | - * the Free Software Foundation; version 3. |
3051 | - * |
3052 | - * This program is distributed in the hope that it will be useful, |
3053 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3054 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3055 | - * GNU General Public License for more details. |
3056 | - * |
3057 | - * You should have received a copy of the GNU General Public License |
3058 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3059 | - */ |
3060 | - |
3061 | -import QtQuick 2.0 |
3062 | -import QtQuick.Layouts 1.1 |
3063 | -import QtTest 1.0 |
3064 | -import ".." |
3065 | -import "../../../qml/Greeter" |
3066 | -import Ubuntu.Components 0.1 |
3067 | -import LightDM 0.1 as LightDM |
3068 | -import Unity.Test 0.1 as UT |
3069 | - |
3070 | -Item { |
3071 | - id: root |
3072 | - width: units.gu(140) |
3073 | - height: units.gu(80) |
3074 | - |
3075 | - QtObject { |
3076 | - id: fakeInputMethod |
3077 | - property bool visible: fakeKeyboard.visible |
3078 | - property var keyboardRectangle: QtObject { |
3079 | - property real x: fakeKeyboard.x |
3080 | - property real y: fakeKeyboard.y |
3081 | - property real width: fakeKeyboard.width |
3082 | - property real height: fakeKeyboard.height |
3083 | - } |
3084 | - } |
3085 | - |
3086 | - Binding { |
3087 | - target: LightDM.Greeter |
3088 | - property: "mockMode" |
3089 | - value: "full" |
3090 | - } |
3091 | - Binding { |
3092 | - target: LightDM.Users |
3093 | - property: "mockMode" |
3094 | - value: "full" |
3095 | - } |
3096 | - |
3097 | - Greeter { |
3098 | - id: greeter |
3099 | - width: units.gu(120) |
3100 | - height: root.height |
3101 | - inputMethod: fakeInputMethod |
3102 | - locked: !LightDM.Greeter.authenticated |
3103 | - } |
3104 | - |
3105 | - Component { |
3106 | - id: greeterComponent |
3107 | - Greeter { |
3108 | - SignalSpy { |
3109 | - objectName: "selectedSpy" |
3110 | - target: parent |
3111 | - signalName: "selected" |
3112 | - } |
3113 | - } |
3114 | - } |
3115 | - |
3116 | - Rectangle { |
3117 | - id: fakeKeyboard |
3118 | - color: "green" |
3119 | - opacity: 0.7 |
3120 | - anchors.bottom: root.bottom |
3121 | - width: greeter.width |
3122 | - height: greeter.height * 0.6 |
3123 | - visible: keyboardVisibleCheckbox.checked |
3124 | - Text { |
3125 | - text: "Keyboard Rectangle" |
3126 | - color: "yellow" |
3127 | - font.bold: true |
3128 | - fontSizeMode: Text.Fit |
3129 | - minimumPixelSize: 10; font.pixelSize: 200 |
3130 | - verticalAlignment: Text.AlignVCenter |
3131 | - x: (parent.width - width) / 2 |
3132 | - y: (parent.height - height) / 2 |
3133 | - width: parent.width |
3134 | - height: parent.height |
3135 | - } |
3136 | - } |
3137 | - |
3138 | - Item { |
3139 | - anchors { |
3140 | - top: root.top |
3141 | - bottom: root.bottom |
3142 | - left: greeter.right |
3143 | - right: root.right |
3144 | - } |
3145 | - RowLayout { |
3146 | - Layout.fillWidth: true |
3147 | - CheckBox { |
3148 | - id: keyboardVisibleCheckbox |
3149 | - } |
3150 | - Label { text: "Keyboard Visible"; anchors.verticalCenter: parent.verticalCenter } |
3151 | - } |
3152 | - } |
3153 | - |
3154 | - SignalSpy { |
3155 | - id: unlockSpy |
3156 | - target: greeter |
3157 | - signalName: "unlocked" |
3158 | - } |
3159 | - |
3160 | - SignalSpy { |
3161 | - id: selectionSpy |
3162 | - target: greeter |
3163 | - signalName: "selected" |
3164 | - } |
3165 | - |
3166 | - SignalSpy { |
3167 | - id: tappedSpy |
3168 | - target: greeter |
3169 | - signalName: "tapped" |
3170 | - } |
3171 | - |
3172 | - UT.UnityTestCase { |
3173 | - name: "MultiGreeter" |
3174 | - when: windowShown |
3175 | - |
3176 | - function cleanup() { |
3177 | - keyboardVisibleCheckbox.checked = false; |
3178 | - } |
3179 | - |
3180 | - function select_index(i) { |
3181 | - // We could be anywhere in list; find target index to know which direction |
3182 | - var userlist = findChild(greeter, "userList") |
3183 | - if (userlist.currentIndex == i) |
3184 | - keyClick(Qt.Key_Escape) // Reset state if we're not moving |
3185 | - while (userlist.currentIndex != i) { |
3186 | - var next = userlist.currentIndex + 1 |
3187 | - if (userlist.currentIndex > i) { |
3188 | - next = userlist.currentIndex - 1 |
3189 | - } |
3190 | - var account = findChild(greeter, "username"+next) |
3191 | - mouseClick(account, 1, 1) |
3192 | - tryCompare(userlist, "currentIndex", next) |
3193 | - tryCompare(userlist, "movingInternally", false) |
3194 | - } |
3195 | - } |
3196 | - |
3197 | - function select_user(name) { |
3198 | - // We could be anywhere in list; find target index to know which direction |
3199 | - for (var i = 0; i < greeter.model.count; i++) { |
3200 | - if (greeter.model.data(i, LightDM.UserRoles.NameRole) == name) { |
3201 | - break |
3202 | - } |
3203 | - } |
3204 | - if (i == greeter.model.count) { |
3205 | - fail("Didn't find name") |
3206 | - return -1 |
3207 | - } |
3208 | - select_index(i) |
3209 | - return i |
3210 | - } |
3211 | - |
3212 | - function test_properties() { |
3213 | - compare(greeter.multiUser, true) |
3214 | - compare(greeter.narrowMode, false) |
3215 | - } |
3216 | - |
3217 | - function test_cycle_data() { |
3218 | - var data = new Array() |
3219 | - for (var i = 0; i < greeter.model.count; i++) { |
3220 | - data[i] = {tag: greeter.model.data(i, LightDM.UserRoles.NameRole), uid: i } |
3221 | - } |
3222 | - return data |
3223 | - } |
3224 | - |
3225 | - function test_cycle(data) { |
3226 | - selectionSpy.clear(); |
3227 | - var userList = findChild(greeter, "userList") |
3228 | - var waitForSignal = data.uid != 0 && userList.currentIndex != data.uid |
3229 | - select_index(data.uid) |
3230 | - tryCompare(userList, "currentIndex", data.uid) |
3231 | - tryCompare(greeter, "locked", data.tag !== "no-password") |
3232 | - if (waitForSignal) { |
3233 | - selectionSpy.wait() |
3234 | - tryCompare(selectionSpy, "count", 1) |
3235 | - } |
3236 | - } |
3237 | - |
3238 | - function test_unlock_password() { |
3239 | - select_user("no-password") // to guarantee a selected signal |
3240 | - unlockSpy.clear() |
3241 | - select_user("has-password") |
3242 | - var passwordInput = findChild(greeter, "passwordInput") |
3243 | - tryCompare(passwordInput, "opacity", 1) |
3244 | - mouseClick(passwordInput, 1, 1) |
3245 | - compare(unlockSpy.count, 0) |
3246 | - typeString("password") |
3247 | - keyClick(Qt.Key_Enter) |
3248 | - unlockSpy.wait() |
3249 | - } |
3250 | - |
3251 | - function test_unlock_wrong_password() { |
3252 | - select_user("no-password") // to guarantee a selected signal |
3253 | - unlockSpy.clear() |
3254 | - select_user("has-password") |
3255 | - wait(0) // spin event loop to start any pending animations |
3256 | - var passwordInput = findChild(greeter, "passwordInput") |
3257 | - tryCompare(passwordInput, "opacity", 1) // wait for opacity animation to be finished |
3258 | - mouseClick(passwordInput, 1, 1) |
3259 | - compare(unlockSpy.count, 0) |
3260 | - typeString("wr0ng p4ssw0rd") |
3261 | - keyClick(Qt.Key_Enter) |
3262 | - compare(unlockSpy.count, 0) |
3263 | - } |
3264 | - |
3265 | - function test_unlock_no_password() { |
3266 | - unlockSpy.clear() |
3267 | - select_user("no-password") |
3268 | - var passwordInput = findChild(greeter, "passwordInput") |
3269 | - tryCompare(passwordInput, "opacity", 1) |
3270 | - mouseClick(passwordInput, 1, 1) |
3271 | - unlockSpy.wait() |
3272 | - compare(unlockSpy.count, 1) |
3273 | - } |
3274 | - |
3275 | - function test_empty_name() { |
3276 | - for (var i = 0; i < greeter.model.count; i++) { |
3277 | - if (greeter.model.data(i, LightDM.UserRoles.NameRole) == "empty-name") { |
3278 | - compare(greeter.model.data(i, LightDM.UserRoles.RealNameRole), greeter.model.data(i, LightDM.UserRoles.NameRole)) |
3279 | - return |
3280 | - } |
3281 | - } |
3282 | - fail("Didn't find empty-name") |
3283 | - } |
3284 | - |
3285 | - function test_auth_error() { |
3286 | - select_user("auth-error") |
3287 | - var passwordInput = findChild(greeter, "passwordInput") |
3288 | - tryCompare(passwordInput, "placeholderText", "Retry") |
3289 | - } |
3290 | - |
3291 | - function test_different_prompt() { |
3292 | - select_user("different-prompt") |
3293 | - var passwordInput = findChild(greeter, "passwordInput") |
3294 | - tryCompare(passwordInput, "placeholderText", "Secret word") |
3295 | - } |
3296 | - |
3297 | - function test_no_response() { |
3298 | - unlockSpy.clear() |
3299 | - select_user("no-response") |
3300 | - var passwordInput = findChild(greeter, "passwordInput") |
3301 | - tryCompare(passwordInput, "opacity", 1) |
3302 | - mouseClick(passwordInput, 1, 1) |
3303 | - compare(unlockSpy.count, 0) |
3304 | - typeString("password") |
3305 | - keyClick(Qt.Key_Enter) |
3306 | - tryCompare(passwordInput, "enabled", false) |
3307 | - keyClick(Qt.Key_Escape) |
3308 | - tryCompare(passwordInput, "enabled", true) |
3309 | - compare(unlockSpy.count, 0) |
3310 | - } |
3311 | - |
3312 | - function test_two_factor_correct() { |
3313 | - unlockSpy.clear() |
3314 | - select_user("two-factor") |
3315 | - var passwordInput = findChild(greeter, "passwordInput") |
3316 | - tryCompare(passwordInput, "opacity", 1) |
3317 | - tryCompare(passwordInput, "echoMode", TextInput.Password) |
3318 | - tryCompare(passwordInput, "placeholderText", "Password") |
3319 | - mouseClick(passwordInput, 1, 1) |
3320 | - compare(unlockSpy.count, 0) |
3321 | - typeString("password") |
3322 | - keyClick(Qt.Key_Enter) |
3323 | - tryCompare(passwordInput, "echoMode", TextInput.Normal) |
3324 | - tryCompare(passwordInput, "placeholderText", "otp") |
3325 | - tryCompare(passwordInput, "enabled", true) |
3326 | - typeString("otp") |
3327 | - keyClick(Qt.Key_Enter) |
3328 | - unlockSpy.wait() |
3329 | - } |
3330 | - |
3331 | - function test_two_factor_wrong1() { |
3332 | - unlockSpy.clear() |
3333 | - select_user("two-factor") |
3334 | - var passwordInput = findChild(greeter, "passwordInput") |
3335 | - tryCompare(passwordInput, "opacity", 1) |
3336 | - tryCompare(passwordInput, "placeholderText", "Password") |
3337 | - mouseClick(passwordInput, 1, 1) |
3338 | - compare(unlockSpy.count, 0) |
3339 | - typeString("wr0ng p4ssw0rd") |
3340 | - keyClick(Qt.Key_Enter) |
3341 | - tryCompare(passwordInput, "placeholderText", "Password") |
3342 | - tryCompare(passwordInput, "enabled", true) |
3343 | - compare(unlockSpy.count, 0) |
3344 | - } |
3345 | - |
3346 | - function test_two_factor_wrong2() { |
3347 | - unlockSpy.clear() |
3348 | - select_user("two-factor") |
3349 | - var passwordInput = findChild(greeter, "passwordInput") |
3350 | - tryCompare(passwordInput, "opacity", 1) |
3351 | - tryCompare(passwordInput, "placeholderText", "Password") |
3352 | - mouseClick(passwordInput, 1, 1) |
3353 | - compare(unlockSpy.count, 0) |
3354 | - typeString("password") |
3355 | - keyClick(Qt.Key_Enter) |
3356 | - tryCompare(passwordInput, "placeholderText", "otp") |
3357 | - tryCompare(passwordInput, "enabled", true) |
3358 | - typeString("wr0ng p4ssw0rd") |
3359 | - keyClick(Qt.Key_Enter) |
3360 | - tryCompare(passwordInput, "placeholderText", "Password") |
3361 | - tryCompare(passwordInput, "enabled", true) |
3362 | - compare(unlockSpy.count, 0) |
3363 | - } |
3364 | - |
3365 | - function test_unicode() { |
3366 | - var index = select_user("unicode") |
3367 | - var label = findChild(greeter, "username"+index) |
3368 | - tryCompare(label, "text", "가나다라마") |
3369 | - } |
3370 | - |
3371 | - function test_long_name() { |
3372 | - var index = select_user("long-name") |
3373 | - var label = findChild(greeter, "username"+index) |
3374 | - tryCompare(label, "truncated", true) |
3375 | - } |
3376 | - |
3377 | - function test_info_prompt() { |
3378 | - select_user("info-prompt") |
3379 | - var label = findChild(greeter, "infoLabel") |
3380 | - tryCompare(label, "text", "Welcome to Unity Greeter") |
3381 | - tryCompare(label, "opacity", 1) |
3382 | - tryCompare(label, "clip", true) |
3383 | - tryCompareFunction(function() {return label.contentWidth > label.width;}, false) // c.f. wide-info-prompt |
3384 | - var passwordInput = findChild(greeter, "passwordInput") |
3385 | - mouseClick(passwordInput, 1, 1) |
3386 | - keyClick(Qt.Key_Escape) |
3387 | - } |
3388 | - |
3389 | - function test_info_prompt_escape() { |
3390 | - select_user("info-prompt") |
3391 | - var passwordInput = findChild(greeter, "passwordInput") |
3392 | - mouseClick(passwordInput, 1, 1) |
3393 | - keyClick(Qt.Key_Escape) |
3394 | - var label = findChild(greeter, "infoLabel") |
3395 | - tryCompare(label, "text", "Welcome to Unity Greeter") |
3396 | - tryCompare(label, "opacity", 1) |
3397 | - } |
3398 | - |
3399 | - function test_wide_info_prompt() { |
3400 | - select_user("wide-info-prompt") |
3401 | - var label = findChild(greeter, "infoLabel") |
3402 | - tryCompare(label, "clip", true) |
3403 | - tryCompareFunction(function() {return label.contentWidth > label.width;}, true) |
3404 | - } |
3405 | - |
3406 | - function test_html_info_prompt() { |
3407 | - select_user("html-info-prompt") |
3408 | - var label = findChild(greeter, "infoLabel") |
3409 | - tryCompare(label, "text", "<b>&</b>") |
3410 | - } |
3411 | - |
3412 | - function test_long_info_prompt() { |
3413 | - select_user("long-info-prompt") |
3414 | - var label = findChild(greeter, "infoLabel") |
3415 | - 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.") |
3416 | - tryCompare(label, "textFormat", Text.StyledText) // for parsing above correctly |
3417 | - tryCompare(label, "clip", true) |
3418 | - tryCompareFunction(function() {return label.contentWidth > label.width;}, true) |
3419 | - } |
3420 | - |
3421 | - function test_multi_info_prompt() { |
3422 | - select_user("multi-info-prompt") |
3423 | - var label = findChild(greeter, "infoLabel") |
3424 | - tryCompare(label, "text", "Welcome to Unity Greeter<br><font color=\"#df382c\">This is an error</font><br>You should have seen three messages") |
3425 | - tryCompare(label, "textFormat", Text.StyledText) // for parsing above correctly |
3426 | - } |
3427 | - |
3428 | - function test_bg_color() { |
3429 | - var index = select_user("color-background") |
3430 | - compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>") |
3431 | - } |
3432 | - |
3433 | - function test_bg_none() { |
3434 | - var index = select_user("no-background") |
3435 | - compare(greeter.model.data(index, LightDM.UserRoles.BackgroundPathRole), "") |
3436 | - } |
3437 | - |
3438 | - function test_tappedSignal_data() { |
3439 | - return [ |
3440 | - {tag: "left", posX: units.gu(2)}, |
3441 | - {tag: "right", posX: greeter.width - units.gu(2)} |
3442 | - ] |
3443 | - } |
3444 | - |
3445 | - function test_tappedSignal(data) { |
3446 | - select_user("no-password"); |
3447 | - tappedSpy.clear(); |
3448 | - tap(greeter, data.posX, greeter.height - units.gu(1)) |
3449 | - tryCompare(tappedSpy, "count", 1) |
3450 | - } |
3451 | - |
3452 | - function test_teaseLockedUnlocked_data() { |
3453 | - return [ |
3454 | - {tag: "unlocked", locked: false, narrow: false}, |
3455 | - {tag: "locked", locked: true, narrow: false}, |
3456 | - ]; |
3457 | - } |
3458 | - |
3459 | - function test_teaseLockedUnlocked(data) { |
3460 | - tappedSpy.clear() |
3461 | - greeter.locked = data.locked; |
3462 | - |
3463 | - tap(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1)); |
3464 | - |
3465 | - if (!data.locked || data.narrow) { |
3466 | - tappedSpy.wait() |
3467 | - tryCompare(tappedSpy, "count", 1); |
3468 | - } else { |
3469 | - // waiting 100ms to make sure nothing happens |
3470 | - wait(100); |
3471 | - compare(tappedSpy.count, 0, "Greeter teasing not disabled even though it's locked."); |
3472 | - } |
3473 | - |
3474 | - // Reset value |
3475 | - greeter.locked = false; |
3476 | - } |
3477 | - |
3478 | - function test_dbus_set_active_entry() { |
3479 | - select_user("no-password") // to guarantee a selected signal |
3480 | - selectionSpy.clear() |
3481 | - LightDM.Greeter.requestAuthenticationUser("has-password") |
3482 | - |
3483 | - selectionSpy.wait() |
3484 | - tryCompare(selectionSpy, "count", 1) |
3485 | - |
3486 | - var userlist = findChild(greeter, "userList") |
3487 | - compare(greeter.model.data(userlist.currentIndex, LightDM.UserRoles.NameRole), "has-password") |
3488 | - } |
3489 | - |
3490 | - function test_initial_selected_signal() { |
3491 | - var greeterObj = greeterComponent.createObject(this) |
3492 | - var spy = findChild(greeterObj, "selectedSpy") |
3493 | - spy.wait() |
3494 | - tryCompare(spy, "count", 1) |
3495 | - greeterObj.destroy() |
3496 | - } |
3497 | - |
3498 | - function test_login_list_not_covered_by_keyboard() { |
3499 | - var loginList = findChild(greeter, "loginLoader").item; |
3500 | - compare(loginList.height, greeter.height); |
3501 | - |
3502 | - // when the vkb shows up, loginList is moved up to remain fully uncovered |
3503 | - |
3504 | - keyboardVisibleCheckbox.checked = true; |
3505 | - |
3506 | - tryCompare(loginList, "height", greeter.height - fakeInputMethod.keyboardRectangle.height); |
3507 | - tryCompareFunction( function() { |
3508 | - var loginListRect = loginList.mapToItem(greeter, 0, 0, loginList.width, loginList.height); |
3509 | - return loginListRect.y + loginListRect.height <= fakeInputMethod.keyboardRectangle.y; |
3510 | - }, true); |
3511 | - |
3512 | - // once the vkb goes away, loginList goes back to its full height |
3513 | - |
3514 | - keyboardVisibleCheckbox.checked = false; |
3515 | - |
3516 | - tryCompare(loginList, "height", greeter.height); |
3517 | - } |
3518 | - } |
3519 | -} |
3520 | |
3521 | === added file 'tests/qmltests/Greeter/tst_NarrowView.qml' |
3522 | --- tests/qmltests/Greeter/tst_NarrowView.qml 1970-01-01 00:00:00 +0000 |
3523 | +++ tests/qmltests/Greeter/tst_NarrowView.qml 2015-02-23 15:44:00 +0000 |
3524 | @@ -0,0 +1,522 @@ |
3525 | +/* |
3526 | + * Copyright 2014 Canonical Ltd. |
3527 | + * |
3528 | + * This program is free software; you can redistribute it and/or modify |
3529 | + * it under the terms of the GNU General Public License as published by |
3530 | + * the Free Software Foundation; version 3. |
3531 | + * |
3532 | + * This program is distributed in the hope that it will be useful, |
3533 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3534 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3535 | + * GNU General Public License for more details. |
3536 | + * |
3537 | + * You should have received a copy of the GNU General Public License |
3538 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3539 | + */ |
3540 | + |
3541 | +import QtQuick 2.0 |
3542 | +import QtTest 1.0 |
3543 | +import ".." |
3544 | +import "../../../qml/Greeter" |
3545 | +import LightDM 0.1 as LightDM |
3546 | +import Ubuntu.Components 0.1 |
3547 | +import Unity.Test 0.1 as UT |
3548 | + |
3549 | +Item { |
3550 | + id: root |
3551 | + width: units.gu(90) |
3552 | + height: units.gu(80) |
3553 | + |
3554 | + Row { |
3555 | + anchors.fill: parent |
3556 | + Loader { |
3557 | + id: loader |
3558 | + width: root.width - controls.width |
3559 | + height: parent.height |
3560 | + |
3561 | + property bool itemDestroyed: false |
3562 | + sourceComponent: Component { |
3563 | + NarrowView { |
3564 | + background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg") |
3565 | + userModel: LightDM.Users |
3566 | + infographicModel: LightDM.Infographic |
3567 | + |
3568 | + launcherOffset: parseFloat(launcherOffsetField.text) |
3569 | + currentIndex: parseInt(currentIndexField.text, 10) |
3570 | + delayMinutes: parseInt(delayMinutesField.text, 10) |
3571 | + backgroundTopMargin: parseFloat(backgroundTopMarginField.text) |
3572 | + locked: lockedCheckBox.checked |
3573 | + alphanumeric: alphanumericCheckBox.checked |
3574 | + |
3575 | + Component.onDestruction: { |
3576 | + loader.itemDestroyed = true |
3577 | + } |
3578 | + |
3579 | + onSelected: { |
3580 | + currentIndexField.text = index; |
3581 | + } |
3582 | + } |
3583 | + } |
3584 | + } |
3585 | + |
3586 | + Rectangle { |
3587 | + id: controls |
3588 | + color: "white" |
3589 | + width: units.gu(40) |
3590 | + height: parent.height |
3591 | + |
3592 | + Column { |
3593 | + anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) } |
3594 | + spacing: units.gu(1) |
3595 | + |
3596 | + Row { |
3597 | + Button { |
3598 | + text: "Show Last Chance" |
3599 | + onClicked: loader.item.showLastChance() |
3600 | + } |
3601 | + } |
3602 | + Row { |
3603 | + Button { |
3604 | + text: "Hide" |
3605 | + onClicked: loader.item.hide() |
3606 | + } |
3607 | + } |
3608 | + Row { |
3609 | + Button { |
3610 | + text: "Reset" |
3611 | + onClicked: loader.item.reset() |
3612 | + } |
3613 | + } |
3614 | + Row { |
3615 | + Button { |
3616 | + text: "Show Message" |
3617 | + onClicked: loader.item.showMessage(messageField.text) |
3618 | + } |
3619 | + TextField { |
3620 | + id: messageField |
3621 | + width: units.gu(10) |
3622 | + text: "" |
3623 | + } |
3624 | + } |
3625 | + Row { |
3626 | + Button { |
3627 | + text: "Show Prompt" |
3628 | + onClicked: loader.item.showPrompt(promptField.text, isSecretCheckBox.checked, isDefaultPromptCheckBox.checked) |
3629 | + } |
3630 | + TextField { |
3631 | + id: promptField |
3632 | + width: units.gu(10) |
3633 | + text: "" |
3634 | + } |
3635 | + CheckBox { |
3636 | + id: isSecretCheckBox |
3637 | + } |
3638 | + Label { |
3639 | + text: "secret" |
3640 | + } |
3641 | + CheckBox { |
3642 | + id: isDefaultPromptCheckBox |
3643 | + } |
3644 | + Label { |
3645 | + text: "default" |
3646 | + } |
3647 | + } |
3648 | + Row { |
3649 | + Button { |
3650 | + text: "Authenticated" |
3651 | + onClicked: { |
3652 | + if (successCheckBox.checked) { |
3653 | + loader.item.notifyAuthenticationSucceeded(); |
3654 | + } else { |
3655 | + loader.item.notifyAuthenticationFailed(); |
3656 | + } |
3657 | + } |
3658 | + } |
3659 | + CheckBox { |
3660 | + id: successCheckBox |
3661 | + } |
3662 | + Label { |
3663 | + text: "success" |
3664 | + } |
3665 | + } |
3666 | + Row { |
3667 | + Button { |
3668 | + text: "Try To Unlock" |
3669 | + onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked) |
3670 | + } |
3671 | + CheckBox { |
3672 | + id: toTheRightCheckBox |
3673 | + } |
3674 | + Label { |
3675 | + text: "toTheRight" |
3676 | + } |
3677 | + } |
3678 | + Row { |
3679 | + TextField { |
3680 | + id: launcherOffsetField |
3681 | + width: units.gu(10) |
3682 | + text: "0" |
3683 | + } |
3684 | + Label { |
3685 | + text: "launcherOffset" |
3686 | + } |
3687 | + } |
3688 | + Row { |
3689 | + TextField { |
3690 | + id: currentIndexField |
3691 | + width: units.gu(10) |
3692 | + text: "0" |
3693 | + } |
3694 | + Label { |
3695 | + text: "currentIndex" |
3696 | + } |
3697 | + } |
3698 | + Row { |
3699 | + TextField { |
3700 | + id: delayMinutesField |
3701 | + width: units.gu(10) |
3702 | + text: "0" |
3703 | + } |
3704 | + Label { |
3705 | + text: "delayMinutes" |
3706 | + } |
3707 | + } |
3708 | + Row { |
3709 | + TextField { |
3710 | + id: backgroundTopMarginField |
3711 | + width: units.gu(10) |
3712 | + text: "0" |
3713 | + } |
3714 | + Label { |
3715 | + text: "backgroundTopMargin" |
3716 | + } |
3717 | + } |
3718 | + Row { |
3719 | + CheckBox { |
3720 | + id: lockedCheckBox |
3721 | + } |
3722 | + Label { |
3723 | + text: "locked" |
3724 | + } |
3725 | + } |
3726 | + Row { |
3727 | + CheckBox { |
3728 | + id: alphanumericCheckBox |
3729 | + } |
3730 | + Label { |
3731 | + text: "alphanumeric" |
3732 | + } |
3733 | + } |
3734 | + Row { |
3735 | + Label { |
3736 | + text: "selected: " + selectedSpy.count |
3737 | + } |
3738 | + } |
3739 | + Row { |
3740 | + Label { |
3741 | + text: "responded: " + respondedSpy.count |
3742 | + } |
3743 | + } |
3744 | + Row { |
3745 | + Label { |
3746 | + text: "teased: " + teaseSpy.count |
3747 | + } |
3748 | + } |
3749 | + Row { |
3750 | + Label { |
3751 | + text: "emergency: " + emergencySpy.count |
3752 | + } |
3753 | + } |
3754 | + Row { |
3755 | + Button { |
3756 | + text: "Reload View" |
3757 | + onClicked: { |
3758 | + loader.active = false; |
3759 | + loader.active = true; |
3760 | + } |
3761 | + } |
3762 | + } |
3763 | + } |
3764 | + } |
3765 | + } |
3766 | + |
3767 | + Binding { |
3768 | + target: LightDM.Infographic |
3769 | + property: "username" |
3770 | + value: "single" |
3771 | + } |
3772 | + |
3773 | + SignalSpy { |
3774 | + id: selectedSpy |
3775 | + target: loader.item |
3776 | + signalName: "selected" |
3777 | + } |
3778 | + |
3779 | + SignalSpy { |
3780 | + id: respondedSpy |
3781 | + target: loader.item |
3782 | + signalName: "responded" |
3783 | + } |
3784 | + |
3785 | + SignalSpy { |
3786 | + id: teaseSpy |
3787 | + target: loader.item |
3788 | + signalName: "tease" |
3789 | + } |
3790 | + |
3791 | + SignalSpy { |
3792 | + id: emergencySpy |
3793 | + target: loader.item |
3794 | + signalName: "emergencyCall" |
3795 | + } |
3796 | + |
3797 | + SignalSpy { |
3798 | + id: infographicDataChangedSpy |
3799 | + target: LightDM.Infographic |
3800 | + signalName: "dataChanged" |
3801 | + } |
3802 | + |
3803 | + UT.UnityTestCase { |
3804 | + name: "NarrowView" |
3805 | + when: windowShown |
3806 | + |
3807 | + property Item view: loader.status === Loader.Ready ? loader.item : null |
3808 | + |
3809 | + function init() { |
3810 | + view.currentIndex = 0; // break binding with text field |
3811 | + |
3812 | + selectedSpy.clear(); |
3813 | + respondedSpy.clear(); |
3814 | + teaseSpy.clear(); |
3815 | + emergencySpy.clear(); |
3816 | + infographicDataChangedSpy.clear(); |
3817 | + } |
3818 | + |
3819 | + function cleanup() { |
3820 | + loader.itemDestroyed = false; |
3821 | + loader.active = false; |
3822 | + tryCompare(loader, "status", Loader.Null); |
3823 | + tryCompare(loader, "item", null); |
3824 | + tryCompare(loader, "itemDestroyed", true); |
3825 | + loader.active = true; |
3826 | + tryCompare(loader, "status", Loader.Ready); |
3827 | + removeTimeConstraintsFromDirectionalDragAreas(loader.item); |
3828 | + } |
3829 | + |
3830 | + function swipeAwayCover(toTheRight) { |
3831 | + if (toTheRight === undefined) { |
3832 | + toTheRight = false; |
3833 | + } |
3834 | + |
3835 | + tryCompare(view, "fullyShown", true); |
3836 | + var touchY = view.height / 2; |
3837 | + if (toTheRight) { |
3838 | + touchFlick(view, 0, touchY, view.width, touchY); |
3839 | + } else { |
3840 | + touchFlick(view, view.width, touchY, 0, touchY); |
3841 | + } |
3842 | + var coverPage = findChild(view, "coverPage"); |
3843 | + tryCompare(coverPage, "showProgress", 0); |
3844 | + waitForRendering(view); |
3845 | + } |
3846 | + |
3847 | + function enterPin(pin) { |
3848 | + for (var i = 0; i < pin.length; ++i) { |
3849 | + var character = pin.charAt(i); |
3850 | + var button = findChild(view, "pinPadButton" + character); |
3851 | + tap(button); |
3852 | + } |
3853 | + } |
3854 | + |
3855 | + function test_tease_data() { |
3856 | + return [ |
3857 | + {tag: "left", x: 0, offset: 0, count: 1}, |
3858 | + {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1}, |
3859 | + {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0}, |
3860 | + {tag: "right", x: view.width, offset: 0, count: 1}, |
3861 | + ] |
3862 | + } |
3863 | + function test_tease(data) { |
3864 | + view.dragHandleLeftMargin = data.offset; |
3865 | + tap(view, data.x, 0); |
3866 | + compare(teaseSpy.count, data.count); |
3867 | + } |
3868 | + |
3869 | + function test_respondedWithPin() { |
3870 | + view.locked = true; |
3871 | + swipeAwayCover(); |
3872 | + enterPin("1234"); |
3873 | + compare(respondedSpy.count, 1); |
3874 | + compare(respondedSpy.signalArguments[0][0], "1234"); |
3875 | + } |
3876 | + |
3877 | + function test_respondedWithPassphrase() { |
3878 | + view.locked = true; |
3879 | + view.alphanumeric = true; |
3880 | + swipeAwayCover(); |
3881 | + typeString("test"); |
3882 | + keyClick(Qt.Key_Enter); |
3883 | + compare(respondedSpy.count, 1); |
3884 | + compare(respondedSpy.signalArguments[0][0], "test"); |
3885 | + } |
3886 | + |
3887 | + function test_respondedWithSwipe_data() { |
3888 | + return [ |
3889 | + {tag: "left", toTheRight: false, hiddenX: -view.width}, |
3890 | + {tag: "right", toTheRight: true, hiddenX: view.width}, |
3891 | + ]; |
3892 | + } |
3893 | + function test_respondedWithSwipe(data) { |
3894 | + swipeAwayCover(data.toTheRight); |
3895 | + var coverPage = findChild(view, "coverPage"); |
3896 | + compare(coverPage.x, data.hiddenX); |
3897 | + compare(respondedSpy.count, 1); |
3898 | + compare(respondedSpy.signalArguments[0][0], ""); |
3899 | + } |
3900 | + |
3901 | + function test_emergencyCall() { |
3902 | + view.locked = true; |
3903 | + swipeAwayCover(); |
3904 | + var emergencyCallLabel = findChild(view, "emergencyCallLabel"); |
3905 | + tap(emergencyCallLabel); |
3906 | + compare(emergencySpy.count, 1); |
3907 | + } |
3908 | + |
3909 | + function test_fullyShown() { |
3910 | + tryCompare(view, "fullyShown", true); |
3911 | + swipeAwayCover(); |
3912 | + tryCompare(view, "fullyShown", false); |
3913 | + view.locked = true; |
3914 | + tryCompare(view, "fullyShown", true); |
3915 | + view.locked = false; |
3916 | + tryCompare(view, "fullyShown", false); |
3917 | + } |
3918 | + |
3919 | + function test_required() { |
3920 | + tryCompare(view, "required", true); |
3921 | + swipeAwayCover(); |
3922 | + tryCompare(view, "required", false); |
3923 | + view.locked = true; |
3924 | + tryCompare(view, "required", true); |
3925 | + view.locked = false; |
3926 | + tryCompare(view, "required", false); |
3927 | + } |
3928 | + |
3929 | + function test_tryToUnlock() { |
3930 | + var coverPage = findChild(view, "coverPage"); |
3931 | + tryCompare(coverPage, "showProgress", 1); |
3932 | + compare(view.tryToUnlock(false), true); |
3933 | + tryCompare(coverPage, "showProgress", 0); |
3934 | + compare(view.tryToUnlock(false), false); |
3935 | + } |
3936 | + |
3937 | + /* |
3938 | + Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359 |
3939 | + "User metrics can no longer be changed by double tap" |
3940 | + */ |
3941 | + function test_doubleTapSwitchesToNextInfographic() { |
3942 | + var infographicPrivate = findInvisibleChild(view, "infographicPrivate"); |
3943 | + verify(infographicPrivate); |
3944 | + |
3945 | + // wait for the UI to settle down before double tapping it |
3946 | + tryCompare(infographicPrivate, "animating", false); |
3947 | + |
3948 | + var dataCircle = findChild(view, "dataCircle"); |
3949 | + verify(dataCircle); |
3950 | + |
3951 | + tap(dataCircle); |
3952 | + wait(1); |
3953 | + tap(dataCircle); |
3954 | + |
3955 | + tryCompare(infographicDataChangedSpy, "count", 1); |
3956 | + } |
3957 | + |
3958 | + function test_movesBackIntoPlaceWhenNotDraggedFarEnough() { |
3959 | + var coverPage = findChild(view, "coverPage"); |
3960 | + |
3961 | + var dragEvaluator = findInvisibleChild(coverPage, "edgeDragEvaluator"); |
3962 | + verify(dragEvaluator); |
3963 | + |
3964 | + // Make it easier to get a rejection/rollback. Otherwise would have to inject |
3965 | + // a fake timer into dragEvaluator. |
3966 | + // Afterall, we are testing if the CoverPage indeed moves back on a |
3967 | + // rollback decision, not the drag evaluation itself. |
3968 | + dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2; |
3969 | + |
3970 | + // it starts as fully shown |
3971 | + compare(coverPage.x, 0); |
3972 | + |
3973 | + // then we drag it a bit |
3974 | + var startX = coverPage.width - 1; |
3975 | + var touchY = coverPage.height / 2; |
3976 | + var dragXDelta = -(dragEvaluator.minDragDistance * 0.3); |
3977 | + touchFlick(coverPage, |
3978 | + startX , touchY, // start pos |
3979 | + startX + dragXDelta, touchY, // end pos |
3980 | + true /* beginTouch */, false /* endTouch */); |
3981 | + |
3982 | + // which should make it move a bit |
3983 | + tryCompareFunction(function() {return coverPage.x < 0;}, true); |
3984 | + |
3985 | + // then we release it |
3986 | + touchRelease(coverPage, startX + dragXDelta, touchY); |
3987 | + |
3988 | + // which should make it move back into its original position as it didn't move |
3989 | + // far enough to have it hidden |
3990 | + tryCompare(coverPage, "x", 0); |
3991 | + } |
3992 | + |
3993 | + function test_dragToHide_data() { |
3994 | + return [ |
3995 | + {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1, hiddenX: -view.width}, |
3996 | + {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95, hiddenX: view.width}, |
3997 | + ]; |
3998 | + } |
3999 | + function test_dragToHide(data) { |
4000 | + var coverPage = findChild(view, "coverPage"); |
4001 | + compare(coverPage.x, 0); |
4002 | + compare(coverPage.visible, true); |
4003 | + compare(coverPage.shown, true); |
4004 | + compare(coverPage.showProgress, 1); |
4005 | + compare(view.fullyShown, true); |
4006 | + |
4007 | + touchFlick(view, |
4008 | + data.startX, view.height / 2, // start pos |
4009 | + data.endX, view.height / 2); // end pos |
4010 | + |
4011 | + tryCompare(coverPage, "x", data.hiddenX); |
4012 | + tryCompare(coverPage, "visible", false); |
4013 | + tryCompare(coverPage, "shown", false); |
4014 | + tryCompare(coverPage, "showProgress", 0); |
4015 | + compare(view.fullyShown, false); |
4016 | + } |
4017 | + |
4018 | + function test_hiddenViewRemainsHiddenAfterResize_data() { |
4019 | + return [ |
4020 | + {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1}, |
4021 | + {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95}, |
4022 | + ]; |
4023 | + } |
4024 | + function test_hiddenViewRemainsHiddenAfterResize(data) { |
4025 | + touchFlick(view, |
4026 | + data.startX, view.height / 2, // start pos |
4027 | + data.endX, view.height / 2); // end pos |
4028 | + |
4029 | + var coverPage = findChild(view, "coverPage"); |
4030 | + tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width); |
4031 | + tryCompare(coverPage, "visible", false); |
4032 | + tryCompare(coverPage, "shown", false); |
4033 | + tryCompare(coverPage, "showProgress", 0); |
4034 | + |
4035 | + // flip dimensions to simulate an orientation change |
4036 | + view.width = loader.height; |
4037 | + view.height = loader.width; |
4038 | + |
4039 | + // All properties should remain consistent |
4040 | + tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width); |
4041 | + tryCompare(coverPage, "visible", false); |
4042 | + tryCompare(coverPage, "shown", false); |
4043 | + tryCompare(coverPage, "showProgress", 0); |
4044 | + } |
4045 | + } |
4046 | +} |
4047 | |
4048 | === removed file 'tests/qmltests/Greeter/tst_SingleGreeter.qml' |
4049 | --- tests/qmltests/Greeter/tst_SingleGreeter.qml 2014-12-08 18:08:38 +0000 |
4050 | +++ tests/qmltests/Greeter/tst_SingleGreeter.qml 1970-01-01 00:00:00 +0000 |
4051 | @@ -1,244 +0,0 @@ |
4052 | -/* |
4053 | - * Copyright 2013 Canonical Ltd. |
4054 | - * |
4055 | - * This program is free software; you can redistribute it and/or modify |
4056 | - * it under the terms of the GNU General Public License as published by |
4057 | - * the Free Software Foundation; version 3. |
4058 | - * |
4059 | - * This program is distributed in the hope that it will be useful, |
4060 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4061 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4062 | - * GNU General Public License for more details. |
4063 | - * |
4064 | - * You should have received a copy of the GNU General Public License |
4065 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4066 | - */ |
4067 | - |
4068 | -import QtQuick 2.0 |
4069 | -import QtTest 1.0 |
4070 | -import ".." |
4071 | -import "../../../qml/Greeter" |
4072 | -import AccountsService 0.1 |
4073 | -import LightDM 0.1 as LightDM |
4074 | -import Ubuntu.Components 0.1 |
4075 | -import Unity.Test 0.1 as UT |
4076 | - |
4077 | -Rectangle { |
4078 | - color: "darkblue" |
4079 | - width: units.gu(60) |
4080 | - height: units.gu(80) |
4081 | - |
4082 | - Button { |
4083 | - anchors.centerIn: parent |
4084 | - text: "Show Greeter" |
4085 | - onClicked: { |
4086 | - if (greeterLoader.item) |
4087 | - greeterLoader.item.show(); |
4088 | - } |
4089 | - } |
4090 | - |
4091 | - Loader { |
4092 | - id: greeterLoader |
4093 | - anchors.fill: parent |
4094 | - |
4095 | - property bool itemDestroyed: false |
4096 | - |
4097 | - sourceComponent: Component { |
4098 | - Greeter { |
4099 | - width: greeterLoader.width |
4100 | - height: greeterLoader.height |
4101 | - |
4102 | - Component.onDestruction: { |
4103 | - greeterLoader.itemDestroyed = true; |
4104 | - } |
4105 | - SignalSpy { |
4106 | - objectName: "selectedSpy" |
4107 | - target: parent |
4108 | - signalName: "selected" |
4109 | - } |
4110 | - } |
4111 | - } |
4112 | - } |
4113 | - |
4114 | - SignalSpy { |
4115 | - id: unlockSpy |
4116 | - target: greeterLoader.item |
4117 | - signalName: "unlocked" |
4118 | - } |
4119 | - |
4120 | - SignalSpy { |
4121 | - id: tappedSpy |
4122 | - target: greeterLoader.item |
4123 | - signalName: "tapped" |
4124 | - } |
4125 | - |
4126 | - SignalSpy { |
4127 | - id: infographicDataChangedSpy |
4128 | - target: LightDM.Infographic |
4129 | - signalName: "dataChanged" |
4130 | - } |
4131 | - |
4132 | - UT.UnityTestCase { |
4133 | - name: "SingleGreeter" |
4134 | - when: windowShown |
4135 | - |
4136 | - property Greeter greeter: greeterLoader.item |
4137 | - |
4138 | - function cleanup() { |
4139 | - AccountsService.statsWelcomeScreen = true |
4140 | - |
4141 | - // force a reload so that we get a fresh Greeter for the next test |
4142 | - greeterLoader.itemDestroyed = false; |
4143 | - greeterLoader.active = false; |
4144 | - tryCompare(greeterLoader, "itemDestroyed", true); |
4145 | - |
4146 | - unlockSpy.clear(); |
4147 | - tappedSpy.clear(); |
4148 | - |
4149 | - greeterLoader.active = true; |
4150 | - tryCompare(greeterLoader, "status", Loader.Ready); |
4151 | - removeTimeConstraintsFromDirectionalDragAreas(greeterLoader.item); |
4152 | - } |
4153 | - |
4154 | - function test_properties() { |
4155 | - compare(greeter.multiUser, false) |
4156 | - compare(greeter.narrowMode, true) |
4157 | - } |
4158 | - |
4159 | - function test_teasingArea_data() { |
4160 | - return [ |
4161 | - {tag: "left", posX: units.gu(2), leftPressed: true, rightPressed: false}, |
4162 | - {tag: "right", posX: greeter.width - units.gu(2), leftPressed: false, rightPressed: true} |
4163 | - ] |
4164 | - } |
4165 | - |
4166 | - function test_teasingArea(data) { |
4167 | - tappedSpy.clear() |
4168 | - tap(greeter, data.posX, greeter.height - units.gu(1)) |
4169 | - tappedSpy.wait() |
4170 | - tryCompare(tappedSpy, "count", 1) |
4171 | - } |
4172 | - |
4173 | - function test_statsWelcomeScreen() { |
4174 | - // Test logic in greeter that turns statsWelcomeScreen setting into infographic changes |
4175 | - compare(AccountsService.statsWelcomeScreen, true) |
4176 | - tryCompare(LightDM.Infographic, "username", "single") |
4177 | - AccountsService.statsWelcomeScreen = false |
4178 | - tryCompare(LightDM.Infographic, "username", "") |
4179 | - AccountsService.statsWelcomeScreen = true |
4180 | - tryCompare(LightDM.Infographic, "username", "single") |
4181 | - } |
4182 | - |
4183 | - function test_initial_selected_signal() { |
4184 | - var selectedSpy = findChild(greeter, "selectedSpy"); |
4185 | - selectedSpy.wait(); |
4186 | - tryCompare(selectedSpy, "count", 1); |
4187 | - } |
4188 | - |
4189 | - /* |
4190 | - Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359 |
4191 | - "User metrics can no longer be changed by double tap" |
4192 | - */ |
4193 | - function test_doubleTapSwitchesToNextInfographic() { |
4194 | - infographicDataChangedSpy.clear(); |
4195 | - |
4196 | - var infographicPrivate = findInvisibleChild(greeter, "infographicPrivate"); |
4197 | - verify(infographicPrivate); |
4198 | - |
4199 | - // wait for the UI to settle down before double tapping it |
4200 | - tryCompare(infographicPrivate, "animating", false); |
4201 | - |
4202 | - var dataCircle = findChild(greeter, "dataCircle"); |
4203 | - verify(dataCircle); |
4204 | - |
4205 | - tap(dataCircle); |
4206 | - wait(1); |
4207 | - tap(dataCircle); |
4208 | - |
4209 | - tryCompare(infographicDataChangedSpy, "count", 1); |
4210 | - } |
4211 | - |
4212 | - function test_movesBackIntoPlaceWhenNotDraggedFarEnough() { |
4213 | - |
4214 | - var dragEvaluator = findInvisibleChild(greeter, "edgeDragEvaluator"); |
4215 | - verify(dragEvaluator); |
4216 | - |
4217 | - // Make it easier to get a rejection/rollback. Otherwise would have to inject |
4218 | - // a fake timer into dragEvaluator. |
4219 | - // Afterall, we are testing if the Greeter indeed moves back on a |
4220 | - // rollback decision, not the drag evaluation itself. |
4221 | - dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2; |
4222 | - |
4223 | - // it starts as fully shown |
4224 | - compare(greeter.x, 0); |
4225 | - |
4226 | - // then we drag it a bit |
4227 | - var startX = greeter.width - 1; |
4228 | - var touchY = greeter.height / 2; |
4229 | - var dragXDelta = -(dragEvaluator.minDragDistance * 0.3); |
4230 | - touchFlick(greeter, |
4231 | - startX , touchY, // start pos |
4232 | - startX + dragXDelta, touchY, // end pos |
4233 | - true /* beginTouch */, false /* endTouch */); |
4234 | - |
4235 | - // which should make it move a bit |
4236 | - tryCompareFunction(function(){return greeter.x < 0;}, true); |
4237 | - |
4238 | - // then we release it |
4239 | - touchRelease(greeter, startX + dragXDelta, touchY); |
4240 | - |
4241 | - // which should make it move back into its original position as it didn't move |
4242 | - // far enough to have it hidden |
4243 | - tryCompare(greeter, "x", 0); |
4244 | - } |
4245 | - |
4246 | - function test_dragToHide_data() { |
4247 | - return [ |
4248 | - {tag: "left", startX: greeter.width * 0.95, endX: greeter.width * 0.1, hiddenX: -greeter.width}, |
4249 | - {tag: "right", startX: greeter.width * 0.1, endX: greeter.width * 0.95, hiddenX: greeter.width}, |
4250 | - ]; |
4251 | - } |
4252 | - function test_dragToHide(data) { |
4253 | - compare(greeter.x, 0); |
4254 | - compare(greeter.visible, true); |
4255 | - compare(greeter.shown, true); |
4256 | - compare(greeter.showProgress, 1); |
4257 | - |
4258 | - touchFlick(greeter, |
4259 | - data.startX, greeter.height / 2, // start pos |
4260 | - data.endX, greeter.height / 2); // end pos |
4261 | - |
4262 | - tryCompare(greeter, "x", data.hiddenX); |
4263 | - tryCompare(greeter, "visible", false); |
4264 | - tryCompare(greeter, "shown", false); |
4265 | - tryCompare(greeter, "showProgress", 0); |
4266 | - } |
4267 | - |
4268 | - function test_hiddenGreeterRemainsHiddenAfterResize_data() { |
4269 | - return [ |
4270 | - {tag: "left", startX: greeter.width * 0.95, endX: greeter.width * 0.1}, |
4271 | - {tag: "right", startX: greeter.width * 0.1, endX: greeter.width * 0.95}, |
4272 | - ]; |
4273 | - } |
4274 | - function test_hiddenGreeterRemainsHiddenAfterResize(data) { |
4275 | - touchFlick(greeter, |
4276 | - data.startX, greeter.height / 2, // start pos |
4277 | - data.endX, greeter.height / 2); // end pos |
4278 | - |
4279 | - tryCompare(greeter, "x", data.tag == "left" ? -greeter.width : greeter.width); |
4280 | - tryCompare(greeter, "visible", false); |
4281 | - tryCompare(greeter, "shown", false); |
4282 | - tryCompare(greeter, "showProgress", 0); |
4283 | - |
4284 | - // flip dimensions to simulate an orientation change |
4285 | - greeter.width = greeterLoader.height; |
4286 | - greeter.height = greeterLoader.width; |
4287 | - |
4288 | - // All properties should remain consistent |
4289 | - tryCompare(greeter, "x", data.tag == "left" ? -greeter.width : greeter.width); |
4290 | - tryCompare(greeter, "visible", false); |
4291 | - tryCompare(greeter, "shown", false); |
4292 | - tryCompare(greeter, "showProgress", 0); |
4293 | - } |
4294 | - } |
4295 | -} |
4296 | |
4297 | === added file 'tests/qmltests/Greeter/tst_WideView.qml' |
4298 | --- tests/qmltests/Greeter/tst_WideView.qml 1970-01-01 00:00:00 +0000 |
4299 | +++ tests/qmltests/Greeter/tst_WideView.qml 2015-02-23 15:44:00 +0000 |
4300 | @@ -0,0 +1,519 @@ |
4301 | +/* |
4302 | + * Copyright 2014 Canonical Ltd. |
4303 | + * |
4304 | + * This program is free software; you can redistribute it and/or modify |
4305 | + * it under the terms of the GNU General Public License as published by |
4306 | + * the Free Software Foundation; version 3. |
4307 | + * |
4308 | + * This program is distributed in the hope that it will be useful, |
4309 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4310 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4311 | + * GNU General Public License for more details. |
4312 | + * |
4313 | + * You should have received a copy of the GNU General Public License |
4314 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4315 | + */ |
4316 | + |
4317 | +import QtQuick 2.0 |
4318 | +import QtTest 1.0 |
4319 | +import ".." |
4320 | +import "../../../qml/Greeter" |
4321 | +import LightDM 0.1 as LightDM |
4322 | +import Ubuntu.Components 0.1 |
4323 | +import Unity.Test 0.1 as UT |
4324 | + |
4325 | +Item { |
4326 | + id: root |
4327 | + width: units.gu(120) |
4328 | + height: units.gu(80) |
4329 | + |
4330 | + Binding { |
4331 | + target: LightDM.Users |
4332 | + property: "mockMode" |
4333 | + value: "full" |
4334 | + } |
4335 | + |
4336 | + Row { |
4337 | + anchors.fill: parent |
4338 | + Loader { |
4339 | + id: loader |
4340 | + width: root.width - controls.width |
4341 | + height: parent.height |
4342 | + |
4343 | + property bool itemDestroyed: false |
4344 | + sourceComponent: Component { |
4345 | + WideView { |
4346 | + id: view |
4347 | + |
4348 | + background: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg") |
4349 | + userModel: LightDM.Users |
4350 | + infographicModel: LightDM.Infographic |
4351 | + |
4352 | + launcherOffset: parseFloat(launcherOffsetField.text) |
4353 | + currentIndex: parseInt(currentIndexField.text, 10) |
4354 | + delayMinutes: parseInt(delayMinutesField.text, 10) |
4355 | + backgroundTopMargin: parseFloat(backgroundTopMarginField.text) |
4356 | + locked: lockedCheckBox.checked |
4357 | + inputMethod: fakeInputMethod |
4358 | + |
4359 | + Component.onDestruction: { |
4360 | + loader.itemDestroyed = true |
4361 | + } |
4362 | + |
4363 | + onSelected: { |
4364 | + currentIndexField.text = index; |
4365 | + } |
4366 | + |
4367 | + QtObject { |
4368 | + id: fakeInputMethod |
4369 | + property bool visible: fakeKeyboard.visible |
4370 | + property var keyboardRectangle: QtObject { |
4371 | + property real x: fakeKeyboard.x |
4372 | + property real y: fakeKeyboard.y |
4373 | + property real width: fakeKeyboard.width |
4374 | + property real height: fakeKeyboard.height |
4375 | + } |
4376 | + } |
4377 | + |
4378 | + Rectangle { |
4379 | + id: fakeKeyboard |
4380 | + color: "green" |
4381 | + opacity: 0.7 |
4382 | + anchors.bottom: view.bottom |
4383 | + width: view.width |
4384 | + height: view.height * 0.6 |
4385 | + visible: keyboardVisibleCheckBox.checked |
4386 | + Text { |
4387 | + text: "Keyboard Rectangle" |
4388 | + color: "yellow" |
4389 | + font.bold: true |
4390 | + fontSizeMode: Text.Fit |
4391 | + minimumPixelSize: 10; font.pixelSize: 200 |
4392 | + verticalAlignment: Text.AlignVCenter |
4393 | + x: (parent.width - width) / 2 |
4394 | + y: (parent.height - height) / 2 |
4395 | + width: parent.width |
4396 | + height: parent.height |
4397 | + } |
4398 | + } |
4399 | + |
4400 | + } |
4401 | + } |
4402 | + } |
4403 | + |
4404 | + Rectangle { |
4405 | + id: controls |
4406 | + color: "white" |
4407 | + width: units.gu(40) |
4408 | + height: parent.height |
4409 | + |
4410 | + Column { |
4411 | + anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) } |
4412 | + spacing: units.gu(1) |
4413 | + |
4414 | + Row { |
4415 | + Button { |
4416 | + text: "Show Last Chance" |
4417 | + onClicked: loader.item.showLastChance() |
4418 | + } |
4419 | + } |
4420 | + Row { |
4421 | + Button { |
4422 | + text: "Hide" |
4423 | + onClicked: loader.item.hide() |
4424 | + } |
4425 | + } |
4426 | + Row { |
4427 | + Button { |
4428 | + text: "Reset" |
4429 | + onClicked: loader.item.reset() |
4430 | + } |
4431 | + } |
4432 | + Row { |
4433 | + Button { |
4434 | + text: "Show Message" |
4435 | + onClicked: loader.item.showMessage(messageField.text) |
4436 | + } |
4437 | + TextField { |
4438 | + id: messageField |
4439 | + width: units.gu(10) |
4440 | + text: "" |
4441 | + } |
4442 | + } |
4443 | + Row { |
4444 | + Button { |
4445 | + text: "Show Prompt" |
4446 | + onClicked: loader.item.showPrompt(promptField.text, isSecretCheckBox.checked, isDefaultPromptCheckBox.checked) |
4447 | + } |
4448 | + TextField { |
4449 | + id: promptField |
4450 | + width: units.gu(10) |
4451 | + text: "" |
4452 | + } |
4453 | + CheckBox { |
4454 | + id: isSecretCheckBox |
4455 | + } |
4456 | + Label { |
4457 | + text: "secret" |
4458 | + } |
4459 | + CheckBox { |
4460 | + id: isDefaultPromptCheckBox |
4461 | + } |
4462 | + Label { |
4463 | + text: "default" |
4464 | + } |
4465 | + } |
4466 | + Row { |
4467 | + Button { |
4468 | + text: "Authenticated" |
4469 | + onClicked: { |
4470 | + if (successCheckBox.checked) { |
4471 | + loader.item.notifyAuthenticationSucceeded(); |
4472 | + } else { |
4473 | + loader.item.notifyAuthenticationFailed(); |
4474 | + } |
4475 | + } |
4476 | + } |
4477 | + CheckBox { |
4478 | + id: successCheckBox |
4479 | + } |
4480 | + Label { |
4481 | + text: "success" |
4482 | + } |
4483 | + } |
4484 | + Row { |
4485 | + Button { |
4486 | + text: "Try To Unlock" |
4487 | + onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked) |
4488 | + } |
4489 | + CheckBox { |
4490 | + id: toTheRightCheckBox |
4491 | + } |
4492 | + Label { |
4493 | + text: "toTheRight" |
4494 | + } |
4495 | + } |
4496 | + Row { |
4497 | + TextField { |
4498 | + id: launcherOffsetField |
4499 | + width: units.gu(10) |
4500 | + text: "0" |
4501 | + } |
4502 | + Label { |
4503 | + text: "launcherOffset" |
4504 | + } |
4505 | + } |
4506 | + Row { |
4507 | + TextField { |
4508 | + id: currentIndexField |
4509 | + width: units.gu(10) |
4510 | + text: "0" |
4511 | + } |
4512 | + Label { |
4513 | + text: "currentIndex" |
4514 | + } |
4515 | + } |
4516 | + Row { |
4517 | + TextField { |
4518 | + id: delayMinutesField |
4519 | + width: units.gu(10) |
4520 | + text: "0" |
4521 | + } |
4522 | + Label { |
4523 | + text: "delayMinutes" |
4524 | + } |
4525 | + } |
4526 | + Row { |
4527 | + TextField { |
4528 | + id: backgroundTopMarginField |
4529 | + width: units.gu(10) |
4530 | + text: "0" |
4531 | + } |
4532 | + Label { |
4533 | + text: "backgroundTopMargin" |
4534 | + } |
4535 | + } |
4536 | + Row { |
4537 | + CheckBox { |
4538 | + id: lockedCheckBox |
4539 | + } |
4540 | + Label { |
4541 | + text: "locked" |
4542 | + } |
4543 | + } |
4544 | + Row { |
4545 | + Label { |
4546 | + text: "selected: " + selectedSpy.count |
4547 | + } |
4548 | + } |
4549 | + Row { |
4550 | + Label { |
4551 | + text: "responded: " + respondedSpy.count |
4552 | + } |
4553 | + } |
4554 | + Row { |
4555 | + Label { |
4556 | + text: "teased: " + teaseSpy.count |
4557 | + } |
4558 | + } |
4559 | + Row { |
4560 | + Label { |
4561 | + text: "emergency: " + emergencySpy.count |
4562 | + } |
4563 | + } |
4564 | + Row { |
4565 | + Button { |
4566 | + text: "Reload View" |
4567 | + onClicked: { |
4568 | + loader.active = false; |
4569 | + loader.active = true; |
4570 | + } |
4571 | + } |
4572 | + } |
4573 | + Row { |
4574 | + CheckBox { |
4575 | + id: keyboardVisibleCheckBox |
4576 | + } |
4577 | + Label { |
4578 | + text: "Keyboard Visible" |
4579 | + } |
4580 | + } |
4581 | + } |
4582 | + } |
4583 | + } |
4584 | + |
4585 | + SignalSpy { |
4586 | + id: selectedSpy |
4587 | + target: loader.item |
4588 | + signalName: "selected" |
4589 | + } |
4590 | + |
4591 | + SignalSpy { |
4592 | + id: respondedSpy |
4593 | + target: loader.item |
4594 | + signalName: "responded" |
4595 | + } |
4596 | + |
4597 | + SignalSpy { |
4598 | + id: teaseSpy |
4599 | + target: loader.item |
4600 | + signalName: "tease" |
4601 | + } |
4602 | + |
4603 | + SignalSpy { |
4604 | + id: emergencySpy |
4605 | + target: loader.item |
4606 | + signalName: "emergencyCall" |
4607 | + } |
4608 | + |
4609 | + UT.UnityTestCase { |
4610 | + name: "WideView" |
4611 | + when: windowShown |
4612 | + |
4613 | + property Item view: loader.status === Loader.Ready ? loader.item : null |
4614 | + |
4615 | + function init() { |
4616 | + view.currentIndex = 0; // break binding with text field |
4617 | + selectedSpy.clear(); |
4618 | + respondedSpy.clear(); |
4619 | + teaseSpy.clear(); |
4620 | + emergencySpy.clear(); |
4621 | + } |
4622 | + |
4623 | + function cleanup() { |
4624 | + keyboardVisibleCheckBox.checked = false; |
4625 | + |
4626 | + loader.itemDestroyed = false; |
4627 | + loader.active = false; |
4628 | + tryCompare(loader, "status", Loader.Null); |
4629 | + tryCompare(loader, "item", null); |
4630 | + tryCompare(loader, "itemDestroyed", true); |
4631 | + loader.active = true; |
4632 | + tryCompare(loader, "status", Loader.Ready); |
4633 | + removeTimeConstraintsFromDirectionalDragAreas(loader.item); |
4634 | + } |
4635 | + |
4636 | + function getIndexOf(name) { |
4637 | + for (var i = 0; i < LightDM.Users.count; i++) { |
4638 | + if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) { |
4639 | + return i; |
4640 | + } |
4641 | + } |
4642 | + fail("Didn't find name") |
4643 | + return -1; |
4644 | + } |
4645 | + |
4646 | + function selectUser(name) { |
4647 | + var i = getIndexOf(name); |
4648 | + view.currentIndex = i; |
4649 | + return i; |
4650 | + } |
4651 | + |
4652 | + function swipeAwayCover() { |
4653 | + tryCompare(view, "fullyShown", true); |
4654 | + var touchY = view.height / 2; |
4655 | + touchFlick(view, view.width, touchY, 0, touchY); |
4656 | + var coverPage = findChild(view, "coverPage"); |
4657 | + tryCompare(coverPage, "showProgress", 0); |
4658 | + waitForRendering(view); |
4659 | + } |
4660 | + |
4661 | + function test_tease_data() { |
4662 | + return [ |
4663 | + {tag: "locked", x: 0, offset: 0, count: 0, locked: true}, |
4664 | + {tag: "left", x: 0, offset: 0, count: 1, locked: false}, |
4665 | + {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1, locked: false}, |
4666 | + {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0, locked: false}, |
4667 | + {tag: "right", x: view.width, offset: 0, count: 1, locked: false}, |
4668 | + ] |
4669 | + } |
4670 | + function test_tease(data) { |
4671 | + view.locked = data.locked; |
4672 | + view.dragHandleLeftMargin = data.offset; |
4673 | + tap(view, data.x, 0); |
4674 | + compare(teaseSpy.count, data.count); |
4675 | + } |
4676 | + |
4677 | + function test_selected() { |
4678 | + var delegate = findChild(view, "username2"); |
4679 | + tap(delegate); |
4680 | + compare(selectedSpy.count, 1); |
4681 | + compare(selectedSpy.signalArguments[0][0], 2); |
4682 | + compare(view.currentIndex, 0); // confirm we didn't change |
4683 | + } |
4684 | + |
4685 | + function test_respondedWithPassword() { |
4686 | + view.locked = true; |
4687 | + view.showPrompt("Prompt", true, true); |
4688 | + var passwordInput = findChild(view, "passwordInput"); |
4689 | + compare(passwordInput.placeholderText, "Prompt"); |
4690 | + compare(passwordInput.echoMode, TextInput.Password); |
4691 | + tap(passwordInput); |
4692 | + typeString("password"); |
4693 | + keyClick(Qt.Key_Enter); |
4694 | + compare(respondedSpy.count, 1); |
4695 | + compare(respondedSpy.signalArguments[0][0], "password"); |
4696 | + } |
4697 | + |
4698 | + function test_respondedWithNonSecret() { |
4699 | + view.locked = true; |
4700 | + view.showPrompt("otp", false, false); |
4701 | + var passwordInput = findChild(view, "passwordInput"); |
4702 | + compare(passwordInput.placeholderText, "otp"); |
4703 | + compare(passwordInput.echoMode, TextInput.Normal); |
4704 | + tap(passwordInput); |
4705 | + typeString("foo"); |
4706 | + keyClick(Qt.Key_Enter); |
4707 | + compare(respondedSpy.count, 1); |
4708 | + compare(respondedSpy.signalArguments[0][0], "foo"); |
4709 | + } |
4710 | + |
4711 | + function test_respondedWithSwipe() { |
4712 | + swipeAwayCover(); |
4713 | + compare(respondedSpy.count, 1); |
4714 | + compare(respondedSpy.signalArguments[0][0], ""); |
4715 | + } |
4716 | + |
4717 | + function test_fullyShown() { |
4718 | + tryCompare(view, "fullyShown", true); |
4719 | + swipeAwayCover(); |
4720 | + tryCompare(view, "fullyShown", false); |
4721 | + } |
4722 | + |
4723 | + function test_required() { |
4724 | + tryCompare(view, "required", true); |
4725 | + swipeAwayCover(); |
4726 | + tryCompare(view, "required", false); |
4727 | + } |
4728 | + |
4729 | + function test_showMessage() { |
4730 | + view.showMessage("Welcome to Unity Greeter"); |
4731 | + view.showMessage("<font color=\"#df382c\">This is an error</font>"); |
4732 | + view.showMessage("You should have seen three messages and this is a really long message too. wow so long much length"); |
4733 | + var infoLabel = findChild(view, "infoLabel"); |
4734 | + 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"); |
4735 | + compare(infoLabel.textFormat, Text.StyledText); |
4736 | + compare(infoLabel.clip, true); |
4737 | + verify(infoLabel.contentWidth > infoLabel.width); |
4738 | + verify(infoLabel.opacity < 1); |
4739 | + tryCompare(infoLabel, "opacity", 1); |
4740 | + } |
4741 | + |
4742 | + // Escape is used to reset the authentication, especially if PAM is unresponsive |
4743 | + function test_escape() { |
4744 | + view.currentIndex = 1; |
4745 | + view.locked = true; |
4746 | + view.showPrompt("Prompt", true, true); |
4747 | + var passwordInput = findChild(view, "passwordInput"); |
4748 | + tap(passwordInput); |
4749 | + compare(passwordInput.focus, true); |
4750 | + compare(passwordInput.enabled, true); |
4751 | + |
4752 | + typeString("password"); |
4753 | + keyClick(Qt.Key_Enter); |
4754 | + compare(passwordInput.focus, false); |
4755 | + compare(passwordInput.enabled, false); |
4756 | + |
4757 | + compare(selectedSpy.count, 0); |
4758 | + keyClick(Qt.Key_Escape); |
4759 | + compare(selectedSpy.count, 1); |
4760 | + compare(selectedSpy.signalArguments[0][0], 1); |
4761 | + |
4762 | + view.reset(); |
4763 | + compare(passwordInput.focus, false); |
4764 | + compare(passwordInput.enabled, true); |
4765 | + } |
4766 | + |
4767 | + function test_unicode() { |
4768 | + var index = selectUser("unicode"); |
4769 | + var label = findChild(view, "username" + index); |
4770 | + tryCompare(label, "text", "가나다라마"); |
4771 | + } |
4772 | + |
4773 | + function test_longName() { |
4774 | + var index = selectUser("long-name"); |
4775 | + var label = findChild(view, "username" + index); |
4776 | + tryCompare(label, "truncated", true); |
4777 | + } |
4778 | + |
4779 | + function test_promptless() { |
4780 | + var passwordInput = findChild(view, "passwordInput"); |
4781 | + |
4782 | + view.locked = true; |
4783 | + compare(passwordInput.placeholderText, "Retry"); |
4784 | + tap(passwordInput); |
4785 | + compare(respondedSpy.count, 0); |
4786 | + compare(selectedSpy.count, 1); |
4787 | + compare(selectedSpy.signalArguments[0][0], 0); |
4788 | + selectedSpy.clear(); |
4789 | + |
4790 | + view.locked = false; |
4791 | + compare(passwordInput.placeholderText, "Tap to unlock"); |
4792 | + tap(passwordInput); |
4793 | + compare(selectedSpy.count, 0); |
4794 | + compare(respondedSpy.count, 1); |
4795 | + compare(respondedSpy.signalArguments[0][0], ""); |
4796 | + } |
4797 | + |
4798 | + function test_loginListNotCoveredByKeyboard() { |
4799 | + var loginList = findChild(view, "loginList"); |
4800 | + compare(loginList.height, view.height); |
4801 | + |
4802 | + // when the vkb shows up, loginList is moved up to remain fully uncovered |
4803 | + |
4804 | + keyboardVisibleCheckBox.checked = true; |
4805 | + |
4806 | + tryCompare(loginList, "height", view.height - view.inputMethod.keyboardRectangle.height); |
4807 | + tryCompareFunction( function() { |
4808 | + var loginListRect = loginList.mapToItem(view, 0, 0, loginList.width, loginList.height); |
4809 | + return loginListRect.y + loginListRect.height <= view.inputMethod.keyboardRectangle.y; |
4810 | + }, true); |
4811 | + |
4812 | + // once the vkb goes away, loginList goes back to its full height |
4813 | + |
4814 | + keyboardVisibleCheckBox.checked = false; |
4815 | + |
4816 | + tryCompare(loginList, "height", view.height); |
4817 | + } |
4818 | + } |
4819 | +} |
4820 | |
4821 | === modified file 'tests/qmltests/Tutorial/tst_Tutorial.qml' |
4822 | --- tests/qmltests/Tutorial/tst_Tutorial.qml 2015-02-05 20:20:28 +0000 |
4823 | +++ tests/qmltests/Tutorial/tst_Tutorial.qml 2015-02-23 15:44:00 +0000 |
4824 | @@ -112,9 +112,9 @@ |
4825 | |
4826 | function init() { |
4827 | tryCompare(shell, "enabled", true); // enabled by greeter when ready |
4828 | - swipeAwayGreeter(); |
4829 | AccountsService.demoEdges = false; |
4830 | AccountsService.demoEdges = true; |
4831 | + swipeAwayGreeter(); |
4832 | } |
4833 | |
4834 | function cleanup() { |
4835 | @@ -151,13 +151,14 @@ |
4836 | } |
4837 | |
4838 | function swipeAwayGreeter() { |
4839 | - var greeter = findChild(shell, "greeter"); |
4840 | - tryCompare(greeter, "showProgress", 1); |
4841 | + var coverPage = findChild(shell, "coverPage"); |
4842 | + tryCompare(coverPage, "showProgress", 1); |
4843 | |
4844 | touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight); |
4845 | |
4846 | // wait until the animation has finished |
4847 | - tryCompare(greeter, "showProgress", 0); |
4848 | + var greeter = findChild(shell, "greeter"); |
4849 | + tryCompare(greeter, "required", false); |
4850 | waitForRendering(greeter); |
4851 | } |
4852 | |
4853 | |
4854 | === modified file 'tests/qmltests/tst_Shell.qml' |
4855 | --- tests/qmltests/tst_Shell.qml 2015-02-11 17:12:49 +0000 |
4856 | +++ tests/qmltests/tst_Shell.qml 2015-02-23 15:44:00 +0000 |
4857 | @@ -19,9 +19,11 @@ |
4858 | |
4859 | import QtQuick 2.0 |
4860 | import QtTest 1.0 |
4861 | +import AccountsService 0.1 |
4862 | import GSettings 1.0 |
4863 | import LightDM 0.1 as LightDM |
4864 | import Ubuntu.Components 1.1 |
4865 | +import Ubuntu.Components.ListItems 1.0 as ListItem |
4866 | import Ubuntu.Telephony 0.1 as Telephony |
4867 | import Unity.Application 0.1 |
4868 | import Unity.Connectivity 0.1 |
4869 | @@ -80,7 +82,7 @@ |
4870 | } |
4871 | |
4872 | Rectangle { |
4873 | - color: "white" |
4874 | + color: "darkgrey" |
4875 | width: units.gu(30) |
4876 | height: shellLoader.height |
4877 | |
4878 | @@ -98,11 +100,23 @@ |
4879 | |
4880 | var greeter = testCase.findChild(shellLoader.item, "greeter"); |
4881 | if (!greeter.shown) { |
4882 | - greeter.show(); |
4883 | + LightDM.Greeter.showGreeter(); |
4884 | } |
4885 | } |
4886 | } |
4887 | } |
4888 | + ListItem.ItemSelector { |
4889 | + anchors { left: parent.left; right: parent.right } |
4890 | + activeFocusOnPress: false |
4891 | + text: "LightDM mock mode" |
4892 | + model: ["single", "single-passphrase", "single-pin"] |
4893 | + onSelectedIndexChanged: { |
4894 | + shellLoader.active = false; |
4895 | + LightDM.Greeter.mockMode = model[selectedIndex]; |
4896 | + LightDM.Users.mockMode = model[selectedIndex]; |
4897 | + shellLoader.active = true; |
4898 | + } |
4899 | + } |
4900 | } |
4901 | } |
4902 | } |
4903 | @@ -284,11 +298,13 @@ |
4904 | |
4905 | function test_leftEdgeDrag_data() { |
4906 | return [ |
4907 | - {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true}, |
4908 | - {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true}, |
4909 | - {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false}, |
4910 | - {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true}, |
4911 | - {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash", launcherHides: false} |
4912 | + {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false}, |
4913 | + {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false}, |
4914 | + {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false, greeterShown: false}, |
4915 | + {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: false}, |
4916 | + {tag: "small swipe with greeter", revealLauncher: false, swipeLength: units.gu(25), appHides: false, focusedApp: "dialer-app", launcherHides: false, greeterShown: true}, |
4917 | + {tag: "long swipe with greeter", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true, greeterShown: true}, |
4918 | + {tag: "swipe over dash", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "unity8-dash", launcherHides: false, greeterShown: false}, |
4919 | ]; |
4920 | } |
4921 | |
4922 | @@ -299,6 +315,12 @@ |
4923 | ApplicationManager.focusApplication(data.focusedApp) |
4924 | waitUntilApplicationWindowIsFullyVisible(); |
4925 | |
4926 | + var greeter = findChild(shell, "greeter"); |
4927 | + if (data.greeterShown) { |
4928 | + LightDM.Greeter.showGreeter(); |
4929 | + tryCompare(greeter, "fullyShown", true); |
4930 | + } |
4931 | + |
4932 | if (data.revealLauncher) { |
4933 | dragLauncherIntoView(); |
4934 | } |
4935 | @@ -306,8 +328,10 @@ |
4936 | swipeFromLeftEdge(data.swipeLength); |
4937 | if (data.appHides) { |
4938 | waitUntilDashIsFocused(); |
4939 | + tryCompare(greeter, "shown", false); |
4940 | } else { |
4941 | waitUntilApplicationWindowIsFullyVisible(); |
4942 | + compare(greeter.fullyShown, data.greeterShown); |
4943 | } |
4944 | |
4945 | var launcher = findChild(shell, "launcherPanel"); |
4946 | @@ -335,20 +359,20 @@ |
4947 | // Suspend while call is active... |
4948 | callManager.foregroundCall = phoneCall; |
4949 | Powerd.status = Powerd.Off; |
4950 | - tryCompare(greeter, "showProgress", 0); |
4951 | + tryCompare(greeter, "shown", false); |
4952 | |
4953 | // Now try again after ending call |
4954 | callManager.foregroundCall = null; |
4955 | Powerd.status = Powerd.On; |
4956 | Powerd.status = Powerd.Off; |
4957 | - tryCompare(greeter, "showProgress", 1); |
4958 | + tryCompare(greeter, "fullyShown", true); |
4959 | |
4960 | tryCompare(ApplicationManager, "suspended", true); |
4961 | compare(mainApp.state, ApplicationInfoInterface.Suspended); |
4962 | |
4963 | // And wake up |
4964 | Powerd.status = Powerd.On; |
4965 | - tryCompare(greeter, "showProgress", 1); |
4966 | + tryCompare(greeter, "fullyShown", true); |
4967 | |
4968 | // Swipe away greeter to focus app |
4969 | swipeAwayGreeter(); |
4970 | @@ -359,14 +383,14 @@ |
4971 | |
4972 | function swipeAwayGreeter() { |
4973 | var greeter = findChild(shell, "greeter"); |
4974 | - tryCompare(greeter, "showProgress", 1); |
4975 | + tryCompare(greeter, "fullyShown", true); |
4976 | |
4977 | var touchX = shell.width - (shell.edgeSize / 2); |
4978 | var touchY = shell.height / 2; |
4979 | touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY); |
4980 | |
4981 | // wait until the animation has finished |
4982 | - tryCompare(greeter, "showProgress", 0); |
4983 | + tryCompare(greeter, "shown", false); |
4984 | waitForRendering(greeter); |
4985 | } |
4986 | |
4987 | @@ -608,12 +632,12 @@ |
4988 | |
4989 | waitUntilDashIsFocused(); |
4990 | |
4991 | - greeter.show(); |
4992 | - tryCompare(greeter, "showProgress", 1); |
4993 | + LightDM.Greeter.showGreeter(); |
4994 | + tryCompare(greeter, "fullyShown", true); |
4995 | |
4996 | // The main point of this test |
4997 | ApplicationManager.requestFocusApplication("dialer-app"); |
4998 | - tryCompare(greeter, "showProgress", 0); |
4999 | + tryCompare(greeter, "shown", false); |
5000 | waitForRendering(greeter); |
I'm going to submit for review, but there is a tiny issue still with the OSK and the NarrowView passphrase interface. The OSK doesn't come back up automatically after an invalid passphrase, and the OSK stays up after a suspend.
So there is some focus issue there I'll look into. But it's good enough to review.