on xenial help sign in
Merge upstream.

[ Michael Terry ] * Bump version so ubuntu-touch-session can reference this one [ CI bot ] * Resync trunk [ Michał Sawicz ] * Move env setup past session init in greeter wrapper. (LP: #1325882) * no change rebuild * drop dbus-x11 dependency of unity8-greeter, it makes us end up with multiple session dbus daemons which breaks many AP tests in the lab [ Michael Terry ] * Bump version for Breaks due to unity8-greeter * In split mode, determine whether the application identifiers in AccountsService are click packages or not, so we know the correct url prefix to use. * Start logrotate in the greeter's session. [ Andrea Cimitan ] * Passes to make tryCommand -qmljsdebugger=port:3768 to enable debug/profiling test apps * Fixes carousel shadow [ Michał Sawicz ] * Use dpkg-architecture, not gcc, to determine the machine triplet. [ Ying-Chun Liu ] * Fix ZoomableImage test failure. (LP: #1317254) [ Albert Astals ] * We don't need iconutils in this mock * Don't reserve space for mascot if no mascot is specified (LP: #1319343) * CardHeader is no more, remove stale line in CMakeLists.txt * GenericScopeViewTest: Wait a bit more Otherwise sometimes we end getting up the wrong delegate (maybe one that will be garbage collected?) (LP: #1322279) * Fix crash in organicgridtest [ Daniel d'Andrada ] * Remove Shell's underlay background image as it cannot be seen anymore Now that the Dash has its own, opaque, background, the underlay's background image can no longer be seen. So it's just a waste of resources to have it. [ Michael Terry ] * Use the same animation when dismissing a greeter slide from the launcher as from a normal greeter drag. (LP: #1316513) [ Michael Zanetti ] * enhance lockscreen add a retry indication label (e.g. 3 attempts left). add an additional label (e.g. phone number for multi sim). add a infoPopup (e.g. to display a warning for last retry). add min and max limit values. add tests for the above (LP: #1302050)

commit49e5760eb0a42bdf190800625037610d67a16b77 authorYing-Chun Liu <paul.liu@canonical.com> dateFri 6 Jun 2014, 20:32 (12 years ago) parent5668b5e1067acd
156 files changed+2850−1021
CMakeLists.txt+1 cmake/modules/QmlTest.cmake+4−4 data/51-unity8-greeter.conf+2 data/CMakeLists.txt+13 data/unity8-greeter-init.conf+12 data/unity8-greeter-logrotate.conf+14 data/unity8-greeter-wrapper+109 data/unity8-greeter.desktop.in+5 debian/changelog+82 debian/control+15 debian/copyright+1−1 debian/rules−4 debian/unity8-greeter.install+6 debian/unity8-private.install+1 debian/unity8.install−1 plugins/AccountsService/AccountsService.cpp+34−11 plugins/AccountsService/AccountsService.h+9 plugins/CMakeLists.txt+1 plugins/Dash/CardCreator.js+3−3 plugins/Dash/abstractdashview.cpp+3 plugins/LightDM/CMakeLists.txt+6−9 plugins/LightDM/Greeter.cpp+3 plugins/LightDM/Greeter.h+2 plugins/LightDM/URLDispatcher.cpp+55 tests/mocks/Unity/iconutils.h → plugins/LightDM/URLDispatcher.h+13−8 plugins/LightDM/plugin.cpp+9 plugins/SessionBroadcast/SessionBroadcast.cpp+35 plugins/SessionBroadcast/SessionBroadcast.h+11 plugins/SessionManager/CMakeLists.txt+22 plugins/SessionManager/SessionManager.cpp+124 plugins/SessionManager/SessionManager.h+61 plugins/SessionManager/plugin.cpp+35 plugins/SessionManager/plugin.h+35 plugins/SessionManager/qmldir+2 plugins/Unity/Launcher/backend/launcherbackend.cpp+16−2 plugins/Unity/Launcher/backend/launcherbackend.h+7−1 plugins/Unity/Launcher/launcheritem.cpp+14−9 plugins/Unity/Launcher/launcheritem.h+2−1 plugins/Unity/Launcher/launchermodel.cpp+57−11 plugins/Unity/Launcher/launchermodel.h+3 po/ca.po+9−7 po/de.po+5−3 po/es.po+6−6 po/gl.po+6−6 po/it.po+5−3 po/lv.po+6−6 po/pl.po+6−4 po/ug.po+2−1 qml/Components/BasicShell.qml+84 qml/Components/EdgeDemo.qml+1−53 qml/Greeter/Lockscreen.qml → qml/Components/Lockscreen.qml+88−24 qml/Components/PageHeader.qml+3−2 qml/Greeter/PassphraseLockscreen.qml → qml/Components/PassphraseLockscreen.qml+6−2 qml/Greeter/PinLockscreen.qml → qml/Components/PinLockscreen.qml+29−11 qml/Components/PinPadButton.qml+2−1 qml/Dash/CardCarousel.qml+1−1 qml/Dash/GenericScopeView.qml+1−1 qml/Dash/Previews/PreviewHeader.qml+3 qml/Dash/ScopeListView.qml+3−2 qml/Greeter/Clock.qml+1−1 qml/Greeter/Greeter.qml+36−3 qml/Greeter/GreeterContent.qml−38 qml/Greeter/GreeterEdgeDemo.qml+102 qml/Greeter/Infographics.qml−2 qml/GreeterShell.qml+283 qml/Hud/Hud.qml+1−4 qml/Notifications/NotificationMenuItemFactory.qml+26−10 qml/Panel/Panel.qml+1−1 qml/Shell.qml+31−182 run.sh+6−4 src/CMakeLists.txt+15−3 src/main.cpp+18−5 tests/autopilot/unity8/application_lifecycle/tests/__init__.py−2 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−29 tests/autopilot/unity8/application_lifecycle/tests/test_greeter.py+75 tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+1 tests/autopilot/unity8/indicators/tests/test_indicators.py−2 tests/autopilot/unity8/process_helpers.py−56 tests/autopilot/unity8/shell/emulators/main_window.py−4 tests/autopilot/unity8/shell/tests/__init__.py+16−4 tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−7 tests/autopilot/unity8/shell/tests/test_emulators.py−3 tests/autopilot/unity8/shell/tests/test_lock_screen.py+12−13 tests/autopilot/unity8/shell/tests/test_notifications.py+2−14 tests/mocks/AccountsService/AccountsService.cpp+9 tests/mocks/AccountsService/AccountsService.h+7 tests/mocks/CMakeLists.txt+2−1 tests/mocks/LightDM/CMakeLists.txt−50 tests/mocks/SessionManager/CMakeLists.txt+19 tests/mocks/SessionManager/SessionManager.cpp+43 tests/mocks/SessionManager/SessionManager.h+49 tests/mocks/SessionManager/plugin.cpp+34 tests/mocks/SessionManager/plugin.h+34 tests/mocks/SessionManager/qmldir+2 tests/mocks/Unity/CMakeLists.txt−1 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+5 tests/mocks/Unity/Launcher/MockLauncherModel.h+1 tests/mocks/Unity/fake_categories.cpp+5−1 tests/mocks/Unity/iconutils.cpp−124 tests/mocks/liblightdm/CMakeLists.txt+4 tests/mocks/LightDM/Greeter.cpp → tests/mocks/liblightdm/Greeter.cpp+11 tests/mocks/LightDM/Greeter.h → tests/mocks/liblightdm/Greeter.h+4 tests/mocks/LightDM/GreeterPrivate.h → tests/mocks/liblightdm/GreeterPrivate.h tests/mocks/LightDM/QLightDM/Greeter → tests/mocks/liblightdm/QLightDM/Greeter tests/mocks/LightDM/QLightDM/InfographicModel → tests/mocks/liblightdm/QLightDM/InfographicModel tests/mocks/LightDM/QLightDM/UsersModel → tests/mocks/liblightdm/QLightDM/UsersModel tests/mocks/LightDM/UsersModel.cpp → tests/mocks/liblightdm/UsersModel.cpp tests/mocks/LightDM/UsersModel.h → tests/mocks/liblightdm/UsersModel.h tests/mocks/LightDM/UsersModelPrivate.h → tests/mocks/liblightdm/UsersModelPrivate.h tests/mocks/LightDM/demo/CMakeLists.txt → tests/mocks/liblightdm/demo/CMakeLists.txt tests/mocks/LightDM/demo/GreeterPrivate.cpp → tests/mocks/liblightdm/demo/GreeterPrivate.cpp tests/mocks/LightDM/demo/UsersModelPrivate.cpp → tests/mocks/liblightdm/demo/UsersModelPrivate.cpp tests/mocks/LightDM/full/CMakeLists.txt → tests/mocks/liblightdm/full/CMakeLists.txt+3−2 tests/mocks/LightDM/full/GreeterPrivate.cpp → tests/mocks/liblightdm/full/GreeterPrivate.cpp tests/mocks/LightDM/full/UsersModelPrivate.cpp → tests/mocks/liblightdm/full/UsersModelPrivate.cpp tests/mocks/LightDM/qmldir → tests/mocks/liblightdm/qmldir tests/mocks/LightDM/single-passphrase/CMakeLists.txt → tests/mocks/liblightdm/single-passphrase/CMakeLists.txt+3−2 tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp → tests/mocks/liblightdm/single-passphrase/GreeterPrivate.cpp tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cpp → tests/mocks/liblightdm/single-passphrase/UsersModelPrivate.cpp tests/mocks/LightDM/single-pin/CMakeLists.txt → tests/mocks/liblightdm/single-pin/CMakeLists.txt+3−2 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp → tests/mocks/liblightdm/single-pin/GreeterPrivate.cpp tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp → tests/mocks/liblightdm/single-pin/UsersModelPrivate.cpp tests/mocks/LightDM/single/CMakeLists.txt → tests/mocks/liblightdm/single/CMakeLists.txt+3−2 tests/mocks/LightDM/single/GreeterPrivate.cpp → tests/mocks/liblightdm/single/GreeterPrivate.cpp tests/mocks/LightDM/single/UsersModelPrivate.cpp → tests/mocks/liblightdm/single/UsersModelPrivate.cpp tests/plugins/CMakeLists.txt+2−1 tests/plugins/Dash/cardcreator/1.res+3−3 tests/plugins/Dash/cardcreator/2.res+1−1 tests/plugins/Dash/cardcreator/3.res+3−3 tests/plugins/Dash/cardcreator/4.res+1−1 tests/plugins/Dash/cardcreator/5.res+3−3 tests/plugins/Dash/organicgridtest.cpp+5 tests/plugins/LightDM/CMakeLists.txt+5−4 tests/plugins/SessionManager/CMakeLists.txt+48 tests/plugins/SessionManager/LightDMSessionServer.cpp+38 tests/plugins/SessionManager/LightDMSessionServer.h+40 tests/plugins/SessionManager/LoginManagerServer.cpp+34 tests/plugins/SessionManager/LoginManagerServer.h+37 tests/plugins/SessionManager/LoginSessionServer.cpp+25 tests/plugins/SessionManager/LoginSessionServer.h+39 tests/plugins/SessionManager/client.cpp+88 tests/plugins/SessionManager/interfaces.xml+22 tests/plugins/SessionManager/server.cpp+57 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+9 tests/qmltests/CMakeLists.txt+40−50 tests/qmltests/Components/tst_ZoomableImage.qml+21−19 tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+12−7 tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−5 tests/qmltests/Dash/tst_DashContent.qml+29−14 tests/qmltests/Dash/tst_GenericScopeView.qml+1 tests/qmltests/Greeter/tst_Lockscreen.qml+208−31 tests/qmltests/Greeter/tst_Phone.qml+1−17 tests/qmltests/tst_GreeterShell.qml+88 tests/qmltests/tst_Shell.qml+10−73 tools/unlock-device+10−18
CMakeLists.txt+1
@@ -96,6 +96,7 @@ include(autopilot)
9696declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
9797
9898set(SHELL_APP unity8)
99+set(GREETER_APP unity8-greeter)
99100set(SCOPE_TOOL unity-scope-tool)
100101
101102include_directories(
cmake/modules/QmlTest.cmake+4−4
@@ -51,7 +51,7 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
5151
5252 set(qmlscene_command
5353 env ${qmltest_ENVIRONMENT}
54− ${qmlscene_exe} ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
54+ ${qmlscene_exe} -qmljsdebugger=port:3768 ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
5555 ${qmlscene_imports}
5656 )
5757 add_custom_target(${qmlscene_TARGET} ${qmlscene_command})
@@ -111,9 +111,9 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
111111 -o -,txt
112112 ${function_ARGS}
113113 )
114− find_program( HAVE_GCC gcc )
115− if (NOT ${HAVE_GCC} STREQUAL "")
116− exec_program( gcc ARGS "-dumpmachine" OUTPUT_VARIABLE ARCH_TRIPLET )
114+ find_program(DPKG dpkg-architecture)
115+ if(DPKG)
116+ exec_program(${DPKG} ARGS "-qDEB_BUILD_MULTIARCH" OUTPUT_VARIABLE ARCH_TRIPLET )
117117 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
118118 endif()
119119 set(qmltest_xvfb_command
data/51-unity8-greeter.confnew file+2
@@ -0,0 +1,2 @@
1+[SeatDefaults]
2+greeter-session=unity8-greeter
data/CMakeLists.txt+13
@@ -1,7 +1,20 @@
11# generate desktop file
22configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3+configure_file(${GREETER_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${GREETER_APP}.desktop @ONLY)
34
45# install desktop files
56install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
67 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
78 )
9+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GREETER_APP}.desktop
10+ DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/greeters
11+ )
12+install(FILES 51-${GREETER_APP}.conf
13+ DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/lightdm.conf.d
14+ )
15+install(FILES ${GREETER_APP}-wrapper
16+ DESTINATION ${CMAKE_INSTALL_BINDIR}
17+ )
18+install(FILES ${GREETER_APP}-init.conf ${GREETER_APP}-logrotate.conf
19+ DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions
20+ )
data/unity8-greeter-init.confnew file+12
@@ -0,0 +1,12 @@
1+description "Unity 8 Greeter initialization"
2+author "Michael Terry <mterry@ubuntu.com>"
3+
4+start on unity8-greeter-started
5+task
6+emits indicator-services-start
7+
8+script
9+ start --no-wait ofono-setup || true
10+ initctl emit --no-wait indicator-services-start || true
11+ start --no-wait maliit-server || true
12+end script
data/unity8-greeter-logrotate.confnew file+14
@@ -0,0 +1,14 @@
1+description "Unity 8 Greeter logrotate support"
2+author "Michael Terry <mterry@ubuntu.com>"
3+
4+start on unity8-greeter-started
5+task
6+
7+script
8+ # logrotate normally starts on startup and then sleeps for 60 seconds.
9+ # Otherwise, it skips that sleep. Since we can't emit the startup signal
10+ # for a unity8-greeter session, we have to trigger logrotate manually.
11+ # So we do the sleep ourselves in its stead.
12+ sleep 60
13+ initctl emit :sys:rotate-logs
14+end script
data/unity8-greeter-wrappernew file+109
@@ -0,0 +1,109 @@
1+#!/bin/sh
2+# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
3+#
4+# Copyright (C) 2011,2013 Canonical Ltd
5+# Author: Michael Terry <michael.terry@canonical.com>
6+#
7+# This program is free software: you can redistribute it and/or modify it under
8+# the terms of the GNU General Public License as published by the Free Software
9+# Foundation, version 3 of the License.
10+#
11+# See http://www.gnu.org/copyleft/gpl.html the full text of the license.
12+
13+# This wrapper merely ensures that init and friends live only as long as this
14+# script does. Otherwise, it's very easy for some processes to not notice that
15+# the session died. We could try to do this in-process, but we want to do this
16+# cleanup even if the greeter aborts.
17+
18+trap cleanup TERM EXIT
19+
20+cleanup()
21+{
22+ trap - TERM EXIT
23+ # Kill upstart and indicators
24+ if [ -n "$INIT_PID" ]; then
25+ kill "$INIT_PID"
26+ fi
27+ if [ -n "$DBUS_PID" ]; then
28+ kill "$DBUS_PID"
29+ fi
30+ if [ -n "$CMD_PID" ]; then
31+ kill "$CMD_PID"
32+ fi
33+ exit 0
34+}
35+
36+set_greeter_var()
37+{
38+ export "$1=$2"
39+ gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "{'$1': '$2'}"
40+ if [ -n "$INIT_PID" ]; then
41+ initctl set-env --global "$1=$2"
42+ fi
43+}
44+
45+# Start dbus if it hasn't already been started for us (such as on the Desktop
46+# images by dbus-launch). We don't want to use dbus-launch ourselves because
47+# we don't want dbus-x11 on the Touch images.
48+if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
49+ export DBUS_SESSION_BUS_ADDRESS=unix:abstract=$(mktemp -u /tmp/dbus-XXXXXXXXXX)
50+ DBUS_PID=$(dbus-daemon --fork --session --print-pid --address="$DBUS_SESSION_BUS_ADDRESS")
51+fi
52+
53+SUB_SOCKET=$XDG_RUNTIME_DIR/mir_socket
54+rm -f $SUB_SOCKET # clear socket in case we were hard shut down
55+
56+# If touch session script (which sets up grid units and mir variables) is available, use it
57+TOUCH_WRAPPER=
58+if [ -x /usr/bin/ubuntu-touch-session ]; then
59+ TOUCH_WRAPPER=/usr/bin/ubuntu-touch-session
60+fi
61+
62+# Pretend that we're in an ubuntu-touch session, so that indicator-network
63+# will start.
64+export DESKTOP_SESSION=ubuntu-touch
65+
66+# We disable ofono using pulse. It causes problems with racing with the user's
67+# pulse. We need to come up with a better long-term fix for this, because we
68+# eventually need the greeter to play ringtones for users that aren't logged in.
69+set_greeter_var PA_DISABLED 1
70+
71+# Normal unity8 sessions are entirely driven by Upstart. But greeters
72+# are special. They need access to the file descriptors that lightdm
73+# creates for them and don't want to start all the services that a normal
74+# session would. So it's inconvenient to live within an upstart session.
75+# But... we still want to use Upstart for some services. So launch here.
76+USC_SOCKET=$MIR_SOCKET MIR_SOCKET=$SUB_SOCKET $TOUCH_WRAPPER /sbin/init --user --no-startup-event &
77+INIT_PID=$!
78+while [ ! -e "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session" ]; do sleep 0.1; done
79+export UPSTART_SESSION=$(/sbin/initctl list-sessions | grep "^$INIT_PID " | cut -d' ' -f2)
80+
81+# Start any pre-greeter tasks if needed (like wizard)
82+/sbin/initctl emit unity8-greeter-starting
83+
84+# Define language here for phone if available. When phone user switches their
85+# language, they expect that to affect the greeter too. But the user doesn't
86+# have permission to switch system language, only their own. So we notice if
87+# the phablet user exists and use their language if so. TODO: talk to design
88+# about whether we should switch language on fly as users are selected (this
89+# is very hard to do technically).
90+#
91+# Do this after unity8-greeter-starting, in case a customization upstart job
92+# changes language.
93+if [ "$(id -u phablet 2>/dev/null)" = "32011" ]; then
94+ USER_LANG=$(gdbus call --system --dest org.freedesktop.Accounts --object-path /org/freedesktop/Accounts/User32011 --method org.freedesktop.DBus.Properties.Get org.freedesktop.Accounts.User Language | cut -d\' -f2)
95+ if [ -n "$USER_LANG" ]; then
96+ set_greeter_var LANGUAGE "$USER_LANG"
97+ fi
98+ USER_LOCALE=$(gdbus call --system --dest org.freedesktop.Accounts --object-path /org/freedesktop/Accounts/User32011 --method org.freedesktop.DBus.Properties.Get org.freedesktop.Accounts.User FormatsLocale | cut -d\' -f2)
99+ if [ -n "$USER_LOCALE" ]; then
100+ set_greeter_var LANG "$USER_LOCALE"
101+ set_greeter_var LC_ALL "$USER_LOCALE"
102+ fi
103+fi
104+
105+# And finally actually start the greeter
106+exec env MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
107+CMD_PID=$!
108+wait $CMD_PID
109+CMD_PID=
data/unity8-greeter.desktop.innew file+5
@@ -0,0 +1,5 @@
1+[Desktop Entry]
2+Type=Application
3+Name=Unity 8 Greeter
4+Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-greeter-wrapper @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-greeter
5+X-LightDM-Session-Type=mir
debian/changelog+82
@@ -1,3 +1,85 @@
1+unity8 (7.88+14.10.20140603.1-0ubuntu1) utopic; urgency=medium
2+
3+ [ Michael Terry ]
4+ * Bump version so ubuntu-touch-session can reference this one
5+
6+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Jun 2014 20:31:00 +0000
7+
8+unity8 (7.87+14.10.20140603.1-0ubuntu1) utopic; urgency=low
9+
10+ [ CI bot ]
11+ * Resync trunk
12+
13+ [ Michał Sawicz ]
14+ * Move env setup past session init in greeter wrapper. (LP: #1325882)
15+
16+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Jun 2014 10:33:11 +0000
17+
18+unity8 (7.87+14.10.20140530.1-0ubuntu3) utopic; urgency=medium
19+
20+ * no change rebuild
21+
22+ -- Oliver Grawert <ogra@ubuntu.com> Mon, 02 Jun 2014 16:19:10 +0200
23+
24+unity8 (7.87+14.10.20140530.1-0ubuntu2) utopic; urgency=medium
25+
26+ * drop dbus-x11 dependency of unity8-greeter, it makes us end up with
27+ multiple session dbus daemons which breaks many AP tests in the lab
28+
29+ -- Oliver Grawert <ogra@ubuntu.com> Mon, 02 Jun 2014 14:50:59 +0200
30+
31+unity8 (7.87+14.10.20140530.1-0ubuntu1) utopic; urgency=medium
32+
33+ [ Michael Terry ]
34+ * Bump version for Breaks due to unity8-greeter
35+ * In split mode, determine whether the application identifiers in
36+ AccountsService are click packages or not, so we know the correct
37+ url prefix to use.
38+ * Start logrotate in the greeter's session.
39+
40+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 30 May 2014 09:29:15 +0000
41+
42+unity8 (7.86+14.10.20140527-0ubuntu1) utopic; urgency=low
43+
44+ [ Andrea Cimitan ]
45+ * Passes to make tryCommand -qmljsdebugger=port:3768 to enable
46+ debug/profiling test apps
47+ * Fixes carousel shadow
48+
49+ [ Michał Sawicz ]
50+ * Use dpkg-architecture, not gcc, to determine the machine triplet.
51+
52+ [ Ying-Chun Liu ]
53+ * Fix ZoomableImage test failure. (LP: #1317254)
54+
55+ [ Albert Astals ]
56+ * We don't need iconutils in this mock
57+ * Don't reserve space for mascot if no mascot is specified (LP:
58+ #1319343)
59+ * CardHeader is no more, remove stale line in CMakeLists.txt
60+ * GenericScopeViewTest: Wait a bit more Otherwise sometimes we end
61+ getting up the wrong delegate (maybe one that will be garbage
62+ collected?) (LP: #1322279)
63+ * Fix crash in organicgridtest
64+
65+ [ Daniel d'Andrada ]
66+ * Remove Shell's underlay background image as it cannot be seen
67+ anymore Now that the Dash has its own, opaque, background, the
68+ underlay's background image can no longer be seen. So it's just a
69+ waste of resources to have it.
70+
71+ [ Michael Terry ]
72+ * Use the same animation when dismissing a greeter slide from the
73+ launcher as from a normal greeter drag. (LP: #1316513)
74+
75+ [ Michael Zanetti ]
76+ * enhance lockscreen add a retry indication label (e.g. 3 attempts
77+ left). add an additional label (e.g. phone number for multi sim).
78+ add a infoPopup (e.g. to display a warning for last retry). add min
79+ and max limit values. add tests for the above (LP: #1302050)
80+
81+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 May 2014 07:47:11 +0000
82+
183unity8 (7.86+14.10.20140522-0ubuntu1) utopic; urgency=low
284
385 [ Albert Astals ]
debian/control+15
@@ -14,6 +14,7 @@ Build-Depends: cmake,
1414 libglib2.0-dev,
1515 libgsettings-qt-dev,
1616 libhud-client2-dev,
17+ liblightdm-qt5-3-dev (>= 1.11.1),
1718 libpulse-dev,
1819 libqmenumodel-dev (>= 0.2.7),
1920 libqt5xmlpatterns5-dev,
@@ -79,6 +80,7 @@ Depends: gsettings-desktop-schemas,
7980 qml-module-qtquick-xmllistmodel,
8081 qtdeclarative5-gsettings1.0,
8182 qtdeclarative5-ubuntu-settings-components,
83+ unity-greeter-session-broadcast,
8284 unity-launcher-impl-3,
8385 unity8-common (= ${source:Version}),
8486 unity8-private (= ${binary:Version}),
@@ -94,6 +96,18 @@ Replaces: ubuntu-touch-session (<< 0.82~),
9496Description: Unity 8 shell
9597 The Unity 8 shell is the primary user interface for Ubuntu devices.
9698
99+Package: unity8-greeter
100+Architecture: any
101+Depends: libglib2.0-bin,
102+ lightdm,
103+ unity8 (= ${source:Version}),
104+ ${misc:Depends},
105+ ${shlibs:Depends},
106+Breaks: unity8 (<< 7.87),
107+Replaces: unity8 (<< 7.87),
108+Description: Ubuntu QML greeter
109+ The Ubuntu QML greeter is the primary greeter for Ubuntu devices.
110+
97111Package: unity8-common
98112Architecture: all
99113Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
@@ -132,6 +146,7 @@ Depends: gir1.2-glib-2.0,
132146 ubuntu-ui-toolkit-autopilot,
133147 unity8 (= ${source:Version}),
134148 unity8-fake-env (= ${source:Version}),
149+ unity8-greeter (= ${source:Version}),
135150 url-dispatcher-tools,
136151 ${misc:Depends},
137152 ${python3:Depends},
debian/copyright+1−1
@@ -22,7 +22,7 @@ License: GPL-3
2222 On Debian systems, the full text of the GNU General Public License
2323 version 3 can be found in the file /usr/share/common-licenses/GPL-3.
2424
25−Files: tests/mocks/LightDM/UsersModel.h tests/mocks/LightDM/Greeter.h
25+Files: tests/mocks/liblightdm/UsersModel.h tests/mocks/liblightdm/Greeter.h
2626Copyright: 2013 Canonical Ltd.
2727 2010-2011 David Edmundson
2828 2010-2011 Robert Ancell
debian/rules−4
@@ -34,7 +34,3 @@ override_dh_install:
3434# use private lib directories
3535override_dh_makeshlibs:
3636 dh_makeshlibs -Nunity8-private -Nunity8-fake-env
37−
38−# libMockLightDM-qml.so links against liblightdm-qt5-2.so which doesn't exist
39−override_dh_shlibdeps:
40− dh_shlibdeps -XlibMockLightDM-qml.so
debian/unity8-greeter.installnew file+6
@@ -0,0 +1,6 @@
1+usr/bin/unity8-greeter
2+usr/bin/unity8-greeter-wrapper
3+usr/share/lightdm
4+usr/share/unity8/GreeterShell.qml
5+usr/share/unity8/Greeter
6+usr/share/upstart/sessions/unity8-greeter-*.conf
debian/unity8-private.install+1
@@ -4,6 +4,7 @@ usr/lib/*/unity8/qml/HudClient
44usr/lib/*/unity8/qml/LightDM
55usr/lib/*/unity8/qml/Powerd
66usr/lib/*/unity8/qml/SessionBroadcast
7+usr/lib/*/unity8/qml/SessionManager
78usr/lib/*/unity8/qml/Ubuntu
89usr/lib/*/unity8/qml/Unity
910usr/lib/*/unity8/qml/Utils
debian/unity8.install−1
@@ -1,7 +1,6 @@
11data/unity8.conf usr/share/upstart/sessions/
22usr/bin/unity8
33usr/share/applications/unity8.desktop
4−usr/share/unity8/Greeter
54usr/share/unity8/Hud
65usr/share/unity8/Launcher
76usr/share/unity8/Panel
plugins/AccountsService/AccountsService.cpp+34−11
@@ -24,7 +24,6 @@
2424AccountsService::AccountsService(QObject* parent)
2525 : QObject(parent),
2626 m_service(new AccountsServiceDBusAdaptor(this)),
27− m_user(qgetenv("USER")),
2827 m_demoEdges(false),
2928 m_statsWelcomeScreen(false)
3029{
@@ -32,6 +31,9 @@ AccountsService::AccountsService(QObject* parent)
3231 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
3332 connect(m_service, SIGNAL(maybeChanged(const QString &)),
3433 this, SLOT(maybeChanged(const QString &)));
34+
35+ setUser(qgetenv("USER"));
36+ updateDemoEdgesForCurrentUser();
3537}
3638
3739QString AccountsService::user() const
@@ -57,9 +59,22 @@ bool AccountsService::demoEdges() const
5759void AccountsService::setDemoEdges(bool demoEdges)
5860{
5961 m_demoEdges = demoEdges;
62+ Q_EMIT demoEdgesChanged();
6063 m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges", demoEdges);
6164}
6265
66+bool AccountsService::demoEdgesForCurrentUser() const
67+{
68+ return m_demoEdgesForCurrentUser;
69+}
70+
71+void AccountsService::setDemoEdgesForCurrentUser(bool demoEdgesForCurrentUser)
72+{
73+ m_demoEdgesForCurrentUser = demoEdgesForCurrentUser;
74+ Q_EMIT demoEdgesForCurrentUserChanged();
75+ m_service->setUserProperty(qgetenv("USER"), "com.canonical.unity.AccountsService", "demo-edges", demoEdgesForCurrentUser);
76+}
77+
6378QString AccountsService::backgroundFile() const
6479{
6580 return m_backgroundFile;
@@ -79,6 +94,15 @@ void AccountsService::updateDemoEdges()
7994 }
8095}
8196
97+void AccountsService::updateDemoEdgesForCurrentUser()
98+{
99+ auto demoEdgesForCurrentUser = m_service->getUserProperty(qgetenv("USER"), "com.canonical.unity.AccountsService", "demo-edges").toBool();
100+ if (m_demoEdgesForCurrentUser != demoEdgesForCurrentUser) {
101+ m_demoEdgesForCurrentUser = demoEdgesForCurrentUser;
102+ Q_EMIT demoEdgesForCurrentUserChanged();
103+ }
104+}
105+
82106void AccountsService::updateBackgroundFile()
83107{
84108 auto backgroundFile = m_service->getUserProperty(m_user, "org.freedesktop.Accounts.User", "BackgroundFile").toString();
@@ -99,13 +123,14 @@ void AccountsService::updateStatsWelcomeScreen()
99123
100124void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
101125{
102− if (m_user != user) {
103− return;
104− }
105−
106126 if (interface == "com.canonical.unity.AccountsService") {
107127 if (changed.contains("demo-edges")) {
108− updateDemoEdges();
128+ if (qgetenv("USER") == user) {
129+ updateDemoEdgesForCurrentUser();
130+ }
131+ if (m_user == user) {
132+ updateDemoEdges();
133+ }
109134 }
110135 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
111136 if (changed.contains("StatsWelcomeScreen")) {
@@ -116,10 +141,8 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
116141
117142void AccountsService::maybeChanged(const QString &user)
118143{
119− if (m_user != user) {
120− return;
144+ if (m_user == user) {
145+ // Standard properties might have changed
146+ updateBackgroundFile();
121147 }
122−
123− // Standard properties might have changed
124− updateBackgroundFile();
125148}
plugins/AccountsService/AccountsService.h+9
@@ -35,6 +35,10 @@ class AccountsService: public QObject
3535 READ demoEdges
3636 WRITE setDemoEdges
3737 NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (bool demoEdgesForCurrentUser
39+ READ demoEdgesForCurrentUser
40+ WRITE setDemoEdgesForCurrentUser
41+ NOTIFY demoEdgesForCurrentUserChanged)
3842 Q_PROPERTY (QString backgroundFile
3943 READ backgroundFile
4044 NOTIFY backgroundFileChanged)
@@ -49,12 +53,15 @@ public:
4953 void setUser(const QString &user);
5054 bool demoEdges() const;
5155 void setDemoEdges(bool demoEdges);
56+ bool demoEdgesForCurrentUser() const;
57+ void setDemoEdgesForCurrentUser(bool demoEdgesForCurrentUser);
5258 QString backgroundFile() const;
5359 bool statsWelcomeScreen() const;
5460
5561Q_SIGNALS:
5662 void userChanged();
5763 void demoEdgesChanged();
64+ void demoEdgesForCurrentUserChanged();
5865 void backgroundFileChanged();
5966 void statsWelcomeScreenChanged();
6067
@@ -64,12 +71,14 @@ private Q_SLOTS:
6471
6572private:
6673 void updateDemoEdges();
74+ void updateDemoEdgesForCurrentUser();
6775 void updateBackgroundFile();
6876 void updateStatsWelcomeScreen();
6977
7078 AccountsServiceDBusAdaptor *m_service;
7179 QString m_user;
7280 bool m_demoEdges;
81+ bool m_demoEdgesForCurrentUser;
7382 QString m_backgroundFile;
7483 bool m_statsWelcomeScreen;
7584};
plugins/CMakeLists.txt+1
@@ -17,6 +17,7 @@ add_subdirectory(LightDM)
1717add_subdirectory(Dash)
1818add_subdirectory(Powerd)
1919add_subdirectory(SessionBroadcast)
20+add_subdirectory(SessionManager)
2021add_subdirectory(Ubuntu)
2122add_subdirectory(Unity)
2223add_subdirectory(Utils)
plugins/Dash/CardCreator.js+3−3
@@ -70,10 +70,9 @@ var kArtShapeHolderCode = 'Item { \n\
7070 radius: "medium"; \n\
7171 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
7272 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
73− Component.onCompleted: updateWidthHeightBindings(); \n\
73+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
7474 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
7575 visible: image.status == Image.Ready; \n\
76− borderSource: artShapeBorderSource; \n\
7776 function updateWidthHeightBindings() { \n\
7877 if (aspectSmallerThanImageAspect) { \n\
7978 width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
@@ -281,7 +280,7 @@ function cardString(template, components) {
281280 property var template; \n\
282281 property var components; \n\
283282 property var cardData; \n\
284− property var artShapeBorderSource; \n\
283+ property var artShapeBorderSource: undefined; \n\
285284 property real fontScale: 1.0; \n\
286285 property int headerAlignment: Text.AlignLeft; \n\
287286 property int fixedHeaderHeight: -1; \n\
@@ -307,6 +306,7 @@ function cardString(template, components) {
307306 }
308307
309308 if (hasArt) {
309+ code += 'onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; } \n';
310310 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
311311
312312 var widthCode, heightCode;
plugins/Dash/abstractdashview.cpp+3
@@ -176,6 +176,9 @@ void AbstractDashView::refill()
176176
177177bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
178178{
179+ if (fillToY <= fillFromY)
180+ return false;
181+
179182 if (!delegate())
180183 return false;
181184
plugins/LightDM/CMakeLists.txt+6−9
@@ -1,23 +1,22 @@
11# Dependencies
22include(FindPkgConfig)
3+pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-3)
34pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
4−# TODO: Once we split out a separate greeter process, uncomment these lines
5−#pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-2)
65
76include_directories(
87 ${CMAKE_CURRENT_SOURCE_DIR}
98 ${CMAKE_CURRENT_BINARY_DIR}
109 ${CMAKE_SOURCE_DIR}/plugins/Utils
11− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
12− #${LIBLIGHTDM_INCLUDE_DIRS}
10+ ${LIBLIGHTDM_INCLUDE_DIRS}
1311 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
1412)
1513
1614set(QMLPLUGIN_SRC
17− ../Utils/qsortfilterproxymodelqml.cpp # FIXME evaluate a more generic approach for using other plugins
15+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qsortfilterproxymodelqml.cpp # FIXME evaluate a more generic approach for using other plugins
1816 DBusGreeterList.cpp
1917 Greeter.cpp
2018 plugin.cpp
19+ URLDispatcher.cpp
2120 UsersModel.cpp
2221 )
2322
@@ -26,12 +25,10 @@ add_library(LightDM-qml MODULE
2625 )
2726
2827target_link_libraries(LightDM-qml
29− MockLightDM-demo
30−# TODO: Once we split out a separate greeter process, uncomment these lines
31−# ${LIBLIGHTDM_LDFLAGS}
28+ ${LIBLIGHTDM_LDFLAGS}
3229 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
3330 )
3431
35−qt5_use_modules(LightDM-qml DBus Gui Qml)
32+qt5_use_modules(LightDM-qml DBus Qml)
3633
3734add_unity8_plugin(LightDM 0.1 LightDM TARGETS LightDM-qml)
plugins/LightDM/Greeter.cpp+3
@@ -56,7 +56,10 @@ Greeter::Greeter(QObject* parent)
5656 this, SLOT(showPromptFilter(QString, QLightDM::Greeter::PromptType)));
5757 connect(d->m_greeter, SIGNAL(authenticationComplete()),
5858 this, SLOT(authenticationCompleteFilter()));
59+ connect(d->m_greeter, SIGNAL(idle()), this, SIGNAL(idle()));
60+ connect(d->m_greeter, SIGNAL(reset()), this, SIGNAL(reset()));
5961
62+ d->m_greeter->setResettable(true);
6063 d->m_greeter->connectSync();
6164}
6265
plugins/LightDM/Greeter.h+2
@@ -56,6 +56,8 @@ Q_SIGNALS:
5656 void authenticationComplete();
5757 void authenticationUserChanged(const QString &user);
5858 void promptlessChanged();
59+ void idle();
60+ void reset();
5961
6062 // This signal is emitted by external agents like indicators, and the UI
6163 // should switch to this user if possible.
plugins/LightDM/URLDispatcher.cppnew file+55
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "URLDispatcher.h"
18+
19+#include <QDBusConnection>
20+
21+class URLDispatcherInterface : public QObject
22+{
23+ Q_OBJECT
24+ Q_CLASSINFO("D-Bus Interface", "com.canonical.URLDispatcher")
25+
26+public:
27+ explicit URLDispatcherInterface(URLDispatcher *parent);
28+
29+ Q_SCRIPTABLE void DispatchURL(const QString &url);
30+};
31+
32+URLDispatcherInterface::URLDispatcherInterface(URLDispatcher *parent)
33+ : QObject(parent)
34+{
35+}
36+
37+void URLDispatcherInterface::DispatchURL(const QString &url)
38+{
39+ Q_EMIT static_cast<URLDispatcher *>(parent())->dispatchURL(url);
40+}
41+
42+URLDispatcher::URLDispatcher(QObject *parent)
43+ : QObject(parent)
44+{
45+ // This class also manages our url-dispatcher interception. We intercept
46+ // url-dispatcher because rather than spawning the handler for the URL in
47+ // our own session, we want to do notify the user session to do it for us
48+ // (and start an unlock in the process).
49+ QDBusConnection connection = QDBusConnection::sessionBus();
50+ URLDispatcherInterface *dispatcher = new URLDispatcherInterface(this);
51+ connection.registerObject("/com/canonical/URLDispatcher", dispatcher, QDBusConnection::ExportScriptableContents);
52+ connection.registerService("com.canonical.URLDispatcher");
53+}
54+
55+#include "URLDispatcher.moc"
tests/mocks/Unity/iconutils.h → plugins/LightDM/URLDispatcher.hrenamed+13−8
@@ -12,18 +12,23 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Michal Hruby <michal.hruby@canonical.com>
1815 */
1916
20−#ifndef ICONUTILS_H
21−#define ICONUTILS_H
17+#ifndef UNITY_URLDISPATCHER_H
18+#define UNITY_URLDISPATCHER_H
2219
20+#include <QObject>
2321#include <QString>
24−#include <QVariant>
2522
26−QString gIconToDeclarativeImageProviderString(QString const &giconString);
27−QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype, QVariantHash const &metadata);
23+class URLDispatcher : public QObject
24+{
25+ Q_OBJECT
26+
27+public:
28+ explicit URLDispatcher(QObject *parent=0);
29+
30+Q_SIGNALS:
31+ void dispatchURL(const QString &url);
32+};
2833
2934#endif
plugins/LightDM/plugin.cpp+9
@@ -20,6 +20,7 @@
2020#include "plugin.h"
2121#include "DBusGreeterList.h"
2222#include "Greeter.h"
23+#include "URLDispatcher.h"
2324#include "UsersModel.h"
2425#include <libusermetricsoutput/ColorTheme.h>
2526#include <libusermetricsoutput/UserMetrics.h>
@@ -46,6 +47,13 @@ static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
4647 return greeter;
4748}
4849
50+static QObject *dispatcher_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
51+{
52+ Q_UNUSED(engine)
53+ Q_UNUSED(scriptEngine)
54+ return new URLDispatcher();
55+}
56+
4957static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
5058{
5159 Q_UNUSED(engine)
@@ -67,6 +75,7 @@ void LightDMPlugin::registerTypes(const char *uri)
6775
6876 Q_ASSERT(uri == QLatin1String("LightDM"));
6977 qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
78+ qmlRegisterSingletonType<URLDispatcher>(uri, 0, 1, "URLDispatcher", dispatcher_provider);
7079 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
7180 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
7281 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
plugins/SessionBroadcast/SessionBroadcast.cpp+35
@@ -18,11 +18,26 @@
1818
1919#include "SessionBroadcast.h"
2020#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
22+#include <QDBusInterface>
2123
2224SessionBroadcast::SessionBroadcast(QObject* parent)
2325 : QObject(parent)
2426{
2527 auto connection = QDBusConnection::SM_BUSNAME();
28+ auto interface = connection.interface();
29+ interface->startService("com.canonical.Unity.Greeter.Broadcast");
30+ m_broadcaster = new QDBusInterface("com.canonical.Unity.Greeter.Broadcast",
31+ "/com/canonical/Unity/Greeter/Broadcast",
32+ "com.canonical.Unity.Greeter.Broadcast",
33+ connection, this);
34+
35+ connection.connect("com.canonical.Unity.Greeter.Broadcast",
36+ "/com/canonical/Unity/Greeter/Broadcast",
37+ "com.canonical.Unity.Greeter.Broadcast",
38+ "StartUrl",
39+ this,
40+ SLOT(onStartUrl(const QString &, const QString &)));
2641
2742 connection.connect("com.canonical.Unity.Greeter.Broadcast",
2843 "/com/canonical/Unity/Greeter/Broadcast",
@@ -32,6 +47,26 @@ SessionBroadcast::SessionBroadcast(QObject* parent)
3247 SLOT(onShowHome(const QString &)));
3348}
3449
50+void SessionBroadcast::requestUrlStart(const QString &username, const QString &url)
51+{
52+ m_broadcaster->asyncCall("RequestUrlStart", username, url);
53+}
54+
55+void SessionBroadcast::requestHomeShown(const QString &username)
56+{
57+ m_broadcaster->asyncCall("RequestHomeShown", username);
58+}
59+
60+void SessionBroadcast::onStartUrl(const QString &username, const QString &url)
61+{
62+ // Since this signal is just used for testing, we don't *really* care if
63+ // username matches, but just in case we do eventually use the signal, we
64+ // should only listen to our own requests.
65+ if (username == qgetenv("USER")) {
66+ Q_EMIT startUrl(url);
67+ }
68+}
69+
3570void SessionBroadcast::onShowHome(const QString &username)
3671{
3772 // Only listen to requests meant for us
plugins/SessionBroadcast/SessionBroadcast.h+11
@@ -31,11 +31,22 @@ class SessionBroadcast: public QObject
3131public:
3232 explicit SessionBroadcast(QObject *parent = 0);
3333
34+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
35+ Q_INVOKABLE void requestHomeShown(const QString &username);
36+
3437Q_SIGNALS:
38+ // This signal isn't actually used by the shell
39+ // (unity-greeter-session-broadcast handles launching an app for us), but
40+ // it's useful for testing the plugin.
41+ void startUrl(const QString &url);
3542 void showHome();
3643
3744private Q_SLOTS:
45+ void onStartUrl(const QString &username, const QString &url);
3846 void onShowHome(const QString &username);
47+
48+private:
49+ QDBusInterface *m_broadcaster;
3950};
4051
4152#endif
plugins/SessionManager/CMakeLists.txtnew file+22
@@ -0,0 +1,22 @@
1+set(QMLPLUGIN_SRC
2+ plugin.cpp
3+ SessionManager.cpp
4+ )
5+
6+include_directories(
7+ ${Qt5DBus_INCLUDE_DIRS}
8+ )
9+
10+add_definitions(-DSM_BUSNAME=systemBus)
11+
12+add_library(SessionManager-qml MODULE
13+ ${QMLPLUGIN_SRC}
14+ )
15+
16+target_link_libraries(SessionManager-qml
17+ ${Qt5DBus_LIBRARIES}
18+ )
19+
20+qt5_use_modules(SessionManager-qml Qml)
21+
22+add_unity8_plugin(SessionManager 0.1 SessionManager TARGETS SessionManager-qml)
plugins/SessionManager/SessionManager.cppnew file+124
@@ -0,0 +1,124 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "SessionManager.h"
20+#include <QtCore/QEvent>
21+#include <QtDBus/QDBusPendingReply>
22+
23+SessionManager::SessionManager(QObject* parent)
24+ : QObject(parent),
25+ l1_manager(NULL),
26+ l1_session(NULL),
27+ ldm_session(NULL),
28+ is_active(true) // assume we're active w/o logind
29+{
30+ l1_manager = new QDBusInterface("org.freedesktop.login1",
31+ "/org/freedesktop/login1",
32+ "org.freedesktop.login1.Manager",
33+ QDBusConnection::SM_BUSNAME(), this);
34+ if (l1_manager->isValid() && QString(qgetenv("XDG_SESSION_ID")) != "") {
35+ QDBusPendingCall pcall = l1_manager->asyncCall("GetSession", QString(qgetenv("XDG_SESSION_ID")));
36+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
37+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
38+ this, SLOT(getSessionSlot(QDBusPendingCallWatcher*)));
39+ }
40+
41+ if (QString(qgetenv("XDG_SESSION_PATH")) != "") {
42+ ldm_session = new QDBusInterface("org.freedesktop.DisplayManager",
43+ QString(qgetenv("XDG_SESSION_PATH")),
44+ "org.freedesktop.DisplayManager.Session",
45+ QDBusConnection::SM_BUSNAME(), this);
46+ }
47+}
48+
49+bool SessionManager::active() const
50+{
51+ // We cache this value for performance reasons, as QDBusInterface does not
52+ // cache for us.
53+ return is_active;
54+}
55+
56+void SessionManager::lock()
57+{
58+ // We use ldm_session to handle locking rather than l1_session, because
59+ // l1_session doesn't have permissions to lock from the user session, but
60+ // ldm_session does. This is what the rest of Ubuntu uses to lock.
61+ if (ldm_session != NULL && ldm_session->isValid())
62+ ldm_session->asyncCall("Lock");
63+}
64+
65+void SessionManager::getSessionSlot(QDBusPendingCallWatcher *watcher)
66+{
67+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
68+ if (!reply.isError()) {
69+ QDBusObjectPath path = reply.argumentAt<0>();
70+ l1_session = new QDBusInterface("org.freedesktop.login1",
71+ path.path(),
72+ "org.freedesktop.login1.Session",
73+ l1_manager->connection(), this);
74+
75+ l1_manager->connection().connect("org.freedesktop.login1",
76+ path.path(),
77+ "org.freedesktop.DBus.Properties",
78+ "PropertiesChanged",
79+ this,
80+ SLOT(propertiesChanged(QString, QVariantMap, QStringList)));
81+
82+ // Login1 is very odd about emitting PropertiesChanged. It often does
83+ // not do so when active is becoming false. But it always does when
84+ // it becomes true. So we watch for the "Lock" signal too, which is a
85+ // reliably hint that active is false.
86+ l1_manager->connection().connect("org.freedesktop.login1",
87+ path.path(),
88+ "org.freedesktop.login1.Session",
89+ "Lock",
90+ this,
91+ SLOT(sessionLocked()));
92+
93+ propertiesChanged("", QVariantMap(), QStringList() << "Active");
94+ }
95+ watcher->deleteLater();
96+}
97+
98+void SessionManager::propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
99+{
100+ Q_UNUSED(interface)
101+
102+ QVariant value = changed.value("Active");
103+
104+ // If Active isn't in changed, check if we were at least notified about it
105+ // changing on the server. Also check if IdleHint was modified, because
106+ // sometimes logind does not emit a changed signal for Active becoming
107+ // false, but will emit for IdleHint when that does happen.
108+ if (!value.isValid() && (invalid.contains("Active") || invalid.contains("IdleHint") || changed.contains("IdleHint")) && l1_session->isValid()) {
109+ value = l1_session->property("Active");
110+ if (!value.isValid())
111+ value = QVariant(false);
112+ }
113+
114+ if (value.isValid() && value.toBool() != is_active) {
115+ is_active = value.toBool();
116+ Q_EMIT activeChanged();
117+ }
118+}
119+
120+void SessionManager::sessionLocked()
121+{
122+ is_active = false;
123+ Q_EMIT activeChanged();
124+}
plugins/SessionManager/SessionManager.hnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_SESSIONMANAGER_H
21+#define UNITY_SESSIONMANAGER_H
22+
23+#include <QtCore/QObject>
24+#include <QtCore/QString>
25+#include <QtCore/QStringList>
26+#include <QtCore/QVariantMap>
27+#include <QtDBus/QDBusInterface>
28+#include <QtDBus/QDBusPendingCallWatcher>
29+
30+class SessionManager: public QObject
31+{
32+ Q_OBJECT
33+ Q_PROPERTY(bool active
34+ READ active
35+ NOTIFY activeChanged
36+ FINAL)
37+
38+public:
39+ explicit SessionManager(QObject *parent = 0);
40+
41+ bool active() const;
42+
43+public Q_SLOTS:
44+ void lock();
45+
46+Q_SIGNALS:
47+ void activeChanged();
48+
49+private Q_SLOTS:
50+ void getSessionSlot(QDBusPendingCallWatcher *watcher);
51+ void propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid);
52+ void sessionLocked();
53+
54+private:
55+ QDBusInterface *l1_manager;
56+ QDBusInterface *l1_session;
57+ QDBusInterface *ldm_session;
58+ bool is_active;
59+};
60+
61+#endif
plugins/SessionManager/plugin.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "plugin.h"
21+#include "SessionManager.h"
22+#include <QtQml/qqml.h>
23+
24+static QObject *sessionmanager_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25+{
26+ Q_UNUSED(engine)
27+ Q_UNUSED(scriptEngine)
28+ return new SessionManager();
29+}
30+
31+void SessionManagerPlugin::registerTypes(const char *uri)
32+{
33+ Q_ASSERT(uri == QLatin1String("SessionManager"));
34+ qmlRegisterSingletonType<SessionManager>(uri, 0, 1, "SessionManager", sessionmanager_provider);
35+}
plugins/SessionManager/plugin.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Gerry Boland <gerry.boland@canonical.com>
17+ * Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef SESSIONMANAGER_PLUGIN_H
21+#define SESSIONMANAGER_PLUGIN_H
22+
23+#include <QtQml/QQmlEngine>
24+#include <QtQml/QQmlExtensionPlugin>
25+
26+class SessionManagerPlugin : public QQmlExtensionPlugin
27+{
28+ Q_OBJECT
29+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30+
31+public:
32+ void registerTypes(const char *uri);
33+};
34+
35+#endif
plugins/SessionManager/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module SessionManager
2+plugin SessionManager-qml
plugins/Unity/Launcher/backend/launcherbackend.cpp+16−2
@@ -37,12 +37,15 @@ public:
3737 bool countVisible;
3838};
3939
40−LauncherBackend::LauncherBackend(QObject *parent):
40+LauncherBackend::LauncherBackend(bool greeterMode, QObject *parent):
4141 QDBusVirtualObject(parent),
42+ m_greeterMode(greeterMode),
4243 m_accounts(nullptr)
4344{
4445#ifndef LAUNCHER_TESTING
4546 m_accounts = new AccountsServiceDBusAdaptor(this);
47+ connect(m_accounts, SIGNAL(propertiesChanged(QString, QString, QStringList)),
48+ this, SLOT(accountsListChanged(QString, QString, QStringList)));
4649#endif
4750 m_user = qgetenv("USER");
4851 syncFromAccounts();
@@ -225,7 +228,7 @@ void LauncherBackend::setCountVisible(const QString &appId, bool visible) const
225228
226229void LauncherBackend::setUser(const QString &username)
227230{
228− if (qgetenv("USER") == "lightdm" && m_user != username) {
231+ if (m_greeterMode && m_user != username) {
229232 m_user = username;
230233 syncFromAccounts();
231234 }
@@ -238,6 +241,15 @@ void LauncherBackend::triggerQuickListAction(const QString &appId, const QString
238241 Q_UNUSED(quickListId)
239242}
240243
244+void LauncherBackend::accountsListChanged(const QString &user, const QString &interface, const QStringList &changed)
245+{
246+ if (m_greeterMode && user == m_user &&
247+ interface == "com.canonical.unity.AccountsService" &&
248+ changed.contains("launcher-items")) {
249+ syncFromAccounts();
250+ }
251+}
252+
241253void LauncherBackend::syncFromAccounts()
242254{
243255 QList<QVariantMap> apps;
@@ -299,6 +311,8 @@ void LauncherBackend::syncFromAccounts()
299311 loadFromVariant(app.toMap());
300312 }
301313 }
314+
315+ Q_EMIT refreshApplications();
302316}
303317
304318void LauncherBackend::syncToAccounts()
plugins/Unity/Launcher/backend/launcherbackend.h+7−1
@@ -42,7 +42,7 @@ class LauncherBackend : public QDBusVirtualObject
4242 friend LauncherBackendTest;
4343
4444public:
45− LauncherBackend(QObject *parent = 0);
45+ LauncherBackend(bool greeterMode = false, QObject *parent = 0);
4646 virtual ~LauncherBackend();
4747
4848 /**
@@ -162,6 +162,7 @@ public:
162162 virtual QString introspect (const QString &path) const;
163163
164164Q_SIGNALS:
165+ void refreshApplications() const;
165166 void quickListChanged(const QString &appId, const QList<QuickListEntry> &quickList) const;
166167 void progressChanged(const QString &appId, int progress) const;
167168 void countChanged(const QString &appId, int count) const;
@@ -178,6 +179,8 @@ private:
178179 void syncFromAccounts();
179180 void syncToAccounts();
180181
182+ bool m_greeterMode;
183+
181184 QList<QString> m_storedApps;
182185 mutable QHash<QString, LauncherBackendItem*> m_itemCache;
183186
@@ -186,6 +189,9 @@ private:
186189
187190 void emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const;
188191
192+private Q_SLOTS:
193+ void accountsListChanged(const QString &user, const QString &interface, const QStringList &changed);
194+
189195protected: /* Protected to allow testing */
190196 LauncherBackendItem* getItem(const QString& appId) const;
191197
plugins/Unity/Launcher/launcheritem.cpp+14−9
@@ -22,12 +22,13 @@
2222
2323#include <libintl.h>
2424
25−LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent) :
25+LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, bool pinnable, QObject *parent) :
2626 LauncherItemInterface(parent),
2727 m_appId(appId),
2828 m_name(name),
2929 m_icon(icon),
3030 m_pinned(false),
31+ m_pinnable(pinnable),
3132 m_running(false),
3233 m_recent(false),
3334 m_progress(-1),
@@ -38,10 +39,12 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3839 QuickListEntry nameAction;
3940 nameAction.setText(m_name);
4041 m_quickList->appendAction(nameAction);
41− QuickListEntry pinningAction;
42− pinningAction.setActionId("pin_item");
43− pinningAction.setText(gettext("Lock to Launcher"));
44− m_quickList->appendAction(pinningAction);
42+ if (m_pinnable) {
43+ QuickListEntry pinningAction;
44+ pinningAction.setActionId("pin_item");
45+ pinningAction.setText(gettext("Lock to Launcher"));
46+ m_quickList->appendAction(pinningAction);
47+ }
4548}
4649
4750QString LauncherItem::appId() const
@@ -68,10 +71,12 @@ void LauncherItem::setPinned(bool pinned)
6871{
6972 if (m_pinned != pinned) {
7073 m_pinned = pinned;
71− QuickListEntry entry;
72− entry.setActionId("pin_item");
73− entry.setText(pinned ? gettext("Unlock from Launcher") : gettext("Lock to Launcher"));
74− m_quickList->updateAction(entry);
74+ if (m_pinnable) {
75+ QuickListEntry entry;
76+ entry.setActionId("pin_item");
77+ entry.setText(pinned ? gettext("Unlock from Launcher") : gettext("Lock to Launcher"));
78+ m_quickList->updateAction(entry);
79+ }
7580 Q_EMIT pinnedChanged(pinned);
7681 }
7782}
plugins/Unity/Launcher/launcheritem.h+2−1
@@ -32,7 +32,7 @@ class LauncherItem: public LauncherItemInterface
3232{
3333 Q_OBJECT
3434public:
35− LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = 0);
35+ LauncherItem(const QString &appId, const QString &name, const QString &icon, bool pinnable, QObject *parent = 0);
3636
3737 QString appId() const;
3838 QString name() const;
@@ -67,6 +67,7 @@ private:
6767 QString m_name;
6868 QString m_icon;
6969 bool m_pinned;
70+ bool m_pinnable;
7071 bool m_running;
7172 bool m_recent;
7273 int m_progress;
plugins/Unity/Launcher/launchermodel.cpp+57−11
@@ -27,20 +27,15 @@ using namespace unity::shell::application;
2727
2828LauncherModel::LauncherModel(QObject *parent):
2929 LauncherModelInterface(parent),
30− m_backend(new LauncherBackend(this)),
30+ m_greeterMode(qgetenv("XDG_SESSION_CLASS") == "greeter"),
31+ m_backend(new LauncherBackend(m_greeterMode, this)),
3132 m_appManager(0)
3233{
3334 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
3435 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
36+ connect(m_backend, SIGNAL(refreshApplications()), SLOT(refreshStoredApplications()));
3537
36− Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
37− LauncherItem *item = new LauncherItem(entry,
38− m_backend->displayName(entry),
39− m_backend->icon(entry),
40− this);
41− item->setPinned(true);
42− m_list.append(item);
43− }
38+ refreshStoredApplications();
4439}
4540
4641LauncherModel::~LauncherModel()
@@ -139,7 +134,8 @@ void LauncherModel::pin(const QString &appId, int index)
139134 beginInsertRows(QModelIndex(), index, index);
140135 LauncherItem *item = new LauncherItem(appId,
141136 m_backend->displayName(appId),
142− m_backend->icon(appId));
137+ m_backend->icon(appId),
138+ !m_greeterMode);
143139 item->setPinned(true);
144140 m_list.insert(index, item);
145141 endInsertRows();
@@ -199,6 +195,56 @@ void LauncherModel::setUser(const QString &username)
199195 m_backend->setUser(username);
200196}
201197
198+QString LauncherModel::getUrlForAppId(const QString &appId) const
199+{
200+ // appId is either an appId or a legacy app name. Let's find out which
201+ if (appId.isEmpty())
202+ return QString();
203+
204+ QString df = m_backend->desktopFile(appId + ".desktop");
205+ if (!df.isEmpty())
206+ return "application:///" + appId + ".desktop";
207+
208+ QStringList parts = appId.split('_');
209+ QString package = parts.value(0);
210+ QString app = parts.value(1, "first-listed-app");
211+ return "appid://" + package + "/" + app + "/current-user-version";
212+}
213+
214+void LauncherModel::refreshStoredApplications()
215+{
216+ // First remove any existing ones
217+ QList<int> storedAppIndices;
218+ for (int i = 0; i < m_list.count(); ++i) {
219+ if (!m_list.at(i)->recent()) {
220+ storedAppIndices << i;
221+ }
222+ }
223+ int run = 0;
224+ while (storedAppIndices.count() > 0) {
225+ beginRemoveRows(QModelIndex(), storedAppIndices.first() - run, storedAppIndices.first() - run);
226+ m_list.takeAt(storedAppIndices.first() - run)->deleteLater();
227+ endRemoveRows();
228+ storedAppIndices.takeFirst();
229+ ++run;
230+ }
231+
232+ // Now insert all stored apps at beginning of list
233+ QStringList storedApplications = m_backend->storedApplications();
234+ beginInsertRows(QModelIndex(), 0, storedApplications.size() - 1);
235+ run = 0;
236+ Q_FOREACH (const QString &entry, storedApplications) {
237+ LauncherItem *item = new LauncherItem(entry,
238+ m_backend->displayName(entry),
239+ m_backend->icon(entry),
240+ !m_greeterMode,
241+ this);
242+ item->setPinned(true);
243+ m_list.insert(run++, item);
244+ }
245+ endInsertRows();
246+}
247+
202248ApplicationManagerInterface *LauncherModel::applicationManager() const
203249{
204250 return m_appManager;
@@ -302,7 +348,7 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
302348 if (found) {
303349 // Shall we paint some running/recent app highlight? If yes, do it here.
304350 } else {
305− LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString());
351+ LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), !m_greeterMode);
306352 item->setRecent(true);
307353 item->setFocused(app->focused());
308354
plugins/Unity/Launcher/launchermodel.h+3
@@ -51,6 +51,7 @@ public:
5151 Q_INVOKABLE void requestRemove(const QString &appId);
5252 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
5353 Q_INVOKABLE void setUser(const QString &username);
54+ Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
5455
5556 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
5657 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
@@ -62,6 +63,7 @@ private:
6263private Q_SLOTS:
6364 void progressChanged(const QString &appId, int progress);
6465 void countChanged(const QString &appId, int count);
66+ void refreshStoredApplications();
6567
6668 void applicationAdded(const QModelIndex &parent, int row);
6769 void applicationRemoved(const QModelIndex &parent, int row);
@@ -69,6 +71,7 @@ private Q_SLOTS:
6971
7072private:
7173 QList<LauncherItem*> m_list;
74+ bool m_greeterMode;
7275 LauncherBackend *m_backend;
7376 ApplicationManagerInterface *m_appManager;
7477};
po/ca.po+9−7
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-14 08:15+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
11+"PO-Revision-Date: 2014-06-05 16:58+0000\n"
12+"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-16 06:52+0000\n"
18−"X-Generator: Launchpad (build 17007)\n"
17+"X-Launchpad-Export-Date: 2014-06-06 06:36+0000\n"
18+"X-Generator: Launchpad (build 17031)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -77,6 +77,8 @@ msgid ""
7777"You have now mastered the edge gestures and can start using the "
7878"phone<br><br>Tap on the screen to start"
7979msgstr ""
80+"Ja heu après com utilitzar els gestos de les vores per fer servir el "
81+"telèfon<br><br>Feu un toc a la pantalla per començar"
8082
8183#: qml/Components/PageHeader.qml:365
8284msgid "Recent searches"
@@ -92,7 +94,7 @@ msgstr "Mostra'n menys"
9294
9395#: qml/Dash/DashApps.qml:37
9496msgid "Recent"
95−msgstr ""
97+msgstr "Recents"
9698
9799#: qml/Dash/PreviewListView.qml:52
98100#, qt-format
@@ -109,7 +111,7 @@ msgstr "Menys..."
109111
110112#: qml/Dash/Previews/PreviewRatingInput.qml:81
111113msgid "Rate this"
112−msgstr ""
114+msgstr "Puntueu-ho"
113115
114116#: qml/Dash/Previews/PreviewRatingInput.qml:126
115117msgid "Add a review"
@@ -121,7 +123,7 @@ msgstr "Envia"
121123
122124#: qml/Greeter/Greeter.qml:151
123125msgid "Swipe to unlock"
124−msgstr ""
126+msgstr "Feu lliscar el dit per desbloquejar"
125127
126128#: qml/Greeter/Lockscreen.qml:149
127129msgid "Emergency Call"
po/de.po+5−3
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-20 13:45+0000\n"
12−"Last-Translator: Tobias Bannert <Unknown>\n"
11+"PO-Revision-Date: 2014-05-27 14:09+0000\n"
12+"Last-Translator: Phillip Sz <Unknown>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-22 06:49+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-28 05:54+0000\n"
1818"X-Generator: Launchpad (build 17017)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -73,6 +73,8 @@ msgid ""
7373"You have now mastered the edge gestures and can start using the "
7474"phone<br><br>Tap on the screen to start"
7575msgstr ""
76+"Sie haben nun die Kantengesten erfolgreich erlernt und können das Telefon "
77+"nun nutzen<br><br>Zum Starten auf den Bildschirm tippen"
7678
7779#: qml/Components/PageHeader.qml:365
7880msgid "Recent searches"
po/es.po+6−6
@@ -8,23 +8,23 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-15 15:38+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
11+"PO-Revision-Date: 2014-06-02 06:28+0000\n"
12+"Last-Translator: benjisp94 <benjisp94@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-16 06:52+0000\n"
18−"X-Generator: Launchpad (build 17007)\n"
17+"X-Launchpad-Export-Date: 2014-06-03 06:16+0000\n"
18+"X-Generator: Launchpad (build 17031)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
2222msgid "Lock to Launcher"
23−msgstr "Mantener en el lanzador"
23+msgstr "Fijar al lanzador"
2424
2525#: plugins/Unity/Launcher/launcheritem.cpp:73
2626msgid "Unlock from Launcher"
27−msgstr "No mantener en el lanzador"
27+msgstr "Quitar del lanzador"
2828
2929#: qml/Components/EdgeDemoOverlay.qml:151
3030msgid "Skip intro"
po/gl.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-03 07:31+0000\n"
11+"PO-Revision-Date: 2014-06-03 17:08+0000\n"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-06 07:34+0000\n"
18−"X-Generator: Launchpad (build 16996)\n"
17+"X-Launchpad-Export-Date: 2014-06-05 06:06+0000\n"
18+"X-Generator: Launchpad (build 17031)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -36,7 +36,7 @@ msgstr "Bordo dereito"
3636
3737#: qml/Components/EdgeDemo.qml:114
3838msgid "Try swiping from the right edge to unlock the phone"
39−msgstr "Probe o arrastre desde o bordo dereito para desbloquear o teléfono"
39+msgstr "Probe a esvarar desde o bordo dereito para desbloquear o teléfono"
4040
4141#: qml/Components/EdgeDemo.qml:145
4242msgid "Top edge"
@@ -71,7 +71,7 @@ msgid ""
7171"You have now mastered the edge gestures and can start using the "
7272"phone<br><br>Tap on the screen to start"
7373msgstr ""
74−"Agora xa domina os xestos do bordo e pode comezar a usar o "
74+"Agora xa domina os xestos dos bordos e pode comezar a usar o "
7575"teléfono<br><br>Toque na pantalla para comezar"
7676
7777#: qml/Components/PageHeader.qml:365
@@ -142,7 +142,7 @@ msgstr "Escriba ou diga unha orde"
142142
143143#: qml/Hud/Hud.qml:84
144144msgid "Loading. Please Wait..."
145−msgstr "Cargando. Agarde…"
145+msgstr "Cargando, agarde…"
146146
147147#: qml/Hud/Hud.qml:89
148148msgid "Speak Now..."
po/it.po+5−3
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-23 11:52+0000\n"
12−"Last-Translator: Gio <gio.scino@gmail.com>\n"
11+"PO-Revision-Date: 2014-05-28 15:13+0000\n"
12+"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-24 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-29 05:49+0000\n"
1818"X-Generator: Launchpad (build 17017)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -76,6 +76,8 @@ msgid ""
7676"You have now mastered the edge gestures and can start using the "
7777"phone<br><br>Tap on the screen to start"
7878msgstr ""
79+"L'apprendimento dei gesti è stato completato. È ora possibile iniziare a "
80+"usare il telefono.<br><br>Toccare lo schermo per iniziare"
7981
8082#: qml/Components/PageHeader.qml:365
8183msgid "Recent searches"
po/lv.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-07 18:17+0000\n"
11+"PO-Revision-Date: 2014-05-31 14:54+0000\n"
1212"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
1313"Language-Team: Latvian <lv@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-10 08:01+0000\n"
18−"X-Generator: Launchpad (build 16996)\n"
17+"X-Launchpad-Export-Date: 2014-06-02 06:11+0000\n"
18+"X-Generator: Launchpad (build 17031)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -64,7 +64,7 @@ msgstr ""
6464
6565#: qml/Components/EdgeDemo.qml:227
6666msgid "Well done"
67−msgstr ""
67+msgstr "Ļoti labi!"
6868
6969#: qml/Components/EdgeDemo.qml:228
7070msgid ""
@@ -91,7 +91,7 @@ msgstr "Nesens"
9191#: qml/Dash/PreviewListView.qml:52
9292#, qt-format
9393msgid "%1 Preview"
94−msgstr ""
94+msgstr "%1 Priekšstats"
9595
9696#: qml/Dash/Previews/PreviewActionCombo.qml:34
9797msgid "More..."
@@ -156,7 +156,7 @@ msgstr "Rādīt paroli"
156156
157157#: qml/Notifications/NotificationMenuItemFactory.qml:103
158158msgid "Please enter SIM PIN"
159−msgstr ""
159+msgstr "Lūdzu ievadiet SIM PIN"
160160
161161#: qml/Panel/SearchIndicator.qml:27
162162msgid "Search"
po/pl.po+6−4
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-23 14:26+0000\n"
12−"Last-Translator: igosoft <igosoft@gmail.com>\n"
11+"PO-Revision-Date: 2014-05-27 15:20+0000\n"
12+"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-24 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-28 05:54+0000\n"
1818"X-Generator: Launchpad (build 17017)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -73,6 +73,8 @@ msgid ""
7373"You have now mastered the edge gestures and can start using the "
7474"phone<br><br>Tap on the screen to start"
7575msgstr ""
76+"Wydawanie poleceń krawędzi zostało opanowane. Urządzenie gotowe do "
77+"pracy.<br><br>Dotknij ekranu by rozpocząć"
7678
7779#: qml/Components/PageHeader.qml:365
7880msgid "Recent searches"
@@ -93,7 +95,7 @@ msgstr "Ostatnio używane"
9395#: qml/Dash/PreviewListView.qml:52
9496#, qt-format
9597msgid "%1 Preview"
96−msgstr ""
98+msgstr "%1 Podgląd"
9799
98100#: qml/Dash/Previews/PreviewActionCombo.qml:34
99101msgid "More..."
po/ug.po+2−1
@@ -6,11 +6,12 @@
66msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2014-04-10 11:13-0400\n"
1111"PO-Revision-Date: 2014-04-22 05:46+0000\n"
1212"Last-Translator: abdurahman <Unknown>\n"
1313"Language-Team: Uyghur <ug@li.org>\n"
14+"Language: \n"
1415"MIME-Version: 1.0\n"
1516"Content-Type: text/plain; charset=UTF-8\n"
1617"Content-Transfer-Encoding: 8bit\n"
qml/Components/BasicShell.qmlnew file+84
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import Unity.Application 0.1
20+import "../Components"
21+
22+FocusScope {
23+ id: shell
24+
25+ // this is only here to select the width / height of the window if not running fullscreen
26+ property bool tablet: false
27+ width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
28+ height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
29+
30+ property real edgeSize: units.gu(2)
31+
32+ property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "../graphics/tablet_background.jpg" : "../graphics/phone_background.jpg")
33+ property url background
34+ property url backgroundSource
35+ property url backgroundFallbackSource
36+ property url backgroundFinal: (backgroundSource != undefined && backgroundSource != "") ? backgroundSource : (backgroundFallbackSource != undefined && backgroundFallbackSource != "") ? backgroundFallbackSource : shell.defaultBackground
37+ onBackgroundFinalChanged: shell.background = backgroundFinal
38+
39+ // This is a dummy image that is needed to determine if the picture url
40+ // in backgroundSettings points to a valid picture file.
41+ // We can't do this with the real background image because setting a
42+ // new source in onStatusChanged triggers a binding loop detection
43+ // inside Image, which causes it not to render even though a valid source
44+ // would be set. We don't mind about this image staying black and just
45+ // use it for verification to populate the source for the real
46+ // background image.
47+ Image {
48+ source: shell.background
49+ height: 0
50+ width: 0
51+ sourceSize.height: 0
52+ sourceSize.width: 0
53+ onStatusChanged: {
54+ if (status == Image.Error) {
55+ if (source != shell.defaultBackground) {
56+ shell.background = defaultBackground
57+ } else {
58+ // In case even our default background fails to load...
59+ shell.background = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#000'/></svg>"
60+ }
61+ }
62+ }
63+ }
64+
65+ VolumeControl {
66+ id: volumeControl
67+ }
68+
69+ Keys.onVolumeUpPressed: volumeControl.volumeUp()
70+ Keys.onVolumeDownPressed: volumeControl.volumeDown()
71+
72+ function hideIndicatorMenu(delay) {
73+ panel.hideIndicatorMenu(delay);
74+ }
75+
76+ focus: true
77+ onFocusChanged: if (!focus) forceActiveFocus();
78+
79+ Binding {
80+ target: i18n
81+ property: "domain"
82+ value: "unity8"
83+ }
84+}
qml/Components/EdgeDemo.qml+1−53
@@ -15,13 +15,11 @@
1515 */
1616
1717import AccountsService 0.1
18−import LightDM 0.1 as LightDM
1918import QtQuick 2.0
2019
2120Item {
2221 id: demo
2322
24− property Item greeter
2523 property Item dash
2624 property Item launcher
2725 property Item indicators
@@ -36,7 +34,6 @@ Item {
3634 property bool paused: false
3735
3836 onPausedChanged: {
39− if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
4037 if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
4138 if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
4239 if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
@@ -45,16 +42,6 @@ Item {
4542
4643 function hideEdgeDemoInShell() {
4744 AccountsService.demoEdges = false;
48− stopDemo();
49− }
50−
51− function hideEdgeDemoInGreeter() {
52− // TODO: AccountsService.demoEdges = false as lightdm user
53− }
54−
55− function hideEdgeDemos() {
56− hideEdgeDemoInGreeter();
57− hideEdgeDemoInShell();
5845 }
5946
6047 function stopDemo() {
@@ -62,7 +49,6 @@ Item {
6249 dashEnabled = true
6350 panelEnabled = true
6451 panelContentEnabled = true
65− if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
6652 if (d.topEdgeDemo) d.topEdgeDemo.destroy()
6753 if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy()
6854 if (d.leftEdgeDemo) d.leftEdgeDemo.destroy()
@@ -79,24 +65,17 @@ Item {
7965 panelEnabled = false;
8066 panelContentEnabled = false;
8167
82− // Begin with either greeter or dash, depending on which is visible
83− if (greeter && greeter.shown) {
84− startRightEdgeDemo()
85− } else {
86− startTopEdgeDemo()
87− }
68+ startTopEdgeDemo()
8869 }
8970
9071 QtObject {
9172 id: d
9273 property Component overlay
93− property QtObject rightEdgeDemo
9474 property QtObject topEdgeDemo
9575 property QtObject bottomEdgeDemo
9676 property QtObject leftEdgeDemo
9777 property QtObject finalEdgeDemo
9878 property bool showEdgeDemo: AccountsService.demoEdges
99− property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
10079
10180 onShowEdgeDemoChanged: {
10281 stopDemo()
@@ -106,37 +85,6 @@ Item {
10685 }
10786 }
10887
109− function startRightEdgeDemo() {
110− if (demo.greeter) {
111− d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
112− "edge": "right",
113− "title": i18n.tr("Right edge"),
114− "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
115− "anchors.fill": demo.greeter,
116− });
117− }
118− if (d.rightEdgeDemo) {
119− d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
120− } else {
121− stopDemo();
122− }
123− }
124−
125− Connections {
126− target: demo.greeter
127−
128− function hide() {
129− if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
130− d.rightEdgeDemo.hide()
131− hideEdgeDemoInGreeter()
132− startTopEdgeDemo()
133− }
134− }
135−
136− onUnlocked: hide()
137− onShownChanged: if (!greeter.shown) hide()
138− }
139−
14088 function startTopEdgeDemo() {
14189 demo.panelEnabled = true;
14290 if (demo.dash && demo.underlay) {
qml/Greeter/Lockscreen.qml → qml/Components/Lockscreen.qmlrenamed+88−24
@@ -15,8 +15,8 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import "../Components"
18+import Ubuntu.Components 1.0
19+import Ubuntu.Components.Popups 1.0
2020
2121Showable {
2222 id: root
@@ -26,19 +26,32 @@ Showable {
2626
2727 // Placeholder text
2828 property string placeholderText: ""
29+ // Placeholder text while shaking (e.g. Incorrect passprase)
30+ property string wrongPlaceholderText: ""
31+ // Retries text (e.g. 3 retries left)
32+ property string retryText: ""
33+
34+ // Informational text. (e.g. some text to tell which domain this is pin is entered for.
35+ property string infoText: ""
2936
3037 // In case the Lockscreen can show a greeter message, this is the username
3138 property string username: ""
3239
33− // Set this to a value greater 0 to enable auto-confirm behavior for the lockscreen.
34− // This is ignored by the alphaNumeric lockscreen as that one is confirmed with pressing enter on the OSK.
35− property int pinLength: -1
36−
37− property url background: ""
40+ // Set those to a value greater 0 to restrict the pin length.
41+ // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
42+ // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
43+ // entered pin is at least that long. If maxPinLength is set, the lockscreen won't allow entering any
44+ // more numbers than that. If both are set to the same value, the lockscreen will enter auto confirming
45+ // behavior, hiding the confirmation button and triggering that automatically when the entered pin reached
46+ // that length. This is ignored by the alphaNumeric lockscreen as that one is always confirmed by pressing
47+ // enter on the OSK.
48+ property int minPinLength: -1
49+ property int maxPinLength: -1
3850
3951 signal entered(string passphrase)
4052 signal cancel()
4153 signal emergencyCall()
54+ signal infoPopupConfirmed()
4255
4356 onRequiredChanged: {
4457 if (required && pinPadLoader.item) {
@@ -56,27 +69,50 @@ Showable {
5669 pinPadLoader.item.clear(showAnimation);
5770 }
5871
59− Rectangle {
60− // In case background fails to load or is undefined
61− id: backgroundBackup
62− anchors.fill: parent
63− color: "black"
72+ function showInfoPopup(title, text) {
73+ PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
6474 }
6575
66− Image {
67− id: backgroundImage
68− objectName: "lockscreenBackground"
76+ MouseArea {
77+ anchors.fill: root
78+ }
79+
80+ Column {
81+ spacing: units.gu(2)
6982 anchors {
70− fill: parent
71− topMargin: backgroundTopMargin
83+ left: parent.left
84+ right: parent.right
85+ bottom: pinPadLoader.top
86+ bottomMargin: units.gu(2)
87+ }
88+ Label {
89+ objectName: "retryCountLabel"
90+ anchors {
91+ left: parent.left
92+ right: parent.right
93+ }
94+ text: root.retryText
95+ horizontalAlignment: Text.AlignHCenter
96+ color: "#f3f3e7"
97+ opacity: 0.6
98+ visible: root.retryText.length > 0
99+ }
100+ Label {
101+ id: infoTextLabel
102+ objectName: "infoTextLabel"
103+ anchors {
104+ left: parent.left
105+ right: parent.right
106+ }
107+ text: root.infoText
108+ horizontalAlignment: Text.AlignHCenter
109+ color: "#f3f3e7"
110+ opacity: 0.6
111+ visible: root.infoText.length > 0
72112 }
73− source: root.required ? root.background : ""
74− fillMode: Image.PreserveAspectCrop
75113 }
76114
77− MouseArea {
78− anchors.fill: root
79− }
115+
80116
81117 Loader {
82118 id: pinPadLoader
@@ -105,14 +141,24 @@ Showable {
105141
106142 Binding {
107143 target: pinPadLoader.item
108− property: "pinLength"
109− value: root.pinLength
144+ property: "minPinLength"
145+ value: root.minPinLength
146+ }
147+ Binding {
148+ target: pinPadLoader.item
149+ property: "maxPinLength"
150+ value: root.maxPinLength
110151 }
111152 Binding {
112153 target: pinPadLoader.item
113154 property: "placeholderText"
114155 value: root.placeholderText
115156 }
157+ Binding {
158+ target: pinPadLoader.item
159+ property: "wrongPlaceholderText"
160+ value: root.wrongPlaceholderText
161+ }
116162 Binding {
117163 target: pinPadLoader.item
118164 property: "username"
@@ -153,4 +199,22 @@ Showable {
153199 anchors.horizontalCenter: parent.horizontalCenter
154200 }
155201 }
202+
203+ Component {
204+ id: infoPopupComponent
205+ Dialog {
206+ id: dialog
207+ objectName: "infoPopup"
208+ modal: true
209+
210+ Button {
211+ objectName: "infoPopupOkButton"
212+ text: i18n.tr("OK")
213+ onClicked: {
214+ PopupUtils.close(dialog)
215+ root.infoPopupConfirmed();
216+ }
217+ }
218+ }
219+ }
156220}
qml/Components/PageHeader.qml+3−2
@@ -18,6 +18,7 @@ import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Ubuntu.Components.Popups 0.1
2020import Ubuntu.Components.ListItems 0.1 as ListItem
21+import SessionManager 0.1
2122import Unity 0.2
2223
2324Item {
@@ -51,8 +52,8 @@ Item {
5152 }
5253
5354 Connections {
54− target: greeter
55− onShownChanged: if (shown) resetSearch()
55+ target: SessionManager
56+ onActiveChanged: if (!SessionManager.active) resetSearch()
5657 }
5758
5859 Flickable {
qml/Greeter/PassphraseLockscreen.qml → qml/Components/PassphraseLockscreen.qmlrenamed+6−2
@@ -22,7 +22,8 @@ Item {
2222 id: root
2323 height: highlightItem.height
2424
25− property alias placeholderText: pinentryField.placeholderText
25+ property string placeholderText
26+ property string wrongPlaceholderText
2627 property string username: ""
2728
2829 signal entered(string passphrase)
@@ -73,9 +74,12 @@ Item {
7374 echoMode: TextInput.Password
7475 opacity: 0.9
7576 hasClearButton: false
77+ placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
7678
7779 onAccepted: {
78− root.entered(pinentryField.text);
80+ if (pinentryField.text) {
81+ root.entered(pinentryField.text);
82+ }
7983 }
8084 }
8185 }
qml/Greeter/PinLockscreen.qml → qml/Components/PinLockscreen.qmlrenamed+29−11
@@ -25,9 +25,11 @@ Column {
2525 spacing: units.gu(3.5)
2626
2727 property alias placeholderText: pinentryField.placeholderText
28+ property alias wrongPlaceholderText: pinentryField.wrongPlaceholderText
2829 property int padWidth: units.gu(34)
2930 property int padHeight: units.gu(28)
30− property int pinLength: -1
31+ property int minPinLength: -1
32+ property int maxPinLength: -1
3133
3234 signal entered(string passphrase)
3335 signal cancel()
@@ -41,6 +43,10 @@ Column {
4143 }
4244 }
4345
46+ QtObject {
47+ id: priv
48+ property bool autoConfirm: root.minPinLength == root.maxPinLength && root.minPinLength != -1
49+ }
4450
4551 UbuntuShape {
4652 id: pinentryField
@@ -52,12 +58,20 @@ Column {
5258 radius: "medium"
5359 property string text: ""
5460 property string placeholderText: ""
61+ property string wrongPlaceholderText: ""
62+
63+ function appendChar(character) {
64+ if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
65+ pinentryField.text = pinentryField.text + character;
66+ }
67+ }
68+
5569 onTextChanged: {
5670 pinentryFieldLabel.text = "";
5771 for (var i = 0; i < text.length; ++i) {
5872 pinentryFieldLabel.text += "•";
5973 }
60− if (text.length === root.pinLength) {
74+ if (priv.autoConfirm && text.length === root.maxPinLength) {
6175 root.entered(text);
6276 }
6377 }
@@ -74,9 +88,11 @@ Column {
7488 }
7589 Label {
7690 id: pinentryFieldPlaceHolder
91+ objectName: "pinentryFieldPlaceHolder"
7792 anchors.centerIn: parent
78− color: "grey"
79− text: parent.placeholderText
93+ color: "#f3f3e7"
94+ opacity: 0.6
95+ text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
8096 visible: pinentryFieldLabel.text.length == 0
8197 }
8298
@@ -91,9 +107,11 @@ Column {
91107 bottom: parent.bottom
92108 bottomMargin: units.gu(1)
93109 }
94− visible: root.pinLength == -1
110+ visible: !priv.autoConfirm
95111 width: height
96112 name: "erase"
113+ color: "#f3f3e7"
114+ opacity: 0.6
97115 MouseArea {
98116 anchors.fill: parent
99117 onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
@@ -168,7 +186,7 @@ Column {
168186 enabled: entryEnabled
169187
170188 onClicked: {
171− pinentryField.text = pinentryField.text + text;
189+ pinentryField.appendChar(text);
172190 }
173191 }
174192 }
@@ -186,7 +204,7 @@ Column {
186204 width: root.padWidth / 3
187205 height: root.padHeight / 4
188206 text: "0"
189− onClicked: pinentryField.text = pinentryField.text + text
207+ onClicked: pinentryField.appendChar(text);
190208 enabled: entryEnabled
191209 }
192210
@@ -194,16 +212,16 @@ Column {
194212 objectName: "pinPadButtonErase"
195213 width: root.padWidth / 3
196214 height: root.padHeight / 4
197− iconName: root.pinLength == -1 ? "" : "erase"
198− subText: root.pinLength == -1 ? "DONE" : ""
215+ iconName: priv.autoConfirm ? "erase" : ""
216+ subText: priv.autoConfirm ? "" : "DONE"
199217 onClicked: {
200− if (root.pinLength !== -1) {
218+ if (priv.autoConfirm) {
201219 pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
202220 } else {
203221 root.entered(pinentryField.text);
204222 }
205223 }
206− enabled: entryEnabled
224+ enabled: priv.autoConfirm ? entryEnabled : pinentryField.text.length >= root.minPinLength
207225 }
208226 }
209227 }
qml/Components/PinPadButton.qml+2−1
@@ -19,6 +19,7 @@ import Ubuntu.Components 0.1
1919
2020Item {
2121 id: root
22+ opacity: enabled ? 1 : 0.6
2223
2324 property alias text: label.text
2425 property alias subText: subTextLabel.text
@@ -62,7 +63,7 @@ Item {
6263 Label {
6364 id: subTextLabel
6465 fontSize: "small"
65− color: "grey"
66+ color: "#f3f3e7"
6667 anchors.horizontalCenter: parent.horizontalCenter
6768 visible: text.length > 0
6869 }
qml/Dash/CardCarousel.qml+1−1
@@ -34,7 +34,7 @@ DashRenderer {
3434 Carousel {
3535 id: carousel
3636 anchors.fill: parent
37− tileAspectRatio: cardCarousel.components && cardCarousel.components["art"]["aspect-ratio"] || 1.0
37+ tileAspectRatio: cardTool.components && cardTool.components["art"]["aspect-ratio"] || 1.0
3838 // FIXME we need to "reverse" the carousel to make the selected item the size
3939 // and push others back.
4040 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
qml/Dash/GenericScopeView.qml+1−1
@@ -128,7 +128,7 @@ FocusScope {
128128
129129 CardTool {
130130 id: cardTool
131−
131+ objectName: "cardTool"
132132 count: results.count
133133 template: model.renderer
134134 components: model.components
qml/Dash/Previews/PreviewHeader.qml+3
@@ -56,6 +56,9 @@ PreviewWidget {
5656
5757 Loader {
5858 id: mascotShapeLoader
59+ objectName: "mascotShapeLoader"
60+ active: headerRoot.mascot != ""
61+ visible: active
5962
6063 anchors.verticalCenter: parent.verticalCenter
6164 // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
qml/Dash/ScopeListView.qml+3−2
@@ -16,13 +16,14 @@
1616
1717import QtQuick 2.0
1818import Dash 0.1
19+import SessionManager 0.1
1920
2021ListViewWithPageHeader {
2122 maximumFlickVelocity: height * 10
2223 flickDeceleration: height * 2
2324
2425 Connections {
25− target: greeter
26− onShownChanged: if (greeter.shown) showHeader()
26+ target: SessionManager
27+ onActiveChanged: if (!SessionManager.active) showHeader()
2728 }
2829}
qml/Greeter/Clock.qml+1−1
@@ -39,7 +39,7 @@ Item {
3939
4040 busName: "com.canonical.indicator.datetime"
4141 actionsObjectPath: "/com/canonical/indicator/datetime"
42− menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
42+ menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone_greeter" : ""
4343
4444 Indicators.RootActionState {
4545 menu: timeModel.model
qml/Greeter/Greeter.qml+36−3
@@ -23,9 +23,9 @@ import "../Components"
2323Showable {
2424 id: greeter
2525 enabled: shown
26− created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
26+ created: greeterContentLoader.status == Loader.Ready
2727
28− property url defaultBackground
28+ property url background
2929
3030 // 1 when fully shown and 0 when fully hidden
3131 property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
@@ -118,6 +118,36 @@ Showable {
118118 }
119119 }
120120
121+ Rectangle {
122+ // To hide lockscreen until background finished fading in
123+ id: backgroundBackup
124+ anchors {
125+ fill: parent
126+ topMargin: -greeter.y
127+ }
128+ color: "black"
129+ }
130+
131+ CrossFadeImage {
132+ id: backgroundImage
133+ objectName: "greeterBackground"
134+ anchors {
135+ fill: parent
136+ topMargin: -greeter.y
137+ }
138+ source: greeter.background
139+ fillMode: Image.PreserveAspectCrop
140+
141+ StandardAnimation {
142+ running: true
143+ target: backgroundImage
144+ property: "opacity"
145+ from: 0.0
146+ to: 1.0
147+ duration: 800
148+ }
149+ }
150+
121151 Loader {
122152 id: greeterContentLoader
123153 objectName: "greeterContentLoader"
@@ -125,7 +155,6 @@ Showable {
125155 property var model: LightDM.Users
126156 property int currentIndex: 0
127157 property var infographicModel: LightDM.Infographic
128− readonly property int backgroundTopMargin: -greeter.y
129158
130159 source: required ? "GreeterContent.qml" : ""
131160
@@ -145,6 +174,10 @@ Showable {
145174 }
146175
147176 onTease: showLabelAnimation.start()
177+ onShownChanged: {
178+ showLabelAnimation.stop()
179+ swipeHint.opacity = 0
180+ }
148181
149182 Label {
150183 id: swipeHint
qml/Greeter/GreeterContent.qml−38
@@ -24,46 +24,9 @@ Item {
2424 id: root
2525 anchors.fill: parent
2626
27− property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
28−
2927 signal selected(int uid)
3028 signal unlocked(int uid)
3129
32− Rectangle {
33− // In case background fails to load
34− id: backgroundBackup
35− anchors.fill: parent
36− color: "black"
37− }
38−
39− property url backgroundValue: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : greeter.defaultBackground
40− onBackgroundValueChanged: background.source = backgroundValue
41−
42− CrossFadeImage {
43− id: background
44− objectName: "greeterBackground"
45− anchors {
46− fill: parent
47− topMargin: backgroundTopMargin
48− }
49− fillMode: Image.PreserveAspectCrop
50− }
51−
52− // See Shell.qml's backgroundSettings treatment for why we need a separate
53− // Image, but it boils down to avoiding binding loop detection.
54− Image {
55− source: background.source
56− height: 0
57− width: 0
58− sourceSize.height: 0
59− sourceSize.width: 0
60− onStatusChanged: {
61− if (status == Image.Error && source != greeter.defaultBackground) {
62− background.source = greeter.defaultBackground
63− }
64− }
65− }
66−
6730 Rectangle {
6831 anchors.fill: parent
6932 color: "black"
@@ -145,7 +108,6 @@ Item {
145108
146109 Clock {
147110 id: clock
148− visible: narrowMode
149111
150112 anchors {
151113 top: parent.top
qml/Greeter/GreeterEdgeDemo.qmlnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import AccountsService 0.1
18+import LightDM 0.1 as LightDM
19+import QtQuick 2.0
20+
21+Item {
22+ id: demo
23+
24+ property Item greeter
25+
26+ property bool active: false
27+ property bool paused: false
28+
29+ onPausedChanged: {
30+ if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
31+ }
32+
33+ function hideEdgeDemoInShell() {
34+ AccountsService.demoEdges = false;
35+ }
36+
37+ function hideEdgeDemoInGreeter() {
38+ AccountsService.demoEdgesForCurrentUser = false;
39+ }
40+
41+ function hideEdgeDemos() {
42+ hideEdgeDemoInGreeter();
43+ hideEdgeDemoInShell();
44+ }
45+
46+ function startDemo() {
47+ if (!d.overlay) {
48+ d.overlay = Qt.createComponent("../Components/EdgeDemoOverlay.qml")
49+ }
50+ startRightEdgeDemo()
51+ }
52+
53+ function stopDemo() {
54+ active = false
55+ if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
56+ }
57+
58+ QtObject {
59+ id: d
60+ property Component overlay
61+ property QtObject rightEdgeDemo
62+ property bool showEdgeDemoInGreeter: AccountsService.demoEdgesForCurrentUser && AccountsService.demoEdges
63+
64+ onShowEdgeDemoInGreeterChanged: {
65+ stopDemo()
66+ if (d.showEdgeDemoInGreeter) {
67+ startDemo()
68+ }
69+ }
70+ }
71+
72+ function startRightEdgeDemo() {
73+ active = true;
74+ if (demo.greeter) {
75+ d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
76+ "edge": "right",
77+ "title": i18n.tr("Right edge"),
78+ "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
79+ "anchors.fill": demo.greeter,
80+ });
81+ }
82+ if (d.rightEdgeDemo) {
83+ d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
84+ } else {
85+ stopDemo();
86+ }
87+ }
88+
89+ Connections {
90+ target: demo.greeter
91+
92+ function hide() {
93+ if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
94+ d.rightEdgeDemo.hide()
95+ hideEdgeDemoInGreeter()
96+ }
97+ }
98+
99+ onUnlocked: hide()
100+ onShownChanged: if (!greeter.shown) hide()
101+ }
102+}
qml/Greeter/Infographics.qml−2
@@ -59,8 +59,6 @@ Item {
5959
6060 visible: model.label !== ""
6161
62− Component.onCompleted: startShowAnimation()
63−
6462 Item {
6563 id: dataCircle
6664 objectName: "dataCircle"
qml/GreeterShell.qmlnew file+283
@@ -0,0 +1,283 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import AccountsService 0.1
18+import GSettings 1.0
19+import LightDM 0.1 as LightDM
20+import Powerd 0.1
21+import QtQuick 2.0
22+import SessionBroadcast 0.1
23+import Ubuntu.Components 0.1
24+import Unity.Application 0.1
25+import Unity.Launcher 0.1
26+import "Components"
27+import "Greeter"
28+import "Launcher"
29+import "Panel"
30+import "Notifications"
31+import Unity.Notifications 1.0 as NotificationBackend
32+
33+BasicShell {
34+ id: shell
35+
36+ function activateApplication(appId) {
37+ SessionBroadcast.requestUrlStart(LightDM.Greeter.authenticationUser, appId)
38+ greeter.hide()
39+ }
40+
41+ GSettings {
42+ id: backgroundSettings
43+ schema.id: "org.gnome.desktop.background"
44+ }
45+ backgroundFallbackSource: backgroundSettings.pictureUri // for ease of customization by system builders
46+ backgroundSource: AccountsService.backgroundFile
47+
48+ Lockscreen {
49+ id: lockscreen
50+ objectName: "lockscreen"
51+
52+ hides: [launcher, panel.indicators]
53+ shown: false
54+ enabled: true
55+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
56+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
57+ y: panel.panelHeight
58+ x: required ? 0 : - width
59+ width: parent.width
60+ height: parent.height - panel.panelHeight
61+ minPinLength: 4
62+ maxPinLength: 4
63+
64+ onEntered: LightDM.Greeter.respond(passphrase);
65+ onCancel: greeter.show()
66+
67+ Component.onCompleted: {
68+ if (LightDM.Users.count == 1) {
69+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
70+ }
71+ }
72+ }
73+
74+ Connections {
75+ target: LightDM.Greeter
76+
77+ onIdle: {
78+ greeter.showNow()
79+ }
80+
81+ onShowPrompt: {
82+ if (LightDM.Users.count == 1) {
83+ // TODO: There's no better way for now to determine if its a PIN or a passphrase.
84+ if (text == "PIN") {
85+ lockscreen.alphaNumeric = false
86+ } else {
87+ lockscreen.alphaNumeric = true
88+ }
89+ lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
90+ lockscreen.show();
91+ }
92+ }
93+
94+ onAuthenticationComplete: {
95+ if (LightDM.Greeter.promptless) {
96+ return;
97+ }
98+ if (LightDM.Greeter.authenticated) {
99+ lockscreen.hide();
100+ } else {
101+ lockscreen.clear(true);
102+ }
103+ }
104+ }
105+
106+ Rectangle {
107+ anchors.fill: parent
108+ color: "black"
109+ opacity: greeterWrapper.showProgress * 0.8
110+ }
111+
112+ Item {
113+ // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
114+ id: greeterWrapper
115+ x: launcher.progress
116+ width: parent.width
117+ height: parent.height
118+
119+ Behavior on x {
120+ enabled: !launcher.dashSwipe
121+ StandardAnimation {}
122+ }
123+
124+ readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
125+ onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
126+
127+ Greeter {
128+ id: greeter
129+ objectName: "greeter"
130+
131+ available: true
132+ hides: [launcher, panel.indicators]
133+ shown: true
134+ background: shell.background
135+
136+ y: panel.panelHeight
137+ width: parent.width
138+ height: parent.height - panel.panelHeight
139+
140+ dragHandleWidth: shell.edgeSize
141+
142+ function login() {
143+ enabled = false;
144+ LightDM.Greeter.startSessionSync();
145+ enabled = true;
146+ }
147+
148+ onShownChanged: {
149+ if (shown) {
150+ lockscreen.reset();
151+ // If there is only one user, we start authenticating with that one here.
152+ // If there are more users, the Greeter will handle that
153+ if (LightDM.Users.count == 1) {
154+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
155+ }
156+ greeter.forceActiveFocus();
157+ }
158+ }
159+
160+ onUnlocked: hide()
161+ onSelected: {
162+ // Update launcher items for new user
163+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
164+ AccountsService.user = user;
165+ LauncherModel.setUser(user);
166+ }
167+ onTease: launcher.tease()
168+ }
169+ }
170+
171+ InputFilterArea {
172+ anchors.fill: parent
173+ blockInput: true
174+ }
175+
176+ Item {
177+ id: overlay
178+
179+ anchors.fill: parent
180+
181+ Panel {
182+ id: panel
183+ anchors.fill: parent //because this draws indicator menus
184+ indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
185+ indicators {
186+ hides: [launcher]
187+ available: !edgeDemo.active
188+ }
189+ fullscreenMode: false
190+ searchVisible: false
191+ }
192+
193+ Launcher {
194+ id: launcher
195+
196+ readonly property bool dashSwipe: progress > 0
197+
198+ anchors.top: parent.top
199+ anchors.bottom: parent.bottom
200+ width: parent.width
201+ dragAreaWidth: shell.edgeSize
202+ available: !edgeDemo.active
203+ onLauncherApplicationSelected: {
204+ shell.activateApplication(LauncherModel.getUrlForAppId(appId))
205+ }
206+ onShownChanged: {
207+ if (shown) {
208+ panel.indicators.hide()
209+ }
210+ }
211+ onDash: {
212+ greeter.hideRight()
213+ hide()
214+ }
215+ onShowDashHome: {
216+ SessionBroadcast.requestHomeShown(LightDM.Greeter.authenticationUser)
217+ greeter.hide()
218+ }
219+ }
220+
221+ Notifications {
222+ id: notifications
223+
224+ model: NotificationBackend.Model
225+ margin: units.gu(1)
226+
227+ anchors {
228+ top: parent.top
229+ right: parent.right
230+ bottom: parent.bottom
231+ topMargin: panel.panelHeight
232+ }
233+ states: [
234+ State {
235+ name: "narrow"
236+ when: overlay.width <= units.gu(60)
237+ AnchorChanges { target: notifications; anchors.left: parent.left }
238+ },
239+ State {
240+ name: "wide"
241+ when: overlay.width > units.gu(60)
242+ AnchorChanges { target: notifications; anchors.left: undefined }
243+ PropertyChanges { target: notifications; width: units.gu(38) }
244+ }
245+ ]
246+
247+ InputFilterArea {
248+ anchors { left: parent.left; right: parent.right }
249+ height: parent.contentHeight
250+ blockInput: height > 0
251+ }
252+ }
253+ }
254+
255+ OSKController {
256+ anchors.topMargin: panel.panelHeight
257+ anchors.fill: parent // as needs to know the geometry of the shell
258+ }
259+
260+ Connections {
261+ id: powerConnection
262+ target: Powerd
263+
264+ onDisplayPowerStateChange: {
265+ if (status == Powerd.Off) {
266+ greeter.show();
267+ edgeDemo.paused = true;
268+ } else if (status == Powerd.On) {
269+ edgeDemo.paused = false;
270+ }
271+ }
272+ }
273+
274+ Connections {
275+ target: LightDM.URLDispatcher
276+ onDispatchURL: shell.activateApplication(url)
277+ }
278+
279+ GreeterEdgeDemo {
280+ id: edgeDemo
281+ greeter: greeter
282+ }
283+}
qml/Hud/Hud.qml+1−4
@@ -227,10 +227,7 @@ Item {
227227 width: parent.width
228228
229229 onYChanged: {
230− if (greeter.shown) {
231− showAnimation.duration = 0
232− hideAnimation.duration = 0
233− } else if (!showAnimation.running && !hideAnimation.running) {
230+ if (!showAnimation.running && !hideAnimation.running) {
234231 if (parent.height > 0) {
235232 showAnimation.duration = Math.min(showableAnimationDuration * (1 - (parent.height - y) / parent.height), showableAnimationDuration)
236233 hideAnimation.duration = showableAnimationDuration - showAnimation.duration
qml/Notifications/NotificationMenuItemFactory.qml+26−10
@@ -20,7 +20,6 @@
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
2222import "../Components"
23−import "../Greeter"
2423
2524Loader {
2625 id: menuFactory
@@ -98,19 +97,36 @@ Loader {
9897 Component {
9998 id: pinLock
10099
101− Lockscreen {
102− anchors.left: parent.left; anchors.right: parent.right
100+ Item {
103101 height: menuFactory.maxHeight
104− placeholderText: i18n.tr("Please enter SIM PIN")
105− background: shell.background
102+ anchors.left: parent.left
103+ anchors.right: parent.right
104+
105+ Rectangle {
106+ // In case background fails to load or is undefined
107+ anchors.fill: parent
108+ color: "black"
109+ }
106110
107− onEntered: {
108− menuModel.changeState(menuIndex, passphrase);
109− entryEnabled = false;
111+ Image {
112+ objectName: "lockscreenBackground"
113+ anchors.fill: parent
114+ source: shell.background
115+ fillMode: Image.PreserveAspectCrop
110116 }
111117
112− onCancel: {
113− menuModel.activate(menuIndex, false);
118+ Lockscreen {
119+ anchors.fill: parent
120+ placeholderText: i18n.tr("Please enter SIM PIN")
121+
122+ onEntered: {
123+ menuModel.changeState(menuIndex, passphrase);
124+ entryEnabled = false;
125+ }
126+
127+ onCancel: {
128+ menuModel.activate(menuIndex, false);
129+ }
114130 }
115131 }
116132 }
qml/Panel/Panel.qml+1−1
@@ -62,7 +62,7 @@ Item {
6262 height: __panelMinusSeparatorLineHeight
6363 y: 0
6464
65− Behavior on y { StandardAnimation { duration: UbuntuAnimation.FastDuration } }
65+ Behavior on y { StandardAnimation { duration: 500 } }
6666 }
6767
6868 PanelSeparatorLine {
qml/Shell.qml+31−182
@@ -21,11 +21,10 @@ import Unity.Application 0.1
2121import Ubuntu.Components 0.1
2222import Ubuntu.Gestures 0.1
2323import Unity.Launcher 0.1
24−import LightDM 0.1 as LightDM
2524import Powerd 0.1
2625import SessionBroadcast 0.1
26+import SessionManager 0.1
2727import "Dash"
28−import "Greeter"
2928import "Launcher"
3029import "Panel"
3130import "Components"
@@ -33,17 +32,9 @@ import "Notifications"
3332import Unity.Notifications 1.0 as NotificationBackend
3433import Unity.Session 0.1
3534
36−FocusScope {
35+BasicShell {
3736 id: shell
3837
39− // this is only here to select the width / height of the window if not running fullscreen
40− property bool tablet: false
41− width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
42− height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
43−
44− property real edgeSize: units.gu(2)
45− property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
46− property url background
4738 readonly property real panelHeight: panel.panelHeight
4839
4940 property bool dashShown: dash.shown
@@ -79,17 +70,7 @@ FocusScope {
7970 id: backgroundSettings
8071 schema.id: "org.gnome.desktop.background"
8172 }
82− property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
83− onGSettingsPictureChanged: {
84− shell.background = gSettingsPicture
85− }
86−
87− VolumeControl {
88− id: volumeControl
89− }
90−
91− Keys.onVolumeUpPressed: volumeControl.volumeUp()
92− Keys.onVolumeDownPressed: volumeControl.volumeDown()
73+ backgroundSource: backgroundSettings.pictureUri
9374
9475 Item {
9576 id: underlayClipper
@@ -118,15 +99,6 @@ FocusScope {
11899 // through the translucent parts of the shell surface.
119100 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
120101
121− CrossFadeImage {
122− id: backgroundImage
123− objectName: "backgroundImage"
124−
125− anchors.fill: parent
126− source: shell.background
127− fillMode: Image.PreserveAspectCrop
128− }
129−
130102 Rectangle {
131103 anchors.fill: parent
132104 color: "black"
@@ -145,10 +117,9 @@ FocusScope {
145117 id: dash
146118 objectName: "dash"
147119
148− available: !greeter.shown && !lockscreen.shown
149120 hides: [stages, launcher, panel.indicators]
150− shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
151− enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
121+ shown: disappearingAnimationProgress !== 1.0
122+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
152123
153124 anchors {
154125 fill: parent
@@ -236,7 +207,6 @@ FocusScope {
236207 shown = true;
237208 panel.indicators.hide();
238209 edgeDemo.stopDemo();
239− greeter.hide();
240210 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
241211 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
242212 }
@@ -301,6 +271,12 @@ FocusScope {
301271 }
302272 }
303273
274+ Binding {
275+ target: ApplicationManager
276+ property: "suspended"
277+ value: !SessionManager.active
278+ }
279+
304280 Loader {
305281 id: applicationsDisplayLoader
306282 anchors.fill: parent
@@ -325,128 +301,9 @@ FocusScope {
325301 }
326302 }
327303
328− Lockscreen {
329− id: lockscreen
330− objectName: "lockscreen"
331−
332− readonly property int backgroundTopMargin: -panel.panelHeight
333−
334− hides: [launcher, panel.indicators]
335− shown: false
336− enabled: true
337− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
338− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
339− y: panel.panelHeight
340− x: required ? 0 : - width
341− width: parent.width
342− height: parent.height - panel.panelHeight
343− background: shell.background
344− pinLength: 4
345−
346− onEntered: LightDM.Greeter.respond(passphrase);
347− onCancel: greeter.show()
348−
349− Component.onCompleted: {
350− if (LightDM.Users.count == 1) {
351− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
352− }
353− }
354− }
355−
356− Connections {
357− target: LightDM.Greeter
358−
359− onShowPrompt: {
360− if (LightDM.Users.count == 1) {
361− // TODO: There's no better way for now to determine if its a PIN or a passphrase.
362− if (text == "PIN") {
363− lockscreen.alphaNumeric = false
364− } else {
365− lockscreen.alphaNumeric = true
366− }
367− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
368− lockscreen.show();
369− }
370− }
371−
372− onAuthenticationComplete: {
373− if (LightDM.Greeter.promptless) {
374− return;
375− }
376− if (LightDM.Greeter.authenticated) {
377− lockscreen.hide();
378− } else {
379− lockscreen.clear(true);
380− }
381− }
382− }
383−
384− Rectangle {
385− anchors.fill: parent
386− color: "black"
387− opacity: greeterWrapper.showProgress * 0.8
388− }
389−
390− Item {
391− // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
392− id: greeterWrapper
393− x: launcher.progress
394− y: panel.panelHeight
395− width: parent.width
396− height: parent.height - panel.panelHeight
397−
398− Behavior on x {SmoothedAnimation{velocity: 600}}
399−
400− readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
401−
402− Greeter {
403− id: greeter
404− objectName: "greeter"
405−
406− available: true
407− hides: [launcher, panel.indicators]
408− shown: true
409−
410− defaultBackground: shell.background
411−
412− width: parent.width
413− height: parent.height
414−
415− dragHandleWidth: shell.edgeSize
416−
417− onShownChanged: {
418− if (shown) {
419− lockscreen.reset();
420− // If there is only one user, we start authenticating with that one here.
421− // If there are more users, the Greeter will handle that
422− if (LightDM.Users.count == 1) {
423− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
424− }
425− greeter.forceActiveFocus();
426− }
427− }
428−
429− onUnlocked: greeter.hide()
430− onSelected: {
431− // Update launcher items for new user
432− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
433− AccountsService.user = user;
434− LauncherModel.setUser(user);
435− }
436−
437− onTease: launcher.tease()
438−
439− Binding {
440− target: ApplicationManager
441− property: "suspended"
442− value: greeter.shown && greeterWrapper.showProgress == 1
443− }
444− }
445− }
446−
447304 InputFilterArea {
448305 anchors.fill: parent
449− blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
306+ blockInput: ApplicationManager.focusedApplicationId.length === 0 || launcher.shown
450307 || panel.indicators.shown
451308 }
452309
@@ -458,7 +315,7 @@ FocusScope {
458315 // We ignore any display-off signals when the proximity sensor
459316 // is active. This usually indicates something like a phone call.
460317 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
461− greeter.showNow();
318+ SessionManager.lock();
462319 }
463320
464321 // No reason to chew demo CPU when user isn't watching
@@ -470,17 +327,12 @@ FocusScope {
470327 }
471328 }
472329
473− function showHome() {
474− var animate = !greeter.shown && !stages.shown
475− greeter.hide()
330+ function showHome(fromLauncher) {
331+ var animate = fromLauncher && !stages.shown
476332 dash.setCurrentScope("clickscope", animate, false)
477333 stages.hide()
478334 }
479335
480− function hideIndicatorMenu(delay) {
481− panel.hideIndicatorMenu(delay);
482− }
483−
484336 Item {
485337 id: overlay
486338
@@ -497,8 +349,8 @@ FocusScope {
497349 }
498350 property string focusedAppId: ApplicationManager.focusedApplicationId
499351 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
500− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
501− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
352+ fullscreenMode: focusedApplication && stages.fullscreen
353+ searchVisible: dash.shown && dash.searchable
502354
503355 InputFilterArea {
504356 anchors {
@@ -536,8 +388,9 @@ FocusScope {
536388 if (edgeDemo.running)
537389 return;
538390
539− showHome()
391+ showHome(true)
540392 }
393+
541394 onDash: {
542395 if (stages.shown && !stages.overlayMode) {
543396 if (!stages.locked) {
@@ -545,10 +398,6 @@ FocusScope {
545398 launcher.hide();
546399 }
547400 }
548− if (greeter.shown) {
549− greeter.hideRight();
550− launcher.hide();
551− }
552401 }
553402 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
554403 onLauncherApplicationSelected: {
@@ -565,7 +414,7 @@ FocusScope {
565414 Rectangle {
566415 id: modalNotificationBackground
567416
568− visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
417+ visible: notifications.useModal && (notifications.state == "narrow")
569418 color: "#000000"
570419 anchors.fill: parent
571420 opacity: 0.5
@@ -612,10 +461,17 @@ FocusScope {
612461 blockInput: height > 0
613462 }
614463 }
615− }
616464
617− focus: true
618− onFocusChanged: if (!focus) forceActiveFocus();
465+ Connections {
466+ target: SessionManager
467+ onActiveChanged: {
468+ if (!SessionManager.active) {
469+ launcher.hide()
470+ panel.indicators.hide()
471+ }
472+ }
473+ }
474+ }
619475
620476 InputFilterArea {
621477 anchors {
@@ -637,12 +493,6 @@ FocusScope {
637493 blockInput: true
638494 }
639495
640− Binding {
641− target: i18n
642− property: "domain"
643− value: "unity8"
644− }
645−
646496 OSKController {
647497 anchors.topMargin: panel.panelHeight
648498 anchors.fill: parent // as needs to know the geometry of the shell
@@ -674,7 +524,6 @@ FocusScope {
674524
675525 EdgeDemo {
676526 id: edgeDemo
677− greeter: greeter
678527 launcher: launcher
679528 dash: dash
680529 indicators: panel.indicators
@@ -683,6 +532,6 @@ FocusScope {
683532
684533 Connections {
685534 target: SessionBroadcast
686− onShowHome: showHome()
535+ onShowHome: showHome(false)
687536 }
688537}
run.sh+6−4
@@ -18,13 +18,14 @@ usage() {
1818 echo " -p, --pinlock Use a pin protected user." >&2
1919 echo " -k, --keylock Use a passphrase protected user." >&2
2020 echo " -g, --gdb Run through gdb." >&2
21+ echo " -G, --greeter Run the greeter instead of the shell." >&2
2122 echo " -h, --help Show this help." >&2
2223 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
2324 echo >&2
2425 exit 1
2526}
2627
27−ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,nomousetouch" -o "fpkghm" -- "$@"`
28+ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,greeter,help,nomousetouch" -o "fpkgGhm" -- "$@"`
2829[ $? -ne 0 ] && usage
2930eval set -- "$ARGS"
3031
@@ -35,6 +36,7 @@ do
3536 -p|--pinlock) PINLOCK=true;;
3637 -k|--keylock) KEYLOCK=true;;
3738 -g|--gdb) GDB=true;;
39+ -G|--greeter) QML_PHONE_SHELL_PATH=./builddir/src/unity8-greeter;;
3840 -h|--help) usage;;
3941 -m|--nomousetouch) MOUSE_TOUCH=false;;
4042 --) shift;break;;
@@ -44,17 +46,17 @@ done
4446
4547if $FAKE; then
4648 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
47− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single
49+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/liblightdm/single
4850fi
4951
5052if $PINLOCK; then
5153 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
52− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-pin
54+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/liblightdm/single-pin
5355fi
5456
5557if $KEYLOCK; then
5658 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
57− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-passphrase
59+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/liblightdm/single-passphrase
5860fi
5961
6062# Force icon theme if running on the desktop, otherwise gnome theme (if running
src/CMakeLists.txt+15−3
@@ -15,24 +15,36 @@ file(GLOB_RECURSE QML_FILES
1515 ${CMAKE_SOURCE_DIR}/tests/*.svg
1616)
1717
18−add_executable(${SHELL_APP}
18+set(COMMON_SRCS
1919 ApplicationArguments.h
2020 main.cpp
2121 MouseTouchAdaptor.cpp
2222 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
23−)
23+ )
24+add_executable(${SHELL_APP} ${COMMON_SRCS})
25+add_executable(${GREETER_APP} ${COMMON_SRCS})
26+
27+set_target_properties(${SHELL_APP} PROPERTIES
28+ COMPILE_DEFINITIONS "UNITY8_GREETER=0"
29+ )
30+set_target_properties(${GREETER_APP} PROPERTIES
31+ COMPILE_DEFINITIONS "UNITY8_GREETER=1"
32+ )
2433
2534qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
35+qt5_use_modules(${GREETER_APP} Gui Qml Quick Test)
2636pkg_check_modules(XCB REQUIRED xcb)
2737
2838if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
2939 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
40+ set_target_properties(${GREETER_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
3041
3142 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
43+ target_link_libraries(${GREETER_APP} ${XCB_LDFLAGS})
3244endif()
3345
3446# install binaries
35−install(TARGETS ${SHELL_APP}
47+install(TARGETS ${SHELL_APP} ${GREETER_APP}
3648 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
3749 )
3850
src/main.cpp+18−5
@@ -26,6 +26,7 @@
2626#include <qpa/qplatformnativeinterface.h>
2727#include <QLibrary>
2828#include <QDebug>
29+#include <QProcess>
2930#include <libintl.h>
3031#include <dlfcn.h>
3132#include <csignal>
@@ -42,7 +43,7 @@ int startShell(int argc, const char** argv, void* server)
4243{
4344 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
4445
45− QGuiApplication::setApplicationName("Unity 8");
46+ QGuiApplication::setApplicationName(UNITY8_GREETER ? "Unity 8 Greeter" : "Unity 8");
4647 QGuiApplication *application;
4748
4849 QCommandLineParser parser;
@@ -93,7 +94,7 @@ Load the testability driver");
9394
9495 QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
9596 if (indicatorProfile.isEmpty()) {
96− indicatorProfile = "phone";
97+ indicatorProfile = UNITY8_GREETER ? "phone_greeter" : "phone";
9798 }
9899
99100 resolveIconTheme();
@@ -152,7 +153,7 @@ Load the testability driver");
152153 nativeInterface->setProperty("ubuntuSessionType", 1);
153154 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
154155
155− QUrl source(::qmlDirectory()+"Shell.qml");
156+ QUrl source(::qmlDirectory() + (UNITY8_GREETER ? "GreeterShell.qml" : "Shell.qml"));
156157 prependImportPaths(view->engine(), ::overrideImportPaths());
157158 if (!isUbuntuMirServer) {
158159 prependImportPaths(view->engine(), ::nonMirImportPaths());
@@ -160,8 +161,7 @@ Load the testability driver");
160161 appendImportPaths(view->engine(), ::fallbackImportPaths());
161162
162163 view->setSource(source);
163− view->setColor("transparent");
164− QObject::connect(view->engine(), SIGNAL(quit()), application, SLOT(quit()));
164+ view->setColor(Qt::transparent);
165165
166166 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {
167167 view->showFullScreen();
@@ -169,6 +169,19 @@ Load the testability driver");
169169 view->show();
170170 }
171171
172+#if UNITY8_GREETER
173+ if (isUbuntuMirServer) {
174+ // Add alpha to surface, so that the greeter can bleed through
175+ QSurfaceFormat format;
176+ format.setAlphaBufferSize(8);
177+ view->setFormat(format);
178+ }
179+
180+ if (!QProcess::startDetached("/sbin/initctl emit --no-wait unity8-greeter-started")) {
181+ qDebug() << "Unable to send unity8-greeter-started event to Upstart";
182+ }
183+#endif
184+
172185 int result = application->exec();
173186
174187 delete view;
tests/autopilot/unity8/application_lifecycle/tests/__init__.py−2
@@ -31,8 +31,6 @@ class ApplicationLifeCycleTestCase(tests.UnityTestCase):
3131 super(ApplicationLifeCycleTestCase, self).setUp()
3232 self._qml_mock_enabled = False
3333 self._data_dirs_mock_enabled = False
34− unity_proxy = self.launch_unity()
35− process_helpers.unlock_unity(unity_proxy)
3634
3735 def create_test_application(self):
3836 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−29
@@ -24,11 +24,8 @@ from __future__ import absolute_import
2424import logging
2525import os
2626
27−from autopilot.matchers import Eventually
2827from autopilot.platform import model
29−from testtools.matchers import Equals
3028
31−from unity8 import process_helpers
3229from unity8.application_lifecycle import tests
3330
3431
@@ -41,6 +38,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4138 if model() == 'Desktop':
4239 self.skipTest('Test cannot be run on the desktop.')
4340 super(ApplicationLifecycleTests, self).setUp()
41+ self.launch_unity()
4442
4543 def swipe_screen_from_right(self):
4644 width = self.main_window.width
@@ -89,29 +87,3 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
8987 self.swipe_screen_from_right()
9088
9189 self.assert_current_focused_application(application1_name)
92−
93− def test_greeter_hides_on_app_open(self):
94− """Greeter should hide when an app is opened"""
95− process_helpers.lock_unity()
96− greeter = self.main_window.get_greeter()
97− self.assertThat(greeter.created, Eventually(Equals(True)))
98−
99− application_name = self.launch_fake_app()
100− self.assertThat(greeter.created, Eventually(Equals(False)))
101− self.assert_current_focused_application(application_name)
102−
103− def test_greeter_hides_on_app_focus(self):
104− """Greeter should hide when an app is re-focused"""
105− application_name = self.launch_fake_app()
106− self.assert_current_focused_application(application_name)
107−
108− self.main_window.show_dash_swiping()
109− self.assert_current_focused_application('')
110−
111− process_helpers.lock_unity()
112− greeter = self.main_window.get_greeter()
113− self.assertThat(greeter.created, Eventually(Equals(True)))
114−
115− self.launch_upstart_application(application_name)
116− self.assertThat(greeter.created, Eventually(Equals(False)))
117− self.assert_current_focused_application(application_name)
tests/autopilot/unity8/application_lifecycle/tests/test_greeter.pynew file+75
@@ -0,0 +1,75 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2013, 2014 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for the application lifecycle in the greeter."""
21+
22+from __future__ import absolute_import
23+
24+import logging
25+import os
26+import subprocess
27+
28+from autopilot.matchers import Eventually
29+from autopilot.platform import model
30+from testtools.matchers import Equals
31+
32+from unity8.application_lifecycle import tests
33+from unity8.shell import with_lightdm_mock
34+
35+
36+logger = logging.getLogger(__name__)
37+
38+
39+class GreeterApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
40+
41+ def setUp(self):
42+ if model() == 'Desktop':
43+ self.skipTest('Test cannot be run on the desktop.')
44+ super(GreeterApplicationLifecycleTests, self).setUp()
45+ # Kill user's url-dispatcher daemon, because we want to own its dbus
46+ # name during these tests.
47+ try:
48+ subprocess.check_output(
49+ ["/sbin/initctl", "stop", "url-dispatcher"],
50+ stderr=subprocess.STDOUT
51+ )
52+ except subprocess.CalledProcessError:
53+ logger.warning("Appears url-dispatcher was already stopped!")
54+ self.addCleanup(self._start_dispatcher)
55+ self.launch_greeter()
56+
57+ def _start_dispatcher(self):
58+ subprocess.call(
59+ ["/sbin/initctl", "start", "url-dispatcher"],
60+ stderr=subprocess.STDOUT
61+ )
62+
63+ @with_lightdm_mock("single")
64+ def test_greeter_hides_on_url_dispatcher(self):
65+ """Greeter should hide when an app is opened"""
66+ greeter = self.main_window.get_greeter()
67+ self.assertThat(greeter.created, Eventually(Equals(True)))
68+
69+ _, desktop_file_path = self.create_test_application()
70+ desktop_file_name = os.path.basename(desktop_file_path)
71+ application_name, _ = os.path.splitext(desktop_file_name)
72+
73+ subprocess.check_call(
74+ ['url-dispatcher', 'application:///{}'.format(desktop_file_name)])
75+ self.assertThat(greeter.created, Eventually(Equals(False)))
tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+1
@@ -33,6 +33,7 @@ class URLDispatcherTestCase(tests.ApplicationLifeCycleTestCase):
3333 if platform.model() == 'Desktop':
3434 self.skipTest("URL dispatcher doesn't work on the desktop.")
3535 super(URLDispatcherTestCase, self).setUp()
36+ self.launch_unity()
3637
3738 def test_swipe_out_application_started_by_url_dispatcher(self):
3839 _, desktop_file_path = self.create_test_application()
tests/autopilot/unity8/indicators/tests/test_indicators.py−2
@@ -22,7 +22,6 @@ from testscenarios import multiply_scenarios
2222
2323from autopilot import platform
2424
25−from unity8.process_helpers import unlock_unity
2625from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2726
2827
@@ -35,7 +34,6 @@ class IndicatorTestCase(UnityTestCase):
3534 self.skipTest('Test cannot be run on the desktop.')
3635 super(IndicatorTestCase, self).setUp()
3736 self.unity_proxy = self.launch_unity()
38− unlock_unity(self.unity_proxy)
3937
4038
4139class IndicatorExistsTestCase(IndicatorTestCase):
tests/autopilot/unity8/process_helpers.py−56
@@ -40,62 +40,6 @@ class CannotAccessUnity(Exception):
4040 pass
4141
4242
43−def unlock_unity(unity_proxy_obj=None):
44− """Helper function that attempts to unlock the unity greeter.
45−
46− If unity_proxy_object is None create a proxy object by querying for the
47− running unity process.
48− Otherwise re-use the passed proxy object.
49−
50− :raises RuntimeError: if the greeter attempts and fails to be unlocked.
51−
52− :raises RuntimeWarning: when the greeter cannot be found because it is
53− already unlocked.
54− :raises CannotAccessUnity: if unity is not introspectable or cannot be
55− found on dbus.
56− :raises CannotAccessUnity: if unity's upstart status is not "start" or the
57− upstart job cannot be found at all.
58−
59− """
60− if unity_proxy_obj is None:
61− try:
62− pid = _get_unity_pid()
63− unity = _get_unity_proxy_object(pid)
64− main_window = unity.select_single(main_window_emulator.QQuickView)
65− except ProcessSearchError as e:
66− raise CannotAccessUnity(
67− "Cannot introspect unity, make sure that it has been started "
68− "with testability. Perhaps use the function "
69− "'restart_unity_with_testability' this module provides."
70− "(%s)"
71− % str(e)
72− )
73− else:
74− main_window = unity_proxy_obj.select_single(
75− main_window_emulator.QQuickView)
76−
77− greeter = main_window.get_greeter()
78− if greeter.created == False:
79− raise RuntimeWarning("Greeter appears to be already unlocked.")
80−
81− # Because of potential input jerkiness under heavy load,
82− # retry unlocking the greeter two times.
83− # https://bugs.launchpad.net/ubuntu/+bug/1260113
84−
85− retries = 3
86− while retries > 0:
87− try:
88− greeter.swipe()
89− except AssertionError:
90− retries -= 1
91− if retries == 0:
92− raise
93− logger.info("Failed to unlock greeter, trying again...")
94− else:
95− logger.info("Greeter unlocked, continuing.")
96− break
97−
98−
9943def lock_unity(unity_proxy_obj=None):
10044 """Helper function that attempts to lock the unity greeter."""
10145 import evdev, time
tests/autopilot/unity8/shell/emulators/main_window.py−4
@@ -114,10 +114,6 @@ class QQuickView(emulators.UnityEmulatorBase):
114114 self.wait_select_single('Indicators', fullyOpened=True)
115115 return self._get_indicator_page(indicator_name)
116116
117− def get_shell_background(self):
118− return self.select_single(
119− "CrossFadeImage", objectName="backgroundImage")
120−
121117 @autopilot_logging.log_action(logger.info)
122118 def show_dash_swiping(self):
123119 """Show the dash swiping from the left."""
tests/autopilot/unity8/shell/tests/__init__.py+16−4
@@ -249,9 +249,9 @@ class UnityTestCase(AutopilotTestCase):
249249 "%s=%s" % (key, value)
250250 ], stderr=subprocess.STDOUT)
251251
252− def launch_unity(self, **kwargs):
252+ def _launch(self, executable, ready_func, **kwargs):
253253 """Launch the unity shell, return a proxy object for it."""
254− binary_path = get_binary_path()
254+ binary_path = get_binary_path(executable)
255255 lib_path = get_lib_path()
256256
257257 logger.info(
@@ -293,11 +293,19 @@ class UnityTestCase(AutopilotTestCase):
293293
294294 # Ensure that the dash is visible before we return:
295295 logger.debug("Unity started, waiting for it to be ready.")
296− self.assertUnityReady()
296+ ready_func()
297297 logger.debug("Unity loaded and ready.")
298298
299299 return app_proxy
300300
301+ def launch_unity(self, **kwargs):
302+ """Launch the unity shell, return a proxy object for it."""
303+ return self._launch("unity8", self.assertUnityReady, **kwargs)
304+
305+ def launch_greeter(self, **kwargs):
306+ """Launch the unity shell, return a proxy object for it."""
307+ return self._launch("unity8-greeter", self.assertGreeterReady, **kwargs)
308+
301309 def _launch_unity_with_upstart(self, binary_path, args):
302310 logger.info("Starting unity")
303311 self._patch_environment("QT_LOAD_TESTABILITY", 1)
@@ -344,7 +352,7 @@ class UnityTestCase(AutopilotTestCase):
344352 def _get_lightdm_mock_path(self, mock_type):
345353 lib_path = get_mocks_library_path()
346354 lightdm_mock_path = os.path.abspath(
347− os.path.join(lib_path, "LightDM", mock_type)
355+ os.path.join(lib_path, "liblightdm", mock_type)
348356 )
349357
350358 if not os.path.exists(lightdm_mock_path):
@@ -386,6 +394,10 @@ class UnityTestCase(AutopilotTestCase):
386394 )
387395 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
388396
397+ def assertGreeterReady(self):
398+ greeter = self.main_window.get_greeter()
399+ self.assertThat(greeter.created, Eventually(Equals(True)))
400+
389401 def get_dash(self):
390402 dash = self._proxy.wait_select_single(Dash)
391403 return dash
tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−7
@@ -19,8 +19,7 @@
1919
2020from __future__ import absolute_import
2121
22−from unity8.process_helpers import unlock_unity
23−from unity8.shell import with_lightdm_mock, DragMixin
22+from unity8.shell import DragMixin
2423from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2524
2625from testtools.matchers import Equals
@@ -40,7 +39,6 @@ class TestHud(UnityTestCase, DragMixin):
4039
4140 """
4241 unity_proxy = self.launch_unity()
43− unlock_unity(unity_proxy)
4442 hud_show_button = self.main_window.get_hud_show_button()
4543 edge_drag_area = self.main_window.get_hud_edge_drag_area()
4644 hud = self.main_window.get_hud()
@@ -81,7 +79,6 @@ class TestHud(UnityTestCase, DragMixin):
8179
8280 """
8381 unity_proxy = self.launch_unity()
84− unlock_unity(unity_proxy)
8582 hud_show_button = self.main_window.get_hud_show_button()
8683 hud = self.main_window.get_hud()
8784
@@ -117,7 +114,6 @@ class TestHud(UnityTestCase, DragMixin):
117114 def test_hide_hud_click(self):
118115 """Tapping the close button of the Hud must dismiss it."""
119116 unity_proxy = self.launch_unity()
120− unlock_unity(unity_proxy)
121117 hud = self.main_window.get_hud()
122118
123119 self._launch_test_app_from_app_screen()
@@ -134,7 +130,6 @@ class TestHud(UnityTestCase, DragMixin):
134130
135131 """
136132 unity_proxy = self.launch_unity()
137− unlock_unity(unity_proxy)
138133 hud = self.main_window.get_hud()
139134
140135 self._launch_test_app_from_app_screen()
@@ -151,7 +146,6 @@ class TestHud(UnityTestCase, DragMixin):
151146 def test_launcher_hides_hud(self):
152147 """Opening the Launcher while the Hud is active must close the Hud."""
153148 unity_proxy = self.launch_unity()
154− unlock_unity(unity_proxy)
155149 hud = self.main_window.get_hud()
156150 launcher = self.main_window.get_launcher()
157151
tests/autopilot/unity8/shell/tests/test_emulators.py−3
@@ -34,7 +34,6 @@ except ImportError:
3434
3535from testtools.matchers import Contains, HasLength
3636
37−from unity8 import process_helpers
3837from unity8.shell import emulators, fixture_setup, tests
3938from unity8.shell.emulators import dash as dash_emulators
4039
@@ -46,7 +45,6 @@ class MainWindowTestCase(tests.UnityTestCase):
4645 def setUp(self):
4746 super(MainWindowTestCase, self).setUp()
4847 unity_proxy = self.launch_unity()
49− process_helpers.unlock_unity(unity_proxy)
5048
5149 def test_search(self):
5250 self.main_window.search('Test')
@@ -62,7 +60,6 @@ class DashBaseTestCase(tests.UnityTestCase):
6260 def setUp(self):
6361 super(DashBaseTestCase, self).setUp()
6462 unity_proxy = self.launch_unity()
65− process_helpers.unlock_unity(unity_proxy)
6663 self.dash = self.main_window.get_dash()
6764
6865
tests/autopilot/unity8/shell/tests/test_lock_screen.py+12−13
@@ -20,7 +20,6 @@
2020
2121from __future__ import absolute_import
2222
23−from unity8.process_helpers import unlock_unity
2423from unity8.shell import with_lightdm_mock
2524from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2625
@@ -47,41 +46,41 @@ class TestLockscreen(UnityTestCase):
4746 @with_lightdm_mock("single-pin")
4847 def test_can_unlock_pin_screen(self):
4948 """Must be able to unlock the PIN entry lock screen."""
50− unity_proxy = self.launch_unity()
49+ self.launch_greeter()
5150 greeter = self.main_window.get_greeter()
5251
5352 if greeter.narrowMode:
54− unlock_unity(unity_proxy)
53+ greeter.swipe()
5554 lockscreen = self._wait_for_lockscreen()
5655 self._enter_pincode("1234")
5756 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
5857 else:
5958 self._enter_prompt_passphrase("1234")
60− self.assertThat(greeter.shown, Eventually(Equals(False)))
59+ self.assertThat(greeter.enabled, Eventually(Equals(False)))
6160
6261 @with_lightdm_mock("single-passphrase")
6362 def test_can_unlock_passphrase_screen(self):
6463 """Must be able to unlock the passphrase entry screen."""
65− unity_proxy = self.launch_unity()
64+ self.launch_greeter()
6665 greeter = self.main_window.get_greeter()
6766
6867 if greeter.narrowMode:
69− unlock_unity(unity_proxy)
68+ greeter.swipe()
7069 lockscreen = self._wait_for_lockscreen()
7170 self._enter_pin_passphrase("password")
7271 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
7372 else:
7473 self._enter_prompt_passphrase("password")
75− self.assertThat(greeter.shown, Eventually(Equals(False)))
74+ self.assertThat(greeter.enabled, Eventually(Equals(False)))
7675
7776 @with_lightdm_mock("single-pin")
7877 def test_pin_screen_wrong_code(self):
7978 """Entering the wrong pin code must not dismiss the lock screen."""
80− unity_proxy = self.launch_unity()
79+ self.launch_greeter()
8180 greeter = self.main_window.get_greeter()
8281
8382 if greeter.narrowMode:
84− unlock_unity(unity_proxy)
83+ greeter.swipe()
8584 lockscreen = self._wait_for_lockscreen()
8685 self._enter_pincode("4321")
8786 pinentryField = self.main_window.get_pinentryField()
@@ -91,16 +90,16 @@ class TestLockscreen(UnityTestCase):
9190 self._enter_prompt_passphrase("4231")
9291 prompt = self.main_window.get_greeter().get_prompt()
9392 self.assertThat(prompt.text, Eventually(Equals("")))
94− self.assertThat(greeter.shown, Eventually(Equals(True)))
93+ self.assertThat(greeter.enabled, Eventually(Equals(True)))
9594
9695 @with_lightdm_mock("single-passphrase")
9796 def test_passphrase_screen_wrong_password(self):
9897 """Entering the wrong password must not dismiss the lock screen."""
99− unity_proxy = self.launch_unity()
98+ self.launch_greeter()
10099 greeter = self.main_window.get_greeter()
101100
102101 if greeter.narrowMode:
103− unlock_unity(unity_proxy)
102+ greeter.swipe()
104103 lockscreen = self._wait_for_lockscreen()
105104 self._enter_pin_passphrase("foobar")
106105 pinentryField = self.main_window.get_pinentryField()
@@ -110,7 +109,7 @@ class TestLockscreen(UnityTestCase):
110109 self._enter_prompt_passphrase("foobar")
111110 prompt = self.main_window.get_greeter().get_prompt()
112111 self.assertThat(prompt.text, Eventually(Equals("")))
113− self.assertThat(greeter.shown, Eventually(Equals(True)))
112+ self.assertThat(greeter.enabled, Eventually(Equals(True)))
114113
115114 def _wait_for_lockscreen(self):
116115 """Wait for the lock screen to load, and return it."""
tests/autopilot/unity8/shell/tests/test_notifications.py+2−14
@@ -22,7 +22,6 @@
2222from __future__ import absolute_import
2323
2424from unity8 import shell
25−from unity8.process_helpers import unlock_unity
2625from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2726
2827from testtools.matchers import Equals, NotEquals
@@ -125,7 +124,6 @@ class InteractiveNotificationBase(NotificationsBase):
125124 def test_interactive(self):
126125 """Interactive notification must react upon click on itself."""
127126 unity_proxy = self.launch_unity()
128− unlock_unity(unity_proxy)
129127
130128 notify_list = self._get_notifications_list()
131129
@@ -164,7 +162,6 @@ class InteractiveNotificationBase(NotificationsBase):
164162 """Rejecting a call should make notification expand and
165163 offer more options."""
166164 unity_proxy = self.launch_unity()
167− unlock_unity(unity_proxy)
168165
169166 summary = "Incoming call"
170167 body = "Frank Zappa\n+44 (0)7736 027340"
@@ -214,7 +211,6 @@ class InteractiveNotificationBase(NotificationsBase):
214211 def test_modal_sd_without_greeter (self):
215212 """A snap-decision on a phone should block input to shell beneath it when there's no greeter."""
216213 unity_proxy = self.launch_unity()
217− unlock_unity(unity_proxy)
218214
219215 summary = "Incoming file"
220216 body = "Frank would like to send you the file: essay.pdf"
@@ -255,7 +251,7 @@ class InteractiveNotificationBase(NotificationsBase):
255251
256252 def test_modal_sd_with_greeter (self):
257253 """A snap-decision on a phone should not block input to the greeter beneath it."""
258− unity_proxy = self.launch_unity()
254+ unity_proxy = self.launch_greeter()
259255
260256 summary = "Incoming file"
261257 body = "Frank would like to send you the file: essay.pdf"
@@ -281,8 +277,8 @@ class InteractiveNotificationBase(NotificationsBase):
281277
282278 # verify that we can swipe away the greeter (interact with the "shell")
283279 time.sleep(1)
284− self.main_window.show_dash_swiping()
285280 greeter = self.main_window.get_greeter()
281+ greeter.swipe()
286282 self.assertThat(greeter.shown, Eventually(Equals(False)))
287283
288284 # verify and interact with the triggered snap-decision notification
@@ -424,7 +420,6 @@ class EphemeralNotificationsTests(NotificationsBase):
424420 def test_icon_summary_body(self):
425421 """Notification must display the expected summary and body text."""
426422 unity_proxy = self.launch_unity()
427− unlock_unity(unity_proxy)
428423
429424 notify_list = self._get_notifications_list()
430425
@@ -464,7 +459,6 @@ class EphemeralNotificationsTests(NotificationsBase):
464459 """Notification must display the expected summary and secondary
465460 icon."""
466461 unity_proxy = self.launch_unity()
467− unlock_unity(unity_proxy)
468462
469463 notify_list = self._get_notifications_list()
470464
@@ -501,7 +495,6 @@ class EphemeralNotificationsTests(NotificationsBase):
501495 """Notifications must be displayed in order according to their
502496 urgency."""
503497 unity_proxy = self.launch_unity()
504− unlock_unity(unity_proxy)
505498
506499 notify_list = self._get_notifications_list()
507500
@@ -588,7 +581,6 @@ class EphemeralNotificationsTests(NotificationsBase):
588581 def test_summary_and_body(self):
589582 """Notification must display the expected summary- and body-text."""
590583 unity_proxy = self.launch_unity()
591− unlock_unity(unity_proxy)
592584
593585 notify_list = self._get_notifications_list()
594586
@@ -613,7 +605,6 @@ class EphemeralNotificationsTests(NotificationsBase):
613605 def test_summary_only(self):
614606 """Notification must display only the expected summary-text."""
615607 unity_proxy = self.launch_unity()
616− unlock_unity(unity_proxy)
617608
618609 notify_list = self._get_notifications_list()
619610
@@ -631,7 +622,6 @@ class EphemeralNotificationsTests(NotificationsBase):
631622 """Notification must allow updating its contents while being
632623 displayed."""
633624 unity_proxy = self.launch_unity()
634− unlock_unity(unity_proxy)
635625
636626 notify_list = self._get_notifications_list()
637627
@@ -669,7 +659,6 @@ class EphemeralNotificationsTests(NotificationsBase):
669659 """Notification must allow updating its contents and layout while
670660 being displayed."""
671661 unity_proxy = self.launch_unity()
672− unlock_unity(unity_proxy)
673662
674663 notify_list = self._get_notifications_list()
675664
@@ -715,7 +704,6 @@ class EphemeralNotificationsTests(NotificationsBase):
715704 def test_append_hint(self):
716705 """Notification has to accumulate body-text using append-hint."""
717706 unity_proxy = self.launch_unity()
718− unlock_unity(unity_proxy)
719707
720708 notify_list = self._get_notifications_list()
721709
tests/mocks/AccountsService/AccountsService.cpp+9
@@ -45,6 +45,15 @@ void AccountsService::setDemoEdges(bool demoEdges)
4545 Q_UNUSED(demoEdges)
4646}
4747
48+bool AccountsService::demoEdgesForCurrentUser() const
49+{
50+ return false;
51+}
52+
53+void AccountsService::setDemoEdgesForCurrentUser(bool)
54+{
55+}
56+
4857QString AccountsService::backgroundFile() const
4958{
5059 return m_backgroundFile;
tests/mocks/AccountsService/AccountsService.h+7
@@ -35,6 +35,10 @@ class AccountsService: public QObject
3535 READ demoEdges
3636 WRITE setDemoEdges
3737 NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (bool demoEdgesForCurrentUser
39+ READ demoEdgesForCurrentUser
40+ WRITE setDemoEdgesForCurrentUser
41+ NOTIFY demoEdgesForCurrentUserChanged)
3842 Q_PROPERTY (QString backgroundFile
3943 READ backgroundFile
4044 WRITE setBackgroundFile // only available in mock
@@ -51,6 +55,8 @@ public:
5155 void setUser(const QString &user);
5256 bool demoEdges() const;
5357 void setDemoEdges(bool demoEdges);
58+ bool demoEdgesForCurrentUser() const;
59+ void setDemoEdgesForCurrentUser(bool demoEdgesForCurrentUser);
5460 QString backgroundFile() const;
5561 void setBackgroundFile(const QString &backgroundFile);
5662 bool statsWelcomeScreen() const;
@@ -59,6 +65,7 @@ public:
5965Q_SIGNALS:
6066 void userChanged();
6167 void demoEdgesChanged();
68+ void demoEdgesForCurrentUserChanged();
6269 void backgroundFileChanged();
6370 void statsWelcomeScreenChanged();
6471
tests/mocks/CMakeLists.txt+2−1
@@ -31,10 +31,11 @@ endmacro()
3131add_subdirectory(AccountsService)
3232add_subdirectory(GSettings.1.0)
3333add_subdirectory(HudClient)
34+add_subdirectory(liblightdm)
3435add_subdirectory(libusermetrics)
35−add_subdirectory(LightDM)
3636add_subdirectory(Powerd)
3737add_subdirectory(QMenuModel)
38+add_subdirectory(SessionManager)
3839add_subdirectory(Ubuntu)
3940add_subdirectory(Unity)
4041add_subdirectory(QtMultimedia)
tests/mocks/LightDM/CMakeLists.txtdeleted−50
@@ -1,50 +0,0 @@
1−# This is a copy of the normal LightDM plugin, but instead of statically
2−# linking in the lightdm bits, this one uses shared libraries so we can swap
3−# out different sets of users for different tests. When we finally switch to
4−# actually using the system liblightdm in the normal plugin, this version can
5−# be deleted.
6−
7−add_subdirectory(demo)
8−add_subdirectory(full)
9−add_subdirectory(single)
10−add_subdirectory(single-pin)
11−add_subdirectory(single-passphrase)
12−
13−include_directories(
14− ${CMAKE_CURRENT_SOURCE_DIR}
15− ${CMAKE_CURRENT_BINARY_DIR}
16− ${CMAKE_SOURCE_DIR}/plugins/Utils
17− ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
18−)
19−
20−set(QMLPLUGIN_SRC
21− ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
22− ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
23− ${CMAKE_SOURCE_DIR}/plugins/LightDM/plugin.cpp
24− ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
25− ${CMAKE_SOURCE_DIR}/plugins/Utils/qsortfilterproxymodelqml.cpp
26− )
27−
28−add_library(MockLightDM-qml MODULE
29− ${QMLPLUGIN_SRC}
30− )
31−
32−# We want to link to liblightdm-qt5-2, but we don't want to depend on it being
33−# installed on the system. So we make sure we link to our full fake version
34−# At run time, we can point to whichever version we happen to be using via
35−# LD_LIBRARY_PATH.
36−add_dependencies(MockLightDM-qml MockLightDM-full MockUserMetrics)
37−target_link_libraries(MockLightDM-qml
38− -L${CMAKE_CURRENT_BINARY_DIR}/full
39− -llightdm-qt5-2
40− -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
41− -lusermetricsoutput
42− )
43−
44−qt5_use_modules(MockLightDM-qml DBus Gui Qml)
45−
46−add_unity8_mock(LightDM 0.1 LightDM
47− PREFIX mocks
48− TARGETS MockLightDM-qml
49− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/full"
50−)
tests/mocks/SessionManager/CMakeLists.txtnew file+19
@@ -0,0 +1,19 @@
1+add_library(MockSessionManager-qml MODULE
2+ plugin.cpp
3+ SessionManager.cpp
4+ )
5+
6+qt5_use_modules(MockSessionManager-qml Qml)
7+
8+# copy qmldir file into build directory for shadow builds
9+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
10+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
11+ )
12+
13+install(TARGETS MockSessionManager-qml
14+ DESTINATION ${SHELL_INSTALL_QML}/mocks/SessionManager
15+ )
16+
17+install(FILES qmldir
18+ DESTINATION ${SHELL_INSTALL_QML}/mocks/SessionManager
19+ )
tests/mocks/SessionManager/SessionManager.cppnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "SessionManager.h"
20+
21+SessionManager::SessionManager(QObject* parent)
22+ : QObject(parent),
23+ m_active(true)
24+{
25+}
26+
27+bool SessionManager::active() const
28+{
29+ return m_active;
30+}
31+
32+void SessionManager::setActive(bool active)
33+{
34+ if (m_active != active) {
35+ m_active = active;
36+ activeChanged();
37+ }
38+}
39+
40+void SessionManager::lock()
41+{
42+ setActive(false);
43+}
tests/mocks/SessionManager/SessionManager.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_MOCK_SESSIONMANAGER_H
20+#define UNITY_MOCK_SESSIONMANAGER_H
21+
22+#include <QObject>
23+
24+class SessionManager: public QObject
25+{
26+ Q_OBJECT
27+ Q_PROPERTY(bool active
28+ READ active
29+ WRITE setActive // Only in mock
30+ NOTIFY activeChanged
31+ FINAL)
32+
33+public:
34+ explicit SessionManager(QObject *parent = 0);
35+
36+ bool active() const;
37+ void setActive(bool active);
38+
39+public Q_SLOTS:
40+ void lock();
41+
42+Q_SIGNALS:
43+ void activeChanged();
44+
45+private:
46+ bool m_active;
47+};
48+
49+#endif
tests/mocks/SessionManager/plugin.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "SessionManager.h"
21+#include <QtQml/qqml.h>
22+
23+static QObject *sessionmanager_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
24+{
25+ Q_UNUSED(engine)
26+ Q_UNUSED(scriptEngine)
27+ return new SessionManager();
28+}
29+
30+void SessionManagerPlugin::registerTypes(const char *uri)
31+{
32+ Q_ASSERT(uri == QLatin1String("SessionManager"));
33+ qmlRegisterSingletonType<SessionManager>(uri, 0, 1, "SessionManager", sessionmanager_provider);
34+}
tests/mocks/SessionManager/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef MOCK_SESSIONMANAGER_PLUGIN_H
20+#define MOCK_SESSIONMANAGER_PLUGIN_H
21+
22+#include <QQmlEngine>
23+#include <QQmlExtensionPlugin>
24+
25+class SessionManagerPlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
tests/mocks/SessionManager/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module SessionManager
2+plugin MockSessionManager-qml
tests/mocks/Unity/CMakeLists.txt−1
@@ -19,7 +19,6 @@ include_directories(
1919add_definitions(-DQT_NO_KEYWORDS)
2020
2121set(UnityQML_SOURCES
22− iconutils.cpp
2322 fake_scope.cpp
2423 fake_scopes.cpp
2524 fake_categories.cpp
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+5
@@ -183,6 +183,11 @@ void MockLauncherModel::setUser(const QString &username)
183183 // TODO: implement this...
184184}
185185
186+QString MockLauncherModel::getUrlForAppId(const QString &appId) const
187+{
188+ return "application:///" + appId + ".desktop";
189+}
190+
186191void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
187192{
188193 Q_UNUSED(applicationManager)
tests/mocks/Unity/Launcher/MockLauncherModel.h+1
@@ -44,6 +44,7 @@ public:
4444 Q_INVOKABLE void requestRemove(const QString &appId);
4545 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
4646 Q_INVOKABLE void setUser(const QString &username);
47+ Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
4748
4849 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
4950 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
tests/mocks/Unity/fake_categories.cpp+5−1
@@ -152,7 +152,11 @@ Categories::data(const QModelIndex& index, int role) const
152152 case RoleComponents:
153153 {
154154 QVariantMap map, artMap;
155− artMap["aspect-ratio"] = "1.0";
155+ if (index.row() % 2 != 0) {
156+ artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
157+ } else {
158+ artMap["aspect-ratio"] = "1.0";
159+ }
156160 artMap["field"] = "art";
157161 map["art"] = artMap;
158162 map["title"] = "HOLA";
tests/mocks/Unity/iconutils.cppdeleted−124
@@ -1,124 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Michal Hruby <michal.hruby@canonical.com>
18− */
19−
20−#include "iconutils.h"
21−
22−#include <gio/gio.h>
23−
24−#include <QStringList>
25−#include <QUrl>
26−#include <QUrlQuery>
27−
28−#define BASE_THEME_ICON_URI "image://theme/"
29−#define BASE_THUMBNAILER_URI "image://thumbnailer/"
30−#define BASE_ALBUMART_URI "image://albumart/"
31−
32−QString gIconToDeclarativeImageProviderString(QString const &giconString)
33−{
34− if (giconString.isEmpty()) return giconString;
35−
36− if (giconString.startsWith('/') || giconString.startsWith(QLatin1String("http")) ||
37− giconString.startsWith(QLatin1String("file:")) || giconString.startsWith(QLatin1String("image:"))) {
38− return giconString;
39− }
40−
41− if (!giconString.startsWith(QLatin1String(". "))) {
42− // must be a themed icon
43− QString themedIcon(BASE_THEME_ICON_URI);
44− themedIcon.append(giconString);
45− return themedIcon;
46− }
47−
48− // special case annotated icon
49− if (giconString.startsWith(QLatin1String(". UnityProtocolAnnotatedIcon "))) {
50− QString annotatedIcon;
51− QString serializedData(QUrl::fromPercentEncoding(giconString.mid(29).toUtf8()));
52− GVariant *variant = g_variant_parse(G_VARIANT_TYPE_VARDICT, serializedData.toUtf8().constData(), NULL, NULL, NULL);
53− gchar *baseUri;
54− if (variant != NULL && g_variant_lookup(variant, "base-icon", "&s", &baseUri)) {
55− annotatedIcon = gIconToDeclarativeImageProviderString(QString(baseUri));
56− // FIXME: enclose in image://annotated/... once unity supports that
57− }
58− if (variant != NULL) g_variant_unref(variant);
59−
60− return annotatedIcon;
61− }
62−
63− // handle real gicon strings
64− QString result;
65− GError *error = NULL;
66− GIcon *icon = g_icon_new_for_string(giconString.toLocal8Bit().constData(), &error);
67− if (error != NULL || icon == NULL) {
68− qWarning("Unable to deserialize icon: %s", giconString.toLocal8Bit().constData());
69− g_clear_error(&error);
70− return result;
71− }
72−
73− if (G_IS_THEMED_ICON(icon)) {
74− QString themedIcon(BASE_THEME_ICON_URI);
75− QStringList list;
76− const char* const *iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon));
77− if (iconNames != NULL) {
78− for (const char * const *iter = iconNames; *iter != NULL; ++iter) {
79− list << QLatin1String(*iter);
80− }
81− }
82− themedIcon.append(list.join(QString(",")));
83− result = themedIcon;
84− } else if (G_IS_FILE_ICON(icon)) {
85− GFile *file = g_file_icon_get_file(G_FILE_ICON(icon));
86− gchar *uri = g_file_get_uri(file);
87− QString iconUri(uri);
88− g_free(uri);
89− result = iconUri;
90− }
91−
92− g_object_unref(icon);
93−
94− return result;
95−}
96−
97−QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype, QVariantHash const &metadata)
98−{
99− if (uri.startsWith(QLatin1String("file:///"))) {
100− bool isAudio = mimetype.startsWith(QLatin1String("audio/"));
101− QString thumbnailerUri;
102− if (isAudio) {
103− thumbnailerUri = BASE_ALBUMART_URI;
104− if (metadata.contains("content")) {
105− QVariantHash contentHash = metadata["content"].toHash();
106− if (contentHash.contains("content")) { // nested content in Home?
107− contentHash = contentHash["content"].toHash();
108− }
109− if (contentHash.contains("album") && contentHash.contains("artist")) {
110− QUrlQuery query;
111− query.addQueryItem(QStringLiteral("artist"), contentHash["artist"].toString());
112− query.addQueryItem(QStringLiteral("album"), contentHash["album"].toString());
113− thumbnailerUri.append(query.toString());
114− }
115− }
116− } else {
117− thumbnailerUri = BASE_THUMBNAILER_URI;
118− thumbnailerUri.append(uri.midRef(7));
119− }
120− return thumbnailerUri;
121− }
122−
123− return QString::null;
124−}
tests/mocks/liblightdm/CMakeLists.txtnew file+4
@@ -0,0 +1,4 @@
1+add_subdirectory(full)
2+add_subdirectory(single)
3+add_subdirectory(single-pin)
4+add_subdirectory(single-passphrase)
tests/mocks/LightDM/Greeter.cpp → tests/mocks/liblightdm/Greeter.cpprenamed+11
@@ -151,12 +151,23 @@ void Greeter::setLanguage (const QString &language)
151151 Q_UNUSED(language)
152152}
153153
154+void Greeter::setResettable (bool resettable)
155+{
156+ Q_UNUSED(resettable)
157+}
158+
154159bool Greeter::startSessionSync(const QString &session)
155160{
156161 Q_UNUSED(session)
157162 return true;
158163}
159164
165+QString Greeter::ensureSharedDataDirSync(const QString &username)
166+{
167+ Q_UNUSED(username)
168+ return "";
169+}
170+
160171void Greeter::respond(const QString &response)
161172{
162173 Q_D(Greeter);
tests/mocks/LightDM/Greeter.h → tests/mocks/liblightdm/Greeter.hrenamed+4
@@ -88,13 +88,17 @@ public Q_SLOTS:
8888 void respond(const QString &response);
8989 void cancelAuthentication();
9090 void setLanguage (const QString &language);
91+ void setResettable (bool resettable);
9192 bool startSessionSync(const QString &session=QString());
93+ QString ensureSharedDataDirSync(const QString &username);
9294
9395Q_SIGNALS:
9496 void showMessage(QString text, QLightDM::Greeter::MessageType type);
9597 void showPrompt(QString text, QLightDM::Greeter::PromptType type);
9698 void authenticationComplete();
9799 void autologinTimerExpired();
100+ void idle();
101+ void reset();
98102
99103private:
100104 GreeterPrivate *d_ptr;
tests/mocks/LightDM/GreeterPrivate.h → tests/mocks/liblightdm/GreeterPrivate.hrenamed

The content is the same.

tests/mocks/LightDM/QLightDM/Greeter → tests/mocks/liblightdm/QLightDM/Greeterrenamed

The content is the same.

tests/mocks/LightDM/QLightDM/InfographicModel → tests/mocks/liblightdm/QLightDM/InfographicModelrenamed

The content is the same.

tests/mocks/LightDM/QLightDM/UsersModel → tests/mocks/liblightdm/QLightDM/UsersModelrenamed

The content is the same.

tests/mocks/LightDM/UsersModel.cpp → tests/mocks/liblightdm/UsersModel.cpprenamed

The content is the same.

tests/mocks/LightDM/UsersModel.h → tests/mocks/liblightdm/UsersModel.hrenamed

The content is the same.

tests/mocks/LightDM/UsersModelPrivate.h → tests/mocks/liblightdm/UsersModelPrivate.hrenamed

The content is the same.

tests/mocks/LightDM/demo/CMakeLists.txt → tests/mocks/liblightdm/demo/CMakeLists.txtrenamed

The content is the same.

tests/mocks/LightDM/demo/GreeterPrivate.cpp → tests/mocks/liblightdm/demo/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/demo/UsersModelPrivate.cpp → tests/mocks/liblightdm/demo/UsersModelPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/full/CMakeLists.txt → tests/mocks/liblightdm/full/CMakeLists.txtrenamed+3−2
@@ -11,8 +11,9 @@ add_library(MockLightDM-full SHARED ${LibLightDM_SOURCES})
1111qt5_use_modules(MockLightDM-full Gui)
1212
1313set_target_properties(MockLightDM-full PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
14+ OUTPUT_NAME lightdm-qt5-3
15+ SOVERSION 0)
1516
1617install(TARGETS MockLightDM-full
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/full
18+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/full
1819 )
tests/mocks/LightDM/full/GreeterPrivate.cpp → tests/mocks/liblightdm/full/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/full/UsersModelPrivate.cpp → tests/mocks/liblightdm/full/UsersModelPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/qmldir → tests/mocks/liblightdm/qmldirrenamed

The content is the same.

tests/mocks/LightDM/single-passphrase/CMakeLists.txt → tests/mocks/liblightdm/single-passphrase/CMakeLists.txtrenamed+3−2
@@ -12,8 +12,9 @@ add_library(MockLightDM-single-passphrase SHARED ${LibLightDM_SOURCES})
1212qt5_use_modules(MockLightDM-single-passphrase Gui)
1313
1414set_target_properties(MockLightDM-single-passphrase PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
15+ OUTPUT_NAME lightdm-qt5-3
16+ SOVERSION 0)
1617
1718install(TARGETS MockLightDM-single-passphrase
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-passphrase
19+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single-passphrase
1920 )
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp → tests/mocks/liblightdm/single-passphrase/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cpp → tests/mocks/liblightdm/single-passphrase/UsersModelPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/single-pin/CMakeLists.txt → tests/mocks/liblightdm/single-pin/CMakeLists.txtrenamed+3−2
@@ -12,8 +12,9 @@ add_library(MockLightDM-single-pin SHARED ${LibLightDM_SOURCES})
1212qt5_use_modules(MockLightDM-single-pin Gui)
1313
1414set_target_properties(MockLightDM-single-pin PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
15+ OUTPUT_NAME lightdm-qt5-3
16+ SOVERSION 0)
1617
1718install(TARGETS MockLightDM-single-pin
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-pin
19+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single-pin
1920 )
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp → tests/mocks/liblightdm/single-pin/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp → tests/mocks/liblightdm/single-pin/UsersModelPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/single/CMakeLists.txt → tests/mocks/liblightdm/single/CMakeLists.txtrenamed+3−2
@@ -11,8 +11,9 @@ add_library(MockLightDM-single SHARED ${LibLightDM_SOURCES})
1111qt5_use_modules(MockLightDM-single Gui)
1212
1313set_target_properties(MockLightDM-single PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
14+ OUTPUT_NAME lightdm-qt5-3
15+ SOVERSION 0)
1516
1617install(TARGETS MockLightDM-single
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single
18+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single
1819 )
tests/mocks/LightDM/single/GreeterPrivate.cpp → tests/mocks/liblightdm/single/GreeterPrivate.cpprenamed

The content is the same.

tests/mocks/LightDM/single/UsersModelPrivate.cpp → tests/mocks/liblightdm/single/UsersModelPrivate.cpprenamed

The content is the same.

tests/plugins/CMakeLists.txt+2−1
@@ -1,6 +1,7 @@
11add_subdirectory(AccountsService)
2−add_subdirectory(LightDM)
32add_subdirectory(Dash)
3+add_subdirectory(LightDM)
4+add_subdirectory(SessionManager)
45add_subdirectory(Ubuntu)
56add_subdirectory(Unity)
67add_subdirectory(Utils)
tests/plugins/Dash/cardcreator/1.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/2.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/3.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/5.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/organicgridtest.cpp+5
@@ -514,6 +514,11 @@ private Q_SLOTS:
514514
515515 model->setCount(21);
516516
517+ QTRY_COMPARE(grid->m_visibleItems.count(), 0);
518+
519+ grid->setDisplayMarginBeginning(0);
520+ grid->setDisplayMarginEnd(0);
521+
517522 // This is exactly the same block as the first again
518523 checkInitialPositions();
519524 }
tests/plugins/LightDM/CMakeLists.txt+5−4
@@ -1,3 +1,6 @@
1+include(FindPkgConfig)
2+pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-3)
3+
14add_executable(test-lightdm-dbus
25 dbus.cpp
36 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
@@ -7,19 +10,17 @@ qt5_use_modules(test-lightdm-dbus Core DBus Quick Test)
710include_directories(
811 ${CMAKE_CURRENT_BINARY_DIR}
912 ${CMAKE_SOURCE_DIR}/plugins/LightDM
10− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
1113 ${LIBLIGHTDM_INCLUDE_DIRS}
1214 )
1315
1416add_dependencies(test-lightdm-dbus MockLightDM-full)
1517target_link_libraries(test-lightdm-dbus
16− -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
17− -llightdm-qt5-2
18+ ${LIBLIGHTDM_LDFLAGS}
1819 )
1920
2021add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
2122
22−add_custom_target(testLightDMDBus dbus-launch env QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full ${CMAKE_CURRENT_BINARY_DIR}/test-lightdm-dbus)
23+add_custom_target(testLightDMDBus dbus-launch env QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/plugins LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/full ${CMAKE_CURRENT_BINARY_DIR}/test-lightdm-dbus)
2324add_dependencies(testLightDMDBus test-lightdm-dbus)
2425
2526add_dependencies(qmluitests testLightDMDBus)
tests/plugins/SessionManager/CMakeLists.txtnew file+48
@@ -0,0 +1,48 @@
1+macro(make_dbus_class PREFIX SERVICE INTERFACE)
2+ if (${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PREFIX}${INTERFACE}Adaptor.h)
3+ execute_process(COMMAND qdbusxml2cpp -c ${PREFIX}${INTERFACE}Adaptor -a ${CMAKE_CURRENT_BINARY_DIR}/${PREFIX}${INTERFACE}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${SERVICE}.${INTERFACE})
4+ endif ()
5+endmacro(make_dbus_class)
6+
7+make_dbus_class(LightDM org.freedesktop.DisplayManager Session)
8+make_dbus_class(Login org.freedesktop.login1 Manager)
9+make_dbus_class(Login org.freedesktop.login1 Session)
10+make_dbus_class(Login org.freedesktop.DBus Properties)
11+
12+include_directories(
13+ ${CMAKE_CURRENT_BINARY_DIR}
14+ ${CMAKE_SOURCE_DIR}/plugins/SessionManager
15+ )
16+
17+add_definitions(-DSM_BUSNAME=sessionBus)
18+
19+add_executable(sessionmanagermock
20+ ${CMAKE_CURRENT_BINARY_DIR}/LightDMSessionAdaptor.cpp
21+ ${CMAKE_CURRENT_BINARY_DIR}/LoginManagerAdaptor.cpp
22+ ${CMAKE_CURRENT_BINARY_DIR}/LoginPropertiesAdaptor.cpp
23+ ${CMAKE_CURRENT_BINARY_DIR}/LoginSessionAdaptor.cpp
24+ server.cpp
25+ LightDMSessionServer.cpp
26+ LoginManagerServer.cpp
27+ LoginSessionServer.cpp
28+ )
29+qt5_use_modules(sessionmanagermock Core DBus)
30+
31+add_executable(sessionmanagertest
32+ ${CMAKE_SOURCE_DIR}/plugins/SessionManager/SessionManager.cpp
33+ client.cpp
34+ )
35+qt5_use_modules(sessionmanagertest Core DBus Test)
36+
37+add_test(NAME sessionmanagertest
38+ COMMAND env
39+ XDG_SESSION_ID=sessionid
40+ XDG_SESSION_PATH=/mocksession/lightdm
41+ dbus-test-runner
42+ --task ${CMAKE_CURRENT_BINARY_DIR}/sessionmanagermock
43+ --task-name server
44+ --ignore-return
45+ --task ${CMAKE_CURRENT_BINARY_DIR}/sessionmanagertest
46+ --task-name client
47+ --wait-for org.freedesktop.login1
48+ )
tests/plugins/SessionManager/LightDMSessionServer.cppnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "LightDMSessionServer.h"
21+
22+LightDMSessionServer::LightDMSessionServer(LoginSessionServer *logind, QObject *parent)
23+ : QObject(parent),
24+ logind(logind)
25+{
26+}
27+
28+void LightDMSessionServer::Lock()
29+{
30+ // As a side effect of locking, change Active state of logind.
31+ // Note that this will say Active is true while the Active property will be
32+ // false. This is intentional, as we don't want to keep any state in the
33+ // mocks. Having this signal side effect lets us test that lock() was
34+ // actually called by the plugin as well as test the changed signal itself.
35+ QVariantMap changes;
36+ changes.insert("Active", QVariant(true));
37+ Q_EMIT logind->PropertiesChanged("org.freedesktop.login1.Session", changes, QStringList());
38+}
tests/plugins/SessionManager/LightDMSessionServer.hnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_LIGHTDMSESSIONSERVER_H
21+#define UNITY_LIGHTDMSESSIONSERVER_H
22+
23+#include "LoginSessionServer.h"
24+#include <QtCore/QObject>
25+
26+class LightDMSessionServer: public QObject
27+{
28+ Q_OBJECT
29+
30+public:
31+ explicit LightDMSessionServer(LoginSessionServer *logind, QObject *parent = 0);
32+
33+public Q_SLOTS:
34+ void Lock();
35+
36+private:
37+ LoginSessionServer *logind;
38+};
39+
40+#endif
tests/plugins/SessionManager/LoginManagerServer.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "LoginManagerServer.h"
21+
22+LoginManagerServer::LoginManagerServer(QObject *parent)
23+ : QObject(parent)
24+{
25+}
26+
27+QDBusObjectPath LoginManagerServer::GetSession(const QString &id)
28+{
29+ if (id == "sessionid") {
30+ return QDBusObjectPath("/mocksession/login1");
31+ } else {
32+ return QDBusObjectPath();
33+ }
34+}
tests/plugins/SessionManager/LoginManagerServer.hnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_LOGINMANAGERSERVER_H
21+#define UNITY_LOGINMANAGERSERVER_H
22+
23+#include <QtCore/QObject>
24+#include <QtDBus/QDBusObjectPath>
25+
26+class LoginManagerServer: public QObject
27+{
28+ Q_OBJECT
29+
30+public:
31+ explicit LoginManagerServer(QObject *parent = 0);
32+
33+public Q_SLOTS:
34+ QDBusObjectPath GetSession(const QString &id);
35+};
36+
37+#endif
tests/plugins/SessionManager/LoginSessionServer.cppnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "LoginSessionServer.h"
21+
22+LoginSessionServer::LoginSessionServer(QObject *parent)
23+ : QObject(parent)
24+{
25+}
tests/plugins/SessionManager/LoginSessionServer.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#ifndef UNITY_LOGINSESSIONSERVER_H
21+#define UNITY_LOGINSESSIONSERVER_H
22+
23+#include <QtCore/QObject>
24+#include <QtCore/QStringList>
25+#include <QtCore/QVariant>
26+#include <QtCore/QVariantMap>
27+
28+class LoginSessionServer: public QObject
29+{
30+ Q_OBJECT
31+
32+public:
33+ explicit LoginSessionServer(QObject *parent = 0);
34+
35+Q_SIGNALS:
36+ void PropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid);
37+};
38+
39+#endif
tests/plugins/SessionManager/client.cppnew file+88
@@ -0,0 +1,88 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "SessionManager.h"
21+#include <QtTest/QSignalSpy>
22+#include <QtTest/QTest>
23+
24+
25+// These tests assume the following behavior from our mock logind server:
26+// * IsActive will return false
27+// * Lock() will emit an ActiveChanged(true) signal
28+//
29+// This should lead to the following behavior from our SessionManager plugin:
30+// * Starts active
31+// * Becomes inactive once it connects to logind and sees IsActive result
32+// * Becomes active once Lock() is called (which isn't really sensible, but
33+// it's just a mock)
34+
35+class SessionManagerTest : public QObject
36+{
37+ Q_OBJECT
38+
39+private Q_SLOTS:
40+
41+ void waitForChange(SessionManager *session)
42+ {
43+ // Wait 5 seconds for initial activeChanged signal
44+ QSignalSpy spy(session, SIGNAL(activeChanged()));
45+ int i = 0;
46+ while (spy.count() == 0 && i++ < 50)
47+ QTest::qWait(100);
48+ QCOMPARE(spy.count(), 1);
49+ }
50+
51+ void testAssumeActive()
52+ {
53+ // Test that active() starts as true before we connect to service
54+ SessionManager session;
55+ QCOMPARE(session.active(), true);
56+ }
57+
58+ void testSafeLock()
59+ {
60+ // Test that lock() doesn't crash if used before we connect to service
61+ // (i.e. just test that we're not being stupid about pointers)
62+ SessionManager session;
63+ session.lock();
64+ }
65+
66+ void testInitialSignal()
67+ {
68+ // Test that we get notified of change once we connect to service
69+ SessionManager session;
70+ waitForChange(&session);
71+ QCOMPARE(session.active(), false);
72+ }
73+
74+ void testLock()
75+ {
76+ // Test that lock() gets called correctly and that we listen for
77+ // the activeChanged signal too.
78+ SessionManager session;
79+ waitForChange(&session);
80+ session.lock();
81+ waitForChange(&session);
82+ QCOMPARE(session.active(), true);
83+ }
84+};
85+
86+QTEST_MAIN(SessionManagerTest)
87+
88+#include "client.moc"
tests/plugins/SessionManager/interfaces.xmlnew file+22
@@ -0,0 +1,22 @@
1+<node>
2+ <interface name="org.freedesktop.login1.Manager">
3+ <method name="GetSession">
4+ <arg name="id" type="s" direction="in" />
5+ <arg name="path" type="o" direction="out" />
6+ </method>
7+ </interface>
8+ <interface name="org.freedesktop.login1.Session">
9+ <property name="Active" type="b" access="read"/>
10+ </interface>
11+ <interface name="org.freedesktop.DBus.Properties">
12+ <signal name="PropertiesChanged">
13+ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
14+ <arg name="interface" type="s" direction="out"/>
15+ <arg name="changed" type="a{sv}" direction="out"/>
16+ <arg name="invalid" type="as" direction="out"/>
17+ </signal>
18+ </interface>
19+ <interface name="org.freedesktop.DisplayManager.Session">
20+ <method name="Lock"/>
21+ </interface>
22+</node>
tests/plugins/SessionManager/server.cppnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ *
17+ * Authored by: Michael Terry <michael.terry@canonical.com>
18+ */
19+
20+#include "LightDMSessionAdaptor.h"
21+#include "LightDMSessionServer.h"
22+#include "LoginManagerAdaptor.h"
23+#include "LoginManagerServer.h"
24+#include "LoginPropertiesAdaptor.h"
25+#include "LoginSessionAdaptor.h"
26+#include "LoginSessionServer.h"
27+#include <QtCore/QCoreApplication>
28+
29+int main(int argc, char *argv[])
30+{
31+ QCoreApplication a(argc, argv);
32+
33+ LoginManagerServer *manager = new LoginManagerServer();
34+ new LoginManagerAdaptor(manager);
35+
36+ LoginSessionServer *l1_session = new LoginSessionServer();
37+ new LoginSessionAdaptor(l1_session);
38+ new LoginPropertiesAdaptor(l1_session);
39+
40+ LightDMSessionServer *ldm_session = new LightDMSessionServer(l1_session);
41+ new LightDMSessionAdaptor(ldm_session);
42+
43+ // We use the session bus for testing. The real plugin uses the system bus
44+ QDBusConnection connection = QDBusConnection::sessionBus();
45+ if (!connection.registerObject("/org/freedesktop/login1", manager))
46+ return 1;
47+ if (!connection.registerObject("/mocksession/login1", l1_session))
48+ return 1;
49+ if (!connection.registerObject("/mocksession/lightdm", ldm_session))
50+ return 1;
51+ if (!connection.registerService("org.freedesktop.DisplayManager"))
52+ return 1;
53+ if (!connection.registerService("org.freedesktop.login1"))
54+ return 1;
55+
56+ return a.exec();
57+}
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -49,7 +49,7 @@ add_executable(launchermodeltestExec
4949 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
5050 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
5151 )
52−target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
52+target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})
5353qt5_use_modules(launchermodeltestExec Test Core DBus)
5454
5555# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+9
@@ -274,6 +274,15 @@ private Q_SLOTS:
274274 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
275275 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
276276 }
277+
278+ void testGetUrlForAppId() {
279+ QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
280+ QCOMPARE(launcherModel->getUrlForAppId(""), QString());
281+ QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));
282+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("appid://com.test.good/first-listed-app/current-user-version"));
283+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));
284+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));
285+ }
277286};
278287
279288QTEST_GUILESS_MAIN(LauncherModelTest)
tests/qmltests/CMakeLists.txt+40−50
@@ -4,6 +4,7 @@ include(QmlTest)
44add_subdirectory(Components)
55
66set(qmltest_DEFAULT_IMPORT_PATHS
7+ ${CMAKE_BINARY_DIR}/plugins
78 ${CMAKE_BINARY_DIR}/tests/utils/modules
89 ${CMAKE_BINARY_DIR}/tests/mocks
910)
@@ -18,12 +19,12 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
1819set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
1920set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
2021
21−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
22− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
22+add_qml_test(. Shell)
23+add_qml_test(. GreeterShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/single")
2324add_qml_test(Components Carousel)
2425add_qml_test(Components DraggingArea)
2526add_qml_test(Components EdgeDemoOverlay)
26−add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
27+add_qml_test(Components FilterGrid)
2728add_qml_test(Components LazyImage)
2829add_qml_test(Components Rating)
2930add_qml_test(Components ResponsiveFlowView)
@@ -32,54 +33,43 @@ add_qml_test(Components SeeMore)
3233add_qml_test(Components Showable)
3334add_qml_test(Components PageHeaderLabel)
3435add_qml_test(Components ZoomableImage)
35−add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
36−add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
37−add_qml_test(Dash Card IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
38−add_qml_benchmark(Dash CardBenchmark 1000 IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
39−add_qml_test(Dash CardHeader)
40−add_qml_test(Dash CardTool IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
41−add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
42−add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
43− ${CMAKE_BINARY_DIR}/tests/mocks
44− ${CMAKE_BINARY_DIR}/plugins)
45−add_qml_test(Dash/Previews Preview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
46−add_qml_test(Dash/Previews PreviewActions IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
47−add_qml_test(Dash/Previews PreviewAudioPlayback IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
48−add_qml_test(Dash/Previews PreviewHeader IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
49−add_qml_test(Dash/Previews PreviewImageGallery IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
50−add_qml_test(Dash/Previews PreviewProgress IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
51−add_qml_test(Dash/Previews PreviewRatingDisplay IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
52−add_qml_test(Dash/Previews PreviewRatingInput IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
53−add_qml_test(Dash/Previews PreviewTextSummary IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
54−add_qml_test(Dash/Previews PreviewVideoPlayback IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
55−add_qml_test(Dash/Previews PreviewWidgetFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
56−add_qml_test(Dash/Previews PreviewZoomableImage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
57−add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
58− PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
59−add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
60− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
61−add_qml_test(Greeter Phone IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
62− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
63−add_qml_test(Greeter Infographics IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
64− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
65−add_qml_test(Greeter Clock IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
66− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
36+add_qml_test(Dash Dash)
37+add_qml_test(Dash DashContent)
38+add_qml_test(Dash Card)
39+add_qml_benchmark(Dash CardBenchmark 1000)
40+add_qml_test(Dash CardTool)
41+add_qml_test(Dash GenericScopeView)
42+add_qml_test(Dash/Apps RunningApplicationsGrid)
43+add_qml_test(Dash/Previews Preview)
44+add_qml_test(Dash/Previews PreviewActions)
45+add_qml_test(Dash/Previews PreviewAudioPlayback)
46+add_qml_test(Dash/Previews PreviewHeader)
47+add_qml_test(Dash/Previews PreviewImageGallery)
48+add_qml_test(Dash/Previews PreviewProgress)
49+add_qml_test(Dash/Previews PreviewRatingDisplay)
50+add_qml_test(Dash/Previews PreviewRatingInput)
51+add_qml_test(Dash/Previews PreviewTextSummary)
52+add_qml_test(Dash/Previews PreviewVideoPlayback)
53+add_qml_test(Dash/Previews PreviewWidgetFactory)
54+add_qml_test(Dash/Previews PreviewZoomableImage)
55+add_qml_test(Greeter Lockscreen ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/full")
56+add_qml_test(Greeter Tablet ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/full")
57+add_qml_test(Greeter Phone ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/single")
58+add_qml_test(Greeter Infographics ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/full")
59+add_qml_test(Greeter Clock ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
6760add_qml_test(Hud Hud)
6861add_qml_test(Hud Result)
69−add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
70−add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
71−add_qml_test(Notifications VisualSnapDecisionsQueue IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
62+add_qml_test(Launcher Launcher)
63+add_qml_test(Notifications Notifications)
64+add_qml_test(Notifications VisualSnapDecisionsQueue)
7265add_qml_test(Panel IndicatorRow)
73−add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
74−add_qml_test(Panel MenuContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
75−add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
66+add_qml_test(Panel Indicators)
67+add_qml_test(Panel MenuContent)
68+add_qml_test(Panel Panel)
7669add_qml_test(Panel SearchIndicator)
77−add_qml_test(Panel/Indicators DefaultIndicatorWidget IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
78− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79−add_qml_test(Panel/Indicators DefaultIndicatorPage IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
80− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
81−add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
82−add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
83−add_qml_test(Stages PhoneStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
84− ${CMAKE_BINARY_DIR}/tests/mocks
85− ${CMAKE_BINARY_DIR}/plugins)
70+add_qml_test(Panel/Indicators DefaultIndicatorWidget ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
71+add_qml_test(Panel/Indicators DefaultIndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
72+# These MenuItemFactory tests need the test/mocks/ to come before plugins/
73+add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
74+add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
75+add_qml_test(Stages PhoneStage)
tests/qmltests/Components/tst_ZoomableImage.qml+21−19
@@ -98,22 +98,23 @@ Rectangle {
9898 // move mouse to center
9999 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
100100
101+ var oldScale = image.scale;
101102 // Test Zoom-in Zoom-out twice.
102103 for (var c=0; c<2; c++) {
103104 // zoom in
104105 for (var i=0; i<10; i++) {
105106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106− tryCompare(image, "scale", 1.0 + (i + 1) * 0.1);
107− compare(flickable.contentWidth, lazyImage.width * image.scale);
108− compare(flickable.contentHeight, lazyImage.height * image.scale);
107+ tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
108+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
109+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
109110 }
110111
111112 // zoom out
112113 for (var i=0; i<10; i++) {
113114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114− tryCompare(image, "scale", 2.0 - (i + 1) * 0.1);
115− compare(flickable.contentWidth, lazyImage.width * image.scale);
116− compare(flickable.contentHeight, lazyImage.height * image.scale);
115+ tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
116+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
117+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
117118 }
118119 }
119120 }
@@ -123,16 +124,12 @@ Rectangle {
123124 zoomable:false,
124125 answer1: true,
125126 answer2: false,
126− answer3: true,
127− answer4: 1.0,
128− answer5: 1.0 },
127+ answer3: true },
129128 { source:widgetData2["source"],
130129 zoomable:true,
131130 answer1: false,
132131 answer2: true,
133− answer3: false,
134− answer4: 1.7740461882048026,
135− answer5: 1.0 }
132+ answer3: false }
136133 ]
137134 }
138135
@@ -163,7 +160,7 @@ Rectangle {
163160 var y2End = zoomableImage.height * 5 / 6;
164161
165162 var oldScale = image.scale;
166−
163+ var newScale = -1;
167164 // move mouse to center
168165 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
169166
@@ -172,20 +169,25 @@ Rectangle {
172169 wait(3000); // have to delay between two consequent pinch event.
173170 // pinch zoom-in
174171 touchPinch(zoomableImage, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End);
172+ waitForRendering(image);
175173
176− tryCompare(image, "scale", data.answer4);
177− var newScale = image.scale;
174+ if (newScale == -1) {
175+ newScale = image.scale;
176+ }
177+ tryCompare(image, "scale", newScale);
178178 compare(newScale == oldScale, data.answer1, "scale factor not equal: "+ oldScale + "=?" + newScale);
179179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
180180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
181− compare(newScale, data.answer4, "scale factor error");
182− compare(flickable.contentWidth, lazyImage.width * image.scale);
183− compare(flickable.contentHeight, lazyImage.height * image.scale);
181+ compare(image.scale, newScale, "scale factor error");
182+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
183+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
184184
185185 wait(3000); // have to delay between two consequent pinch event.
186186 // pinch zoom-out
187187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
188− tryCompare(image, "scale", data.answer5);
188+ tryCompare(image, "scale", oldScale);
189+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
190+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
189191 }
190192 }
191193 }
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+12−7
@@ -53,12 +53,12 @@ Rectangle {
5353 name: "PreviewHeaderTest"
5454 when: windowShown
5555
56− property Item mascot: findChild(previewHeader, "mascotShape")
56+ property Item mascotShapeLoader: findChild(previewHeader, "mascotShapeLoader")
5757 property Item outerRow: findChild(previewHeader, "outerRow")
5858 property Item column: findChild(previewHeader, "column")
5959
6060 function initTestCase() {
61− verify(typeof testCase.mascot === "object", "Couldn't find mascot object.");
61+ verify(typeof testCase.mascotShapeLoader === "object", "Couldn't find mascot loader object.");
6262 verify(typeof testCase.outerRow === "object", "Couldn't find outerRow object.");
6363 verify(typeof testCase.column === "object", "Couldn't find column object.");
6464
@@ -70,9 +70,9 @@ Rectangle {
7070
7171 function test_mascot_data() {
7272 return [
73− { tag: "Empty", source: "", visible: false },
74− { tag: "Invalid", source: "bad_path", visible: false },
75− { tag: "Valid", source: "../graphics/play_button.png", visible: true },
73+ { tag: "Empty", source: "", loaderVisible: false, visible: false },
74+ { tag: "Invalid", source: "bad_path", loaderVisible: true, visible: false },
75+ { tag: "Valid", source: "../graphics/play_button.png", loaderVisible: true, visible: true },
7676 ]
7777 }
7878
@@ -80,12 +80,17 @@ Rectangle {
8080 headerjson.mascot = data.source;
8181 previewHeader.widgetData = headerjson;
8282
83− tryCompare(testCase.mascot, "visible", data.visible);
83+ tryCompare(testCase.mascotShapeLoader, "visible", data.loaderVisible);
84+ if (data.loaderVisible) {
85+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
86+ var mascot = findChild(previewHeader, "mascotShape")
87+ tryCompare(mascot, "visible", data.visible);
88+ }
8489 }
8590
8691 function test_dimensions_data() {
8792 return [
88− { tag: "Column width with mascot", object: column, width: previewHeader.width - mascot.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
93+ { tag: "Column width with mascot", object: column, width: previewHeader.width - mascotShapeLoader.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
8994 { tag: "Header height", object: previewHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
9095 ]
9196 }
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−5
@@ -26,12 +26,12 @@ Rectangle {
2626 color: "lightgrey"
2727
2828 property var widgetData0: {
29− "source": ""
29+ "source": Qt.resolvedUrl("../artwork/checkers.png"),
30+ "zoomable": false
3031 }
3132
3233 property var widgetData1: {
33− "source": Qt.resolvedUrl("../artwork/checkers.png"),
34− "zoomable": false
34+ "source": ""
3535 }
3636
3737 PreviewZoomableImage {
@@ -49,11 +49,13 @@ Rectangle {
4949
5050 zoomableImage.widgetData = widgetData0;
5151 waitForRendering(zoomableImage);
52− tryCompare(image, "imageState", "default");
52+ waitForRendering(image);
53+ tryCompare(image, "imageState", "ready");
5354
5455 zoomableImage.widgetData = widgetData1;
5556 waitForRendering(zoomableImage);
56− tryCompare(image, "imageState", "ready");
57+ waitForRendering(image);
58+ tryCompare(image, "imageState", "default");
5759 }
5860 }
5961}
tests/qmltests/Dash/tst_DashContent.qml+29−14
@@ -67,13 +67,17 @@ Item {
6767 name: "DashContent"
6868 when: scopesModel.loaded && windowShown
6969
70− function init() {
71− scopesModel.clear();
70+ function loadScopes() {
7271 scopeLoadedSpy.clear();
7372 scopesModel.load();
7473 tryCompare(scopeLoadedSpy, "count", 4);
7574 }
7675
76+ function init() {
77+ scopesModel.clear();
78+ loadScopes();
79+ }
80+
7781 function cleanup() {
7882 movementStartedSpy.clear();
7983 dashContent.visible = true;
@@ -93,9 +97,7 @@ Item {
9397 compare(dashContentList.count, 0, "DashContent should have 0 items when it starts");
9498 tryCompare(dashContentList, "currentIndex", -1);
9599
96− scopeLoadedSpy.clear();
97− scopesModel.load();
98− tryCompare(scopeLoadedSpy, "count", 4);
100+ loadScopes();
99101
100102 verify(dashContentList.currentIndex >= 0);
101103 }
@@ -109,9 +111,7 @@ Item {
109111 // pretend we're running after a model reset
110112 dashContentList.currentIndex = 27;
111113
112− scopeLoadedSpy.clear();
113− scopesModel.load();
114− tryCompare(scopeLoadedSpy, "count", 4);
114+ loadScopes();
115115
116116 verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
117117 }
@@ -326,8 +326,6 @@ Item {
326326 }
327327
328328 function test_previewOpenClose() {
329− tryCompare(scopeLoadedSpy, "count", 4);
330−
331329 var previewListView = findChild(dashContent, "dashContentPreviewList");
332330 tryCompare(previewListView, "open", false);
333331
@@ -352,8 +350,6 @@ Item {
352350 },
353351 true);
354352
355− tryCompare(scopeLoadedSpy, "count", 4);
356−
357353 var previewListView = findChild(dashContent, "dashContentPreviewList");
358354 tryCompare(previewListView, "open", false);
359355
@@ -368,8 +364,6 @@ Item {
368364 }
369365
370366 function test_previewCycle() {
371− tryCompare(scopeLoadedSpy, "count", 4);
372−
373367 var categoryListView = findChild(dashContent, "categoryListView");
374368 categoryListView.positionAtBeginning();
375369
@@ -392,5 +386,26 @@ Item {
392386 }
393387 closePreview();
394388 }
389+
390+ function test_carouselAspectRatio() {
391+ tryCompareFunction(function() {
392+ var scope = findChild(dashContent, "MockScope1 loader");
393+ if (scope != null) {
394+ var dashCategory1 = findChild(scope, "dashCategory1");
395+ if (dashCategory1 != null) {
396+ var tile = findChild(dashCategory1, "carouselDelegate1");
397+ return tile != null;
398+ }
399+ }
400+ return false;
401+ },
402+ true);
403+
404+ var scope = findChild(dashContent, "MockScope1 loader");
405+ var dashCategory1 = findChild(scope, "dashCategory1");
406+ var cardTool = findChild(dashCategory1, "cardTool");
407+ var carouselLV = findChild(dashCategory1, "listView");
408+ verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
409+ }
395410 }
396411}
tests/qmltests/Dash/tst_GenericScopeView.qml+1
@@ -126,6 +126,7 @@ Item {
126126
127127 function test_filter_expand_collapse() {
128128 // wait for the item to be there
129+ waitForRendering(genericScopeView);
129130 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
130131
131132 var header = findChild(genericScopeView, "dashSectionHeader0")
tests/qmltests/Greeter/tst_Lockscreen.qml+208−31
@@ -17,31 +17,39 @@
1717import QtQuick 2.0
1818import QtTest 1.0
1919import ".."
20−import "../../../qml/Greeter"
20+import "../../../qml/Components"
2121import Ubuntu.Components 0.1
2222import LightDM 0.1 as LightDM
2323import Unity.Test 0.1 as UT
2424
2525Rectangle {
26− width: units.gu(60)
26+ id: root
27+ width: units.gu(80)
2728 height: units.gu(80)
2829 color: "orange"
2930
3031 Lockscreen {
3132 id: lockscreen
3233 anchors.fill: parent
33− anchors.rightMargin: units.gu(18)
34+ anchors.rightMargin: units.gu(30)
3435 placeholderText: "Please enter your PIN"
36+ wrongPlaceholderText: "Incorrect PIN"
37+ retryText: retryCountTextField.text
3538 alphaNumeric: pinPadCheckBox.checked
36− pinLength: pinLengthTextField.text
39+ minPinLength: minPinLengthTextField.text
40+ maxPinLength: maxPinLengthTextField.text
3741 username: "Lola"
42+ infoText: infoTextTextField.text
3843 }
3944
4045 Connections {
4146 target: lockscreen
4247
4348 onEmergencyCall: emergencyCheckBox.checked = true
44− onEntered: enteredLabel.text = passphrase
49+ onEntered: {
50+ enteredLabel.text = passphrase
51+ lockscreen.clear(true)
52+ }
4553 }
4654
4755 Connections {
@@ -49,9 +57,9 @@ Rectangle {
4957
5058 onShowPrompt: {
5159 if (text.indexOf("PIN") >= 0) {
52− lockscreen.alphaNumeric = false
60+ pinPadCheckBox.checked = false
5361 } else {
54− lockscreen.alphaNumeric = true
62+ pinPadCheckBox.checked = true
5563 }
5664 lockscreen.placeholderText = text;
5765 }
@@ -65,6 +73,7 @@ Rectangle {
6573 Column {
6674 anchors.fill: parent
6775 anchors.margins: units.gu(1)
76+ spacing: units.gu(1)
6877
6978 Row {
7079 CheckBox {
@@ -86,12 +95,32 @@ Rectangle {
8695 }
8796 Row {
8897 TextField {
89− id: pinLengthTextField
98+ id: minPinLengthTextField
9099 width: units.gu(7)
91100 text: "4"
92101 }
93102 Label {
94− text: "PIN length"
103+ text: "Min PIN length"
104+ }
105+ }
106+ Row {
107+ TextField {
108+ id: maxPinLengthTextField
109+ width: units.gu(7)
110+ text: "4"
111+ }
112+ Label {
113+ text: "Max PIN length"
114+ }
115+ }
116+ Row {
117+ TextField {
118+ id: retryCountTextField
119+ width: units.gu(7)
120+ text: "3 retries left"
121+ }
122+ Label {
123+ text: "Retries left"
95124 }
96125 }
97126 Label {
@@ -108,12 +137,40 @@ Rectangle {
108137 }
109138 Button {
110139 text: "start auth (1234)"
140+ width: parent.width
111141 onClicked: LightDM.Greeter.authenticate("has-pin")
112142 }
113143 Button {
114144 text: "start auth (password)"
145+ width: parent.width
115146 onClicked: LightDM.Greeter.authenticate("has-password")
116147 }
148+
149+ TextField {
150+ id: infoTextTextField
151+ width: parent.width
152+ placeholderText: "Infotext"
153+ text: "+49 179 3253553"
154+ }
155+
156+ TextField {
157+ id: infoPopupTitleTextField
158+ width: parent.width
159+ placeholderText: "Popup title"
160+ text: "This will be the last attempt"
161+ }
162+
163+ TextArea {
164+ id: infoPopupTextArea
165+ width: parent.width
166+ text: "If the SIM PIN is entered incorrectly, your SIM will be blocked and would require the PUK code to unlock."
167+ }
168+
169+ Button {
170+ text: "open info popup"
171+ width: parent.width
172+ onClicked: lockscreen.showInfoPopup(infoPopupTitleTextField.text, infoPopupTextArea.text)
173+ }
117174 }
118175 }
119176
@@ -121,6 +178,16 @@ Rectangle {
121178 name: "Lockscreen"
122179 when: windowShown
123180
181+ function cleanup() {
182+ lockscreen.clear(false);
183+ }
184+
185+ function waitForLockscreenReady() {
186+ var pinPadLoader = findChild(lockscreen, "pinPadLoader");
187+ tryCompare(pinPadLoader, "status", Loader.Ready)
188+ waitForRendering(lockscreen)
189+ }
190+
124191 function test_loading_data() {
125192 return [
126193 {tag: "numeric", alphanumeric: false, pinPadAvailable: true },
@@ -129,8 +196,8 @@ Rectangle {
129196 }
130197
131198 function test_loading(data) {
132− lockscreen.alphaNumeric = data.alphanumeric
133− waitForRendering(lockscreen)
199+ pinPadCheckBox.checked = data.alphanumeric
200+ waitForLockscreenReady();
134201 if (data.pinPadAvailable) {
135202 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
136203 } else {
@@ -147,8 +214,8 @@ Rectangle {
147214
148215 function test_emergency_call(data) {
149216 emergencyCheckBox.checked = false
150− lockscreen.alphaNumeric = data.alphanumeric
151− waitForRendering(lockscreen)
217+ pinPadCheckBox.checked = data.alphanumeric
218+ waitForLockscreenReady();
152219 var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
153220 mouseClick(emergencyButton, units.gu(1), units.gu(1))
154221 tryCompare(emergencyCheckBox, "checked", true)
@@ -163,9 +230,9 @@ Rectangle {
163230 }
164231
165232 function test_labels(data) {
166− lockscreen.alphaNumeric = data.alphanumeric
233+ pinPadCheckBox.checked = data.alphanumeric
167234 lockscreen.placeholderText = data.placeholderText
168− waitForRendering(lockscreen)
235+ waitForLockscreenReady();
169236 compare(findChild(lockscreen, "pinentryField").placeholderText, data.placeholderText, "Placeholdertext is not what it should be")
170237 if (data.alphanumeric) {
171238 compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
@@ -175,23 +242,25 @@ Rectangle {
175242
176243 function test_unlock_data() {
177244 return [
178− {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", pinLength: 4},
179− {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", pinLength: -1},
180− {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", pinLength: 4},
181− {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", pinLength: -1},
182− {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", pinLength: -1},
245+ {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
246+ {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", minPinLength: -1, maxPinLength: -1},
247+ {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", minPinLength: 4, maxPinLength: 4},
248+ {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", minPinLength: -1, maxPinLength: -1},
249+ {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: -1, maxPinLength: -1},
183250 ]
184251 }
185252
186253 function test_unlock(data) {
187254 enteredLabel.text = ""
188− pinLengthTextField.text = data.pinLength
255+ minPinLengthTextField.text = data.minPinLength
256+ maxPinLengthTextField.text = data.maxPinLength
189257 LightDM.Greeter.authenticate(data.username)
190− waitForRendering(lockscreen)
258+ waitForLockscreenReady();
191259
192260 var inputField = findChild(lockscreen, "pinentryField")
193261 if (data.alphanumeric) {
194262 mouseClick(inputField, units.gu(1), units.gu(1))
263+ tryCompare(inputField, "focus", true);
195264 typeString(data.password)
196265 keyClick(Qt.Key_Enter)
197266 } else {
@@ -200,7 +269,7 @@ Rectangle {
200269 var button = findChild(lockscreen, "pinPadButton" + character)
201270 mouseClick(button, units.gu(1), units.gu(1))
202271 }
203− if (data.pinLength == -1) {
272+ if (data.minPinLength !== data.maxPinLength || data.minPinLength == -1) {
204273 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
205274 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
206275 }
@@ -219,11 +288,12 @@ Rectangle {
219288
220289 function test_clear(data) {
221290 pinPadCheckBox.checked = data.alphanumeric
222− waitForRendering(lockscreen)
291+ waitForLockscreenReady();
223292
224293 var inputField = findChild(lockscreen, "pinentryField")
225294 if (data.alphanumeric) {
226295 mouseClick(inputField, units.gu(1), units.gu(1))
296+ tryCompare(inputField, "activeFocus", true);
227297 typeString("1")
228298 } else {
229299 var button = findChild(lockscreen, "pinPadButton1")
@@ -239,6 +309,16 @@ Rectangle {
239309
240310 wait(0) // Trigger event loop to make sure the animation would start running
241311 compare(animation.running, data.animation)
312+ if (data.animation) {
313+ if (data.alphanumeric) {
314+ tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
315+ tryCompare(inputField, "placeholderText", lockscreen.placeholderText)
316+ } else {
317+ var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
318+ tryCompare(label, "text", lockscreen.wrongPlaceholderText)
319+ tryCompare(label, "text", lockscreen.placeholderText)
320+ }
321+ }
242322
243323 // wait for animation to finish to not disturb other tests
244324 tryCompare(animation, "running", false)
@@ -246,33 +326,130 @@ Rectangle {
246326
247327 function test_backspace_data() {
248328 return [
249− {tag: "fixed length", pinLength: 4},
250− {tag: "variable length", pinLength: -1}
329+ {tag: "fixed length", minPinLength: 4, maxPinLength: 4},
330+ {tag: "variable undefined length", minPinLength: -1, maxPinLength: -1},
331+ {tag: "variable restricted length", minPinLength: 4, maxPinLength: 8}
251332 ];
252333 }
253334
254335 function test_backspace(data) {
255336 pinPadCheckBox.checked = false
256− pinLengthTextField.text = data.pinLength
257− waitForRendering(lockscreen);
337+ minPinLengthTextField.text = data.minPinLength
338+ maxPinLengthTextField.text = data.maxPinLength
339+ waitForLockscreenReady();
258340
259341 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
260342 var backspaceIcon = findChild(lockscreen, "backspaceIcon");
261343 var pinEntryField = findChild(lockscreen, "pinentryField");
262344
263− compare(pinPadButtonErase.iconName, data.pinLength == -1 ? "" : "erase");
264− compare(backspaceIcon.visible, data.pinLength == -1);
345+ var autoConfirmEnabled = data.minPinLength === data.maxPinLength && data.minPinLength !== -1;
346+ compare(pinPadButtonErase.iconName, autoConfirmEnabled ? "erase" : "");
347+ compare(backspaceIcon.visible, !autoConfirmEnabled);
265348
266349 var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
267350 mouseClick(pinPadButton5, units.gu(1), units.gu(1));
268351 compare(pinEntryField.text, "5");
269352
270− if (data.pinLength == -1) {
353+ if (data.minPinLength !== data.maxPinLength) {
271354 mouseClick(backspaceIcon, units.gu(1), units.gu(1));
272355 } else {
273356 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
274357 }
275358 compare(pinEntryField.text, "");
276359 }
360+
361+ function test_minMaxLength_data() {
362+ return [
363+ {tag: "undefined", minPinLength: -1, maxPinLength: -1},
364+ {tag: "fixed", minPinLength: 4, maxPinLength: 4},
365+ {tag: "variable, limited", minPinLength: 4, maxPinLength: 8},
366+ ];
367+ }
368+
369+ function test_minMaxLength(data) {
370+ pinPadCheckBox.checked = false
371+ minPinLengthTextField.text = data.minPinLength
372+ maxPinLengthTextField.text = data.maxPinLength
373+ waitForLockscreenReady();
374+
375+ var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
376+ var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
377+ var inputField = findChild(lockscreen, "pinentryField")
378+
379+ for (var i = 0; i < 10; i++) {
380+ mouseClick(pinPadButton5, units.gu(1), units.gu(1));
381+
382+ if (data.minPinLength == data.maxPinLength && data.minPinLength != -1) {
383+ // auto confirm mode...
384+ compare(inputField.text.length, (i+1) % data.minPinLength);
385+ } else {
386+ // manual confirm mode
387+ tryCompare(pinPadButtonErase, "enabled", (i+1) >= data.minPinLength)
388+ if (data.maxPinLength == -1) {
389+ compare(inputField.text.length, i+1);
390+ } else {
391+ compare(inputField.text.length, Math.min(data.maxPinLength, i+1));
392+ }
393+
394+ }
395+ }
396+ }
397+
398+ function test_retryDisplay_data() {
399+ return [
400+ {tag: "empty", retryText: "", shown: false},
401+ {tag: "3 retries left", retryText: "3 retries left", shown: true},
402+ ]
403+ }
404+
405+ function test_retryDisplay(data) {
406+ pinPadCheckBox.checked = false
407+ waitForLockscreenReady();
408+
409+ retryCountTextField.text = data.retryText;
410+ var label = findChild(lockscreen, "retryCountLabel")
411+ compare(label.visible, data.shown);
412+ }
413+
414+ function test_infoPopup() {
415+ verify(findChild(root, "infoPopup") === null);
416+ lockscreen.showInfoPopup("foo", "bar");
417+ tryCompareFunction(function() { return findChild(root, "infoPopup") !== null}, true);
418+
419+ var infoPopup = findChild(root, "infoPopup");
420+ compare(infoPopup.title, "foo");
421+ compare(infoPopup.text, "bar");
422+
423+ signalSpy.signalName = "infoPopupConfirmed"
424+ signalSpy.clear();
425+
426+ var okButton = findChild(root, "infoPopupOkButton");
427+ mouseClick(okButton, okButton.width / 2, okButton.height / 2);
428+
429+ tryCompareFunction(function() { return findChild(root, "infoPopup") === null}, true);
430+
431+ tryCompare(signalSpy, "count", 1);
432+ }
433+
434+ function test_infoTextDisplay_data() {
435+ return [
436+ {tag: "empty string", text: "", shown: false},
437+ {tag: "hello world", text: "hello world", shown: true},
438+ ]
439+ }
440+
441+ function test_infoTextDisplay(data) {
442+ pinPadCheckBox.checked = false
443+ waitForLockscreenReady();
444+
445+ infoTextTextField.text = data.text;
446+ var label = findChild(lockscreen, "infoTextLabel")
447+ compare(label.visible, data.shown);
448+ }
449+ }
450+
451+ SignalSpy {
452+ id: signalSpy
453+ target: lockscreen
277454 }
278455}
tests/qmltests/Greeter/tst_Phone.qml+1−17
@@ -33,7 +33,7 @@ Item {
3333 height: parent.height
3434 x: 0; y: 0
3535
36− defaultBackground: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
36+ background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
3737
3838 property int minX: 0
3939
@@ -104,22 +104,6 @@ Item {
104104 tryCompare(LightDM.Infographic, "username", "single")
105105 }
106106
107− function test_background_data() {
108− return [
109− {tag: "set", accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"), expected: "blue.png"},
110− {tag: "unset", accounts: "", expected: "background.jpg"},
111− {tag: "invalid", accounts: "invalid", expected: "background.jpg"},
112− ]
113− }
114−
115− function test_background(data) {
116− var loader = findChild(greeter, "greeterContentLoader")
117− var background = findChild(loader.item, "greeterBackground")
118− AccountsService.backgroundFile = data.accounts
119− tryCompareFunction(function() { return background.source.toString().indexOf(data.expected) !== -1; }, true)
120− tryCompare(background, "status", Image.Ready)
121− }
122−
123107 function test_initial_selected_signal() {
124108 var greeterObj = greeterComponent.createObject(this)
125109 var spy = findChild(greeterObj, "selectedSpy")
tests/qmltests/tst_GreeterShell.qmlnew file+88
@@ -0,0 +1,88 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Michael Terry <michael.terry@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+import QtQuick 2.0
21+import QtTest 1.0
22+import AccountsService 0.1
23+import GSettings 1.0
24+import Unity.Application 0.1
25+import Unity.Test 0.1 as UT
26+import Powerd 0.1
27+import LightDM 0.1 as LightDM
28+
29+import "../../qml"
30+
31+Item {
32+ width: shell.width
33+ height: shell.height
34+
35+ QtObject {
36+ id: applicationArguments
37+
38+ function hasGeometry() {
39+ return false;
40+ }
41+
42+ function width() {
43+ return 0;
44+ }
45+
46+ function height() {
47+ return 0;
48+ }
49+ }
50+
51+ GreeterShell {
52+ id: shell
53+ defaultBackground: Qt.resolvedUrl("../../qml/graphics/phone_background.jpg")
54+ }
55+
56+ UT.UnityTestCase {
57+ name: "GreeterShell"
58+ when: windowShown
59+
60+ function test_wallpaper_data() {
61+ return [
62+ {tag: "both", accounts: "../tests/data/unity/backgrounds/blue.png", gsettings: "../tests/data/unity/backgrounds/red.png", expected: "blue.png"},
63+ {tag: "accounts", accounts: "../tests/data/unity/backgrounds/blue.png", gsettings: "", expected: "blue.png"},
64+ {tag: "gsettings", accounts: "", gsettings: "../tests/data/unity/backgrounds/red.png", expected: "red.png"},
65+ {tag: "none", accounts: "", gsettings: "", expected: shell.defaultBackground},
66+ {tag: "invalid-both", accounts: "invalid", gsettings: "invalid", expected: shell.defaultBackground},
67+ {tag: "invalid-accounts", accounts: "invalid", gsettings: "../tests/data/unity/backgrounds/red.png", expected: shell.defaultBackground},
68+ {tag: "invalid-gsettings", accounts: "../tests/data/unity/backgrounds/blue.png", gsettings: "invalid", expected: "blue.png"},
69+ ]
70+ }
71+
72+ function test_wallpaper(data) {
73+ var greeterBackground = findChild(shell, "greeterBackground")
74+ GSettingsController.setPictureUri(data.gsettings)
75+ AccountsService.backgroundFile = data.accounts
76+ verify(greeterBackground.source.toString().indexOf(data.expected) !== -1)
77+ tryCompare(greeterBackground, "status", Image.Ready)
78+ }
79+
80+ function test_dispatchURLHidesGreeter() {
81+ var greeter = findChild(shell, "greeter")
82+ greeter.showNow()
83+ tryCompare(greeter, "showProgress", 1)
84+ LightDM.URLDispatcher.dispatchURL("application:///notes-app")
85+ tryCompare(greeter, "showProgress", 0)
86+ }
87+ }
88+}
tests/qmltests/tst_Shell.qml+10−73
@@ -23,6 +23,7 @@ import GSettings 1.0
2323import Unity.Application 0.1
2424import Unity.Test 0.1 as UT
2525import Powerd 0.1
26+import SessionManager 0.1
2627
2728import "../../qml"
2829
@@ -48,6 +49,7 @@ Item {
4849
4950 Shell {
5051 id: shell
52+ defaultBackground: Qt.resolvedUrl("../../qml/graphics/phone_background.jpg")
5153 }
5254
5355 UT.UnityTestCase {
@@ -72,9 +74,6 @@ Item {
7274 ok = homeLoader !== null
7375 && homeLoader.item !== undefined;
7476
75− var greeter = findChild(shell, "greeter");
76− ok &= greeter !== null;
77−
7877 var launcherPanel = findChild(shell, "launcherPanel");
7978 ok &= launcherPanel !== null;
8079
@@ -89,17 +88,9 @@ Item {
8988 } while (!ok && attempts <= maxAttempts);
9089
9190 verify(ok);
92−
93− swipeAwayGreeter();
9491 }
9592
9693 function cleanup() {
97− // If a test invoked the greeter, make sure we swipe it away again
98− var greeter = findChild(shell, "greeter");
99− if (greeter.shown) {
100− swipeAwayGreeter();
101− }
102−
10394 // kill all (fake) running apps
10495 killApps(ApplicationManager);
10596
@@ -181,8 +172,6 @@ Item {
181172 }
182173
183174 function test_suspend() {
184− var greeter = findChild(shell, "greeter");
185−
186175 // Launch an app from the launcher
187176 dragLauncherIntoView();
188177 tapOnAppIconInLauncher();
@@ -196,40 +185,27 @@ Item {
196185
197186 // Try to suspend while proximity is engaged...
198187 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
199− tryCompare(greeter, "showProgress", 0);
188+ tryCompare(SessionManager, "active", true);
200189
201190 // Now really suspend
202191 print("suspending")
203192 Powerd.displayPowerStateChange(Powerd.Off, 0);
204193 print("done suspending")
205− tryCompare(greeter, "showProgress", 1);
194+ tryCompare(SessionManager, "active", false);
206195
207196 tryCompare(ApplicationManager, "suspended", true);
208197 compare(mainApp.state, ApplicationInfo.Suspended);
209198
210199 // And wake up
211200 Powerd.displayPowerStateChange(Powerd.On, 0);
212− tryCompare(greeter, "showProgress", 1);
213201
214− // Swipe away greeter to focus app
215− swipeAwayGreeter();
202+ // Activate session to focus app
203+ SessionManager.active = true;
216204 tryCompare(ApplicationManager, "suspended", false);
217205 compare(mainApp.state, ApplicationInfo.Running);
218206 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
219207 }
220208
221− function swipeAwayGreeter() {
222− var greeter = findChild(shell, "greeter");
223− tryCompare(greeter, "showProgress", 1);
224−
225− var touchX = shell.width - (shell.edgeSize / 2);
226− var touchY = shell.height / 2;
227− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
228−
229− // wait until the animation has finished
230− tryCompare(greeter, "showProgress", 0);
231− }
232−
233209 /*
234210 Perform a right-edge drag when the Dash is being show and there are
235211 no running/minimized apps to be restored.
@@ -475,33 +451,16 @@ Item {
475451 } while (!isStill);
476452 }
477453
478− function test_background_data() {
479− return [
480− {tag: "red", url: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), expectedUrl: "tests/data/unity/backgrounds/red.png"},
481− {tag: "blue", url: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), expectedUrl: "tests/data/unity/backgrounds/blue.png"},
482− {tag: "invalid", url: "invalid", expectedUrl: shell.defaultBackground},
483− {tag: "empty", url: "", expectedUrl: shell.defaultBackground}
484− ]
485− }
486−
487454 function test_DashShown_data() {
488455 return [
489− {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},
490− {tag: "under greeter", greeter: true, app: false, launcher: false, indicators: false, expectedShown: false},
491− {tag: "under app", greeter: false, app: true, launcher: false, indicators: false, expectedShown: false},
492− {tag: "under launcher", greeter: false, app: false, launcher: true, indicators: false, expectedShown: true},
493− {tag: "under indicators", greeter: false, app: false, launcher: false, indicators: true, expectedShown: true},
456+ {tag: "in focus", app: false, launcher: false, indicators: false, expectedShown: true},
457+ {tag: "under app", app: true, launcher: false, indicators: false, expectedShown: false},
458+ {tag: "under launcher", app: false, launcher: true, indicators: false, expectedShown: true},
459+ {tag: "under indicators", app: false, launcher: false, indicators: true, expectedShown: true},
494460 ]
495461 }
496462
497463 function test_DashShown(data) {
498− if (data.greeter) {
499− // Swipe the greeter in
500− var greeter = findChild(shell, "greeter");
501− Powerd.displayPowerStateChange(Powerd.Off, 0);
502− tryCompare(greeter, "showProgress", 1);
503− }
504−
505464 if (data.app) {
506465 dragLauncherIntoView();
507466 tapOnAppIconInLauncher();
@@ -531,17 +490,6 @@ Item {
531490 tryCompare(searchIndicator, "opacity", 0);
532491 }
533492
534− function test_searchIndicatorHidesOnGreeterShown() {
535− var searchIndicator = findChild(shell, "container")
536− var greeter = findChild(shell, "greeter");
537−
538− tryCompare(searchIndicator, "opacity", 1)
539−
540− greeter.show()
541− tryCompare(greeter, "shown", true)
542− tryCompare(searchIndicator, "opacity", 0)
543− }
544−
545493 function test_searchIndicatorHideOnPreviewShown() {
546494 var searchIndicator = findChild(shell, "container");
547495 var dashContent = findChild(shell, "dashContent");
@@ -554,16 +502,5 @@ Item {
554502
555503 tryCompare(searchIndicator, "opacity", 0);
556504 }
557−
558− function test_focusRequestedHidesGreeter() {
559− var greeter = findChild(shell, "greeter")
560−
561− greeter.show()
562− tryCompare(greeter, "showProgress", 1)
563−
564− ApplicationManager.focusRequested("notes-app")
565− tryCompare(greeter, "showProgress", 0)
566− waitUntilApplicationWindowIsFullyVisible()
567− }
568505 }
569506}
tools/unlock-device+10−18
@@ -17,24 +17,16 @@ while getopts s:w: opt; do
1717 esac
1818done
1919
20−UNLOCK_SCRIPT='
21−import dbus, logging;
22−from unity8 import process_helpers as helpers;
23−logging.basicConfig(level=logging.INFO);
24−bus = dbus.SystemBus().get_object("com.canonical.powerd", "/com/canonical/powerd");
25−cookie = bus.requestSysState("unlock-device-hold", 1, dbus_interface="com.canonical.powerd");
26−helpers.restart_unity_with_testability();
27−bus.clearSysState(cookie, dbus_interface="com.canonical.powerd");
28−helpers.unlock_unity()
29−'
20+LIGHTDM_CONFIG="
21+[SeatDefaults]
22+autologin-in-background=false
23+"
24+LIGHTDM_CONFIG_FILE="/etc/lightdm/lightdm.conf.d/99-unity8-greeter-unlock.conf"
25+
26+adb shell "echo '$LIGHTDM_CONFIG' > $LIGHTDM_CONFIG_FILE"
3027
3128eval "$WAIT_COMMAND"
3229
33−UNLOCK_OUTPUT=$(adb shell "sudo -u phablet -i python3 -c '$UNLOCK_SCRIPT'" 2>&1)
34−if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
35− echo "I: Unlock passed"
36− exit 0
37−else
38− echo "I: Unlock failed, script output: '$UNLOCK_OUTPUT'"
39− exit 1
40−fi
30+adb shell "rm -f $LIGHTDM_CONFIG_FILE"
31+
32+exit 0