on xenial help sign in
Merge trunk.
6f8457eMichał Sawicz
CMakeLists.txt+1
@@ -96,6 +96,7 @@ include(autopilot)
96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
96declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
97
97
98set(SHELL_APP unity8)
98set(SHELL_APP unity8)
99+set(GREETER_APP unity8-greeter)
99set(SCOPE_TOOL unity-scope-tool)
100set(SCOPE_TOOL unity-scope-tool)
100
101
101include_directories(
102include_directories(
cmake/modules/QmlTest.cmake+4−4
@@ -51,7 +51,7 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
51
51
52 set(qmlscene_command
52 set(qmlscene_command
53 env ${qmltest_ENVIRONMENT}
53 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
55 ${qmlscene_imports}
55 ${qmlscene_imports}
56 )
56 )
57 add_custom_target(${qmlscene_TARGET} ${qmlscene_command})
57 add_custom_target(${qmlscene_TARGET} ${qmlscene_command})
@@ -111,9 +111,9 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
111 -o -,txt
111 -o -,txt
112 ${function_ARGS}
112 ${function_ARGS}
113 )
113 )
114− find_program( HAVE_GCC gcc )
114+ find_program(DPKG dpkg-architecture)
115− if (NOT ${HAVE_GCC} STREQUAL "")
115+ if(DPKG)
116− exec_program( gcc ARGS "-dumpmachine" OUTPUT_VARIABLE ARCH_TRIPLET )
116+ exec_program(${DPKG} ARGS "-qDEB_BUILD_MULTIARCH" OUTPUT_VARIABLE ARCH_TRIPLET )
117 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
117 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
118 endif()
118 endif()
119 set(qmltest_xvfb_command
119 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 @@
1# generate desktop file
1# generate desktop file
2configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
2configure_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)
3
4
4# install desktop files
5# install desktop files
5install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
6install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
6 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
7 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
7 )
8 )
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+
1unity8 (7.86+14.10.20140522-0ubuntu1) utopic; urgency=low
76unity8 (7.86+14.10.20140522-0ubuntu1) utopic; urgency=low
2
77
3 [ Albert Astals ]
78 [ Albert Astals ]
debian/control+15
@@ -14,6 +14,7 @@ Build-Depends: cmake,
14 libglib2.0-dev,
14 libglib2.0-dev,
15 libgsettings-qt-dev,
15 libgsettings-qt-dev,
16 libhud-client2-dev,
16 libhud-client2-dev,
17+ liblightdm-qt5-3-dev (>= 1.11.1),
17 libpulse-dev,
18 libpulse-dev,
18 libqmenumodel-dev (>= 0.2.7),
19 libqmenumodel-dev (>= 0.2.7),
19 libqt5xmlpatterns5-dev,
20 libqt5xmlpatterns5-dev,
@@ -79,6 +80,7 @@ Depends: gsettings-desktop-schemas,
79 qml-module-qtquick-xmllistmodel,
80 qml-module-qtquick-xmllistmodel,
80 qtdeclarative5-gsettings1.0,
81 qtdeclarative5-gsettings1.0,
81 qtdeclarative5-ubuntu-settings-components,
82 qtdeclarative5-ubuntu-settings-components,
83+ unity-greeter-session-broadcast,
82 unity-launcher-impl-3,
84 unity-launcher-impl-3,
83 unity8-common (= ${source:Version}),
85 unity8-common (= ${source:Version}),
84 unity8-private (= ${binary:Version}),
86 unity8-private (= ${binary:Version}),
@@ -94,6 +96,18 @@ Replaces: ubuntu-touch-session (<< 0.82~),
94Description: Unity 8 shell
96Description: Unity 8 shell
95 The Unity 8 shell is the primary user interface for Ubuntu devices.
97 The Unity 8 shell is the primary user interface for Ubuntu devices.
96
98
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+
97Package: unity8-common
111Package: unity8-common
98Architecture: all
112Architecture: all
99Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
113Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
@@ -132,6 +146,7 @@ Depends: gir1.2-glib-2.0,
132 ubuntu-ui-toolkit-autopilot,
146 ubuntu-ui-toolkit-autopilot,
133 unity8 (= ${source:Version}),
147 unity8 (= ${source:Version}),
134 unity8-fake-env (= ${source:Version}),
148 unity8-fake-env (= ${source:Version}),
149+ unity8-greeter (= ${source:Version}),
135 url-dispatcher-tools,
150 url-dispatcher-tools,
136 ${misc:Depends},
151 ${misc:Depends},
137 ${python3:Depends},
152 ${python3:Depends},
debian/copyright+1−1
@@ -22,7 +22,7 @@ License: GPL-3
22 On Debian systems, the full text of the GNU General Public License
22 On Debian systems, the full text of the GNU General Public License
23 version 3 can be found in the file /usr/share/common-licenses/GPL-3.
23 version 3 can be found in the file /usr/share/common-licenses/GPL-3.
24
24
25−Files: tests/mocks/LightDM/UsersModel.h tests/mocks/LightDM/Greeter.h
25+Files: tests/mocks/liblightdm/UsersModel.h tests/mocks/liblightdm/Greeter.h
26Copyright: 2013 Canonical Ltd.
26Copyright: 2013 Canonical Ltd.
27 2010-2011 David Edmundson
27 2010-2011 David Edmundson
28 2010-2011 Robert Ancell
28 2010-2011 Robert Ancell
debian/rules−4
@@ -34,7 +34,3 @@ override_dh_install:
34# use private lib directories
34# use private lib directories
35override_dh_makeshlibs:
35override_dh_makeshlibs:
36 dh_makeshlibs -Nunity8-private -Nunity8-fake-env
36 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
4usr/lib/*/unity8/qml/LightDM
4usr/lib/*/unity8/qml/LightDM
5usr/lib/*/unity8/qml/Powerd
5usr/lib/*/unity8/qml/Powerd
6usr/lib/*/unity8/qml/SessionBroadcast
6usr/lib/*/unity8/qml/SessionBroadcast
7+usr/lib/*/unity8/qml/SessionManager
7usr/lib/*/unity8/qml/Ubuntu
8usr/lib/*/unity8/qml/Ubuntu
8usr/lib/*/unity8/qml/Unity
9usr/lib/*/unity8/qml/Unity
9usr/lib/*/unity8/qml/Utils
10usr/lib/*/unity8/qml/Utils
debian/unity8.install−1
@@ -1,7 +1,6 @@
1data/unity8.conf usr/share/upstart/sessions/
1data/unity8.conf usr/share/upstart/sessions/
2usr/bin/unity8
2usr/bin/unity8
3usr/share/applications/unity8.desktop
3usr/share/applications/unity8.desktop
4−usr/share/unity8/Greeter
5usr/share/unity8/Hud
4usr/share/unity8/Hud
6usr/share/unity8/Launcher
5usr/share/unity8/Launcher
7usr/share/unity8/Panel
6usr/share/unity8/Panel
plugins/AccountsService/AccountsService.cpp+34−11
@@ -24,7 +24,6 @@
24AccountsService::AccountsService(QObject* parent)
24AccountsService::AccountsService(QObject* parent)
25 : QObject(parent),
25 : QObject(parent),
26 m_service(new AccountsServiceDBusAdaptor(this)),
26 m_service(new AccountsServiceDBusAdaptor(this)),
27− m_user(qgetenv("USER")),
28 m_demoEdges(false),
27 m_demoEdges(false),
29 m_statsWelcomeScreen(false)
28 m_statsWelcomeScreen(false)
30{
29{
@@ -32,6 +31,9 @@ AccountsService::AccountsService(QObject* parent)
32 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
31 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
33 connect(m_service, SIGNAL(maybeChanged(const QString &)),
32 connect(m_service, SIGNAL(maybeChanged(const QString &)),
34 this, SLOT(maybeChanged(const QString &)));
33 this, SLOT(maybeChanged(const QString &)));
34+
35+ setUser(qgetenv("USER"));
36+ updateDemoEdgesForCurrentUser();
35}
37}
36
38
37QString AccountsService::user() const
39QString AccountsService::user() const
@@ -57,9 +59,22 @@ bool AccountsService::demoEdges() const
57void AccountsService::setDemoEdges(bool demoEdges)
59void AccountsService::setDemoEdges(bool demoEdges)
58{
60{
59 m_demoEdges = demoEdges;
61 m_demoEdges = demoEdges;
62+ Q_EMIT demoEdgesChanged();
60 m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges", demoEdges);
63 m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges", demoEdges);
61}
64}
62
65
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+
63QString AccountsService::backgroundFile() const
78QString AccountsService::backgroundFile() const
64{
79{
65 return m_backgroundFile;
80 return m_backgroundFile;
@@ -79,6 +94,15 @@ void AccountsService::updateDemoEdges()
79 }
94 }
80}
95}
81
96
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+
82void AccountsService::updateBackgroundFile()
106void AccountsService::updateBackgroundFile()
83{
107{
84 auto backgroundFile = m_service->getUserProperty(m_user, "org.freedesktop.Accounts.User", "BackgroundFile").toString();
108 auto backgroundFile = m_service->getUserProperty(m_user, "org.freedesktop.Accounts.User", "BackgroundFile").toString();
@@ -99,13 +123,14 @@ void AccountsService::updateStatsWelcomeScreen()
99
123
100void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
124void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
101{
125{
102− if (m_user != user) {
103− return;
104− }
105−
106 if (interface == "com.canonical.unity.AccountsService") {
126 if (interface == "com.canonical.unity.AccountsService") {
107 if (changed.contains("demo-edges")) {
127 if (changed.contains("demo-edges")) {
128+ if (qgetenv("USER") == user) {
129+ updateDemoEdgesForCurrentUser();
130+ }
131+ if (m_user == user) {
108− updateDemoEdges();
132+ updateDemoEdges();
133+ }
109 }
134 }
110 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
135 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
111 if (changed.contains("StatsWelcomeScreen")) {
136 if (changed.contains("StatsWelcomeScreen")) {
@@ -116,10 +141,8 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
116
141
117void AccountsService::maybeChanged(const QString &user)
142void AccountsService::maybeChanged(const QString &user)
118{
143{
119− if (m_user != user) {
144+ if (m_user == user) {
120− return;
145+ // Standard properties might have changed
146+ updateBackgroundFile();
121 }
147 }
122−
123− // Standard properties might have changed
124− updateBackgroundFile();
125}
148}
plugins/AccountsService/AccountsService.h+9
@@ -35,6 +35,10 @@ class AccountsService: public QObject
35 READ demoEdges
35 READ demoEdges
36 WRITE setDemoEdges
36 WRITE setDemoEdges
37 NOTIFY demoEdgesChanged)
37 NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (bool demoEdgesForCurrentUser
39+ READ demoEdgesForCurrentUser
40+ WRITE setDemoEdgesForCurrentUser
41+ NOTIFY demoEdgesForCurrentUserChanged)
38 Q_PROPERTY (QString backgroundFile
42 Q_PROPERTY (QString backgroundFile
39 READ backgroundFile
43 READ backgroundFile
40 NOTIFY backgroundFileChanged)
44 NOTIFY backgroundFileChanged)
@@ -49,12 +53,15 @@ public:
49 void setUser(const QString &user);
53 void setUser(const QString &user);
50 bool demoEdges() const;
54 bool demoEdges() const;
51 void setDemoEdges(bool demoEdges);
55 void setDemoEdges(bool demoEdges);
56+ bool demoEdgesForCurrentUser() const;
57+ void setDemoEdgesForCurrentUser(bool demoEdgesForCurrentUser);
52 QString backgroundFile() const;
58 QString backgroundFile() const;
53 bool statsWelcomeScreen() const;
59 bool statsWelcomeScreen() const;
54
60
55Q_SIGNALS:
61Q_SIGNALS:
56 void userChanged();
62 void userChanged();
57 void demoEdgesChanged();
63 void demoEdgesChanged();
64+ void demoEdgesForCurrentUserChanged();
58 void backgroundFileChanged();
65 void backgroundFileChanged();
59 void statsWelcomeScreenChanged();
66 void statsWelcomeScreenChanged();
60
67
@@ -64,12 +71,14 @@ private Q_SLOTS:
64
71
65private:
72private:
66 void updateDemoEdges();
73 void updateDemoEdges();
74+ void updateDemoEdgesForCurrentUser();
67 void updateBackgroundFile();
75 void updateBackgroundFile();
68 void updateStatsWelcomeScreen();
76 void updateStatsWelcomeScreen();
69
77
70 AccountsServiceDBusAdaptor *m_service;
78 AccountsServiceDBusAdaptor *m_service;
71 QString m_user;
79 QString m_user;
72 bool m_demoEdges;
80 bool m_demoEdges;
81+ bool m_demoEdgesForCurrentUser;
73 QString m_backgroundFile;
82 QString m_backgroundFile;
74 bool m_statsWelcomeScreen;
83 bool m_statsWelcomeScreen;
75};
84};
plugins/CMakeLists.txt+1
@@ -17,6 +17,7 @@ add_subdirectory(LightDM)
17add_subdirectory(Dash)
17add_subdirectory(Dash)
18add_subdirectory(Powerd)
18add_subdirectory(Powerd)
19add_subdirectory(SessionBroadcast)
19add_subdirectory(SessionBroadcast)
20+add_subdirectory(SessionManager)
20add_subdirectory(Ubuntu)
21add_subdirectory(Ubuntu)
21add_subdirectory(Unity)
22add_subdirectory(Unity)
22add_subdirectory(Utils)
23add_subdirectory(Utils)
plugins/Dash/CardCreator.js+3−3
@@ -70,10 +70,9 @@ var kArtShapeHolderCode = 'Item { \n\
70 radius: "medium"; \n\
70 radius: "medium"; \n\
71 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
71 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
72 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
72 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
73− Component.onCompleted: updateWidthHeightBindings(); \n\
73+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
74 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
74 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
75 visible: image.status == Image.Ready; \n\
75 visible: image.status == Image.Ready; \n\
76− borderSource: artShapeBorderSource; \n\
77 function updateWidthHeightBindings() { \n\
76 function updateWidthHeightBindings() { \n\
78 if (aspectSmallerThanImageAspect) { \n\
77 if (aspectSmallerThanImageAspect) { \n\
79 width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
78 width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
@@ -281,7 +280,7 @@ function cardString(template, components) {
281 property var template; \n\
280 property var template; \n\
282 property var components; \n\
281 property var components; \n\
283 property var cardData; \n\
282 property var cardData; \n\
284− property var artShapeBorderSource; \n\
283+ property var artShapeBorderSource: undefined; \n\
285 property real fontScale: 1.0; \n\
284 property real fontScale: 1.0; \n\
286 property int headerAlignment: Text.AlignLeft; \n\
285 property int headerAlignment: Text.AlignLeft; \n\
287 property int fixedHeaderHeight: -1; \n\
286 property int fixedHeaderHeight: -1; \n\
@@ -307,6 +306,7 @@ function cardString(template, components) {
307 }
306 }
308
307
309 if (hasArt) {
308 if (hasArt) {
309+ code += 'onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; } \n';
310 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
310 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
311
311
312 var widthCode, heightCode;
312 var widthCode, heightCode;
plugins/Dash/abstractdashview.cpp+3
@@ -176,6 +176,9 @@ void AbstractDashView::refill()
176
176
177bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
177bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
178{
178{
179+ if (fillToY <= fillFromY)
180+ return false;
181+
179 if (!delegate())
182 if (!delegate())
180 return false;
183 return false;
181
184
plugins/LightDM/CMakeLists.txt+6−9
@@ -1,23 +1,22 @@
1# Dependencies
1# Dependencies
2include(FindPkgConfig)
2include(FindPkgConfig)
3+pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-3)
3pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
4pkg_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)
6
5
7include_directories(
6include_directories(
8 ${CMAKE_CURRENT_SOURCE_DIR}
7 ${CMAKE_CURRENT_SOURCE_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}
10 ${CMAKE_SOURCE_DIR}/plugins/Utils
9 ${CMAKE_SOURCE_DIR}/plugins/Utils
11− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
12− #${LIBLIGHTDM_INCLUDE_DIRS}
10+ ${LIBLIGHTDM_INCLUDE_DIRS}
13 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
11 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
14)
12)
15
13
16set(QMLPLUGIN_SRC
14set(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
18 DBusGreeterList.cpp
16 DBusGreeterList.cpp
19 Greeter.cpp
17 Greeter.cpp
20 plugin.cpp
18 plugin.cpp
19+ URLDispatcher.cpp
21 UsersModel.cpp
20 UsersModel.cpp
22 )
21 )
23
22
@@ -26,12 +25,10 @@ add_library(LightDM-qml MODULE
26 )
25 )
27
26
28target_link_libraries(LightDM-qml
27target_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}
32 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
29 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
33 )
30 )
34
31
35−qt5_use_modules(LightDM-qml DBus Gui Qml)
32+qt5_use_modules(LightDM-qml DBus Qml)
36
33
37add_unity8_plugin(LightDM 0.1 LightDM TARGETS LightDM-qml)
34add_unity8_plugin(LightDM 0.1 LightDM TARGETS LightDM-qml)
plugins/LightDM/Greeter.cpp+3
@@ -56,7 +56,10 @@ Greeter::Greeter(QObject* parent)
56 this, SLOT(showPromptFilter(QString, QLightDM::Greeter::PromptType)));
56 this, SLOT(showPromptFilter(QString, QLightDM::Greeter::PromptType)));
57 connect(d->m_greeter, SIGNAL(authenticationComplete()),
57 connect(d->m_greeter, SIGNAL(authenticationComplete()),
58 this, SLOT(authenticationCompleteFilter()));
58 this, SLOT(authenticationCompleteFilter()));
59+ connect(d->m_greeter, SIGNAL(idle()), this, SIGNAL(idle()));
60+ connect(d->m_greeter, SIGNAL(reset()), this, SIGNAL(reset()));
59
61
62+ d->m_greeter->setResettable(true);
60 d->m_greeter->connectSync();
63 d->m_greeter->connectSync();
61}
64}
62
65
plugins/LightDM/Greeter.h+2
@@ -56,6 +56,8 @@ Q_SIGNALS:
56 void authenticationComplete();
56 void authenticationComplete();
57 void authenticationUserChanged(const QString &user);
57 void authenticationUserChanged(const QString &user);
58 void promptlessChanged();
58 void promptlessChanged();
59+ void idle();
60+ void reset();
59
61
60 // This signal is emitted by external agents like indicators, and the UI
62 // This signal is emitted by external agents like indicators, and the UI
61 // should switch to this user if possible.
63 // 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 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Michal Hruby <michal.hruby@canonical.com>
18 */
15 */
19
16
20−#ifndef ICONUTILS_H
17+#ifndef UNITY_URLDISPATCHER_H
21−#define ICONUTILS_H
18+#define UNITY_URLDISPATCHER_H
22
19
20+#include <QObject>
23#include <QString>
21#include <QString>
24−#include <QVariant>
25
22
26−QString gIconToDeclarativeImageProviderString(QString const &giconString);
23+class URLDispatcher : public QObject
27−QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype, QVariantHash const &metadata);
24+{
25+ Q_OBJECT
26+
27+public:
28+ explicit URLDispatcher(QObject *parent=0);
29+
30+Q_SIGNALS:
31+ void dispatchURL(const QString &url);
32+};
28
33
29#endif
34#endif
plugins/LightDM/plugin.cpp+9
@@ -20,6 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "DBusGreeterList.h"
21#include "DBusGreeterList.h"
22#include "Greeter.h"
22#include "Greeter.h"
23+#include "URLDispatcher.h"
23#include "UsersModel.h"
24#include "UsersModel.h"
24#include <libusermetricsoutput/ColorTheme.h>
25#include <libusermetricsoutput/ColorTheme.h>
25#include <libusermetricsoutput/UserMetrics.h>
26#include <libusermetricsoutput/UserMetrics.h>
@@ -46,6 +47,13 @@ static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
46 return greeter;
47 return greeter;
47}
48}
48
49
50+static QObject *dispatcher_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
51+{
52+ Q_UNUSED(engine)
53+ Q_UNUSED(scriptEngine)
54+ return new URLDispatcher();
55+}
56+
49static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
57static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
50{
58{
51 Q_UNUSED(engine)
59 Q_UNUSED(engine)
@@ -67,6 +75,7 @@ void LightDMPlugin::registerTypes(const char *uri)
67
75
68 Q_ASSERT(uri == QLatin1String("LightDM"));
76 Q_ASSERT(uri == QLatin1String("LightDM"));
69 qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
77 qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
78+ qmlRegisterSingletonType<URLDispatcher>(uri, 0, 1, "URLDispatcher", dispatcher_provider);
70 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
79 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
71 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
80 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
72 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
81 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
plugins/SessionBroadcast/SessionBroadcast.cpp+35
@@ -18,11 +18,26 @@
18
18
19#include "SessionBroadcast.h"
19#include "SessionBroadcast.h"
20#include <QDBusConnection>
20#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
22+#include <QDBusInterface>
21
23
22SessionBroadcast::SessionBroadcast(QObject* parent)
24SessionBroadcast::SessionBroadcast(QObject* parent)
23 : QObject(parent)
25 : QObject(parent)
24{
26{
25 auto connection = QDBusConnection::SM_BUSNAME();
27 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 &)));
26
41
27 connection.connect("com.canonical.Unity.Greeter.Broadcast",
42 connection.connect("com.canonical.Unity.Greeter.Broadcast",
28 "/com/canonical/Unity/Greeter/Broadcast",
43 "/com/canonical/Unity/Greeter/Broadcast",
@@ -32,6 +47,26 @@ SessionBroadcast::SessionBroadcast(QObject* parent)
32 SLOT(onShowHome(const QString &)));
47 SLOT(onShowHome(const QString &)));
33}
48}
34
49
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+
35void SessionBroadcast::onShowHome(const QString &username)
70void SessionBroadcast::onShowHome(const QString &username)
36{
71{
37 // Only listen to requests meant for us
72 // Only listen to requests meant for us
plugins/SessionBroadcast/SessionBroadcast.h+11
@@ -31,11 +31,22 @@ class SessionBroadcast: public QObject
31public:
31public:
32 explicit SessionBroadcast(QObject *parent = 0);
32 explicit SessionBroadcast(QObject *parent = 0);
33
33
34+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
35+ Q_INVOKABLE void requestHomeShown(const QString &username);
36+
34Q_SIGNALS:
37Q_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);
35 void showHome();
42 void showHome();
36
43
37private Q_SLOTS:
44private Q_SLOTS:
45+ void onStartUrl(const QString &username, const QString &url);
38 void onShowHome(const QString &username);
46 void onShowHome(const QString &username);
47+
48+private:
49+ QDBusInterface *m_broadcaster;
39};
50};
40
51
41#endif
52#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:
37 bool countVisible;
37 bool countVisible;
38};
38};
39
39
40−LauncherBackend::LauncherBackend(QObject *parent):
40+LauncherBackend::LauncherBackend(bool greeterMode, QObject *parent):
41 QDBusVirtualObject(parent),
41 QDBusVirtualObject(parent),
42+ m_greeterMode(greeterMode),
42 m_accounts(nullptr)
43 m_accounts(nullptr)
43{
44{
44#ifndef LAUNCHER_TESTING
45#ifndef LAUNCHER_TESTING
45 m_accounts = new AccountsServiceDBusAdaptor(this);
46 m_accounts = new AccountsServiceDBusAdaptor(this);
47+ connect(m_accounts, SIGNAL(propertiesChanged(QString, QString, QStringList)),
48+ this, SLOT(accountsListChanged(QString, QString, QStringList)));
46#endif
49#endif
47 m_user = qgetenv("USER");
50 m_user = qgetenv("USER");
48 syncFromAccounts();
51 syncFromAccounts();
@@ -225,7 +228,7 @@ void LauncherBackend::setCountVisible(const QString &appId, bool visible) const
225
228
226void LauncherBackend::setUser(const QString &username)
229void LauncherBackend::setUser(const QString &username)
227{
230{
228− if (qgetenv("USER") == "lightdm" && m_user != username) {
231+ if (m_greeterMode && m_user != username) {
229 m_user = username;
232 m_user = username;
230 syncFromAccounts();
233 syncFromAccounts();
231 }
234 }
@@ -238,6 +241,15 @@ void LauncherBackend::triggerQuickListAction(const QString &appId, const QString
238 Q_UNUSED(quickListId)
241 Q_UNUSED(quickListId)
239}
242}
240
243
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+
241void LauncherBackend::syncFromAccounts()
253void LauncherBackend::syncFromAccounts()
242{
254{
243 QList<QVariantMap> apps;
255 QList<QVariantMap> apps;
@@ -299,6 +311,8 @@ void LauncherBackend::syncFromAccounts()
299 loadFromVariant(app.toMap());
311 loadFromVariant(app.toMap());
300 }
312 }
301 }
313 }
314+
315+ Q_EMIT refreshApplications();
302}
316}
303
317
304void LauncherBackend::syncToAccounts()
318void LauncherBackend::syncToAccounts()
plugins/Unity/Launcher/backend/launcherbackend.h+7−1
@@ -42,7 +42,7 @@ class LauncherBackend : public QDBusVirtualObject
42 friend LauncherBackendTest;
42 friend LauncherBackendTest;
43
43
44public:
44public:
45− LauncherBackend(QObject *parent = 0);
45+ LauncherBackend(bool greeterMode = false, QObject *parent = 0);
46 virtual ~LauncherBackend();
46 virtual ~LauncherBackend();
47
47
48 /**
48 /**
@@ -162,6 +162,7 @@ public:
162 virtual QString introspect (const QString &path) const;
162 virtual QString introspect (const QString &path) const;
163
163
164Q_SIGNALS:
164Q_SIGNALS:
165+ void refreshApplications() const;
165 void quickListChanged(const QString &appId, const QList<QuickListEntry> &quickList) const;
166 void quickListChanged(const QString &appId, const QList<QuickListEntry> &quickList) const;
166 void progressChanged(const QString &appId, int progress) const;
167 void progressChanged(const QString &appId, int progress) const;
167 void countChanged(const QString &appId, int count) const;
168 void countChanged(const QString &appId, int count) const;
@@ -178,6 +179,8 @@ private:
178 void syncFromAccounts();
179 void syncFromAccounts();
179 void syncToAccounts();
180 void syncToAccounts();
180
181
182+ bool m_greeterMode;
183+
181 QList<QString> m_storedApps;
184 QList<QString> m_storedApps;
182 mutable QHash<QString, LauncherBackendItem*> m_itemCache;
185 mutable QHash<QString, LauncherBackendItem*> m_itemCache;
183
186
@@ -186,6 +189,9 @@ private:
186
189
187 void emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const;
190 void emitPropChangedDbus(const QString& appId, const QString& property, QVariant &value) const;
188
191
192+private Q_SLOTS:
193+ void accountsListChanged(const QString &user, const QString &interface, const QStringList &changed);
194+
189protected: /* Protected to allow testing */
195protected: /* Protected to allow testing */
190 LauncherBackendItem* getItem(const QString& appId) const;
196 LauncherBackendItem* getItem(const QString& appId) const;
191
197
plugins/Unity/Launcher/launcheritem.cpp+14−9
@@ -22,12 +22,13 @@
22
22
23#include <libintl.h>
23#include <libintl.h>
24
24
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) :
26 LauncherItemInterface(parent),
26 LauncherItemInterface(parent),
27 m_appId(appId),
27 m_appId(appId),
28 m_name(name),
28 m_name(name),
29 m_icon(icon),
29 m_icon(icon),
30 m_pinned(false),
30 m_pinned(false),
31+ m_pinnable(pinnable),
31 m_running(false),
32 m_running(false),
32 m_recent(false),
33 m_recent(false),
33 m_progress(-1),
34 m_progress(-1),
@@ -38,10 +39,12 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
38 QuickListEntry nameAction;
39 QuickListEntry nameAction;
39 nameAction.setText(m_name);
40 nameAction.setText(m_name);
40 m_quickList->appendAction(nameAction);
41 m_quickList->appendAction(nameAction);
42+ if (m_pinnable) {
41− QuickListEntry pinningAction;
43+ QuickListEntry pinningAction;
42− pinningAction.setActionId("pin_item");
44+ pinningAction.setActionId("pin_item");
43− pinningAction.setText(gettext("Lock to Launcher"));
45+ pinningAction.setText(gettext("Lock to Launcher"));
44− m_quickList->appendAction(pinningAction);
46+ m_quickList->appendAction(pinningAction);
47+ }
45}
48}
46
49
47QString LauncherItem::appId() const
50QString LauncherItem::appId() const
@@ -68,10 +71,12 @@ void LauncherItem::setPinned(bool pinned)
68{
71{
69 if (m_pinned != pinned) {
72 if (m_pinned != pinned) {
70 m_pinned = pinned;
73 m_pinned = pinned;
74+ if (m_pinnable) {
71− QuickListEntry entry;
75+ QuickListEntry entry;
72− entry.setActionId("pin_item");
76+ entry.setActionId("pin_item");
73− entry.setText(pinned ? gettext("Unlock from Launcher") : gettext("Lock to Launcher"));
77+ entry.setText(pinned ? gettext("Unlock from Launcher") : gettext("Lock to Launcher"));
74− m_quickList->updateAction(entry);
78+ m_quickList->updateAction(entry);
79+ }
75 Q_EMIT pinnedChanged(pinned);
80 Q_EMIT pinnedChanged(pinned);
76 }
81 }
77}
82}
plugins/Unity/Launcher/launcheritem.h+2−1
@@ -32,7 +32,7 @@ class LauncherItem: public LauncherItemInterface
32{
32{
33 Q_OBJECT
33 Q_OBJECT
34public:
34public:
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);
36
36
37 QString appId() const;
37 QString appId() const;
38 QString name() const;
38 QString name() const;
@@ -67,6 +67,7 @@ private:
67 QString m_name;
67 QString m_name;
68 QString m_icon;
68 QString m_icon;
69 bool m_pinned;
69 bool m_pinned;
70+ bool m_pinnable;
70 bool m_running;
71 bool m_running;
71 bool m_recent;
72 bool m_recent;
72 int m_progress;
73 int m_progress;
plugins/Unity/Launcher/launchermodel.cpp+57−11
@@ -27,20 +27,15 @@ using namespace unity::shell::application;
27
27
28LauncherModel::LauncherModel(QObject *parent):
28LauncherModel::LauncherModel(QObject *parent):
29 LauncherModelInterface(parent),
29 LauncherModelInterface(parent),
30+ m_greeterMode(qgetenv("XDG_SESSION_CLASS") == "greeter"),
30− m_backend(new LauncherBackend(this)),
31+ m_backend(new LauncherBackend(m_greeterMode, this)),
31 m_appManager(0)
32 m_appManager(0)
32{
33{
33 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
34 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
34 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
35 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
36+ connect(m_backend, SIGNAL(refreshApplications()), SLOT(refreshStoredApplications()));
35
37
36− Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
38+ refreshStoredApplications();
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− }
44}
39}
45
40
46LauncherModel::~LauncherModel()
41LauncherModel::~LauncherModel()
@@ -139,7 +134,8 @@ void LauncherModel::pin(const QString &appId, int index)
139 beginInsertRows(QModelIndex(), index, index);
134 beginInsertRows(QModelIndex(), index, index);
140 LauncherItem *item = new LauncherItem(appId,
135 LauncherItem *item = new LauncherItem(appId,
141 m_backend->displayName(appId),
136 m_backend->displayName(appId),
142− m_backend->icon(appId));
137+ m_backend->icon(appId),
138+ !m_greeterMode);
143 item->setPinned(true);
139 item->setPinned(true);
144 m_list.insert(index, item);
140 m_list.insert(index, item);
145 endInsertRows();
141 endInsertRows();
@@ -199,6 +195,56 @@ void LauncherModel::setUser(const QString &username)
199 m_backend->setUser(username);
195 m_backend->setUser(username);
200}
196}
201
197
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+
202ApplicationManagerInterface *LauncherModel::applicationManager() const
248ApplicationManagerInterface *LauncherModel::applicationManager() const
203{
249{
204 return m_appManager;
250 return m_appManager;
@@ -302,7 +348,7 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
302 if (found) {
348 if (found) {
303 // Shall we paint some running/recent app highlight? If yes, do it here.
349 // Shall we paint some running/recent app highlight? If yes, do it here.
304 } else {
350 } 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);
306 item->setRecent(true);
352 item->setRecent(true);
307 item->setFocused(app->focused());
353 item->setFocused(app->focused());
308
354
plugins/Unity/Launcher/launchermodel.h+3
@@ -51,6 +51,7 @@ public:
51 Q_INVOKABLE void requestRemove(const QString &appId);
51 Q_INVOKABLE void requestRemove(const QString &appId);
52 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
52 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
53 Q_INVOKABLE void setUser(const QString &username);
53 Q_INVOKABLE void setUser(const QString &username);
54+ Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
54
55
55 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
57 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
@@ -62,6 +63,7 @@ private:
62private Q_SLOTS:
63private Q_SLOTS:
63 void progressChanged(const QString &appId, int progress);
64 void progressChanged(const QString &appId, int progress);
64 void countChanged(const QString &appId, int count);
65 void countChanged(const QString &appId, int count);
66+ void refreshStoredApplications();
65
67
66 void applicationAdded(const QModelIndex &parent, int row);
68 void applicationAdded(const QModelIndex &parent, int row);
67 void applicationRemoved(const QModelIndex &parent, int row);
69 void applicationRemoved(const QModelIndex &parent, int row);
@@ -69,6 +71,7 @@ private Q_SLOTS:
69
71
70private:
72private:
71 QList<LauncherItem*> m_list;
73 QList<LauncherItem*> m_list;
74+ bool m_greeterMode;
72 LauncherBackend *m_backend;
75 LauncherBackend *m_backend;
73 ApplicationManagerInterface *m_appManager;
76 ApplicationManagerInterface *m_appManager;
74};
77};
po/ca.po+5−5
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-14 08:15+0000\n"
11+"PO-Revision-Date: 2014-05-30 16:00+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
12+"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
13"Language-Team: Catalan <ca@li.org>\n"
13"Language-Team: Catalan <ca@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-16 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-06-02 06:11+0000\n"
18−"X-Generator: Launchpad (build 17007)\n"
18+"X-Generator: Launchpad (build 17031)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
21#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -92,7 +92,7 @@ msgstr "Mostra'n menys"
92
92
93#: qml/Dash/DashApps.qml:37
93#: qml/Dash/DashApps.qml:37
94msgid "Recent"
94msgid "Recent"
95−msgstr ""
95+msgstr "Recents"
96
96
97#: qml/Dash/PreviewListView.qml:52
97#: qml/Dash/PreviewListView.qml:52
98#, qt-format
98#, qt-format
po/de.po+5−3
@@ -8,13 +8,13 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-20 13:45+0000\n"
11+"PO-Revision-Date: 2014-05-27 14:09+0000\n"
12−"Last-Translator: Tobias Bannert <Unknown>\n"
12+"Last-Translator: Phillip Sz <Unknown>\n"
13"Language-Team: German <de@li.org>\n"
13"Language-Team: German <de@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-22 06:49+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-28 05:54+0000\n"
18"X-Generator: Launchpad (build 17017)\n"
18"X-Generator: Launchpad (build 17017)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -73,6 +73,8 @@ msgid ""
73"You have now mastered the edge gestures and can start using the "
73"You have now mastered the edge gestures and can start using the "
74"phone<br><br>Tap on the screen to start"
74"phone<br><br>Tap on the screen to start"
75msgstr ""
75msgstr ""
76+"Sie haben nun die Kantengesten erfolgreich erlernt und können das Telefon "
77+"nun nutzen<br><br>Zum Starten auf den Bildschirm tippen"
76
78
77#: qml/Components/PageHeader.qml:365
79#: qml/Components/PageHeader.qml:365
78msgid "Recent searches"
80msgid "Recent searches"
po/es.po+6−6
@@ -8,23 +8,23 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-15 15:38+0000\n"
11+"PO-Revision-Date: 2014-06-02 06:28+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
12+"Last-Translator: benjisp94 <benjisp94@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-16 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-06-03 06:16+0000\n"
18−"X-Generator: Launchpad (build 17007)\n"
18+"X-Generator: Launchpad (build 17031)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
21#: plugins/Unity/Launcher/launcheritem.cpp:73
22msgid "Lock to Launcher"
22msgid "Lock to Launcher"
23−msgstr "Mantener en el lanzador"
23+msgstr "Fijar al lanzador"
24
24
25#: plugins/Unity/Launcher/launcheritem.cpp:73
25#: plugins/Unity/Launcher/launcheritem.cpp:73
26msgid "Unlock from Launcher"
26msgid "Unlock from Launcher"
27−msgstr "No mantener en el lanzador"
27+msgstr "Quitar del lanzador"
28
28
29#: qml/Components/EdgeDemoOverlay.qml:151
29#: qml/Components/EdgeDemoOverlay.qml:151
30msgid "Skip intro"
30msgid "Skip intro"
po/gl.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"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"
12"Last-Translator: Marcos Lans <Unknown>\n"
12"Last-Translator: Marcos Lans <Unknown>\n"
13"Language-Team: Galician <gl@li.org>\n"
13"Language-Team: Galician <gl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-06 07:34+0000\n"
17+"X-Launchpad-Export-Date: 2014-06-04 06:34+0000\n"
18−"X-Generator: Launchpad (build 16996)\n"
18+"X-Generator: Launchpad (build 17031)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
21#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -36,7 +36,7 @@ msgstr "Bordo dereito"
36
36
37#: qml/Components/EdgeDemo.qml:114
37#: qml/Components/EdgeDemo.qml:114
38msgid "Try swiping from the right edge to unlock the phone"
38msgid "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"
40
40
41#: qml/Components/EdgeDemo.qml:145
41#: qml/Components/EdgeDemo.qml:145
42msgid "Top edge"
42msgid "Top edge"
@@ -71,7 +71,7 @@ msgid ""
71"You have now mastered the edge gestures and can start using the "
71"You have now mastered the edge gestures and can start using the "
72"phone<br><br>Tap on the screen to start"
72"phone<br><br>Tap on the screen to start"
73msgstr ""
73msgstr ""
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 "
75"teléfono<br><br>Toque na pantalla para comezar"
75"teléfono<br><br>Toque na pantalla para comezar"
76
76
77#: qml/Components/PageHeader.qml:365
77#: qml/Components/PageHeader.qml:365
@@ -142,7 +142,7 @@ msgstr "Escriba ou diga unha orde"
142
142
143#: qml/Hud/Hud.qml:84
143#: qml/Hud/Hud.qml:84
144msgid "Loading. Please Wait..."
144msgid "Loading. Please Wait..."
145−msgstr "Cargando. Agarde…"
145+msgstr "Cargando, agarde…"
146
146
147#: qml/Hud/Hud.qml:89
147#: qml/Hud/Hud.qml:89
148msgid "Speak Now..."
148msgid "Speak Now..."
po/it.po+5−3
@@ -8,13 +8,13 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-23 11:52+0000\n"
11+"PO-Revision-Date: 2014-05-28 15:13+0000\n"
12−"Last-Translator: Gio <gio.scino@gmail.com>\n"
12+"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
13"Language-Team: Italian <it@li.org>\n"
13"Language-Team: Italian <it@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-24 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-29 05:49+0000\n"
18"X-Generator: Launchpad (build 17017)\n"
18"X-Generator: Launchpad (build 17017)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -76,6 +76,8 @@ msgid ""
76"You have now mastered the edge gestures and can start using the "
76"You have now mastered the edge gestures and can start using the "
77"phone<br><br>Tap on the screen to start"
77"phone<br><br>Tap on the screen to start"
78msgstr ""
78msgstr ""
79+"L'apprendimento dei gesti è stato completato. È ora possibile iniziare a "
80+"usare il telefono.<br><br>Toccare lo schermo per iniziare"
79
81
80#: qml/Components/PageHeader.qml:365
82#: qml/Components/PageHeader.qml:365
81msgid "Recent searches"
83msgid "Recent searches"
po/lv.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"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"
12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
12"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
13"Language-Team: Latvian <lv@li.org>\n"
13"Language-Team: Latvian <lv@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-10 08:01+0000\n"
17+"X-Launchpad-Export-Date: 2014-06-02 06:11+0000\n"
18−"X-Generator: Launchpad (build 16996)\n"
18+"X-Generator: Launchpad (build 17031)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
21#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -64,7 +64,7 @@ msgstr ""
64
64
65#: qml/Components/EdgeDemo.qml:227
65#: qml/Components/EdgeDemo.qml:227
66msgid "Well done"
66msgid "Well done"
67−msgstr ""
67+msgstr "Ļoti labi!"
68
68
69#: qml/Components/EdgeDemo.qml:228
69#: qml/Components/EdgeDemo.qml:228
70msgid ""
70msgid ""
@@ -91,7 +91,7 @@ msgstr "Nesens"
91#: qml/Dash/PreviewListView.qml:52
91#: qml/Dash/PreviewListView.qml:52
92#, qt-format
92#, qt-format
93msgid "%1 Preview"
93msgid "%1 Preview"
94−msgstr ""
94+msgstr "%1 Priekšstats"
95
95
96#: qml/Dash/Previews/PreviewActionCombo.qml:34
96#: qml/Dash/Previews/PreviewActionCombo.qml:34
97msgid "More..."
97msgid "More..."
@@ -156,7 +156,7 @@ msgstr "Rādīt paroli"
156
156
157#: qml/Notifications/NotificationMenuItemFactory.qml:103
157#: qml/Notifications/NotificationMenuItemFactory.qml:103
158msgid "Please enter SIM PIN"
158msgid "Please enter SIM PIN"
159−msgstr ""
159+msgstr "Lūdzu ievadiet SIM PIN"
160
160
161#: qml/Panel/SearchIndicator.qml:27
161#: qml/Panel/SearchIndicator.qml:27
162msgid "Search"
162msgid "Search"
po/pl.po+6−4
@@ -8,13 +8,13 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11−"PO-Revision-Date: 2014-05-23 14:26+0000\n"
11+"PO-Revision-Date: 2014-05-27 15:20+0000\n"
12−"Last-Translator: igosoft <igosoft@gmail.com>\n"
12+"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
13"Language-Team: Polish <pl@li.org>\n"
13"Language-Team: Polish <pl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-05-24 06:52+0000\n"
17+"X-Launchpad-Export-Date: 2014-05-28 05:54+0000\n"
18"X-Generator: Launchpad (build 17017)\n"
18"X-Generator: Launchpad (build 17017)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -73,6 +73,8 @@ msgid ""
73"You have now mastered the edge gestures and can start using the "
73"You have now mastered the edge gestures and can start using the "
74"phone<br><br>Tap on the screen to start"
74"phone<br><br>Tap on the screen to start"
75msgstr ""
75msgstr ""
76+"Wydawanie poleceń krawędzi zostało opanowane. Urządzenie gotowe do "
77+"pracy.<br><br>Dotknij ekranu by rozpocząć"
76
78
77#: qml/Components/PageHeader.qml:365
79#: qml/Components/PageHeader.qml:365
78msgid "Recent searches"
80msgid "Recent searches"
@@ -93,7 +95,7 @@ msgstr "Ostatnio używane"
93#: qml/Dash/PreviewListView.qml:52
95#: qml/Dash/PreviewListView.qml:52
94#, qt-format
96#, qt-format
95msgid "%1 Preview"
97msgid "%1 Preview"
96−msgstr ""
98+msgstr "%1 Podgląd"
97
99
98#: qml/Dash/Previews/PreviewActionCombo.qml:34
100#: qml/Dash/Previews/PreviewActionCombo.qml:34
99msgid "More..."
101msgid "More..."
po/ug.po+2−1
@@ -6,11 +6,12 @@
6msgid ""
6msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9−"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9+"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
10"POT-Creation-Date: 2014-04-10 11:13-0400\n"
11"PO-Revision-Date: 2014-04-22 05:46+0000\n"
11"PO-Revision-Date: 2014-04-22 05:46+0000\n"
12"Last-Translator: abdurahman <Unknown>\n"
12"Last-Translator: abdurahman <Unknown>\n"
13"Language-Team: Uyghur <ug@li.org>\n"
13"Language-Team: Uyghur <ug@li.org>\n"
14+"Language: \n"
14"MIME-Version: 1.0\n"
15"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
17"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 @@
15 */
15 */
16
16
17import AccountsService 0.1
17import AccountsService 0.1
18−import LightDM 0.1 as LightDM
19import QtQuick 2.0
18import QtQuick 2.0
20
19
21Item {
20Item {
22 id: demo
21 id: demo
23
22
24− property Item greeter
25 property Item dash
23 property Item dash
26 property Item launcher
24 property Item launcher
27 property Item indicators
25 property Item indicators
@@ -36,7 +34,6 @@ Item {
36 property bool paused: false
34 property bool paused: false
37
35
38 onPausedChanged: {
36 onPausedChanged: {
39− if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
40 if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
37 if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
41 if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
38 if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
42 if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
39 if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
@@ -45,16 +42,6 @@ Item {
45
42
46 function hideEdgeDemoInShell() {
43 function hideEdgeDemoInShell() {
47 AccountsService.demoEdges = false;
44 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();
58 }
45 }
59
46
60 function stopDemo() {
47 function stopDemo() {
@@ -62,7 +49,6 @@ Item {
62 dashEnabled = true
49 dashEnabled = true
63 panelEnabled = true
50 panelEnabled = true
64 panelContentEnabled = true
51 panelContentEnabled = true
65− if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
66 if (d.topEdgeDemo) d.topEdgeDemo.destroy()
52 if (d.topEdgeDemo) d.topEdgeDemo.destroy()
67 if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy()
53 if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy()
68 if (d.leftEdgeDemo) d.leftEdgeDemo.destroy()
54 if (d.leftEdgeDemo) d.leftEdgeDemo.destroy()
@@ -79,24 +65,17 @@ Item {
79 panelEnabled = false;
65 panelEnabled = false;
80 panelContentEnabled = false;
66 panelContentEnabled = false;
81
67
82− // Begin with either greeter or dash, depending on which is visible
83− if (greeter && greeter.shown) {
84− startRightEdgeDemo()
85− } else {
86− startTopEdgeDemo()
68+ startTopEdgeDemo()
87− }
88 }
69 }
89
70
90 QtObject {
71 QtObject {
91 id: d
72 id: d
92 property Component overlay
73 property Component overlay
93− property QtObject rightEdgeDemo
94 property QtObject topEdgeDemo
74 property QtObject topEdgeDemo
95 property QtObject bottomEdgeDemo
75 property QtObject bottomEdgeDemo
96 property QtObject leftEdgeDemo
76 property QtObject leftEdgeDemo
97 property QtObject finalEdgeDemo
77 property QtObject finalEdgeDemo
98 property bool showEdgeDemo: AccountsService.demoEdges
78 property bool showEdgeDemo: AccountsService.demoEdges
99− property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
100
79
101 onShowEdgeDemoChanged: {
80 onShowEdgeDemoChanged: {
102 stopDemo()
81 stopDemo()
@@ -106,37 +85,6 @@ Item {
106 }
85 }
107 }
86 }
108
87
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−
140 function startTopEdgeDemo() {
88 function startTopEdgeDemo() {
141 demo.panelEnabled = true;
89 demo.panelEnabled = true;
142 if (demo.dash && demo.underlay) {
90 if (demo.dash && demo.underlay) {
qml/Greeter/Lockscreen.qml → qml/Components/Lockscreen.qmlrenamed+88−24
@@ -15,8 +15,8 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.0
19−import "../Components"
19+import Ubuntu.Components.Popups 1.0
20
20
21Showable {
21Showable {
22 id: root
22 id: root
@@ -26,19 +26,32 @@ Showable {
26
26
27 // Placeholder text
27 // Placeholder text
28 property string placeholderText: ""
28 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: ""
29
36
30 // In case the Lockscreen can show a greeter message, this is the username
37 // In case the Lockscreen can show a greeter message, this is the username
31 property string username: ""
38 property string username: ""
32
39
33− // Set this to a value greater 0 to enable auto-confirm behavior for the lockscreen.
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
34− // This is ignored by the alphaNumeric lockscreen as that one is confirmed with pressing enter on the OSK.
46+ // that length. This is ignored by the alphaNumeric lockscreen as that one is always confirmed by pressing
47+ // enter on the OSK.
35− property int pinLength: -1
48+ property int minPinLength: -1
36−
49+ property int maxPinLength: -1
37− property url background: ""
38
50
39 signal entered(string passphrase)
51 signal entered(string passphrase)
40 signal cancel()
52 signal cancel()
41 signal emergencyCall()
53 signal emergencyCall()
54+ signal infoPopupConfirmed()
42
55
43 onRequiredChanged: {
56 onRequiredChanged: {
44 if (required && pinPadLoader.item) {
57 if (required && pinPadLoader.item) {
@@ -56,27 +69,50 @@ Showable {
56 pinPadLoader.item.clear(showAnimation);
69 pinPadLoader.item.clear(showAnimation);
57 }
70 }
58
71
59− Rectangle {
72+ function showInfoPopup(title, text) {
60− // In case background fails to load or is undefined
73+ PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
61− id: backgroundBackup
62− anchors.fill: parent
63− color: "black"
64 }
74 }
65
75
66− Image {
76+ MouseArea {
67− id: backgroundImage
77+ anchors.fill: root
68− objectName: "lockscreenBackground"
78+ }
79+
80+ Column {
81+ spacing: units.gu(2)
69 anchors {
82 anchors {
70− fill: parent
83+ left: parent.left
71− topMargin: backgroundTopMargin
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
72 }
112 }
73− source: root.required ? root.background : ""
74− fillMode: Image.PreserveAspectCrop
75 }
113 }
76
114
77− MouseArea {
115+
78− anchors.fill: root
79− }
80
116
81 Loader {
117 Loader {
82 id: pinPadLoader
118 id: pinPadLoader
@@ -105,14 +141,24 @@ Showable {
105
141
106 Binding {
142 Binding {
107 target: pinPadLoader.item
143 target: pinPadLoader.item
108− property: "pinLength"
144+ property: "minPinLength"
145+ value: root.minPinLength
146+ }
147+ Binding {
148+ target: pinPadLoader.item
149+ property: "maxPinLength"
109− value: root.pinLength
150+ value: root.maxPinLength
110 }
151 }
111 Binding {
152 Binding {
112 target: pinPadLoader.item
153 target: pinPadLoader.item
113 property: "placeholderText"
154 property: "placeholderText"
114 value: root.placeholderText
155 value: root.placeholderText
115 }
156 }
157+ Binding {
158+ target: pinPadLoader.item
159+ property: "wrongPlaceholderText"
160+ value: root.wrongPlaceholderText
161+ }
116 Binding {
162 Binding {
117 target: pinPadLoader.item
163 target: pinPadLoader.item
118 property: "username"
164 property: "username"
@@ -153,4 +199,22 @@ Showable {
153 anchors.horizontalCenter: parent.horizontalCenter
199 anchors.horizontalCenter: parent.horizontalCenter
154 }
200 }
155 }
201 }
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+ }
156}
220}
qml/Components/PageHeader.qml+3−2
@@ -18,6 +18,7 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Ubuntu.Components.Popups 0.1
19import Ubuntu.Components.Popups 0.1
20import Ubuntu.Components.ListItems 0.1 as ListItem
20import Ubuntu.Components.ListItems 0.1 as ListItem
21+import SessionManager 0.1
21import Unity 0.2
22import Unity 0.2
22
23
23Item {
24Item {
@@ -51,8 +52,8 @@ Item {
51 }
52 }
52
53
53 Connections {
54 Connections {
54− target: greeter
55+ target: SessionManager
55− onShownChanged: if (shown) resetSearch()
56+ onActiveChanged: if (!SessionManager.active) resetSearch()
56 }
57 }
57
58
58 Flickable {
59 Flickable {
qml/Greeter/PassphraseLockscreen.qml → qml/Components/PassphraseLockscreen.qmlrenamed+6−2
@@ -22,7 +22,8 @@ Item {
22 id: root
22 id: root
23 height: highlightItem.height
23 height: highlightItem.height
24
24
25− property alias placeholderText: pinentryField.placeholderText
25+ property string placeholderText
26+ property string wrongPlaceholderText
26 property string username: ""
27 property string username: ""
27
28
28 signal entered(string passphrase)
29 signal entered(string passphrase)
@@ -73,9 +74,12 @@ Item {
73 echoMode: TextInput.Password
74 echoMode: TextInput.Password
74 opacity: 0.9
75 opacity: 0.9
75 hasClearButton: false
76 hasClearButton: false
77+ placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
76
78
77 onAccepted: {
79 onAccepted: {
80+ if (pinentryField.text) {
78− root.entered(pinentryField.text);
81+ root.entered(pinentryField.text);
82+ }
79 }
83 }
80 }
84 }
81 }
85 }
qml/Greeter/PinLockscreen.qml → qml/Components/PinLockscreen.qmlrenamed+29−11
@@ -25,9 +25,11 @@ Column {
25 spacing: units.gu(3.5)
25 spacing: units.gu(3.5)
26
26
27 property alias placeholderText: pinentryField.placeholderText
27 property alias placeholderText: pinentryField.placeholderText
28+ property alias wrongPlaceholderText: pinentryField.wrongPlaceholderText
28 property int padWidth: units.gu(34)
29 property int padWidth: units.gu(34)
29 property int padHeight: units.gu(28)
30 property int padHeight: units.gu(28)
30− property int pinLength: -1
31+ property int minPinLength: -1
32+ property int maxPinLength: -1
31
33
32 signal entered(string passphrase)
34 signal entered(string passphrase)
33 signal cancel()
35 signal cancel()
@@ -41,6 +43,10 @@ Column {
41 }
43 }
42 }
44 }
43
45
46+ QtObject {
47+ id: priv
48+ property bool autoConfirm: root.minPinLength == root.maxPinLength && root.minPinLength != -1
49+ }
44
50
45 UbuntuShape {
51 UbuntuShape {
46 id: pinentryField
52 id: pinentryField
@@ -52,12 +58,20 @@ Column {
52 radius: "medium"
58 radius: "medium"
53 property string text: ""
59 property string text: ""
54 property string placeholderText: ""
60 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+
55 onTextChanged: {
69 onTextChanged: {
56 pinentryFieldLabel.text = "";
70 pinentryFieldLabel.text = "";
57 for (var i = 0; i < text.length; ++i) {
71 for (var i = 0; i < text.length; ++i) {
58 pinentryFieldLabel.text += "•";
72 pinentryFieldLabel.text += "•";
59 }
73 }
60− if (text.length === root.pinLength) {
74+ if (priv.autoConfirm && text.length === root.maxPinLength) {
61 root.entered(text);
75 root.entered(text);
62 }
76 }
63 }
77 }
@@ -74,9 +88,11 @@ Column {
74 }
88 }
75 Label {
89 Label {
76 id: pinentryFieldPlaceHolder
90 id: pinentryFieldPlaceHolder
91+ objectName: "pinentryFieldPlaceHolder"
77 anchors.centerIn: parent
92 anchors.centerIn: parent
78− color: "grey"
93+ color: "#f3f3e7"
94+ opacity: 0.6
79− text: parent.placeholderText
95+ text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
80 visible: pinentryFieldLabel.text.length == 0
96 visible: pinentryFieldLabel.text.length == 0
81 }
97 }
82
98
@@ -91,9 +107,11 @@ Column {
91 bottom: parent.bottom
107 bottom: parent.bottom
92 bottomMargin: units.gu(1)
108 bottomMargin: units.gu(1)
93 }
109 }
94− visible: root.pinLength == -1
110+ visible: !priv.autoConfirm
95 width: height
111 width: height
96 name: "erase"
112 name: "erase"
113+ color: "#f3f3e7"
114+ opacity: 0.6
97 MouseArea {
115 MouseArea {
98 anchors.fill: parent
116 anchors.fill: parent
99 onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
117 onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
@@ -168,7 +186,7 @@ Column {
168 enabled: entryEnabled
186 enabled: entryEnabled
169
187
170 onClicked: {
188 onClicked: {
171− pinentryField.text = pinentryField.text + text;
189+ pinentryField.appendChar(text);
172 }
190 }
173 }
191 }
174 }
192 }
@@ -186,7 +204,7 @@ Column {
186 width: root.padWidth / 3
204 width: root.padWidth / 3
187 height: root.padHeight / 4
205 height: root.padHeight / 4
188 text: "0"
206 text: "0"
189− onClicked: pinentryField.text = pinentryField.text + text
207+ onClicked: pinentryField.appendChar(text);
190 enabled: entryEnabled
208 enabled: entryEnabled
191 }
209 }
192
210
@@ -194,16 +212,16 @@ Column {
194 objectName: "pinPadButtonErase"
212 objectName: "pinPadButtonErase"
195 width: root.padWidth / 3
213 width: root.padWidth / 3
196 height: root.padHeight / 4
214 height: root.padHeight / 4
197− iconName: root.pinLength == -1 ? "" : "erase"
215+ iconName: priv.autoConfirm ? "erase" : ""
198− subText: root.pinLength == -1 ? "DONE" : ""
216+ subText: priv.autoConfirm ? "" : "DONE"
199 onClicked: {
217 onClicked: {
200− if (root.pinLength !== -1) {
218+ if (priv.autoConfirm) {
201 pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
219 pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
202 } else {
220 } else {
203 root.entered(pinentryField.text);
221 root.entered(pinentryField.text);
204 }
222 }
205 }
223 }
206− enabled: entryEnabled
224+ enabled: priv.autoConfirm ? entryEnabled : pinentryField.text.length >= root.minPinLength
207 }
225 }
208 }
226 }
209 }
227 }
qml/Components/PinPadButton.qml+2−1
@@ -19,6 +19,7 @@ import Ubuntu.Components 0.1
19
19
20Item {
20Item {
21 id: root
21 id: root
22+ opacity: enabled ? 1 : 0.6
22
23
23 property alias text: label.text
24 property alias text: label.text
24 property alias subText: subTextLabel.text
25 property alias subText: subTextLabel.text
@@ -62,7 +63,7 @@ Item {
62 Label {
63 Label {
63 id: subTextLabel
64 id: subTextLabel
64 fontSize: "small"
65 fontSize: "small"
65− color: "grey"
66+ color: "#f3f3e7"
66 anchors.horizontalCenter: parent.horizontalCenter
67 anchors.horizontalCenter: parent.horizontalCenter
67 visible: text.length > 0
68 visible: text.length > 0
68 }
69 }
qml/Dash/CardCarousel.qml+1−1
@@ -34,7 +34,7 @@ DashRenderer {
34 Carousel {
34 Carousel {
35 id: carousel
35 id: carousel
36 anchors.fill: parent
36 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
38 // FIXME we need to "reverse" the carousel to make the selected item the size
38 // FIXME we need to "reverse" the carousel to make the selected item the size
39 // and push others back.
39 // and push others back.
40 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
40 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
qml/Dash/GenericScopeView.qml+1−1
@@ -128,7 +128,7 @@ FocusScope {
128
128
129 CardTool {
129 CardTool {
130 id: cardTool
130 id: cardTool
131−
131+ objectName: "cardTool"
132 count: results.count
132 count: results.count
133 template: model.renderer
133 template: model.renderer
134 components: model.components
134 components: model.components
qml/Dash/Previews/PreviewHeader.qml+3
@@ -56,6 +56,9 @@ PreviewWidget {
56
56
57 Loader {
57 Loader {
58 id: mascotShapeLoader
58 id: mascotShapeLoader
59+ objectName: "mascotShapeLoader"
60+ active: headerRoot.mascot != ""
61+ visible: active
59
62
60 anchors.verticalCenter: parent.verticalCenter
63 anchors.verticalCenter: parent.verticalCenter
61 // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
64 // 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 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Dash 0.1
18import Dash 0.1
19+import SessionManager 0.1
19
20
20ListViewWithPageHeader {
21ListViewWithPageHeader {
21 maximumFlickVelocity: height * 10
22 maximumFlickVelocity: height * 10
22 flickDeceleration: height * 2
23 flickDeceleration: height * 2
23
24
24 Connections {
25 Connections {
25− target: greeter
26+ target: SessionManager
26− onShownChanged: if (greeter.shown) showHeader()
27+ onActiveChanged: if (!SessionManager.active) showHeader()
27 }
28 }
28}
29}
qml/Greeter/Clock.qml+1−1
@@ -39,7 +39,7 @@ Item {
39
39
40 busName: "com.canonical.indicator.datetime"
40 busName: "com.canonical.indicator.datetime"
41 actionsObjectPath: "/com/canonical/indicator/datetime"
41 actionsObjectPath: "/com/canonical/indicator/datetime"
42− menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
42+ menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone_greeter" : ""
43
43
44 Indicators.RootActionState {
44 Indicators.RootActionState {
45 menu: timeModel.model
45 menu: timeModel.model
qml/Greeter/Greeter.qml+36−3
@@ -23,9 +23,9 @@ import "../Components"
23Showable {
23Showable {
24 id: greeter
24 id: greeter
25 enabled: shown
25 enabled: shown
26− created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
26+ created: greeterContentLoader.status == Loader.Ready
27
27
28− property url defaultBackground
28+ property url background
29
29
30 // 1 when fully shown and 0 when fully hidden
30 // 1 when fully shown and 0 when fully hidden
31 property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
31 property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
@@ -118,6 +118,36 @@ Showable {
118 }
118 }
119 }
119 }
120
120
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+
121 Loader {
151 Loader {
122 id: greeterContentLoader
152 id: greeterContentLoader
123 objectName: "greeterContentLoader"
153 objectName: "greeterContentLoader"
@@ -125,7 +155,6 @@ Showable {
125 property var model: LightDM.Users
155 property var model: LightDM.Users
126 property int currentIndex: 0
156 property int currentIndex: 0
127 property var infographicModel: LightDM.Infographic
157 property var infographicModel: LightDM.Infographic
128− readonly property int backgroundTopMargin: -greeter.y
129
158
130 source: required ? "GreeterContent.qml" : ""
159 source: required ? "GreeterContent.qml" : ""
131
160
@@ -145,6 +174,10 @@ Showable {
145 }
174 }
146
175
147 onTease: showLabelAnimation.start()
176 onTease: showLabelAnimation.start()
177+ onShownChanged: {
178+ showLabelAnimation.stop()
179+ swipeHint.opacity = 0
180+ }
148
181
149 Label {
182 Label {
150 id: swipeHint
183 id: swipeHint
qml/Greeter/GreeterContent.qml−38
@@ -24,46 +24,9 @@ Item {
24 id: root
24 id: root
25 anchors.fill: parent
25 anchors.fill: parent
26
26
27− property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
28−
29 signal selected(int uid)
27 signal selected(int uid)
30 signal unlocked(int uid)
28 signal unlocked(int uid)
31
29
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−
67 Rectangle {
30 Rectangle {
68 anchors.fill: parent
31 anchors.fill: parent
69 color: "black"
32 color: "black"
@@ -145,7 +108,6 @@ Item {
145
108
146 Clock {
109 Clock {
147 id: clock
110 id: clock
148− visible: narrowMode
149
111
150 anchors {
112 anchors {
151 top: parent.top
113 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 {
59
59
60 visible: model.label !== ""
60 visible: model.label !== ""
61
61
62− Component.onCompleted: startShowAnimation()
63−
64 Item {
62 Item {
65 id: dataCircle
63 id: dataCircle
66 objectName: "dataCircle"
64 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 {
227 width: parent.width
227 width: parent.width
228
228
229 onYChanged: {
229 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) {
234 if (parent.height > 0) {
231 if (parent.height > 0) {
235 showAnimation.duration = Math.min(showableAnimationDuration * (1 - (parent.height - y) / parent.height), showableAnimationDuration)
232 showAnimation.duration = Math.min(showableAnimationDuration * (1 - (parent.height - y) / parent.height), showableAnimationDuration)
236 hideAnimation.duration = showableAnimationDuration - showAnimation.duration
233 hideAnimation.duration = showableAnimationDuration - showAnimation.duration
qml/Notifications/NotificationMenuItemFactory.qml+26−10
@@ -20,7 +20,6 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import "../Components"
22import "../Components"
23−import "../Greeter"
24
23
25Loader {
24Loader {
26 id: menuFactory
25 id: menuFactory
@@ -98,19 +97,36 @@ Loader {
98 Component {
97 Component {
99 id: pinLock
98 id: pinLock
100
99
101− Lockscreen {
100+ Item {
102− anchors.left: parent.left; anchors.right: parent.right
103 height: menuFactory.maxHeight
101 height: menuFactory.maxHeight
104− placeholderText: i18n.tr("Please enter SIM PIN")
102+ anchors.left: parent.left
105− background: shell.background
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+ }
106
110
107− onEntered: {
111+ Image {
108− menuModel.changeState(menuIndex, passphrase);
112+ objectName: "lockscreenBackground"
109− entryEnabled = false;
113+ anchors.fill: parent
114+ source: shell.background
115+ fillMode: Image.PreserveAspectCrop
110 }
116 }
111
117
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+
112− onCancel: {
127+ onCancel: {
113− menuModel.activate(menuIndex, false);
128+ menuModel.activate(menuIndex, false);
129+ }
114 }
130 }
115 }
131 }
116 }
132 }
qml/Panel/Panel.qml+1−1
@@ -62,7 +62,7 @@ Item {
62 height: __panelMinusSeparatorLineHeight
62 height: __panelMinusSeparatorLineHeight
63 y: 0
63 y: 0
64
64
65− Behavior on y { StandardAnimation { duration: UbuntuAnimation.FastDuration } }
65+ Behavior on y { StandardAnimation { duration: 500 } }
66 }
66 }
67
67
68 PanelSeparatorLine {
68 PanelSeparatorLine {
qml/Shell.qml+31−182
@@ -21,28 +21,19 @@ import Unity.Application 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Gestures 0.1
22import Ubuntu.Gestures 0.1
23import Unity.Launcher 0.1
23import Unity.Launcher 0.1
24−import LightDM 0.1 as LightDM
25import Powerd 0.1
24import Powerd 0.1
26import SessionBroadcast 0.1
25import SessionBroadcast 0.1
26+import SessionManager 0.1
27import "Dash"
27import "Dash"
28−import "Greeter"
29import "Launcher"
28import "Launcher"
30import "Panel"
29import "Panel"
31import "Components"
30import "Components"
32import "Notifications"
31import "Notifications"
33import Unity.Notifications 1.0 as NotificationBackend
32import Unity.Notifications 1.0 as NotificationBackend
34
33
35−FocusScope {
34+BasicShell {
36 id: shell
35 id: shell
37
36
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
46 readonly property real panelHeight: panel.panelHeight
37 readonly property real panelHeight: panel.panelHeight
47
38
48 property bool dashShown: dash.shown
39 property bool dashShown: dash.shown
@@ -78,17 +69,7 @@ FocusScope {
78 id: backgroundSettings
69 id: backgroundSettings
79 schema.id: "org.gnome.desktop.background"
70 schema.id: "org.gnome.desktop.background"
80 }
71 }
81− property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
72+ backgroundSource: backgroundSettings.pictureUri
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()
92
73
93 Item {
74 Item {
94 id: underlayClipper
75 id: underlayClipper
@@ -117,15 +98,6 @@ FocusScope {
117 // through the translucent parts of the shell surface.
98 // through the translucent parts of the shell surface.
118 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
99 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
119
100
120− CrossFadeImage {
121− id: backgroundImage
122− objectName: "backgroundImage"
123−
124− anchors.fill: parent
125− source: shell.background
126− fillMode: Image.PreserveAspectCrop
127− }
128−
129 Rectangle {
101 Rectangle {
130 anchors.fill: parent
102 anchors.fill: parent
131 color: "black"
103 color: "black"
@@ -144,10 +116,9 @@ FocusScope {
144 id: dash
116 id: dash
145 objectName: "dash"
117 objectName: "dash"
146
118
147− available: !greeter.shown && !lockscreen.shown
148 hides: [stages, launcher, panel.indicators]
119 hides: [stages, launcher, panel.indicators]
149− shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
120+ shown: disappearingAnimationProgress !== 1.0
150− enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
121+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
151
122
152 anchors {
123 anchors {
153 fill: parent
124 fill: parent
@@ -235,7 +206,6 @@ FocusScope {
235 shown = true;
206 shown = true;
236 panel.indicators.hide();
207 panel.indicators.hide();
237 edgeDemo.stopDemo();
208 edgeDemo.stopDemo();
238− greeter.hide();
239 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
209 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
240 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
210 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
241 }
211 }
@@ -276,6 +246,12 @@ FocusScope {
276 }
246 }
277 }
247 }
278
248
249+ Binding {
250+ target: ApplicationManager
251+ property: "suspended"
252+ value: !SessionManager.active
253+ }
254+
279 Loader {
255 Loader {
280 id: applicationsDisplayLoader
256 id: applicationsDisplayLoader
281 anchors.fill: parent
257 anchors.fill: parent
@@ -300,128 +276,9 @@ FocusScope {
300 }
276 }
301 }
277 }
302
278
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−
422 InputFilterArea {
279 InputFilterArea {
423 anchors.fill: parent
280 anchors.fill: parent
424− blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
281+ blockInput: ApplicationManager.focusedApplicationId.length === 0 || launcher.shown
425 || panel.indicators.shown
282 || panel.indicators.shown
426 }
283 }
427
284
@@ -433,7 +290,7 @@ FocusScope {
433 // We ignore any display-off signals when the proximity sensor
290 // We ignore any display-off signals when the proximity sensor
434 // is active. This usually indicates something like a phone call.
291 // is active. This usually indicates something like a phone call.
435 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
292 if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
436− greeter.showNow();
293+ SessionManager.lock();
437 }
294 }
438
295
439 // No reason to chew demo CPU when user isn't watching
296 // No reason to chew demo CPU when user isn't watching
@@ -445,17 +302,12 @@ FocusScope {
445 }
302 }
446 }
303 }
447
304
448− function showHome() {
305+ function showHome(fromLauncher) {
449− var animate = !greeter.shown && !stages.shown
306+ var animate = fromLauncher && !stages.shown
450− greeter.hide()
451 dash.setCurrentScope("clickscope", animate, false)
307 dash.setCurrentScope("clickscope", animate, false)
452 stages.hide()
308 stages.hide()
453 }
309 }
454
310
455− function hideIndicatorMenu(delay) {
456− panel.hideIndicatorMenu(delay);
457− }
458−
459 Item {
311 Item {
460 id: overlay
312 id: overlay
461
313
@@ -472,8 +324,8 @@ FocusScope {
472 }
324 }
473 property string focusedAppId: ApplicationManager.focusedApplicationId
325 property string focusedAppId: ApplicationManager.focusedApplicationId
474 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
326 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
475− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
327+ fullscreenMode: focusedApplication && stages.fullscreen
476− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
328+ searchVisible: dash.shown && dash.searchable
477
329
478 InputFilterArea {
330 InputFilterArea {
479 anchors {
331 anchors {
@@ -511,8 +363,9 @@ FocusScope {
511 if (edgeDemo.running)
363 if (edgeDemo.running)
512 return;
364 return;
513
365
514− showHome()
366+ showHome(true)
515 }
367 }
368+
516 onDash: {
369 onDash: {
517 if (stages.shown && !stages.overlayMode) {
370 if (stages.shown && !stages.overlayMode) {
518 if (!stages.locked) {
371 if (!stages.locked) {
@@ -520,10 +373,6 @@ FocusScope {
520 launcher.hide();
373 launcher.hide();
521 }
374 }
522 }
375 }
523− if (greeter.shown) {
524− greeter.hideRight();
525− launcher.hide();
526− }
527 }
376 }
528 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
377 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
529 onLauncherApplicationSelected: {
378 onLauncherApplicationSelected: {
@@ -540,7 +389,7 @@ FocusScope {
540 Rectangle {
389 Rectangle {
541 id: modalNotificationBackground
390 id: modalNotificationBackground
542
391
543− visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
392+ visible: notifications.useModal && (notifications.state == "narrow")
544 color: "#000000"
393 color: "#000000"
545 anchors.fill: parent
394 anchors.fill: parent
546 opacity: 0.5
395 opacity: 0.5
@@ -587,10 +436,17 @@ FocusScope {
587 blockInput: height > 0
436 blockInput: height > 0
588 }
437 }
589 }
438 }
590− }
591
439
592− focus: true
440+ Connections {
593− onFocusChanged: if (!focus) forceActiveFocus();
441+ target: SessionManager
442+ onActiveChanged: {
443+ if (!SessionManager.active) {
444+ launcher.hide()
445+ panel.indicators.hide()
446+ }
447+ }
448+ }
449+ }
594
450
595 InputFilterArea {
451 InputFilterArea {
596 anchors {
452 anchors {
@@ -612,12 +468,6 @@ FocusScope {
612 blockInput: true
468 blockInput: true
613 }
469 }
614
470
615− Binding {
616− target: i18n
617− property: "domain"
618− value: "unity8"
619− }
620−
621 OSKController {
471 OSKController {
622 anchors.topMargin: panel.panelHeight
472 anchors.topMargin: panel.panelHeight
623 anchors.fill: parent // as needs to know the geometry of the shell
473 anchors.fill: parent // as needs to know the geometry of the shell
@@ -649,7 +499,6 @@ FocusScope {
649
499
650 EdgeDemo {
500 EdgeDemo {
651 id: edgeDemo
501 id: edgeDemo
652− greeter: greeter
653 launcher: launcher
502 launcher: launcher
654 dash: dash
503 dash: dash
655 indicators: panel.indicators
504 indicators: panel.indicators
@@ -658,6 +507,6 @@ FocusScope {
658
507
659 Connections {
508 Connections {
660 target: SessionBroadcast
509 target: SessionBroadcast
661− onShowHome: showHome()
510+ onShowHome: showHome(false)
662 }
511 }
663}
512}
run.sh+6−4
@@ -18,13 +18,14 @@ usage() {
18 echo " -p, --pinlock Use a pin protected user." >&2
18 echo " -p, --pinlock Use a pin protected user." >&2
19 echo " -k, --keylock Use a passphrase protected user." >&2
19 echo " -k, --keylock Use a passphrase protected user." >&2
20 echo " -g, --gdb Run through gdb." >&2
20 echo " -g, --gdb Run through gdb." >&2
21+ echo " -G, --greeter Run the greeter instead of the shell." >&2
21 echo " -h, --help Show this help." >&2
22 echo " -h, --help Show this help." >&2
22 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
23 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
23 echo >&2
24 echo >&2
24 exit 1
25 exit 1
25}
26}
26
27
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" -- "$@"`
28[ $? -ne 0 ] && usage
29[ $? -ne 0 ] && usage
29eval set -- "$ARGS"
30eval set -- "$ARGS"
30
31
@@ -35,6 +36,7 @@ do
35 -p|--pinlock) PINLOCK=true;;
36 -p|--pinlock) PINLOCK=true;;
36 -k|--keylock) KEYLOCK=true;;
37 -k|--keylock) KEYLOCK=true;;
37 -g|--gdb) GDB=true;;
38 -g|--gdb) GDB=true;;
39+ -G|--greeter) QML_PHONE_SHELL_PATH=./builddir/src/unity8-greeter;;
38 -h|--help) usage;;
40 -h|--help) usage;;
39 -m|--nomousetouch) MOUSE_TOUCH=false;;
41 -m|--nomousetouch) MOUSE_TOUCH=false;;
40 --) shift;break;;
42 --) shift;break;;
@@ -44,17 +46,17 @@ done
44
46
45if $FAKE; then
47if $FAKE; then
46 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
48 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
48fi
50fi
49
51
50if $PINLOCK; then
52if $PINLOCK; then
51 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
53 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
53fi
55fi
54
56
55if $KEYLOCK; then
57if $KEYLOCK; then
56 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
58 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
58fi
60fi
59
61
60# Force icon theme if running on the desktop, otherwise gnome theme (if running
62# 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
15 ${CMAKE_SOURCE_DIR}/tests/*.svg
15 ${CMAKE_SOURCE_DIR}/tests/*.svg
16)
16)
17
17
18−add_executable(${SHELL_APP}
18+set(COMMON_SRCS
19 ApplicationArguments.h
19 ApplicationArguments.h
20 main.cpp
20 main.cpp
21 MouseTouchAdaptor.cpp
21 MouseTouchAdaptor.cpp
22 CachingNetworkManagerFactory.cpp
22 CachingNetworkManagerFactory.cpp
23 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
23 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
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"
24−)
33+ )
25
34
26qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
35qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
36+qt5_use_modules(${GREETER_APP} Gui Qml Quick Test)
27pkg_check_modules(XCB REQUIRED xcb)
37pkg_check_modules(XCB REQUIRED xcb)
28
38
29if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
39if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
30 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
40 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
41+ set_target_properties(${GREETER_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
31
42
32 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
43 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
44+ target_link_libraries(${GREETER_APP} ${XCB_LDFLAGS})
33endif()
45endif()
34
46
35# install binaries
47# install binaries
36−install(TARGETS ${SHELL_APP}
48+install(TARGETS ${SHELL_APP} ${GREETER_APP}
37 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
49 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
38 )
50 )
39
51
src/main.cpp+18−4
@@ -26,6 +26,7 @@
26#include <qpa/qplatformnativeinterface.h>
26#include <qpa/qplatformnativeinterface.h>
27#include <QLibrary>
27#include <QLibrary>
28#include <QDebug>
28#include <QDebug>
29+#include <QProcess>
29#include <libintl.h>
30#include <libintl.h>
30#include <dlfcn.h>
31#include <dlfcn.h>
31#include <csignal>
32#include <csignal>
@@ -42,7 +43,7 @@ int startShell(int argc, const char** argv, void* server)
42{
43{
43 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
44 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
44
45
45− QGuiApplication::setApplicationName("unity8");
46+ QGuiApplication::setApplicationName(UNITY8_GREETER ? "unity8-greeter" : "unity8");
46 QGuiApplication *application;
47 QGuiApplication *application;
47
48
48 QCommandLineParser parser;
49 QCommandLineParser parser;
@@ -93,7 +94,7 @@ Load the testability driver");
93
94
94 QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
95 QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
95 if (indicatorProfile.isEmpty()) {
96 if (indicatorProfile.isEmpty()) {
96− indicatorProfile = "phone";
97+ indicatorProfile = UNITY8_GREETER ? "phone_greeter" : "phone";
97 }
98 }
98
99
99 resolveIconTheme();
100 resolveIconTheme();
@@ -152,7 +153,7 @@ Load the testability driver");
152 nativeInterface->setProperty("ubuntuSessionType", 1);
153 nativeInterface->setProperty("ubuntuSessionType", 1);
153 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
154 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
154
155
155− QUrl source(::qmlDirectory()+"Shell.qml");
156+ QUrl source(::qmlDirectory() + (UNITY8_GREETER ? "GreeterShell.qml" : "Shell.qml"));
156 prependImportPaths(view->engine(), ::overrideImportPaths());
157 prependImportPaths(view->engine(), ::overrideImportPaths());
157 if (!isUbuntuMirServer) {
158 if (!isUbuntuMirServer) {
158 prependImportPaths(view->engine(), ::nonMirImportPaths());
159 prependImportPaths(view->engine(), ::nonMirImportPaths());
@@ -163,7 +164,7 @@ Load the testability driver");
163 view->engine()->setNetworkAccessManagerFactory(managerFactory);
164 view->engine()->setNetworkAccessManagerFactory(managerFactory);
164
165
165 view->setSource(source);
166 view->setSource(source);
166− view->setColor("transparent");
167+ view->setColor(Qt::transparent);
167
168
168 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {
169 if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || parser.isSet(fullscreenOption)) {
169 view->showFullScreen();
170 view->showFullScreen();
@@ -171,6 +172,19 @@ Load the testability driver");
171 view->show();
172 view->show();
172 }
173 }
173
174
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+
174 int result = application->exec();
188 int result = application->exec();
175
189
176 delete view;
190 delete view;
tests/autopilot/unity8/application_lifecycle/tests/__init__.py−2
@@ -31,8 +31,6 @@ class ApplicationLifeCycleTestCase(tests.UnityTestCase):
31 super(ApplicationLifeCycleTestCase, self).setUp()
31 super(ApplicationLifeCycleTestCase, self).setUp()
32 self._qml_mock_enabled = False
32 self._qml_mock_enabled = False
33 self._data_dirs_mock_enabled = False
33 self._data_dirs_mock_enabled = False
34− unity_proxy = self.launch_unity()
35− process_helpers.unlock_unity(unity_proxy)
36
34
37 def create_test_application(self):
35 def create_test_application(self):
38 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
36 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
24import logging
24import logging
25import os
25import os
26
26
27−from autopilot.matchers import Eventually
28from autopilot.platform import model
27from autopilot.platform import model
29−from testtools.matchers import Equals
30
28
31−from unity8 import process_helpers
32from unity8.application_lifecycle import tests
29from unity8.application_lifecycle import tests
33
30
34
31
@@ -41,6 +38,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
41 if model() == 'Desktop':
38 if model() == 'Desktop':
42 self.skipTest('Test cannot be run on the desktop.')
39 self.skipTest('Test cannot be run on the desktop.')
43 super(ApplicationLifecycleTests, self).setUp()
40 super(ApplicationLifecycleTests, self).setUp()
41+ self.launch_unity()
44
42
45 def swipe_screen_from_right(self):
43 def swipe_screen_from_right(self):
46 width = self.main_window.width
44 width = self.main_window.width
@@ -89,29 +87,3 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
89 self.swipe_screen_from_right()
87 self.swipe_screen_from_right()
90
88
91 self.assert_current_focused_application(application1_name)
89 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):
33 if platform.model() == 'Desktop':
33 if platform.model() == 'Desktop':
34 self.skipTest("URL dispatcher doesn't work on the desktop.")
34 self.skipTest("URL dispatcher doesn't work on the desktop.")
35 super(URLDispatcherTestCase, self).setUp()
35 super(URLDispatcherTestCase, self).setUp()
36+ self.launch_unity()
36
37
37 def test_swipe_out_application_started_by_url_dispatcher(self):
38 def test_swipe_out_application_started_by_url_dispatcher(self):
38 _, desktop_file_path = self.create_test_application()
39 _, 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
22
22
23from autopilot import platform
23from autopilot import platform
24
24
25−from unity8.process_helpers import unlock_unity
26from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
27
26
28
27
@@ -35,7 +34,6 @@ class IndicatorTestCase(UnityTestCase):
35 self.skipTest('Test cannot be run on the desktop.')
34 self.skipTest('Test cannot be run on the desktop.')
36 super(IndicatorTestCase, self).setUp()
35 super(IndicatorTestCase, self).setUp()
37 self.unity_proxy = self.launch_unity()
36 self.unity_proxy = self.launch_unity()
38− unlock_unity(self.unity_proxy)
39
37
40
38
41class IndicatorExistsTestCase(IndicatorTestCase):
39class IndicatorExistsTestCase(IndicatorTestCase):
tests/autopilot/unity8/process_helpers.py−56
@@ -40,62 +40,6 @@ class CannotAccessUnity(Exception):
40 pass
40 pass
41
41
42
42
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−
99def lock_unity(unity_proxy_obj=None):
43def lock_unity(unity_proxy_obj=None):
100 """Helper function that attempts to lock the unity greeter."""
44 """Helper function that attempts to lock the unity greeter."""
101 import evdev, time
45 import evdev, time
tests/autopilot/unity8/shell/emulators/main_window.py−4
@@ -114,10 +114,6 @@ class QQuickView(emulators.UnityEmulatorBase):
114 self.wait_select_single('Indicators', fullyOpened=True)
114 self.wait_select_single('Indicators', fullyOpened=True)
115 return self._get_indicator_page(indicator_name)
115 return self._get_indicator_page(indicator_name)
116
116
117− def get_shell_background(self):
118− return self.select_single(
119− "CrossFadeImage", objectName="backgroundImage")
120−
121 @autopilot_logging.log_action(logger.info)
117 @autopilot_logging.log_action(logger.info)
122 def show_dash_swiping(self):
118 def show_dash_swiping(self):
123 """Show the dash swiping from the left."""
119 """Show the dash swiping from the left."""
tests/autopilot/unity8/shell/tests/__init__.py+16−4
@@ -249,9 +249,9 @@ class UnityTestCase(AutopilotTestCase):
249 "%s=%s" % (key, value)
249 "%s=%s" % (key, value)
250 ], stderr=subprocess.STDOUT)
250 ], stderr=subprocess.STDOUT)
251
251
252− def launch_unity(self, **kwargs):
252+ def _launch(self, executable, ready_func, **kwargs):
253 """Launch the unity shell, return a proxy object for it."""
253 """Launch the unity shell, return a proxy object for it."""
254− binary_path = get_binary_path()
254+ binary_path = get_binary_path(executable)
255 lib_path = get_lib_path()
255 lib_path = get_lib_path()
256
256
257 logger.info(
257 logger.info(
@@ -293,11 +293,19 @@ class UnityTestCase(AutopilotTestCase):
293
293
294 # Ensure that the dash is visible before we return:
294 # Ensure that the dash is visible before we return:
295 logger.debug("Unity started, waiting for it to be ready.")
295 logger.debug("Unity started, waiting for it to be ready.")
296− self.assertUnityReady()
296+ ready_func()
297 logger.debug("Unity loaded and ready.")
297 logger.debug("Unity loaded and ready.")
298
298
299 return app_proxy
299 return app_proxy
300
300
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+
301 def _launch_unity_with_upstart(self, binary_path, args):
309 def _launch_unity_with_upstart(self, binary_path, args):
302 logger.info("Starting unity")
310 logger.info("Starting unity")
303 self._patch_environment("QT_LOAD_TESTABILITY", 1)
311 self._patch_environment("QT_LOAD_TESTABILITY", 1)
@@ -344,7 +352,7 @@ class UnityTestCase(AutopilotTestCase):
344 def _get_lightdm_mock_path(self, mock_type):
352 def _get_lightdm_mock_path(self, mock_type):
345 lib_path = get_mocks_library_path()
353 lib_path = get_mocks_library_path()
346 lightdm_mock_path = os.path.abspath(
354 lightdm_mock_path = os.path.abspath(
347− os.path.join(lib_path, "LightDM", mock_type)
355+ os.path.join(lib_path, "liblightdm", mock_type)
348 )
356 )
349
357
350 if not os.path.exists(lightdm_mock_path):
358 if not os.path.exists(lightdm_mock_path):
@@ -386,6 +394,10 @@ class UnityTestCase(AutopilotTestCase):
386 )
394 )
387 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
395 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
388
396
397+ def assertGreeterReady(self):
398+ greeter = self.main_window.get_greeter()
399+ self.assertThat(greeter.created, Eventually(Equals(True)))
400+
389 def get_dash(self):
401 def get_dash(self):
390 dash = self._proxy.wait_select_single(Dash)
402 dash = self._proxy.wait_select_single(Dash)
391 return dash
403 return dash
tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−7
@@ -19,8 +19,7 @@
19
19
20from __future__ import absolute_import
20from __future__ import absolute_import
21
21
22−from unity8.process_helpers import unlock_unity
23−from unity8.shell import with_lightdm_mock, DragMixin
22+from unity8.shell import DragMixin
24from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
23from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25
24
26from testtools.matchers import Equals
25from testtools.matchers import Equals
@@ -40,7 +39,6 @@ class TestHud(UnityTestCase, DragMixin):
40
39
41 """
40 """
42 unity_proxy = self.launch_unity()
41 unity_proxy = self.launch_unity()
43− unlock_unity(unity_proxy)
44 hud_show_button = self.main_window.get_hud_show_button()
42 hud_show_button = self.main_window.get_hud_show_button()
45 edge_drag_area = self.main_window.get_hud_edge_drag_area()
43 edge_drag_area = self.main_window.get_hud_edge_drag_area()
46 hud = self.main_window.get_hud()
44 hud = self.main_window.get_hud()
@@ -81,7 +79,6 @@ class TestHud(UnityTestCase, DragMixin):
81
79
82 """
80 """
83 unity_proxy = self.launch_unity()
81 unity_proxy = self.launch_unity()
84− unlock_unity(unity_proxy)
85 hud_show_button = self.main_window.get_hud_show_button()
82 hud_show_button = self.main_window.get_hud_show_button()
86 hud = self.main_window.get_hud()
83 hud = self.main_window.get_hud()
87
84
@@ -117,7 +114,6 @@ class TestHud(UnityTestCase, DragMixin):
117 def test_hide_hud_click(self):
114 def test_hide_hud_click(self):
118 """Tapping the close button of the Hud must dismiss it."""
115 """Tapping the close button of the Hud must dismiss it."""
119 unity_proxy = self.launch_unity()
116 unity_proxy = self.launch_unity()
120− unlock_unity(unity_proxy)
121 hud = self.main_window.get_hud()
117 hud = self.main_window.get_hud()
122
118
123 self._launch_test_app_from_app_screen()
119 self._launch_test_app_from_app_screen()
@@ -134,7 +130,6 @@ class TestHud(UnityTestCase, DragMixin):
134
130
135 """
131 """
136 unity_proxy = self.launch_unity()
132 unity_proxy = self.launch_unity()
137− unlock_unity(unity_proxy)
138 hud = self.main_window.get_hud()
133 hud = self.main_window.get_hud()
139
134
140 self._launch_test_app_from_app_screen()
135 self._launch_test_app_from_app_screen()
@@ -151,7 +146,6 @@ class TestHud(UnityTestCase, DragMixin):
151 def test_launcher_hides_hud(self):
146 def test_launcher_hides_hud(self):
152 """Opening the Launcher while the Hud is active must close the Hud."""
147 """Opening the Launcher while the Hud is active must close the Hud."""
153 unity_proxy = self.launch_unity()
148 unity_proxy = self.launch_unity()
154− unlock_unity(unity_proxy)
155 hud = self.main_window.get_hud()
149 hud = self.main_window.get_hud()
156 launcher = self.main_window.get_launcher()
150 launcher = self.main_window.get_launcher()
157
151
tests/autopilot/unity8/shell/tests/test_emulators.py−3
@@ -34,7 +34,6 @@ except ImportError:
34
34
35from testtools.matchers import Contains, HasLength
35from testtools.matchers import Contains, HasLength
36
36
37−from unity8 import process_helpers
38from unity8.shell import emulators, fixture_setup, tests
37from unity8.shell import emulators, fixture_setup, tests
39from unity8.shell.emulators import dash as dash_emulators
38from unity8.shell.emulators import dash as dash_emulators
40
39
@@ -46,7 +45,6 @@ class MainWindowTestCase(tests.UnityTestCase):
46 def setUp(self):
45 def setUp(self):
47 super(MainWindowTestCase, self).setUp()
46 super(MainWindowTestCase, self).setUp()
48 unity_proxy = self.launch_unity()
47 unity_proxy = self.launch_unity()
49− process_helpers.unlock_unity(unity_proxy)
50
48
51 def test_search(self):
49 def test_search(self):
52 self.main_window.search('Test')
50 self.main_window.search('Test')
@@ -62,7 +60,6 @@ class DashBaseTestCase(tests.UnityTestCase):
62 def setUp(self):
60 def setUp(self):
63 super(DashBaseTestCase, self).setUp()
61 super(DashBaseTestCase, self).setUp()
64 unity_proxy = self.launch_unity()
62 unity_proxy = self.launch_unity()
65− process_helpers.unlock_unity(unity_proxy)
66 self.dash = self.main_window.get_dash()
63 self.dash = self.main_window.get_dash()
67
64
68
65
tests/autopilot/unity8/shell/tests/test_lock_screen.py+12−13
@@ -20,7 +20,6 @@
20
20
21from __future__ import absolute_import
21from __future__ import absolute_import
22
22
23−from unity8.process_helpers import unlock_unity
24from unity8.shell import with_lightdm_mock
23from unity8.shell import with_lightdm_mock
25from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
24from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
26
25
@@ -47,41 +46,41 @@ class TestLockscreen(UnityTestCase):
47 @with_lightdm_mock("single-pin")
46 @with_lightdm_mock("single-pin")
48 def test_can_unlock_pin_screen(self):
47 def test_can_unlock_pin_screen(self):
49 """Must be able to unlock the PIN entry lock screen."""
48 """Must be able to unlock the PIN entry lock screen."""
50− unity_proxy = self.launch_unity()
49+ self.launch_greeter()
51 greeter = self.main_window.get_greeter()
50 greeter = self.main_window.get_greeter()
52
51
53 if greeter.narrowMode:
52 if greeter.narrowMode:
54− unlock_unity(unity_proxy)
53+ greeter.swipe()
55 lockscreen = self._wait_for_lockscreen()
54 lockscreen = self._wait_for_lockscreen()
56 self._enter_pincode("1234")
55 self._enter_pincode("1234")
57 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
56 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
58 else:
57 else:
59 self._enter_prompt_passphrase("1234")
58 self._enter_prompt_passphrase("1234")
60− self.assertThat(greeter.shown, Eventually(Equals(False)))
59+ self.assertThat(greeter.enabled, Eventually(Equals(False)))
61
60
62 @with_lightdm_mock("single-passphrase")
61 @with_lightdm_mock("single-passphrase")
63 def test_can_unlock_passphrase_screen(self):
62 def test_can_unlock_passphrase_screen(self):
64 """Must be able to unlock the passphrase entry screen."""
63 """Must be able to unlock the passphrase entry screen."""
65− unity_proxy = self.launch_unity()
64+ self.launch_greeter()
66 greeter = self.main_window.get_greeter()
65 greeter = self.main_window.get_greeter()
67
66
68 if greeter.narrowMode:
67 if greeter.narrowMode:
69− unlock_unity(unity_proxy)
68+ greeter.swipe()
70 lockscreen = self._wait_for_lockscreen()
69 lockscreen = self._wait_for_lockscreen()
71 self._enter_pin_passphrase("password")
70 self._enter_pin_passphrase("password")
72 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
71 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
73 else:
72 else:
74 self._enter_prompt_passphrase("password")
73 self._enter_prompt_passphrase("password")
75− self.assertThat(greeter.shown, Eventually(Equals(False)))
74+ self.assertThat(greeter.enabled, Eventually(Equals(False)))
76
75
77 @with_lightdm_mock("single-pin")
76 @with_lightdm_mock("single-pin")
78 def test_pin_screen_wrong_code(self):
77 def test_pin_screen_wrong_code(self):
79 """Entering the wrong pin code must not dismiss the lock screen."""
78 """Entering the wrong pin code must not dismiss the lock screen."""
80− unity_proxy = self.launch_unity()
79+ self.launch_greeter()
81 greeter = self.main_window.get_greeter()
80 greeter = self.main_window.get_greeter()
82
81
83 if greeter.narrowMode:
82 if greeter.narrowMode:
84− unlock_unity(unity_proxy)
83+ greeter.swipe()
85 lockscreen = self._wait_for_lockscreen()
84 lockscreen = self._wait_for_lockscreen()
86 self._enter_pincode("4321")
85 self._enter_pincode("4321")
87 pinentryField = self.main_window.get_pinentryField()
86 pinentryField = self.main_window.get_pinentryField()
@@ -91,16 +90,16 @@ class TestLockscreen(UnityTestCase):
91 self._enter_prompt_passphrase("4231")
90 self._enter_prompt_passphrase("4231")
92 prompt = self.main_window.get_greeter().get_prompt()
91 prompt = self.main_window.get_greeter().get_prompt()
93 self.assertThat(prompt.text, Eventually(Equals("")))
92 self.assertThat(prompt.text, Eventually(Equals("")))
94− self.assertThat(greeter.shown, Eventually(Equals(True)))
93+ self.assertThat(greeter.enabled, Eventually(Equals(True)))
95
94
96 @with_lightdm_mock("single-passphrase")
95 @with_lightdm_mock("single-passphrase")
97 def test_passphrase_screen_wrong_password(self):
96 def test_passphrase_screen_wrong_password(self):
98 """Entering the wrong password must not dismiss the lock screen."""
97 """Entering the wrong password must not dismiss the lock screen."""
99− unity_proxy = self.launch_unity()
98+ self.launch_greeter()
100 greeter = self.main_window.get_greeter()
99 greeter = self.main_window.get_greeter()
101
100
102 if greeter.narrowMode:
101 if greeter.narrowMode:
103− unlock_unity(unity_proxy)
102+ greeter.swipe()
104 lockscreen = self._wait_for_lockscreen()
103 lockscreen = self._wait_for_lockscreen()
105 self._enter_pin_passphrase("foobar")
104 self._enter_pin_passphrase("foobar")
106 pinentryField = self.main_window.get_pinentryField()
105 pinentryField = self.main_window.get_pinentryField()
@@ -110,7 +109,7 @@ class TestLockscreen(UnityTestCase):
110 self._enter_prompt_passphrase("foobar")
109 self._enter_prompt_passphrase("foobar")
111 prompt = self.main_window.get_greeter().get_prompt()
110 prompt = self.main_window.get_greeter().get_prompt()
112 self.assertThat(prompt.text, Eventually(Equals("")))
111 self.assertThat(prompt.text, Eventually(Equals("")))
113− self.assertThat(greeter.shown, Eventually(Equals(True)))
112+ self.assertThat(greeter.enabled, Eventually(Equals(True)))
114
113
115 def _wait_for_lockscreen(self):
114 def _wait_for_lockscreen(self):
116 """Wait for the lock screen to load, and return it."""
115 """Wait for the lock screen to load, and return it."""
tests/autopilot/unity8/shell/tests/test_notifications.py+2−14
@@ -22,7 +22,6 @@
22from __future__ import absolute_import
22from __future__ import absolute_import
23
23
24from unity8 import shell
24from unity8 import shell
25−from unity8.process_helpers import unlock_unity
26from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
27
26
28from testtools.matchers import Equals, NotEquals
27from testtools.matchers import Equals, NotEquals
@@ -125,7 +124,6 @@ class InteractiveNotificationBase(NotificationsBase):
125 def test_interactive(self):
124 def test_interactive(self):
126 """Interactive notification must react upon click on itself."""
125 """Interactive notification must react upon click on itself."""
127 unity_proxy = self.launch_unity()
126 unity_proxy = self.launch_unity()
128− unlock_unity(unity_proxy)
129
127
130 notify_list = self._get_notifications_list()
128 notify_list = self._get_notifications_list()
131
129
@@ -164,7 +162,6 @@ class InteractiveNotificationBase(NotificationsBase):
164 """Rejecting a call should make notification expand and
162 """Rejecting a call should make notification expand and
165 offer more options."""
163 offer more options."""
166 unity_proxy = self.launch_unity()
164 unity_proxy = self.launch_unity()
167− unlock_unity(unity_proxy)
168
165
169 summary = "Incoming call"
166 summary = "Incoming call"
170 body = "Frank Zappa\n+44 (0)7736 027340"
167 body = "Frank Zappa\n+44 (0)7736 027340"
@@ -214,7 +211,6 @@ class InteractiveNotificationBase(NotificationsBase):
214 def test_modal_sd_without_greeter (self):
211 def test_modal_sd_without_greeter (self):
215 """A snap-decision on a phone should block input to shell beneath it when there's no greeter."""
212 """A snap-decision on a phone should block input to shell beneath it when there's no greeter."""
216 unity_proxy = self.launch_unity()
213 unity_proxy = self.launch_unity()
217− unlock_unity(unity_proxy)
218
214
219 summary = "Incoming file"
215 summary = "Incoming file"
220 body = "Frank would like to send you the file: essay.pdf"
216 body = "Frank would like to send you the file: essay.pdf"
@@ -255,7 +251,7 @@ class InteractiveNotificationBase(NotificationsBase):
255
251
256 def test_modal_sd_with_greeter (self):
252 def test_modal_sd_with_greeter (self):
257 """A snap-decision on a phone should not block input to the greeter beneath it."""
253 """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()
259
255
260 summary = "Incoming file"
256 summary = "Incoming file"
261 body = "Frank would like to send you the file: essay.pdf"
257 body = "Frank would like to send you the file: essay.pdf"
@@ -281,8 +277,8 @@ class InteractiveNotificationBase(NotificationsBase):
281
277
282 # verify that we can swipe away the greeter (interact with the "shell")
278 # verify that we can swipe away the greeter (interact with the "shell")
283 time.sleep(1)
279 time.sleep(1)
284− self.main_window.show_dash_swiping()
285 greeter = self.main_window.get_greeter()
280 greeter = self.main_window.get_greeter()
281+ greeter.swipe()
286 self.assertThat(greeter.shown, Eventually(Equals(False)))
282 self.assertThat(greeter.shown, Eventually(Equals(False)))
287
283
288 # verify and interact with the triggered snap-decision notification
284 # verify and interact with the triggered snap-decision notification
@@ -424,7 +420,6 @@ class EphemeralNotificationsTests(NotificationsBase):
424 def test_icon_summary_body(self):
420 def test_icon_summary_body(self):
425 """Notification must display the expected summary and body text."""
421 """Notification must display the expected summary and body text."""
426 unity_proxy = self.launch_unity()
422 unity_proxy = self.launch_unity()
427− unlock_unity(unity_proxy)
428
423
429 notify_list = self._get_notifications_list()
424 notify_list = self._get_notifications_list()
430
425
@@ -464,7 +459,6 @@ class EphemeralNotificationsTests(NotificationsBase):
464 """Notification must display the expected summary and secondary
459 """Notification must display the expected summary and secondary
465 icon."""
460 icon."""
466 unity_proxy = self.launch_unity()
461 unity_proxy = self.launch_unity()
467− unlock_unity(unity_proxy)
468
462
469 notify_list = self._get_notifications_list()
463 notify_list = self._get_notifications_list()
470
464
@@ -501,7 +495,6 @@ class EphemeralNotificationsTests(NotificationsBase):
501 """Notifications must be displayed in order according to their
495 """Notifications must be displayed in order according to their
502 urgency."""
496 urgency."""
503 unity_proxy = self.launch_unity()
497 unity_proxy = self.launch_unity()
504− unlock_unity(unity_proxy)
505
498
506 notify_list = self._get_notifications_list()
499 notify_list = self._get_notifications_list()
507
500
@@ -588,7 +581,6 @@ class EphemeralNotificationsTests(NotificationsBase):
588 def test_summary_and_body(self):
581 def test_summary_and_body(self):
589 """Notification must display the expected summary- and body-text."""
582 """Notification must display the expected summary- and body-text."""
590 unity_proxy = self.launch_unity()
583 unity_proxy = self.launch_unity()
591− unlock_unity(unity_proxy)
592
584
593 notify_list = self._get_notifications_list()
585 notify_list = self._get_notifications_list()
594
586
@@ -613,7 +605,6 @@ class EphemeralNotificationsTests(NotificationsBase):
613 def test_summary_only(self):
605 def test_summary_only(self):
614 """Notification must display only the expected summary-text."""
606 """Notification must display only the expected summary-text."""
615 unity_proxy = self.launch_unity()
607 unity_proxy = self.launch_unity()
616− unlock_unity(unity_proxy)
617
608
618 notify_list = self._get_notifications_list()
609 notify_list = self._get_notifications_list()
619
610
@@ -631,7 +622,6 @@ class EphemeralNotificationsTests(NotificationsBase):
631 """Notification must allow updating its contents while being
622 """Notification must allow updating its contents while being
632 displayed."""
623 displayed."""
633 unity_proxy = self.launch_unity()
624 unity_proxy = self.launch_unity()
634− unlock_unity(unity_proxy)
635
625
636 notify_list = self._get_notifications_list()
626 notify_list = self._get_notifications_list()
637
627
@@ -669,7 +659,6 @@ class EphemeralNotificationsTests(NotificationsBase):
669 """Notification must allow updating its contents and layout while
659 """Notification must allow updating its contents and layout while
670 being displayed."""
660 being displayed."""
671 unity_proxy = self.launch_unity()
661 unity_proxy = self.launch_unity()
672− unlock_unity(unity_proxy)
673
662
674 notify_list = self._get_notifications_list()
663 notify_list = self._get_notifications_list()
675
664
@@ -715,7 +704,6 @@ class EphemeralNotificationsTests(NotificationsBase):
715 def test_append_hint(self):
704 def test_append_hint(self):
716 """Notification has to accumulate body-text using append-hint."""
705 """Notification has to accumulate body-text using append-hint."""
717 unity_proxy = self.launch_unity()
706 unity_proxy = self.launch_unity()
718− unlock_unity(unity_proxy)
719
707
720 notify_list = self._get_notifications_list()
708 notify_list = self._get_notifications_list()
721
709
tests/mocks/AccountsService/AccountsService.cpp+9
@@ -45,6 +45,15 @@ void AccountsService::setDemoEdges(bool demoEdges)
45 Q_UNUSED(demoEdges)
45 Q_UNUSED(demoEdges)
46}
46}
47
47
48+bool AccountsService::demoEdgesForCurrentUser() const
49+{
50+ return false;
51+}
52+
53+void AccountsService::setDemoEdgesForCurrentUser(bool)
54+{
55+}
56+
48QString AccountsService::backgroundFile() const
57QString AccountsService::backgroundFile() const
49{
58{
50 return m_backgroundFile;
59 return m_backgroundFile;
tests/mocks/AccountsService/AccountsService.h+7
@@ -35,6 +35,10 @@ class AccountsService: public QObject
35 READ demoEdges
35 READ demoEdges
36 WRITE setDemoEdges
36 WRITE setDemoEdges
37 NOTIFY demoEdgesChanged)
37 NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (bool demoEdgesForCurrentUser
39+ READ demoEdgesForCurrentUser
40+ WRITE setDemoEdgesForCurrentUser
41+ NOTIFY demoEdgesForCurrentUserChanged)
38 Q_PROPERTY (QString backgroundFile
42 Q_PROPERTY (QString backgroundFile
39 READ backgroundFile
43 READ backgroundFile
40 WRITE setBackgroundFile // only available in mock
44 WRITE setBackgroundFile // only available in mock
@@ -51,6 +55,8 @@ public:
51 void setUser(const QString &user);
55 void setUser(const QString &user);
52 bool demoEdges() const;
56 bool demoEdges() const;
53 void setDemoEdges(bool demoEdges);
57 void setDemoEdges(bool demoEdges);
58+ bool demoEdgesForCurrentUser() const;
59+ void setDemoEdgesForCurrentUser(bool demoEdgesForCurrentUser);
54 QString backgroundFile() const;
60 QString backgroundFile() const;
55 void setBackgroundFile(const QString &backgroundFile);
61 void setBackgroundFile(const QString &backgroundFile);
56 bool statsWelcomeScreen() const;
62 bool statsWelcomeScreen() const;
@@ -59,6 +65,7 @@ public:
59Q_SIGNALS:
65Q_SIGNALS:
60 void userChanged();
66 void userChanged();
61 void demoEdgesChanged();
67 void demoEdgesChanged();
68+ void demoEdgesForCurrentUserChanged();
62 void backgroundFileChanged();
69 void backgroundFileChanged();
63 void statsWelcomeScreenChanged();
70 void statsWelcomeScreenChanged();
64
71
tests/mocks/CMakeLists.txt+2−1
@@ -31,10 +31,11 @@ endmacro()
31add_subdirectory(AccountsService)
31add_subdirectory(AccountsService)
32add_subdirectory(GSettings.1.0)
32add_subdirectory(GSettings.1.0)
33add_subdirectory(HudClient)
33add_subdirectory(HudClient)
34+add_subdirectory(liblightdm)
34add_subdirectory(libusermetrics)
35add_subdirectory(libusermetrics)
35−add_subdirectory(LightDM)
36add_subdirectory(Powerd)
36add_subdirectory(Powerd)
37add_subdirectory(QMenuModel)
37add_subdirectory(QMenuModel)
38+add_subdirectory(SessionManager)
38add_subdirectory(Ubuntu)
39add_subdirectory(Ubuntu)
39add_subdirectory(Unity)
40add_subdirectory(Unity)
40add_subdirectory(QtMultimedia)
41add_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(
19add_definitions(-DQT_NO_KEYWORDS)
19add_definitions(-DQT_NO_KEYWORDS)
20
20
21set(UnityQML_SOURCES
21set(UnityQML_SOURCES
22− iconutils.cpp
23 fake_scope.cpp
22 fake_scope.cpp
24 fake_scopes.cpp
23 fake_scopes.cpp
25 fake_categories.cpp
24 fake_categories.cpp
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+5
@@ -183,6 +183,11 @@ void MockLauncherModel::setUser(const QString &username)
183 // TODO: implement this...
183 // TODO: implement this...
184}
184}
185
185
186+QString MockLauncherModel::getUrlForAppId(const QString &appId) const
187+{
188+ return "application:///" + appId + ".desktop";
189+}
190+
186void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
191void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
187{
192{
188 Q_UNUSED(applicationManager)
193 Q_UNUSED(applicationManager)
tests/mocks/Unity/Launcher/MockLauncherModel.h+1
@@ -44,6 +44,7 @@ public:
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46 Q_INVOKABLE void setUser(const QString &username);
46 Q_INVOKABLE void setUser(const QString &username);
47+ Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
47
48
48 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
50 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
152 case RoleComponents:
152 case RoleComponents:
153 {
153 {
154 QVariantMap map, artMap;
154 QVariantMap map, artMap;
155+ if (index.row() % 2 != 0) {
155− artMap["aspect-ratio"] = "1.0";
156+ artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
157+ } else {
158+ artMap["aspect-ratio"] = "1.0";
159+ }
156 artMap["field"] = "art";
160 artMap["field"] = "art";
157 map["art"] = artMap;
161 map["art"] = artMap;
158 map["title"] = "HOLA";
162 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)
151 Q_UNUSED(language)
151 Q_UNUSED(language)
152}
152}
153
153
154+void Greeter::setResettable (bool resettable)
155+{
156+ Q_UNUSED(resettable)
157+}
158+
154bool Greeter::startSessionSync(const QString &session)
159bool Greeter::startSessionSync(const QString &session)
155{
160{
156 Q_UNUSED(session)
161 Q_UNUSED(session)
157 return true;
162 return true;
158}
163}
159
164
165+QString Greeter::ensureSharedDataDirSync(const QString &username)
166+{
167+ Q_UNUSED(username)
168+ return "";
169+}
170+
160void Greeter::respond(const QString &response)
171void Greeter::respond(const QString &response)
161{
172{
162 Q_D(Greeter);
173 Q_D(Greeter);
tests/mocks/LightDM/Greeter.h → tests/mocks/liblightdm/Greeter.hrenamed+4
@@ -88,13 +88,17 @@ public Q_SLOTS:
88 void respond(const QString &response);
88 void respond(const QString &response);
89 void cancelAuthentication();
89 void cancelAuthentication();
90 void setLanguage (const QString &language);
90 void setLanguage (const QString &language);
91+ void setResettable (bool resettable);
91 bool startSessionSync(const QString &session=QString());
92 bool startSessionSync(const QString &session=QString());
93+ QString ensureSharedDataDirSync(const QString &username);
92
94
93Q_SIGNALS:
95Q_SIGNALS:
94 void showMessage(QString text, QLightDM::Greeter::MessageType type);
96 void showMessage(QString text, QLightDM::Greeter::MessageType type);
95 void showPrompt(QString text, QLightDM::Greeter::PromptType type);
97 void showPrompt(QString text, QLightDM::Greeter::PromptType type);
96 void authenticationComplete();
98 void authenticationComplete();
97 void autologinTimerExpired();
99 void autologinTimerExpired();
100+ void idle();
101+ void reset();
98
102
99private:
103private:
100 GreeterPrivate *d_ptr;
104 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})
11qt5_use_modules(MockLightDM-full Gui)
11qt5_use_modules(MockLightDM-full Gui)
12
12
13set_target_properties(MockLightDM-full PROPERTIES
13set_target_properties(MockLightDM-full PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
14+ OUTPUT_NAME lightdm-qt5-3
15+ SOVERSION 0)
15
16
16install(TARGETS MockLightDM-full
17install(TARGETS MockLightDM-full
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/full
18+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/full
18 )
19 )
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})
12qt5_use_modules(MockLightDM-single-passphrase Gui)
12qt5_use_modules(MockLightDM-single-passphrase Gui)
13
13
14set_target_properties(MockLightDM-single-passphrase PROPERTIES
14set_target_properties(MockLightDM-single-passphrase PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
15+ OUTPUT_NAME lightdm-qt5-3
16+ SOVERSION 0)
16
17
17install(TARGETS MockLightDM-single-passphrase
18install(TARGETS MockLightDM-single-passphrase
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-passphrase
19+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single-passphrase
19 )
20 )
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})
12qt5_use_modules(MockLightDM-single-pin Gui)
12qt5_use_modules(MockLightDM-single-pin Gui)
13
13
14set_target_properties(MockLightDM-single-pin PROPERTIES
14set_target_properties(MockLightDM-single-pin PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
15+ OUTPUT_NAME lightdm-qt5-3
16+ SOVERSION 0)
16
17
17install(TARGETS MockLightDM-single-pin
18install(TARGETS MockLightDM-single-pin
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-pin
19+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single-pin
19 )
20 )
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})
11qt5_use_modules(MockLightDM-single Gui)
11qt5_use_modules(MockLightDM-single Gui)
12
12
13set_target_properties(MockLightDM-single PROPERTIES
13set_target_properties(MockLightDM-single PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
14+ OUTPUT_NAME lightdm-qt5-3
15+ SOVERSION 0)
15
16
16install(TARGETS MockLightDM-single
17install(TARGETS MockLightDM-single
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single
18+ DESTINATION ${SHELL_INSTALL_QML}/mocks/liblightdm/single
18 )
19 )
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 @@
1add_subdirectory(AccountsService)
1add_subdirectory(AccountsService)
2−add_subdirectory(LightDM)
3add_subdirectory(Dash)
2add_subdirectory(Dash)
3+add_subdirectory(LightDM)
4+add_subdirectory(SessionManager)
4add_subdirectory(Ubuntu)
5add_subdirectory(Ubuntu)
5add_subdirectory(Unity)
6add_subdirectory(Unity)
6add_subdirectory(Utils)
7add_subdirectory(Utils)
tests/plugins/Dash/cardcreator/1.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
3 property var template;
3 property var template;
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property int headerAlignment: Text.AlignLeft;
8 property int headerAlignment: Text.AlignLeft;
9 property int fixedHeaderHeight: -1;
9 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
12 property bool asynchronous: true;
12 property bool asynchronous: true;
13 property bool showHeader: true;
13 property bool showHeader: true;
14 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
15readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16Item {
17Item {
17 id: artShapeHolder;
18 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
30 radius: "medium";
31 radius: "medium";
31 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
34 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 visible: image.status == Image.Ready;
36 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
37 function updateWidthHeightBindings() {
37 function updateWidthHeightBindings() {
38 if (aspectSmallerThanImageAspect) {
38 if (aspectSmallerThanImageAspect) {
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/2.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
3 property var template;
3 property var template;
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property int headerAlignment: Text.AlignLeft;
8 property int headerAlignment: Text.AlignLeft;
9 property int fixedHeaderHeight: -1;
9 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/3.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
3 property var template;
3 property var template;
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property int headerAlignment: Text.AlignLeft;
8 property int headerAlignment: Text.AlignLeft;
9 property int fixedHeaderHeight: -1;
9 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
12 property bool asynchronous: true;
12 property bool asynchronous: true;
13 property bool showHeader: true;
13 property bool showHeader: true;
14 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
15readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16Item {
17Item {
17 id: artShapeHolder;
18 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
30 radius: "medium";
31 radius: "medium";
31 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
34 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 visible: image.status == Image.Ready;
36 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
37 function updateWidthHeightBindings() {
37 function updateWidthHeightBindings() {
38 if (aspectSmallerThanImageAspect) {
38 if (aspectSmallerThanImageAspect) {
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
3 property var template;
3 property var template;
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property int headerAlignment: Text.AlignLeft;
8 property int headerAlignment: Text.AlignLeft;
9 property int fixedHeaderHeight: -1;
9 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/5.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
3 property var template;
3 property var template;
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property int headerAlignment: Text.AlignLeft;
8 property int headerAlignment: Text.AlignLeft;
9 property int fixedHeaderHeight: -1;
9 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
12 property bool asynchronous: true;
12 property bool asynchronous: true;
13 property bool showHeader: true;
13 property bool showHeader: true;
14 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
15readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
16Item {
17Item {
17 id: artShapeHolder;
18 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
30 radius: "medium";
31 radius: "medium";
31 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
34 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35 visible: image.status == Image.Ready;
36 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
37 function updateWidthHeightBindings() {
37 function updateWidthHeightBindings() {
38 if (aspectSmallerThanImageAspect) {
38 if (aspectSmallerThanImageAspect) {
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
39 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/organicgridtest.cpp+5
@@ -514,6 +514,11 @@ private Q_SLOTS:
514
514
515 model->setCount(21);
515 model->setCount(21);
516
516
517+ QTRY_COMPARE(grid->m_visibleItems.count(), 0);
518+
519+ grid->setDisplayMarginBeginning(0);
520+ grid->setDisplayMarginEnd(0);
521+
517 // This is exactly the same block as the first again
522 // This is exactly the same block as the first again
518 checkInitialPositions();
523 checkInitialPositions();
519 }
524 }
tests/plugins/LightDM/CMakeLists.txt+5−4
@@ -1,3 +1,6 @@
1+include(FindPkgConfig)
2+pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-3)
3+
1add_executable(test-lightdm-dbus
4add_executable(test-lightdm-dbus
2 dbus.cpp
5 dbus.cpp
3 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
6 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
@@ -7,19 +10,17 @@ qt5_use_modules(test-lightdm-dbus Core DBus Quick Test)
7include_directories(
10include_directories(
8 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_SOURCE_DIR}/plugins/LightDM
12 ${CMAKE_SOURCE_DIR}/plugins/LightDM
10− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
11 ${LIBLIGHTDM_INCLUDE_DIRS}
13 ${LIBLIGHTDM_INCLUDE_DIRS}
12 )
14 )
13
15
14add_dependencies(test-lightdm-dbus MockLightDM-full)
16add_dependencies(test-lightdm-dbus MockLightDM-full)
15target_link_libraries(test-lightdm-dbus
17target_link_libraries(test-lightdm-dbus
16− -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
18+ ${LIBLIGHTDM_LDFLAGS}
17− -llightdm-qt5-2
18 )
19 )
19
20
20add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
21add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
21
22
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)
23add_dependencies(testLightDMDBus test-lightdm-dbus)
24add_dependencies(testLightDMDBus test-lightdm-dbus)
24
25
25add_dependencies(qmluitests testLightDMDBus)
26add_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
49 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
49 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
50 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
50 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
51 )
51 )
52−target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
52+target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})
53qt5_use_modules(launchermodeltestExec Test Core DBus)
53qt5_use_modules(launchermodeltestExec Test Core DBus)
54
54
55# copy .desktop files into build directory for shadow builds
55# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+9
@@ -274,6 +274,15 @@ private Q_SLOTS:
274 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
274 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
275 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
275 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
276 }
276 }
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+ }
277};
286};
278
287
279QTEST_GUILESS_MAIN(LauncherModelTest)
288QTEST_GUILESS_MAIN(LauncherModelTest)
tests/qmltests/CMakeLists.txt+40−50
@@ -4,6 +4,7 @@ include(QmlTest)
4add_subdirectory(Components)
4add_subdirectory(Components)
5
5
6set(qmltest_DEFAULT_IMPORT_PATHS
6set(qmltest_DEFAULT_IMPORT_PATHS
7+ ${CMAKE_BINARY_DIR}/plugins
7 ${CMAKE_BINARY_DIR}/tests/utils/modules
8 ${CMAKE_BINARY_DIR}/tests/utils/modules
8 ${CMAKE_BINARY_DIR}/tests/mocks
9 ${CMAKE_BINARY_DIR}/tests/mocks
9)
10)
@@ -18,12 +19,12 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
18set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
20
21
21−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
22+add_qml_test(. Shell)
22− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
23+add_qml_test(. GreeterShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/single")
23add_qml_test(Components Carousel)
24add_qml_test(Components Carousel)
24add_qml_test(Components DraggingArea)
25add_qml_test(Components DraggingArea)
25add_qml_test(Components EdgeDemoOverlay)
26add_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)
27add_qml_test(Components LazyImage)
28add_qml_test(Components LazyImage)
28add_qml_test(Components Rating)
29add_qml_test(Components Rating)
29add_qml_test(Components ResponsiveFlowView)
30add_qml_test(Components ResponsiveFlowView)
@@ -32,54 +33,43 @@ add_qml_test(Components SeeMore)
32add_qml_test(Components Showable)
33add_qml_test(Components Showable)
33add_qml_test(Components PageHeaderLabel)
34add_qml_test(Components PageHeaderLabel)
34add_qml_test(Components ZoomableImage)
35add_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 Dash)
36−add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
37+add_qml_test(Dash DashContent)
37−add_qml_test(Dash Card IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
38+add_qml_test(Dash Card)
38−add_qml_benchmark(Dash CardBenchmark 1000 IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
39+add_qml_benchmark(Dash CardBenchmark 1000)
39−add_qml_test(Dash CardHeader)
40−add_qml_test(Dash CardTool IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
40+add_qml_test(Dash CardTool)
41−add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
41+add_qml_test(Dash GenericScopeView)
42−add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
42+add_qml_test(Dash/Apps RunningApplicationsGrid)
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)
43+add_qml_test(Dash/Previews Preview)
46−add_qml_test(Dash/Previews PreviewActions IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
44+add_qml_test(Dash/Previews PreviewActions)
47−add_qml_test(Dash/Previews PreviewAudioPlayback IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
45+add_qml_test(Dash/Previews PreviewAudioPlayback)
48−add_qml_test(Dash/Previews PreviewHeader IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
46+add_qml_test(Dash/Previews PreviewHeader)
49−add_qml_test(Dash/Previews PreviewImageGallery IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
47+add_qml_test(Dash/Previews PreviewImageGallery)
50−add_qml_test(Dash/Previews PreviewProgress IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
48+add_qml_test(Dash/Previews PreviewProgress)
51−add_qml_test(Dash/Previews PreviewRatingDisplay IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
49+add_qml_test(Dash/Previews PreviewRatingDisplay)
52−add_qml_test(Dash/Previews PreviewRatingInput IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
50+add_qml_test(Dash/Previews PreviewRatingInput)
53−add_qml_test(Dash/Previews PreviewTextSummary IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
51+add_qml_test(Dash/Previews PreviewTextSummary)
54−add_qml_test(Dash/Previews PreviewVideoPlayback IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
52+add_qml_test(Dash/Previews PreviewVideoPlayback)
55−add_qml_test(Dash/Previews PreviewWidgetFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
53+add_qml_test(Dash/Previews PreviewWidgetFactory)
56−add_qml_test(Dash/Previews PreviewZoomableImage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
54+add_qml_test(Dash/Previews PreviewZoomableImage)
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")
55+add_qml_test(Greeter Lockscreen ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/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")
56+add_qml_test(Greeter Tablet ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/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")
57+add_qml_test(Greeter Phone ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/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")
58+add_qml_test(Greeter Infographics ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/liblightdm/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")
59+add_qml_test(Greeter Clock ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
67add_qml_test(Hud Hud)
60add_qml_test(Hud Hud)
68add_qml_test(Hud Result)
61add_qml_test(Hud Result)
69−add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
62+add_qml_test(Launcher Launcher)
70−add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
63+add_qml_test(Notifications Notifications)
71−add_qml_test(Notifications VisualSnapDecisionsQueue IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
64+add_qml_test(Notifications VisualSnapDecisionsQueue)
72add_qml_test(Panel IndicatorRow)
65add_qml_test(Panel IndicatorRow)
73−add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
66+add_qml_test(Panel Indicators)
74−add_qml_test(Panel MenuContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
67+add_qml_test(Panel MenuContent)
75−add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
68+add_qml_test(Panel Panel)
76add_qml_test(Panel SearchIndicator)
69add_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")
70+add_qml_test(Panel/Indicators DefaultIndicatorWidget 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")
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/
81−add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
73+add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
82−add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
74+add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
83−add_qml_test(Stages PhoneStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
75+add_qml_test(Stages PhoneStage)
84− ${CMAKE_BINARY_DIR}/tests/mocks
85− ${CMAKE_BINARY_DIR}/plugins)
tests/qmltests/Components/tst_ZoomableImage.qml+21−19
@@ -98,22 +98,23 @@ Rectangle {
98 // move mouse to center
98 // move mouse to center
99 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
99 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
100
100
101+ var oldScale = image.scale;
101 // Test Zoom-in Zoom-out twice.
102 // Test Zoom-in Zoom-out twice.
102 for (var c=0; c<2; c++) {
103 for (var c=0; c<2; c++) {
103 // zoom in
104 // zoom in
104 for (var i=0; i<10; i++) {
105 for (var i=0; i<10; i++) {
105 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106− tryCompare(image, "scale", 1.0 + (i + 1) * 0.1);
107+ tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
107− compare(flickable.contentWidth, lazyImage.width * image.scale);
108+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
108− compare(flickable.contentHeight, lazyImage.height * image.scale);
109+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
109 }
110 }
110
111
111 // zoom out
112 // zoom out
112 for (var i=0; i<10; i++) {
113 for (var i=0; i<10; i++) {
113 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114− tryCompare(image, "scale", 2.0 - (i + 1) * 0.1);
115+ tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
115− compare(flickable.contentWidth, lazyImage.width * image.scale);
116+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
116− compare(flickable.contentHeight, lazyImage.height * image.scale);
117+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
117 }
118 }
118 }
119 }
119 }
120 }
@@ -123,16 +124,12 @@ Rectangle {
123 zoomable:false,
124 zoomable:false,
124 answer1: true,
125 answer1: true,
125 answer2: false,
126 answer2: false,
126− answer3: true,
127+ answer3: true },
127− answer4: 1.0,
128− answer5: 1.0 },
129 { source:widgetData2["source"],
128 { source:widgetData2["source"],
130 zoomable:true,
129 zoomable:true,
131 answer1: false,
130 answer1: false,
132 answer2: true,
131 answer2: true,
133− answer3: false,
132+ answer3: false }
134− answer4: 1.7740461882048026,
135− answer5: 1.0 }
136 ]
133 ]
137 }
134 }
138
135
@@ -163,7 +160,7 @@ Rectangle {
163 var y2End = zoomableImage.height * 5 / 6;
160 var y2End = zoomableImage.height * 5 / 6;
164
161
165 var oldScale = image.scale;
162 var oldScale = image.scale;
166−
163+ var newScale = -1;
167 // move mouse to center
164 // move mouse to center
168 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
165 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
169
166
@@ -172,20 +169,25 @@ Rectangle {
172 wait(3000); // have to delay between two consequent pinch event.
169 wait(3000); // have to delay between two consequent pinch event.
173 // pinch zoom-in
170 // pinch zoom-in
174 touchPinch(zoomableImage, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End);
171 touchPinch(zoomableImage, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End);
172+ waitForRendering(image);
175
173
176− tryCompare(image, "scale", data.answer4);
174+ if (newScale == -1) {
177− var newScale = image.scale;
175+ newScale = image.scale;
176+ }
177+ tryCompare(image, "scale", newScale);
178 compare(newScale == oldScale, data.answer1, "scale factor not equal: "+ oldScale + "=?" + newScale);
178 compare(newScale == oldScale, data.answer1, "scale factor not equal: "+ oldScale + "=?" + newScale);
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
181− compare(newScale, data.answer4, "scale factor error");
181+ compare(image.scale, newScale, "scale factor error");
182− compare(flickable.contentWidth, lazyImage.width * image.scale);
182+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
183− compare(flickable.contentHeight, lazyImage.height * image.scale);
183+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
184
184
185 wait(3000); // have to delay between two consequent pinch event.
185 wait(3000); // have to delay between two consequent pinch event.
186 // pinch zoom-out
186 // pinch zoom-out
187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
187 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));
189 }
191 }
190 }
192 }
191 }
193 }
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+12−7
@@ -53,12 +53,12 @@ Rectangle {
53 name: "PreviewHeaderTest"
53 name: "PreviewHeaderTest"
54 when: windowShown
54 when: windowShown
55
55
56− property Item mascot: findChild(previewHeader, "mascotShape")
56+ property Item mascotShapeLoader: findChild(previewHeader, "mascotShapeLoader")
57 property Item outerRow: findChild(previewHeader, "outerRow")
57 property Item outerRow: findChild(previewHeader, "outerRow")
58 property Item column: findChild(previewHeader, "column")
58 property Item column: findChild(previewHeader, "column")
59
59
60 function initTestCase() {
60 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.");
62 verify(typeof testCase.outerRow === "object", "Couldn't find outerRow object.");
62 verify(typeof testCase.outerRow === "object", "Couldn't find outerRow object.");
63 verify(typeof testCase.column === "object", "Couldn't find column object.");
63 verify(typeof testCase.column === "object", "Couldn't find column object.");
64
64
@@ -70,9 +70,9 @@ Rectangle {
70
70
71 function test_mascot_data() {
71 function test_mascot_data() {
72 return [
72 return [
73− { tag: "Empty", source: "", visible: false },
73+ { tag: "Empty", source: "", loaderVisible: false, visible: false },
74− { tag: "Invalid", source: "bad_path", visible: false },
74+ { tag: "Invalid", source: "bad_path", loaderVisible: true, visible: false },
75− { tag: "Valid", source: "../graphics/play_button.png", visible: true },
75+ { tag: "Valid", source: "../graphics/play_button.png", loaderVisible: true, visible: true },
76 ]
76 ]
77 }
77 }
78
78
@@ -80,12 +80,17 @@ Rectangle {
80 headerjson.mascot = data.source;
80 headerjson.mascot = data.source;
81 previewHeader.widgetData = headerjson;
81 previewHeader.widgetData = headerjson;
82
82
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")
83− tryCompare(testCase.mascot, "visible", data.visible);
87+ tryCompare(mascot, "visible", data.visible);
88+ }
84 }
89 }
85
90
86 function test_dimensions_data() {
91 function test_dimensions_data() {
87 return [
92 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" },
89 { tag: "Header height", object: previewHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
94 { tag: "Header height", object: previewHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
90 ]
95 ]
91 }
96 }
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−5
@@ -26,12 +26,12 @@ Rectangle {
26 color: "lightgrey"
26 color: "lightgrey"
27
27
28 property var widgetData0: {
28 property var widgetData0: {
29− "source": ""
29+ "source": Qt.resolvedUrl("../artwork/checkers.png"),
30+ "zoomable": false
30 }
31 }
31
32
32 property var widgetData1: {
33 property var widgetData1: {
33− "source": Qt.resolvedUrl("../artwork/checkers.png"),
34+ "source": ""
34− "zoomable": false
35 }
35 }
36
36
37 PreviewZoomableImage {
37 PreviewZoomableImage {
@@ -49,11 +49,13 @@ Rectangle {
49
49
50 zoomableImage.widgetData = widgetData0;
50 zoomableImage.widgetData = widgetData0;
51 waitForRendering(zoomableImage);
51 waitForRendering(zoomableImage);
52+ waitForRendering(image);
52− tryCompare(image, "imageState", "default");
53+ tryCompare(image, "imageState", "ready");
53
54
54 zoomableImage.widgetData = widgetData1;
55 zoomableImage.widgetData = widgetData1;
55 waitForRendering(zoomableImage);
56 waitForRendering(zoomableImage);
57+ waitForRendering(image);
56− tryCompare(image, "imageState", "ready");
58+ tryCompare(image, "imageState", "default");
57 }
59 }
58 }
60 }
59}
61}
tests/qmltests/Dash/tst_DashContent.qml+29−14
@@ -67,13 +67,17 @@ Item {
67 name: "DashContent"
67 name: "DashContent"
68 when: scopesModel.loaded && windowShown
68 when: scopesModel.loaded && windowShown
69
69
70− function init() {
70+ function loadScopes() {
71− scopesModel.clear();
72 scopeLoadedSpy.clear();
71 scopeLoadedSpy.clear();
73 scopesModel.load();
72 scopesModel.load();
74 tryCompare(scopeLoadedSpy, "count", 4);
73 tryCompare(scopeLoadedSpy, "count", 4);
75 }
74 }
76
75
76+ function init() {
77+ scopesModel.clear();
78+ loadScopes();
79+ }
80+
77 function cleanup() {
81 function cleanup() {
78 movementStartedSpy.clear();
82 movementStartedSpy.clear();
79 dashContent.visible = true;
83 dashContent.visible = true;
@@ -93,9 +97,7 @@ Item {
93 compare(dashContentList.count, 0, "DashContent should have 0 items when it starts");
97 compare(dashContentList.count, 0, "DashContent should have 0 items when it starts");
94 tryCompare(dashContentList, "currentIndex", -1);
98 tryCompare(dashContentList, "currentIndex", -1);
95
99
96− scopeLoadedSpy.clear();
100+ loadScopes();
97− scopesModel.load();
98− tryCompare(scopeLoadedSpy, "count", 4);
99
101
100 verify(dashContentList.currentIndex >= 0);
102 verify(dashContentList.currentIndex >= 0);
101 }
103 }
@@ -109,9 +111,7 @@ Item {
109 // pretend we're running after a model reset
111 // pretend we're running after a model reset
110 dashContentList.currentIndex = 27;
112 dashContentList.currentIndex = 27;
111
113
112− scopeLoadedSpy.clear();
114+ loadScopes();
113− scopesModel.load();
114− tryCompare(scopeLoadedSpy, "count", 4);
115
115
116 verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
116 verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
117 }
117 }
@@ -326,8 +326,6 @@ Item {
326 }
326 }
327
327
328 function test_previewOpenClose() {
328 function test_previewOpenClose() {
329− tryCompare(scopeLoadedSpy, "count", 4);
330−
331 var previewListView = findChild(dashContent, "dashContentPreviewList");
329 var previewListView = findChild(dashContent, "dashContentPreviewList");
332 tryCompare(previewListView, "open", false);
330 tryCompare(previewListView, "open", false);
333
331
@@ -352,8 +350,6 @@ Item {
352 },
350 },
353 true);
351 true);
354
352
355− tryCompare(scopeLoadedSpy, "count", 4);
356−
357 var previewListView = findChild(dashContent, "dashContentPreviewList");
353 var previewListView = findChild(dashContent, "dashContentPreviewList");
358 tryCompare(previewListView, "open", false);
354 tryCompare(previewListView, "open", false);
359
355
@@ -368,8 +364,6 @@ Item {
368 }
364 }
369
365
370 function test_previewCycle() {
366 function test_previewCycle() {
371− tryCompare(scopeLoadedSpy, "count", 4);
372−
373 var categoryListView = findChild(dashContent, "categoryListView");
367 var categoryListView = findChild(dashContent, "categoryListView");
374 categoryListView.positionAtBeginning();
368 categoryListView.positionAtBeginning();
375
369
@@ -392,5 +386,26 @@ Item {
392 }
386 }
393 closePreview();
387 closePreview();
394 }
388 }
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+ }
395 }
410 }
396}
411}
tests/qmltests/Dash/tst_GenericScopeView.qml+1
@@ -126,6 +126,7 @@ Item {
126
126
127 function test_filter_expand_collapse() {
127 function test_filter_expand_collapse() {
128 // wait for the item to be there
128 // wait for the item to be there
129+ waitForRendering(genericScopeView);
129 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
130 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
130
131
131 var header = findChild(genericScopeView, "dashSectionHeader0")
132 var header = findChild(genericScopeView, "dashSectionHeader0")
tests/qmltests/Greeter/tst_Lockscreen.qml+208−31
@@ -17,31 +17,39 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import ".."
19import ".."
20−import "../../../qml/Greeter"
20+import "../../../qml/Components"
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import LightDM 0.1 as LightDM
22import LightDM 0.1 as LightDM
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Rectangle {
25Rectangle {
26+ id: root
26− width: units.gu(60)
27+ width: units.gu(80)
27 height: units.gu(80)
28 height: units.gu(80)
28 color: "orange"
29 color: "orange"
29
30
30 Lockscreen {
31 Lockscreen {
31 id: lockscreen
32 id: lockscreen
32 anchors.fill: parent
33 anchors.fill: parent
33− anchors.rightMargin: units.gu(18)
34+ anchors.rightMargin: units.gu(30)
34 placeholderText: "Please enter your PIN"
35 placeholderText: "Please enter your PIN"
36+ wrongPlaceholderText: "Incorrect PIN"
37+ retryText: retryCountTextField.text
35 alphaNumeric: pinPadCheckBox.checked
38 alphaNumeric: pinPadCheckBox.checked
36− pinLength: pinLengthTextField.text
39+ minPinLength: minPinLengthTextField.text
40+ maxPinLength: maxPinLengthTextField.text
37 username: "Lola"
41 username: "Lola"
42+ infoText: infoTextTextField.text
38 }
43 }
39
44
40 Connections {
45 Connections {
41 target: lockscreen
46 target: lockscreen
42
47
43 onEmergencyCall: emergencyCheckBox.checked = true
48 onEmergencyCall: emergencyCheckBox.checked = true
49+ onEntered: {
44− onEntered: enteredLabel.text = passphrase
50+ enteredLabel.text = passphrase
51+ lockscreen.clear(true)
52+ }
45 }
53 }
46
54
47 Connections {
55 Connections {
@@ -49,9 +57,9 @@ Rectangle {
49
57
50 onShowPrompt: {
58 onShowPrompt: {
51 if (text.indexOf("PIN") >= 0) {
59 if (text.indexOf("PIN") >= 0) {
52− lockscreen.alphaNumeric = false
60+ pinPadCheckBox.checked = false
53 } else {
61 } else {
54− lockscreen.alphaNumeric = true
62+ pinPadCheckBox.checked = true
55 }
63 }
56 lockscreen.placeholderText = text;
64 lockscreen.placeholderText = text;
57 }
65 }
@@ -65,6 +73,7 @@ Rectangle {
65 Column {
73 Column {
66 anchors.fill: parent
74 anchors.fill: parent
67 anchors.margins: units.gu(1)
75 anchors.margins: units.gu(1)
76+ spacing: units.gu(1)
68
77
69 Row {
78 Row {
70 CheckBox {
79 CheckBox {
@@ -86,12 +95,32 @@ Rectangle {
86 }
95 }
87 Row {
96 Row {
88 TextField {
97 TextField {
89− id: pinLengthTextField
98+ id: minPinLengthTextField
90 width: units.gu(7)
99 width: units.gu(7)
91 text: "4"
100 text: "4"
92 }
101 }
93 Label {
102 Label {
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 {
94− text: "PIN length"
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"
95 }
124 }
96 }
125 }
97 Label {
126 Label {
@@ -108,12 +137,40 @@ Rectangle {
108 }
137 }
109 Button {
138 Button {
110 text: "start auth (1234)"
139 text: "start auth (1234)"
140+ width: parent.width
111 onClicked: LightDM.Greeter.authenticate("has-pin")
141 onClicked: LightDM.Greeter.authenticate("has-pin")
112 }
142 }
113 Button {
143 Button {
114 text: "start auth (password)"
144 text: "start auth (password)"
145+ width: parent.width
115 onClicked: LightDM.Greeter.authenticate("has-password")
146 onClicked: LightDM.Greeter.authenticate("has-password")
116 }
147 }
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+ }
117 }
174 }
118 }
175 }
119
176
@@ -121,6 +178,16 @@ Rectangle {
121 name: "Lockscreen"
178 name: "Lockscreen"
122 when: windowShown
179 when: windowShown
123
180
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+
124 function test_loading_data() {
191 function test_loading_data() {
125 return [
192 return [
126 {tag: "numeric", alphanumeric: false, pinPadAvailable: true },
193 {tag: "numeric", alphanumeric: false, pinPadAvailable: true },
@@ -129,8 +196,8 @@ Rectangle {
129 }
196 }
130
197
131 function test_loading(data) {
198 function test_loading(data) {
132− lockscreen.alphaNumeric = data.alphanumeric
199+ pinPadCheckBox.checked = data.alphanumeric
133− waitForRendering(lockscreen)
200+ waitForLockscreenReady();
134 if (data.pinPadAvailable) {
201 if (data.pinPadAvailable) {
135 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
202 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
136 } else {
203 } else {
@@ -147,8 +214,8 @@ Rectangle {
147
214
148 function test_emergency_call(data) {
215 function test_emergency_call(data) {
149 emergencyCheckBox.checked = false
216 emergencyCheckBox.checked = false
150− lockscreen.alphaNumeric = data.alphanumeric
217+ pinPadCheckBox.checked = data.alphanumeric
151− waitForRendering(lockscreen)
218+ waitForLockscreenReady();
152 var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
219 var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
153 mouseClick(emergencyButton, units.gu(1), units.gu(1))
220 mouseClick(emergencyButton, units.gu(1), units.gu(1))
154 tryCompare(emergencyCheckBox, "checked", true)
221 tryCompare(emergencyCheckBox, "checked", true)
@@ -163,9 +230,9 @@ Rectangle {
163 }
230 }
164
231
165 function test_labels(data) {
232 function test_labels(data) {
166− lockscreen.alphaNumeric = data.alphanumeric
233+ pinPadCheckBox.checked = data.alphanumeric
167 lockscreen.placeholderText = data.placeholderText
234 lockscreen.placeholderText = data.placeholderText
168− waitForRendering(lockscreen)
235+ waitForLockscreenReady();
169 compare(findChild(lockscreen, "pinentryField").placeholderText, data.placeholderText, "Placeholdertext is not what it should be")
236 compare(findChild(lockscreen, "pinentryField").placeholderText, data.placeholderText, "Placeholdertext is not what it should be")
170 if (data.alphanumeric) {
237 if (data.alphanumeric) {
171 compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
238 compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
@@ -175,23 +242,25 @@ Rectangle {
175
242
176 function test_unlock_data() {
243 function test_unlock_data() {
177 return [
244 return [
178− {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", pinLength: 4},
245+ {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
179− {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", pinLength: -1},
246+ {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", minPinLength: -1, maxPinLength: -1},
180− {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", pinLength: 4},
247+ {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", minPinLength: 4, maxPinLength: 4},
181− {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", pinLength: -1},
248+ {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", minPinLength: -1, maxPinLength: -1},
182− {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", pinLength: -1},
249+ {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: -1, maxPinLength: -1},
183 ]
250 ]
184 }
251 }
185
252
186 function test_unlock(data) {
253 function test_unlock(data) {
187 enteredLabel.text = ""
254 enteredLabel.text = ""
188− pinLengthTextField.text = data.pinLength
255+ minPinLengthTextField.text = data.minPinLength
256+ maxPinLengthTextField.text = data.maxPinLength
189 LightDM.Greeter.authenticate(data.username)
257 LightDM.Greeter.authenticate(data.username)
190− waitForRendering(lockscreen)
258+ waitForLockscreenReady();
191
259
192 var inputField = findChild(lockscreen, "pinentryField")
260 var inputField = findChild(lockscreen, "pinentryField")
193 if (data.alphanumeric) {
261 if (data.alphanumeric) {
194 mouseClick(inputField, units.gu(1), units.gu(1))
262 mouseClick(inputField, units.gu(1), units.gu(1))
263+ tryCompare(inputField, "focus", true);
195 typeString(data.password)
264 typeString(data.password)
196 keyClick(Qt.Key_Enter)
265 keyClick(Qt.Key_Enter)
197 } else {
266 } else {
@@ -200,7 +269,7 @@ Rectangle {
200 var button = findChild(lockscreen, "pinPadButton" + character)
269 var button = findChild(lockscreen, "pinPadButton" + character)
201 mouseClick(button, units.gu(1), units.gu(1))
270 mouseClick(button, units.gu(1), units.gu(1))
202 }
271 }
203− if (data.pinLength == -1) {
272+ if (data.minPinLength !== data.maxPinLength || data.minPinLength == -1) {
204 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
273 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
205 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
274 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
206 }
275 }
@@ -219,11 +288,12 @@ Rectangle {
219
288
220 function test_clear(data) {
289 function test_clear(data) {
221 pinPadCheckBox.checked = data.alphanumeric
290 pinPadCheckBox.checked = data.alphanumeric
222− waitForRendering(lockscreen)
291+ waitForLockscreenReady();
223
292
224 var inputField = findChild(lockscreen, "pinentryField")
293 var inputField = findChild(lockscreen, "pinentryField")
225 if (data.alphanumeric) {
294 if (data.alphanumeric) {
226 mouseClick(inputField, units.gu(1), units.gu(1))
295 mouseClick(inputField, units.gu(1), units.gu(1))
296+ tryCompare(inputField, "activeFocus", true);
227 typeString("1")
297 typeString("1")
228 } else {
298 } else {
229 var button = findChild(lockscreen, "pinPadButton1")
299 var button = findChild(lockscreen, "pinPadButton1")
@@ -239,6 +309,16 @@ Rectangle {
239
309
240 wait(0) // Trigger event loop to make sure the animation would start running
310 wait(0) // Trigger event loop to make sure the animation would start running
241 compare(animation.running, data.animation)
311 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+ }
242
322
243 // wait for animation to finish to not disturb other tests
323 // wait for animation to finish to not disturb other tests
244 tryCompare(animation, "running", false)
324 tryCompare(animation, "running", false)
@@ -246,33 +326,130 @@ Rectangle {
246
326
247 function test_backspace_data() {
327 function test_backspace_data() {
248 return [
328 return [
249− {tag: "fixed length", pinLength: 4},
329+ {tag: "fixed length", minPinLength: 4, maxPinLength: 4},
250− {tag: "variable length", pinLength: -1}
330+ {tag: "variable undefined length", minPinLength: -1, maxPinLength: -1},
331+ {tag: "variable restricted length", minPinLength: 4, maxPinLength: 8}
251 ];
332 ];
252 }
333 }
253
334
254 function test_backspace(data) {
335 function test_backspace(data) {
255 pinPadCheckBox.checked = false
336 pinPadCheckBox.checked = false
256− pinLengthTextField.text = data.pinLength
337+ minPinLengthTextField.text = data.minPinLength
257− waitForRendering(lockscreen);
338+ maxPinLengthTextField.text = data.maxPinLength
339+ waitForLockscreenReady();
258
340
259 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
341 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
260 var backspaceIcon = findChild(lockscreen, "backspaceIcon");
342 var backspaceIcon = findChild(lockscreen, "backspaceIcon");
261 var pinEntryField = findChild(lockscreen, "pinentryField");
343 var pinEntryField = findChild(lockscreen, "pinentryField");
262
344
345+ var autoConfirmEnabled = data.minPinLength === data.maxPinLength && data.minPinLength !== -1;
263− compare(pinPadButtonErase.iconName, data.pinLength == -1 ? "" : "erase");
346+ compare(pinPadButtonErase.iconName, autoConfirmEnabled ? "erase" : "");
264− compare(backspaceIcon.visible, data.pinLength == -1);
347+ compare(backspaceIcon.visible, !autoConfirmEnabled);
265
348
266 var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
349 var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
267 mouseClick(pinPadButton5, units.gu(1), units.gu(1));
350 mouseClick(pinPadButton5, units.gu(1), units.gu(1));
268 compare(pinEntryField.text, "5");
351 compare(pinEntryField.text, "5");
269
352
270− if (data.pinLength == -1) {
353+ if (data.minPinLength !== data.maxPinLength) {
271 mouseClick(backspaceIcon, units.gu(1), units.gu(1));
354 mouseClick(backspaceIcon, units.gu(1), units.gu(1));
272 } else {
355 } else {
273 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
356 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
274 }
357 }
275 compare(pinEntryField.text, "");
358 compare(pinEntryField.text, "");
276 }
359 }
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
277 }
454 }
278}
455}
tests/qmltests/Greeter/tst_Phone.qml+1−17
@@ -33,7 +33,7 @@ Item {
33 height: parent.height
33 height: parent.height
34 x: 0; y: 0
34 x: 0; y: 0
35
35
36− defaultBackground: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
36+ background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
37
37
38 property int minX: 0
38 property int minX: 0
39
39
@@ -104,22 +104,6 @@ Item {
104 tryCompare(LightDM.Infographic, "username", "single")
104 tryCompare(LightDM.Infographic, "username", "single")
105 }
105 }
106
106
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−
123 function test_initial_selected_signal() {
107 function test_initial_selected_signal() {
124 var greeterObj = greeterComponent.createObject(this)
108 var greeterObj = greeterComponent.createObject(this)
125 var spy = findChild(greeterObj, "selectedSpy")
109 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
23import Unity.Application 0.1
23import Unity.Application 0.1
24import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
25import Powerd 0.1
25import Powerd 0.1
26+import SessionManager 0.1
26
27
27import "../../qml"
28import "../../qml"
28
29
@@ -48,6 +49,7 @@ Item {
48
49
49 Shell {
50 Shell {
50 id: shell
51 id: shell
52+ defaultBackground: Qt.resolvedUrl("../../qml/graphics/phone_background.jpg")
51 }
53 }
52
54
53 UT.UnityTestCase {
55 UT.UnityTestCase {
@@ -72,9 +74,6 @@ Item {
72 ok = homeLoader !== null
74 ok = homeLoader !== null
73 && homeLoader.item !== undefined;
75 && homeLoader.item !== undefined;
74
76
75− var greeter = findChild(shell, "greeter");
76− ok &= greeter !== null;
77−
78 var launcherPanel = findChild(shell, "launcherPanel");
77 var launcherPanel = findChild(shell, "launcherPanel");
79 ok &= launcherPanel !== null;
78 ok &= launcherPanel !== null;
80
79
@@ -89,17 +88,9 @@ Item {
89 } while (!ok && attempts <= maxAttempts);
88 } while (!ok && attempts <= maxAttempts);
90
89
91 verify(ok);
90 verify(ok);
92−
93− swipeAwayGreeter();
94 }
91 }
95
92
96 function cleanup() {
93 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−
103 // kill all (fake) running apps
94 // kill all (fake) running apps
104 killApps(ApplicationManager);
95 killApps(ApplicationManager);
105
96
@@ -181,8 +172,6 @@ Item {
181 }
172 }
182
173
183 function test_suspend() {
174 function test_suspend() {
184− var greeter = findChild(shell, "greeter");
185−
186 // Launch an app from the launcher
175 // Launch an app from the launcher
187 dragLauncherIntoView();
176 dragLauncherIntoView();
188 tapOnAppIconInLauncher();
177 tapOnAppIconInLauncher();
@@ -196,40 +185,27 @@ Item {
196
185
197 // Try to suspend while proximity is engaged...
186 // Try to suspend while proximity is engaged...
198 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
187 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
199− tryCompare(greeter, "showProgress", 0);
188+ tryCompare(SessionManager, "active", true);
200
189
201 // Now really suspend
190 // Now really suspend
202 print("suspending")
191 print("suspending")
203 Powerd.displayPowerStateChange(Powerd.Off, 0);
192 Powerd.displayPowerStateChange(Powerd.Off, 0);
204 print("done suspending")
193 print("done suspending")
205− tryCompare(greeter, "showProgress", 1);
194+ tryCompare(SessionManager, "active", false);
206
195
207 tryCompare(ApplicationManager, "suspended", true);
196 tryCompare(ApplicationManager, "suspended", true);
208 compare(mainApp.state, ApplicationInfo.Suspended);
197 compare(mainApp.state, ApplicationInfo.Suspended);
209
198
210 // And wake up
199 // And wake up
211 Powerd.displayPowerStateChange(Powerd.On, 0);
200 Powerd.displayPowerStateChange(Powerd.On, 0);
212− tryCompare(greeter, "showProgress", 1);
213
201
214− // Swipe away greeter to focus app
202+ // Activate session to focus app
215− swipeAwayGreeter();
203+ SessionManager.active = true;
216 tryCompare(ApplicationManager, "suspended", false);
204 tryCompare(ApplicationManager, "suspended", false);
217 compare(mainApp.state, ApplicationInfo.Running);
205 compare(mainApp.state, ApplicationInfo.Running);
218 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
206 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
219 }
207 }
220
208
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−
233 /*
209 /*
234 Perform a right-edge drag when the Dash is being show and there are
210 Perform a right-edge drag when the Dash is being show and there are
235 no running/minimized apps to be restored.
211 no running/minimized apps to be restored.
@@ -475,33 +451,16 @@ Item {
475 } while (!isStill);
451 } while (!isStill);
476 }
452 }
477
453
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−
487 function test_DashShown_data() {
454 function test_DashShown_data() {
488 return [
455 return [
489− {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},
456+ {tag: "in focus", 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},
457+ {tag: "under app", app: true, launcher: false, indicators: false, expectedShown: false},
492− {tag: "under launcher", greeter: false, app: false, launcher: true, indicators: false, expectedShown: true},
458+ {tag: "under launcher", app: false, launcher: true, indicators: false, expectedShown: true},
493− {tag: "under indicators", greeter: false, app: false, launcher: false, indicators: true, expectedShown: true},
459+ {tag: "under indicators", app: false, launcher: false, indicators: true, expectedShown: true},
494 ]
460 ]
495 }
461 }
496
462
497 function test_DashShown(data) {
463 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−
505 if (data.app) {
464 if (data.app) {
506 dragLauncherIntoView();
465 dragLauncherIntoView();
507 tapOnAppIconInLauncher();
466 tapOnAppIconInLauncher();
@@ -531,17 +490,6 @@ Item {
531 tryCompare(searchIndicator, "opacity", 0);
490 tryCompare(searchIndicator, "opacity", 0);
532 }
491 }
533
492
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−
545 function test_searchIndicatorHideOnPreviewShown() {
493 function test_searchIndicatorHideOnPreviewShown() {
546 var searchIndicator = findChild(shell, "container");
494 var searchIndicator = findChild(shell, "container");
547 var dashContent = findChild(shell, "dashContent");
495 var dashContent = findChild(shell, "dashContent");
@@ -554,16 +502,5 @@ Item {
554
502
555 tryCompare(searchIndicator, "opacity", 0);
503 tryCompare(searchIndicator, "opacity", 0);
556 }
504 }
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− }
568 }
505 }
569}
506}
tools/unlock-device+10−18
@@ -17,24 +17,16 @@ while getopts s:w: opt; do
17 esac
17 esac
18done
18done
19
19
20−UNLOCK_SCRIPT='
20+LIGHTDM_CONFIG="
21−import dbus, logging;
21+[SeatDefaults]
22−from unity8 import process_helpers as helpers;
22+autologin-in-background=false
23−logging.basicConfig(level=logging.INFO);
23+"
24−bus = dbus.SystemBus().get_object("com.canonical.powerd", "/com/canonical/powerd");
24+LIGHTDM_CONFIG_FILE="/etc/lightdm/lightdm.conf.d/99-unity8-greeter-unlock.conf"
25−cookie = bus.requestSysState("unlock-device-hold", 1, dbus_interface="com.canonical.powerd");
25+
26−helpers.restart_unity_with_testability();
26+adb shell "echo '$LIGHTDM_CONFIG' > $LIGHTDM_CONFIG_FILE"
27−bus.clearSysState(cookie, dbus_interface="com.canonical.powerd");
28−helpers.unlock_unity()
29−'
30
27
31eval "$WAIT_COMMAND"
28eval "$WAIT_COMMAND"
32
29
33−UNLOCK_OUTPUT=$(adb shell "sudo -u phablet -i python3 -c '$UNLOCK_SCRIPT'" 2>&1)
30+adb shell "rm -f $LIGHTDM_CONFIG_FILE"
34−if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
31+
35− echo "I: Unlock passed"
36− exit 0
32+exit 0
37−else
38− echo "I: Unlock failed, script output: '$UNLOCK_OUTPUT'"
39− exit 1
40−fi