on xenial help sign in
Merge trunk.
commit6f8457e3e8c93bae008a54730f94e635ef7572bb authorMichał Sawicz <michal.sawicz@canonical.com> dateWed 4 Jun 2014, 13:12 (12 years ago) parent401fc446efb4eb
156 files changed+2833−1018
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+103 data/unity8-greeter.desktop.in+5 debian/changelog+75 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+5−5 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−4 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+103
@@ -0,0 +1,103 @@
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 "$CMD_PID" ]; then
28+ kill "$CMD_PID"
29+ fi
30+ exit 0
31+}
32+
33+set_greeter_var()
34+{
35+ # Only use this after upstart has been started, otherwise multiple dbus
36+ # daemons are spawned.
37+ export "$1=$2"
38+ gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "{'$1': '$2'}"
39+ if [ -n "$INIT_PID" ]; then
40+ initctl set-env --global "$1=$2"
41+ fi
42+}
43+
44+SUB_SOCKET=$XDG_RUNTIME_DIR/mir_socket
45+rm -f $SUB_SOCKET # clear socket in case we were hard shut down
46+
47+# If touch session script (which sets up grid units and mir variables) is available, use it
48+TOUCH_WRAPPER=
49+if [ -x /usr/bin/ubuntu-touch-session ]; then
50+ TOUCH_WRAPPER=/usr/bin/ubuntu-touch-session
51+fi
52+
53+# Pretend that we're in an ubuntu-touch session, so that indicator-network
54+# will start.
55+export DESKTOP_SESSION=ubuntu-touch
56+
57+# Normal unity8 sessions are entirely driven by Upstart. But greeters
58+# are special. They need access to the file descriptors that lightdm
59+# creates for them and don't want to start all the services that a normal
60+# session would. So it's inconvenient to live within an upstart session.
61+# But... we still want to use Upstart for some services. So launch here.
62+USC_SOCKET=$MIR_SOCKET MIR_SOCKET=$SUB_SOCKET $TOUCH_WRAPPER /sbin/init --user --no-startup-event &
63+INIT_PID=$!
64+while [ ! -e "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session" ]; do sleep 0.1; done
65+export UPSTART_SESSION=$(/sbin/initctl list-sessions | grep "^$INIT_PID " | cut -d' ' -f2)
66+
67+/sbin/initctl start dbus
68+export DBUS_SESSION_BUS_ADDRESS="$(/sbin/initctl get-env DBUS_SESSION_BUS_ADDRESS)"
69+
70+# We disable ofono using pulse. It causes problems with racing with the user's
71+# pulse. We need to come up with a better long-term fix for this, because we
72+# eventually need the greeter to play ringtones for users that aren't logged in.
73+set_greeter_var PA_DISABLED 1
74+
75+# Start any pre-greeter tasks if needed (like wizard)
76+/sbin/initctl emit unity8-greeter-starting
77+
78+# Define language here for phone if available. When phone user switches their
79+# language, they expect that to affect the greeter too. But the user doesn't
80+# have permission to switch system language, only their own. So we notice if
81+# the phablet user exists and use their language if so. TODO: talk to design
82+# about whether we should switch language on fly as users are selected (this
83+# is very hard to do technically).
84+#
85+# Do this after unity8-greeter-starting, in case a customization upstart job
86+# changes language.
87+if [ "$(id -u phablet 2>/dev/null)" = "32011" ]; then
88+ 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)
89+ if [ -n "$USER_LANG" ]; then
90+ set_greeter_var LANGUAGE "$USER_LANG"
91+ fi
92+ 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)
93+ if [ -n "$USER_LOCALE" ]; then
94+ set_greeter_var LANG "$USER_LOCALE"
95+ set_greeter_var LC_ALL "$USER_LOCALE"
96+ fi
97+fi
98+
99+# And finally actually start the greeter
100+exec env MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
101+CMD_PID=$!
102+wait $CMD_PID
103+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+75
@@ -1,3 +1,78 @@
1+unity8 (7.87+14.10.20140603.1-0ubuntu1) utopic; urgency=low
2+
3+ [ CI bot ]
4+ * Resync trunk
5+
6+ [ Michał Sawicz ]
7+ * Move env setup past session init in greeter wrapper. (LP: #1325882)
8+
9+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Jun 2014 10:33:11 +0000
10+
11+unity8 (7.87+14.10.20140530.1-0ubuntu3) utopic; urgency=medium
12+
13+ * no change rebuild
14+
15+ -- Oliver Grawert <ogra@ubuntu.com> Mon, 02 Jun 2014 16:19:10 +0200
16+
17+unity8 (7.87+14.10.20140530.1-0ubuntu2) utopic; urgency=medium
18+
19+ * drop dbus-x11 dependency of unity8-greeter, it makes us end up with
20+ multiple session dbus daemons which breaks many AP tests in the lab
21+
22+ -- Oliver Grawert <ogra@ubuntu.com> Mon, 02 Jun 2014 14:50:59 +0200
23+
24+unity8 (7.87+14.10.20140530.1-0ubuntu1) utopic; urgency=medium
25+
26+ [ Michael Terry ]
27+ * Bump version for Breaks due to unity8-greeter
28+ * In split mode, determine whether the application identifiers in
29+ AccountsService are click packages or not, so we know the correct
30+ url prefix to use.
31+ * Start logrotate in the greeter's session.
32+
33+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 30 May 2014 09:29:15 +0000
34+
35+unity8 (7.86+14.10.20140527-0ubuntu1) utopic; urgency=low
36+
37+ [ Andrea Cimitan ]
38+ * Passes to make tryCommand -qmljsdebugger=port:3768 to enable
39+ debug/profiling test apps
40+ * Fixes carousel shadow
41+
42+ [ Michał Sawicz ]
43+ * Use dpkg-architecture, not gcc, to determine the machine triplet.
44+
45+ [ Ying-Chun Liu ]
46+ * Fix ZoomableImage test failure. (LP: #1317254)
47+
48+ [ Albert Astals ]
49+ * We don't need iconutils in this mock
50+ * Don't reserve space for mascot if no mascot is specified (LP:
51+ #1319343)
52+ * CardHeader is no more, remove stale line in CMakeLists.txt
53+ * GenericScopeViewTest: Wait a bit more Otherwise sometimes we end
54+ getting up the wrong delegate (maybe one that will be garbage
55+ collected?) (LP: #1322279)
56+ * Fix crash in organicgridtest
57+
58+ [ Daniel d'Andrada ]
59+ * Remove Shell's underlay background image as it cannot be seen
60+ anymore Now that the Dash has its own, opaque, background, the
61+ underlay's background image can no longer be seen. So it's just a
62+ waste of resources to have it.
63+
64+ [ Michael Terry ]
65+ * Use the same animation when dismissing a greeter slide from the
66+ launcher as from a normal greeter drag. (LP: #1316513)
67+
68+ [ Michael Zanetti ]
69+ * enhance lockscreen add a retry indication label (e.g. 3 attempts
70+ left). add an additional label (e.g. phone number for multi sim).
71+ add a infoPopup (e.g. to display a warning for last retry). add min
72+ and max limit values. add tests for the above (LP: #1302050)
73+
74+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 May 2014 07:47:11 +0000
75+
176unity8 (7.86+14.10.20140522-0ubuntu1) utopic; urgency=low
277
378 [ 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+5−5
@@ -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-05-30 16:00+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-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
@@ -92,7 +92,7 @@ msgstr "Mostra'n menys"
9292
9393#: qml/Dash/DashApps.qml:37
9494msgid "Recent"
95−msgstr ""
95+msgstr "Recents"
9696
9797#: qml/Dash/PreviewListView.qml:52
9898#, qt-format
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-04 06:34+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,28 +21,19 @@ 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"
3231import "Notifications"
3332import Unity.Notifications 1.0 as NotificationBackend
3433
35−FocusScope {
34+BasicShell {
3635 id: shell
3736
38− // this is only here to select the width / height of the window if not running fullscreen
39− property bool tablet: false
40− width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
41− height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
42−
43− property real edgeSize: units.gu(2)
44− property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
45− property url background
4637 readonly property real panelHeight: panel.panelHeight
4738
4839 property bool dashShown: dash.shown
@@ -78,17 +69,7 @@ FocusScope {
7869 id: backgroundSettings
7970 schema.id: "org.gnome.desktop.background"
8071 }
81− property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
82− onGSettingsPictureChanged: {
83− shell.background = gSettingsPicture
84− }
85−
86− VolumeControl {
87− id: volumeControl
88− }
89−
90− Keys.onVolumeUpPressed: volumeControl.volumeUp()
91− Keys.onVolumeDownPressed: volumeControl.volumeDown()
72+ backgroundSource: backgroundSettings.pictureUri
9273
9374 Item {
9475 id: underlayClipper
@@ -117,15 +98,6 @@ FocusScope {
11798 // through the translucent parts of the shell surface.
11899 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
119100
120− CrossFadeImage {
121− id: backgroundImage
122− objectName: "backgroundImage"
123−
124− anchors.fill: parent
125− source: shell.background
126− fillMode: Image.PreserveAspectCrop
127− }
128−
129101 Rectangle {
130102 anchors.fill: parent
131103 color: "black"
@@ -144,10 +116,9 @@ FocusScope {
144116 id: dash
145117 objectName: "dash"
146118
147− available: !greeter.shown && !lockscreen.shown
148119 hides: [stages, launcher, panel.indicators]
149− shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
150− enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
120+ shown: disappearingAnimationProgress !== 1.0
121+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
151122
152123 anchors {
153124 fill: parent
@@ -235,7 +206,6 @@ FocusScope {
235206 shown = true;
236207 panel.indicators.hide();
237208 edgeDemo.stopDemo();
238− greeter.hide();
239209 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
240210 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
241211 }
@@ -276,6 +246,12 @@ FocusScope {
276246 }
277247 }
278248
249+ Binding {
250+ target: ApplicationManager
251+ property: "suspended"
252+ value: !SessionManager.active
253+ }
254+
279255 Loader {
280256 id: applicationsDisplayLoader
281257 anchors.fill: parent
@@ -300,128 +276,9 @@ FocusScope {
300276 }
301277 }
302278
303− Lockscreen {
304− id: lockscreen
305− objectName: "lockscreen"
306−
307− readonly property int backgroundTopMargin: -panel.panelHeight
308−
309− hides: [launcher, panel.indicators]
310− shown: false
311− enabled: true
312− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
313− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
314− y: panel.panelHeight
315− x: required ? 0 : - width
316− width: parent.width
317− height: parent.height - panel.panelHeight
318− background: shell.background
319− pinLength: 4
320−
321− onEntered: LightDM.Greeter.respond(passphrase);
322− onCancel: greeter.show()
323−
324− Component.onCompleted: {
325− if (LightDM.Users.count == 1) {
326− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
327− }
328− }
329− }
330−
331− Connections {
332− target: LightDM.Greeter
333−
334− onShowPrompt: {
335− if (LightDM.Users.count == 1) {
336− // TODO: There's no better way for now to determine if its a PIN or a passphrase.
337− if (text == "PIN") {
338− lockscreen.alphaNumeric = false
339− } else {
340− lockscreen.alphaNumeric = true
341− }
342− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text);
343− lockscreen.show();
344− }
345− }
346−
347− onAuthenticationComplete: {
348− if (LightDM.Greeter.promptless) {
349− return;
350− }
351− if (LightDM.Greeter.authenticated) {
352− lockscreen.hide();
353− } else {
354− lockscreen.clear(true);
355− }
356− }
357− }
358−
359− Rectangle {
360− anchors.fill: parent
361− color: "black"
362− opacity: greeterWrapper.showProgress * 0.8
363− }
364−
365− Item {
366− // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
367− id: greeterWrapper
368− x: launcher.progress
369− y: panel.panelHeight
370− width: parent.width
371− height: parent.height - panel.panelHeight
372−
373− Behavior on x {SmoothedAnimation{velocity: 600}}
374−
375− readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
376−
377− Greeter {
378− id: greeter
379− objectName: "greeter"
380−
381− available: true
382− hides: [launcher, panel.indicators]
383− shown: true
384−
385− defaultBackground: shell.background
386−
387− width: parent.width
388− height: parent.height
389−
390− dragHandleWidth: shell.edgeSize
391−
392− onShownChanged: {
393− if (shown) {
394− lockscreen.reset();
395− // If there is only one user, we start authenticating with that one here.
396− // If there are more users, the Greeter will handle that
397− if (LightDM.Users.count == 1) {
398− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
399− }
400− greeter.forceActiveFocus();
401− }
402− }
403−
404− onUnlocked: greeter.hide()
405− onSelected: {
406− // Update launcher items for new user
407− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
408− AccountsService.user = user;
409− LauncherModel.setUser(user);
410− }
411−
412− onTease: launcher.tease()
413−
414− Binding {
415− target: ApplicationManager
416− property: "suspended"
417− value: greeter.shown && greeterWrapper.showProgress == 1
418− }
419− }
420− }
421−
422279 InputFilterArea {
423280 anchors.fill: parent
424− blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
281+ blockInput: ApplicationManager.focusedApplicationId.length === 0 || launcher.shown
425282 || panel.indicators.shown
426283 }
427284
@@ -433,7 +290,7 @@ FocusScope {
433290 // We ignore any display-off signals when the proximity sensor
434291 // is active. This usually indicates something like a phone call.
435292 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
436− greeter.showNow();
293+ SessionManager.lock();
437294 }
438295
439296 // No reason to chew demo CPU when user isn't watching
@@ -445,17 +302,12 @@ FocusScope {
445302 }
446303 }
447304
448− function showHome() {
449− var animate = !greeter.shown && !stages.shown
450− greeter.hide()
305+ function showHome(fromLauncher) {
306+ var animate = fromLauncher && !stages.shown
451307 dash.setCurrentScope("clickscope", animate, false)
452308 stages.hide()
453309 }
454310
455− function hideIndicatorMenu(delay) {
456− panel.hideIndicatorMenu(delay);
457− }
458−
459311 Item {
460312 id: overlay
461313
@@ -472,8 +324,8 @@ FocusScope {
472324 }
473325 property string focusedAppId: ApplicationManager.focusedApplicationId
474326 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
475− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
476− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
327+ fullscreenMode: focusedApplication && stages.fullscreen
328+ searchVisible: dash.shown && dash.searchable
477329
478330 InputFilterArea {
479331 anchors {
@@ -511,8 +363,9 @@ FocusScope {
511363 if (edgeDemo.running)
512364 return;
513365
514− showHome()
366+ showHome(true)
515367 }
368+
516369 onDash: {
517370 if (stages.shown && !stages.overlayMode) {
518371 if (!stages.locked) {
@@ -520,10 +373,6 @@ FocusScope {
520373 launcher.hide();
521374 }
522375 }
523− if (greeter.shown) {
524− greeter.hideRight();
525− launcher.hide();
526− }
527376 }
528377 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
529378 onLauncherApplicationSelected: {
@@ -540,7 +389,7 @@ FocusScope {
540389 Rectangle {
541390 id: modalNotificationBackground
542391
543− visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
392+ visible: notifications.useModal && (notifications.state == "narrow")
544393 color: "#000000"
545394 anchors.fill: parent
546395 opacity: 0.5
@@ -587,10 +436,17 @@ FocusScope {
587436 blockInput: height > 0
588437 }
589438 }
590− }
591439
592− focus: true
593− onFocusChanged: if (!focus) forceActiveFocus();
440+ Connections {
441+ target: SessionManager
442+ onActiveChanged: {
443+ if (!SessionManager.active) {
444+ launcher.hide()
445+ panel.indicators.hide()
446+ }
447+ }
448+ }
449+ }
594450
595451 InputFilterArea {
596452 anchors {
@@ -612,12 +468,6 @@ FocusScope {
612468 blockInput: true
613469 }
614470
615− Binding {
616− target: i18n
617− property: "domain"
618− value: "unity8"
619− }
620−
621471 OSKController {
622472 anchors.topMargin: panel.panelHeight
623473 anchors.fill: parent // as needs to know the geometry of the shell
@@ -649,7 +499,6 @@ FocusScope {
649499
650500 EdgeDemo {
651501 id: edgeDemo
652− greeter: greeter
653502 launcher: launcher
654503 dash: dash
655504 indicators: panel.indicators
@@ -658,6 +507,6 @@ FocusScope {
658507
659508 Connections {
660509 target: SessionBroadcast
661− onShowHome: showHome()
510+ onShowHome: showHome(false)
662511 }
663512}
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,25 +15,37 @@ 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 CachingNetworkManagerFactory.cpp
2323 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
24−)
24+ )
25+add_executable(${SHELL_APP} ${COMMON_SRCS})
26+add_executable(${GREETER_APP} ${COMMON_SRCS})
27+
28+set_target_properties(${SHELL_APP} PROPERTIES
29+ COMPILE_DEFINITIONS "UNITY8_GREETER=0"
30+ )
31+set_target_properties(${GREETER_APP} PROPERTIES
32+ COMPILE_DEFINITIONS "UNITY8_GREETER=1"
33+ )
2534
2635qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
36+qt5_use_modules(${GREETER_APP} Gui Qml Quick Test)
2737pkg_check_modules(XCB REQUIRED xcb)
2838
2939if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
3040 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
41+ set_target_properties(${GREETER_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
3142
3243 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
44+ target_link_libraries(${GREETER_APP} ${XCB_LDFLAGS})
3345endif()
3446
3547# install binaries
36−install(TARGETS ${SHELL_APP}
48+install(TARGETS ${SHELL_APP} ${GREETER_APP}
3749 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
3850 )
3951
src/main.cpp+18−4
@@ -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("unity8");
46+ QGuiApplication::setApplicationName(UNITY8_GREETER ? "unity8-greeter" : "unity8");
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());
@@ -163,7 +164,7 @@ Load the testability driver");
163164 view->engine()->setNetworkAccessManagerFactory(managerFactory);
164165
165166 view->setSource(source);
166− view->setColor("transparent");
167+ view->setColor(Qt::transparent);
167168
168169 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {
169170 view->showFullScreen();
@@ -171,6 +172,19 @@ Load the testability driver");
171172 view->show();
172173 }
173174
175+#if UNITY8_GREETER
176+ if (isUbuntuMirServer) {
177+ // Add alpha to surface, so that the greeter can bleed through
178+ QSurfaceFormat format;
179+ format.setAlphaBufferSize(8);
180+ view->setFormat(format);
181+ }
182+
183+ if (!QProcess::startDetached("/sbin/initctl emit --no-wait unity8-greeter-started")) {
184+ qDebug() << "Unable to send unity8-greeter-started event to Upstart";
185+ }
186+#endif
187+
174188 int result = application->exec();
175189
176190 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