on xenial help sign in
Merge lp:~saviq/unity8/grey-from-palette
f460ff1Michał Sawicz
cmake/modules/QmlTest.cmake+2−2
@@ -103,7 +103,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
103 endif()
103 endif()
104
104
105 set(qmltest_command
105 set(qmltest_command
106− env ${qmltest_ENVIRONMENT}
106+ env ${qmltest_ENVIRONMENT} UNITY_TESTING=1
107 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
107 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
108 ${qmltestrunner_imports}
108 ${qmltestrunner_imports}
109 ${ITERATIONS_STRING}
109 ${ITERATIONS_STRING}
@@ -117,7 +117,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
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
120− env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH}
120+ env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH} UNITY_TESTING=1
121 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
121 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
122 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
122 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
123 ${qmltestrunner_imports}
123 ${qmltestrunner_imports}
cmake/modules/autopilot.cmake+2−2
@@ -2,7 +2,7 @@ add_custom_target(autopilot)
2
2
3function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
3function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
4 add_custom_target(autopilot-${TEST_NAME}
4 add_custom_target(autopilot-${TEST_NAME}
5− COMMAND LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python3 -m autopilot.run run ${TEST_SUITE}
5+ COMMAND UNITY_TESTING=1 LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python3 -m autopilot.run run ${TEST_SUITE}
6 WORKING_DIRECTORY ${WORKING_DIR}
6 WORKING_DIRECTORY ${WORKING_DIR}
7 DEPENDS fake_install
7 DEPENDS fake_install
8 )
8 )
@@ -14,7 +14,7 @@ function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
14 add_dependencies(autopilot autopilot-${TEST_NAME})
14 add_dependencies(autopilot autopilot-${TEST_NAME})
15
15
16 add_custom_target(autopilot2-${TEST_NAME}
16 add_custom_target(autopilot2-${TEST_NAME}
17− COMMAND LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python2 -m autopilot.run run ${TEST_SUITE}
17+ COMMAND UNITY_TESTING=1 LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python2 -m autopilot.run run ${TEST_SUITE}
18 WORKING_DIRECTORY ${WORKING_DIR}
18 WORKING_DIRECTORY ${WORKING_DIR}
19 DEPENDS fake_install
19 DEPENDS fake_install
20 )
20 )
debian/changelog+47
@@ -1,3 +1,50 @@
1+unity8 (8.00+14.10.20140814.1-0ubuntu1) utopic; urgency=low
2+
3+ [ Michael Terry ]
4+ * Add --lightdm= argument to ./run.sh that lets developers choose
5+ which lightdm backend to use. Stop letting a user that is
6+ immediately denied via PAM into the shell by fixing some assumptions
7+ that a user which was not prompted was successfully authenticated.
8+ This is not a common situation, you'd have to manually change your
9+ PAM config. Fix a small console warning .
10+ * Make wrong-password handling much nicer by showing a pretty spinner
11+ while we wait for PAM, by improving the prompt text to match
12+ designs, by forcing the user to wait five seconds after every five
13+ failed attemps, and by supporting (but not yet enabling) an opt-in
14+ "factory-reset your phone after X failed attemps" feature.
15+
16+ [ Michael Zanetti ]
17+ * bring back network caching in dash (LP: #1355729)
18+
19+ [ Michał Sawicz ]
20+ * Add --lightdm= argument to ./run.sh that lets developers choose
21+ which lightdm backend to use. Stop letting a user that is
22+ immediately denied via PAM into the shell by fixing some assumptions
23+ that a user which was not prompted was successfully authenticated.
24+ This is not a common situation, you'd have to manually change your
25+ PAM config. Fix a small console warning .
26+ * Fix anchor in PreviewListView.qml.
27+ * Make wrong-password handling much nicer by showing a pretty spinner
28+ while we wait for PAM, by improving the prompt text to match
29+ designs, by forcing the user to wait five seconds after every five
30+ failed attemps, and by supporting (but not yet enabling) an opt-in
31+ "factory-reset your phone after X failed attemps" feature.
32+ * Add new horizontal list category layout. (LP: #1352226)
33+ * Fix qml tests - loader around PageHeader, more retries for selecting
34+ a scope and undefined attributes in mock overview scope.
35+
36+ [ Leo Arias ]
37+ * Added autopilot helpers and tests for the launcher and dash icon.
38+ * Added an autopilot helper to click a scope item.
39+ * Added an autopilot test for focusing an app clicking the icon on the
40+ launcher.
41+
42+ [ Mirco Müller ]
43+ * Allow ENTER/RETURN in a TextField to accept a snap-decision
44+ notification. (LP: #1305885)
45+
46+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 14 Aug 2014 01:29:55 +0000
47+
1unity8 (8.00+14.10.20140811-0ubuntu1) utopic; urgency=low
48unity8 (8.00+14.10.20140811-0ubuntu1) utopic; urgency=low
2
49
3 [ Andrea Cimitan ]
50 [ Andrea Cimitan ]
debian/control+2−1
@@ -165,7 +165,8 @@ Package: unity8-private
165Architecture: any
165Architecture: any
166Multi-Arch: same
166Multi-Arch: same
167Pre-Depends: ${misc:Pre-Depends},
167Pre-Depends: ${misc:Pre-Depends},
168+Depends: accountsservice-ubuntu-schemas,
168−Depends: gsettings-ubuntu-schemas,
169+ gsettings-ubuntu-schemas,
169 libhardware2,
170 libhardware2,
170 libunity-core-6.0-9,
171 libunity-core-6.0-9,
171 pay-service,
172 pay-service,
debian/unity8-private.install−2
@@ -10,5 +10,3 @@ usr/lib/*/unity8/qml/Unity
10usr/lib/*/unity8/qml/Utils
10usr/lib/*/unity8/qml/Utils
11usr/share/accountsservice/interfaces
11usr/share/accountsservice/interfaces
12usr/share/dbus-1/interfaces
12usr/share/dbus-1/interfaces
13−usr/share/polkit-1
14−var/lib/polkit-1
plugins/AccountsService/50-com.canonical.unity.AccountsService.pkladeleted−6
@@ -1,6 +0,0 @@
1−[Allow LightDM to set Unity AccountsService fields]
2−Identity=unix-user:lightdm
3−Action=com.canonical.unity.AccountsService.ModifyAnyUser
4−ResultActive=yes
5−ResultInactive=yes
6−ResultAny=yes
plugins/AccountsService/AccountsService.cpp+27−1
@@ -27,7 +27,8 @@ AccountsService::AccountsService(QObject* parent)
27 m_user(qgetenv("USER")),
27 m_user(qgetenv("USER")),
28 m_demoEdges(false),
28 m_demoEdges(false),
29 m_statsWelcomeScreen(false),
29 m_statsWelcomeScreen(false),
30− m_passwordDisplayHint(Keyboard)
30+ m_passwordDisplayHint(Keyboard),
31+ m_failedLogins(0)
31{
32{
32 connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
33 connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
33 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
34 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
@@ -49,6 +50,7 @@ void AccountsService::setUser(const QString &user)
49 updateBackgroundFile();
50 updateBackgroundFile();
50 updateStatsWelcomeScreen();
51 updateStatsWelcomeScreen();
51 updatePasswordDisplayHint();
52 updatePasswordDisplayHint();
53+ updateFailedLogins();
52}
54}
53
55
54bool AccountsService::demoEdges() const
56bool AccountsService::demoEdges() const
@@ -113,6 +115,26 @@ void AccountsService::updatePasswordDisplayHint()
113 }
115 }
114}
116}
115
117
118+void AccountsService::updateFailedLogins()
119+{
120+ uint failedLogins = m_service->getUserProperty(m_user, "com.canonical.unity.AccountsService.Private", "FailedLogins").toUInt();
121+ if (m_failedLogins != failedLogins) {
122+ m_failedLogins = failedLogins;
123+ Q_EMIT failedLoginsChanged();
124+ }
125+}
126+
127+uint AccountsService::failedLogins() const
128+{
129+ return m_failedLogins;
130+}
131+
132+void AccountsService::setFailedLogins(uint failedLogins)
133+{
134+ m_failedLogins = failedLogins;
135+ m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService.Private", "FailedLogins", failedLogins);
136+}
137+
116void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
138void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
117{
139{
118 if (m_user != user) {
140 if (m_user != user) {
@@ -123,6 +145,10 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
123 if (changed.contains("demo-edges")) {
145 if (changed.contains("demo-edges")) {
124 updateDemoEdges();
146 updateDemoEdges();
125 }
147 }
148+ } else if (interface == "com.canonical.unity.AccountsService.Private") {
149+ if (changed.contains("FailedLogins")) {
150+ updateFailedLogins();
151+ }
126 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
152 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
127 if (changed.contains("StatsWelcomeScreen")) {
153 if (changed.contains("StatsWelcomeScreen")) {
128 updateStatsWelcomeScreen();
154 updateStatsWelcomeScreen();
plugins/AccountsService/AccountsService.h+9
@@ -45,6 +45,10 @@ class AccountsService: public QObject
45 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
45 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
46 READ passwordDisplayHint
46 READ passwordDisplayHint
47 NOTIFY passwordDisplayHintChanged)
47 NOTIFY passwordDisplayHintChanged)
48+ Q_PROPERTY (uint failedLogins
49+ READ failedLogins
50+ WRITE setFailedLogins
51+ NOTIFY failedLoginsChanged)
48
52
49public:
53public:
50 enum PasswordDisplayHint {
54 enum PasswordDisplayHint {
@@ -61,6 +65,8 @@ public:
61 QString backgroundFile() const;
65 QString backgroundFile() const;
62 bool statsWelcomeScreen() const;
66 bool statsWelcomeScreen() const;
63 PasswordDisplayHint passwordDisplayHint() const;
67 PasswordDisplayHint passwordDisplayHint() const;
68+ uint failedLogins() const;
69+ void setFailedLogins(uint failedLogins);
64
70
65Q_SIGNALS:
71Q_SIGNALS:
66 void userChanged();
72 void userChanged();
@@ -68,6 +74,7 @@ Q_SIGNALS:
68 void backgroundFileChanged();
74 void backgroundFileChanged();
69 void statsWelcomeScreenChanged();
75 void statsWelcomeScreenChanged();
70 void passwordDisplayHintChanged();
76 void passwordDisplayHintChanged();
77+ void failedLoginsChanged();
71
78
72private Q_SLOTS:
79private Q_SLOTS:
73 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
80 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
@@ -78,6 +85,7 @@ private:
78 void updateBackgroundFile();
85 void updateBackgroundFile();
79 void updateStatsWelcomeScreen();
86 void updateStatsWelcomeScreen();
80 void updatePasswordDisplayHint();
87 void updatePasswordDisplayHint();
88+ void updateFailedLogins();
81
89
82 AccountsServiceDBusAdaptor *m_service;
90 AccountsServiceDBusAdaptor *m_service;
83 QString m_user;
91 QString m_user;
@@ -85,6 +93,7 @@ private:
85 QString m_backgroundFile;
93 QString m_backgroundFile;
86 bool m_statsWelcomeScreen;
94 bool m_statsWelcomeScreen;
87 PasswordDisplayHint m_passwordDisplayHint;
95 PasswordDisplayHint m_passwordDisplayHint;
96+ uint m_failedLogins;
88};
97};
89
98
90#endif
99#endif
plugins/AccountsService/CMakeLists.txt−10
@@ -10,8 +10,6 @@ qt5_use_modules(AccountsService-qml DBus Qml)
10
10
11add_unity8_plugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
11add_unity8_plugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
12
12
13−set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
14−set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
15set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
13set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
16set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
14set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
17
15
@@ -24,11 +22,3 @@ install(CODE "
24execute_process(COMMAND mkdir -p \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
22execute_process(COMMAND mkdir -p \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
25execute_process(COMMAND ln -sf ../../dbus-1/interfaces/com.canonical.unity.AccountsService.xml \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
23execute_process(COMMAND ln -sf ../../dbus-1/interfaces/com.canonical.unity.AccountsService.xml \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
26 ")
24 ")
27−
28−install(FILES com.canonical.unity.AccountsService.policy
29− DESTINATION "${POLKIT_DATA_DIR}/actions"
30− )
31−
32−install(FILES 50-com.canonical.unity.AccountsService.pkla
33− DESTINATION "${POLKIT_LIB_DIR}/localauthority/10-vendor.d"
34− )
plugins/AccountsService/com.canonical.unity.AccountsService.policydeleted−24
@@ -1,24 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−
3−<policyconfig>
4− <action id="com.canonical.unity.AccountsService.ModifyOwnUser">
5− <description>Set properties of own user</description>
6− <message>Authentication is required to set one's own unity properties.</message>
7− <defaults>
8− <allow_any>yes</allow_any>
9− <allow_inactive>yes</allow_inactive>
10− <allow_active>yes</allow_active>
11− </defaults>
12− </action>
13−
14− <action id="com.canonical.unity.AccountsService.ModifyAnyUser">
15− <description>Set properties of any user</description>
16− <message>Authentication is required to set another user's unity
17−properties.</message>
18− <defaults>
19− <allow_any>no</allow_any>
20− <allow_inactive>no</allow_inactive>
21− <allow_active>no</allow_active>
22− </defaults>
23− </action>
24−</policyconfig>
plugins/AccountsService/com.canonical.unity.AccountsService.xml+28−5
@@ -3,22 +3,45 @@
3
3
4 <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
4 <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
5
5
6− <annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
7− value="com.canonical.unity.AccountsService.ModifyOwnUser"/>
8−
9 <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
6 <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
10− value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
7+ value="com.ubuntu.AccountsService.GreeterReadAny"/>
11
8
12 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
9 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
13− value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
10+ value="com.ubuntu.AccountsService.GreeterChangeAny"/>
14
11
12+ <!-- Should have been named DemoEdges, sorry folks. -mterry -->
15 <property name="demo-edges" type="b" access="readwrite">
13 <property name="demo-edges" type="b" access="readwrite">
16 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
14 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
17 </property>
15 </property>
18
16
17+ <!-- Should have been named LauncherItems, sorry folks. -mterry -->
19 <property name="launcher-items" type="aa{sv}" access="readwrite">
18 <property name="launcher-items" type="aa{sv}" access="readwrite">
20 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
19 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
21 </property>
20 </property>
22
21
23 </interface>
22 </interface>
23+
24+ <!-- This interface is for bits of data that the greeter wants to track
25+ per-user in a persistent way, but that users shouldn't be able to edit
26+ in an ideal world.
27+
28+ This interface is identical in permissions to the above one for now,
29+ but once we stop running the greeter in user-space, we should
30+ disallow org.freedesktop.Accounts.Authentication.ChangeOwn from the
31+ Private inteface. -->
32+ <interface name="com.canonical.unity.AccountsService.Private">
33+
34+ <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
35+
36+ <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
37+ value="com.ubuntu.AccountsService.GreeterReadAny"/>
38+
39+ <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
40+ value="com.ubuntu.AccountsService.GreeterChangeAny"/>
41+
42+ <property name="FailedLogins" type="u" access="readwrite">
43+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/>
44+ </property>
45+
46+ </interface>
24</node>
47</node>
plugins/LightDM/Greeter.cpp+4−1
@@ -17,6 +17,7 @@
17 */
17 */
18
18
19#include "Greeter.h"
19#include "Greeter.h"
20+#include <libintl.h>
20#include <QLightDM/Greeter>
21#include <QLightDM/Greeter>
21
22
22class GreeterPrivate
23class GreeterPrivate
@@ -125,13 +126,15 @@ void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptTyp
125 Q_D(Greeter);
126 Q_D(Greeter);
126 d->wasPrompted = true;
127 d->wasPrompted = true;
127
128
129+ bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
130+
128 // Strip prompt of any colons at the end
131 // Strip prompt of any colons at the end
129 QString trimmedText = text.trimmed();
132 QString trimmedText = text.trimmed();
130 if (trimmedText.endsWith(":") || trimmedText.endsWith(":")) {
133 if (trimmedText.endsWith(":") || trimmedText.endsWith(":")) {
131 trimmedText.chop(1);
134 trimmedText.chop(1);
132 }
135 }
133
136
134− Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret);
137+ Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);
135}
138}
136
139
137void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
140void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
plugins/LightDM/Greeter.h+1−1
@@ -55,7 +55,7 @@ public Q_SLOTS:
55
55
56Q_SIGNALS:
56Q_SIGNALS:
57 void showMessage(const QString &text, bool isError);
57 void showMessage(const QString &text, bool isError);
58− void showPrompt(const QString &text, bool isSecret);
58+ void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
59 void authenticationComplete();
59 void authenticationComplete();
60 void authenticationUserChanged(const QString &user);
60 void authenticationUserChanged(const QString &user);
61 void isActiveChanged();
61 void isActiveChanged();
plugins/Ubuntu/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1add_subdirectory(Gestures)
1add_subdirectory(Gestures)
2add_subdirectory(DownloadDaemonListener)
2add_subdirectory(DownloadDaemonListener)
3add_subdirectory(Payments)
3add_subdirectory(Payments)
4+add_subdirectory(SystemImage)
plugins/Ubuntu/SystemImage/CMakeLists.txtnew file+10
@@ -0,0 +1,10 @@
1+set(SYSTEMIMAGE_SOURCES
2+ plugin.cpp
3+ SystemImage.cpp
4+)
5+
6+add_library(SystemImage MODULE ${SYSTEMIMAGE_SOURCES})
7+
8+qt5_use_modules(SystemImage Qml DBus Core)
9+
10+add_unity8_plugin(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS SystemImage)
plugins/Ubuntu/SystemImage/SystemImage.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "SystemImage.h"
18+#include <QDBusConnection>
19+#include <QDBusInterface>
20+
21+SystemImage::SystemImage(QObject *parent)
22+ : QObject(parent),
23+ m_interface(new QDBusInterface("com.canonical.SystemImage",
24+ "/Service",
25+ "com.canonical.SystemImage",
26+ QDBusConnection::systemBus(),
27+ this))
28+{
29+}
30+
31+void SystemImage::factoryReset()
32+{
33+ m_interface->call("FactoryReset");
34+}
plugins/Ubuntu/SystemImage/SystemImage.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef SYSTEMIMAGE_H
18+#define SYSTEMIMAGE_H
19+
20+#include <QObject>
21+
22+class QDBusInterface;
23+
24+class SystemImage : public QObject
25+{
26+ Q_OBJECT
27+ Q_DISABLE_COPY(SystemImage)
28+
29+public:
30+ explicit SystemImage(QObject *parent = 0);
31+
32+ Q_INVOKABLE void factoryReset();
33+
34+private:
35+ QDBusInterface *m_interface;
36+};
37+
38+#endif // SYSTEMIMAGE_H
plugins/Ubuntu/SystemImage/SystemImage.qmltypesnew file+17
@@ -0,0 +1,17 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Ubuntu.SystemImage 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "SystemImage"
12+ prototype: "QObject"
13+ exports: ["Ubuntu.SystemImage/SystemImage 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Method { name: "factoryReset" }
16+ }
17+}
plugins/Ubuntu/SystemImage/plugin.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "SystemImage.h"
19+
20+#include <QtQml>
21+
22+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
23+{
24+ Q_UNUSED(engine)
25+ Q_UNUSED(scriptEngine)
26+ return new SystemImage();
27+}
28+
29+void BackendPlugin::registerTypes(const char *uri)
30+{
31+ Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
32+
33+ qmlRegisterSingletonType<SystemImage>(uri, 0, 1, "SystemImage", service_provider);
34+}
plugins/Ubuntu/SystemImage/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef SYSTEMIMAGE_PLUGIN_H
18+#define SYSTEMIMAGE_PLUGIN_H
19+
20+#include <QQmlExtensionPlugin>
21+
22+class BackendPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri);
29+};
30+
31+#endif // SYSTEMIMAGE_PLUGIN_H
plugins/Ubuntu/SystemImage/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Ubuntu.SystemImage
2+plugin SystemImage
3+typeinfo SystemImage.qmltypes
po/en_AU.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
po/fi.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
po/gl.po+13−13
@@ -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-08-08 11:17+0200\n"
10"POT-Creation-Date: 2014-08-08 11:17+0200\n"
11−"PO-Revision-Date: 2014-07-25 17:22+0000\n"
11+"PO-Revision-Date: 2014-08-13 09:36+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-08-12 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,19 +28,19 @@ msgstr "Eliminar o atallo"
28
28
29#: qml/Components/Dialogs.qml:142
29#: qml/Components/Dialogs.qml:142
30msgid "Are you sure you would like to turn power off?"
30msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Está certo de apagar o aparato?"
32
32
33#: qml/Components/Dialogs.qml:144
33#: qml/Components/Dialogs.qml:144
34msgid "Power off"
34msgid "Power off"
35−msgstr ""
35+msgstr "Apagar"
36
36
37#: qml/Components/Dialogs.qml:153
37#: qml/Components/Dialogs.qml:153
38msgid "Restart"
38msgid "Restart"
39−msgstr ""
39+msgstr "Reiniciar"
40
40
41#: qml/Components/Dialogs.qml:162
41#: qml/Components/Dialogs.qml:162
42msgid "Cancel"
42msgid "Cancel"
43−msgstr ""
43+msgstr "Cancelar"
44
44
45#: qml/Components/EdgeDemoOverlay.qml:151
45#: qml/Components/EdgeDemoOverlay.qml:151
46msgid "Skip intro"
46msgid "Skip intro"
@@ -147,23 +147,23 @@ msgstr "Enviar"
147
147
148#: qml/Dash/ScopesOverview.qml:200
148#: qml/Dash/ScopesOverview.qml:200
149msgid "Manage Dash"
149msgid "Manage Dash"
150−msgstr ""
150+msgstr "Xestionar o Panel"
151
151
152#: qml/Dash/ScopesOverview.qml:405
152#: qml/Dash/ScopesOverview.qml:405
153msgid "Done"
153msgid "Done"
154−msgstr ""
154+msgstr "Feito"
155
155
156#: qml/Dash/ScopesOverview.qml:431
156#: qml/Dash/ScopesOverview.qml:431
157msgid "Store"
157msgid "Store"
158−msgstr ""
158+msgstr "Tenda"
159
159
160#: qml/Dash/ScopesOverviewTab.qml:36
160#: qml/Dash/ScopesOverviewTab.qml:36
161msgid "Favorites"
161msgid "Favorites"
162−msgstr ""
162+msgstr "Favoritos"
163
163
164#: qml/Dash/ScopesOverviewTab.qml:54
164#: qml/Dash/ScopesOverviewTab.qml:54
165msgid "All"
165msgid "All"
166−msgstr ""
166+msgstr "Todos"
167
167
168#: qml/Greeter/Greeter.qml:157
168#: qml/Greeter/Greeter.qml:157
169msgid "Swipe to unlock"
169msgid "Swipe to unlock"
@@ -231,11 +231,11 @@ msgstr "Fallou, toque para reintentar"
231
231
232#: qml/Panel/Indicators/ModemInfoItem.qml:105
232#: qml/Panel/Indicators/ModemInfoItem.qml:105
233msgid "Unlock SIM"
233msgid "Unlock SIM"
234−msgstr ""
234+msgstr "Desbloquear a SIM"
235
235
236#: qml/Panel/Indicators/RoamingIndication.qml:27
236#: qml/Panel/Indicators/RoamingIndication.qml:27
237msgid "Roaming"
237msgid "Roaming"
238−msgstr ""
238+msgstr "Itinerancia"
239
239
240#: qml/Panel/SearchIndicator.qml:27
240#: qml/Panel/SearchIndicator.qml:27
241msgid "Search"
241msgid "Search"
po/it.po+13−13
@@ -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-08-08 11:17+0200\n"
10"POT-Creation-Date: 2014-08-08 11:17+0200\n"
11−"PO-Revision-Date: 2014-07-25 16:09+0000\n"
11+"PO-Revision-Date: 2014-08-13 11:45+0000\n"
12"Last-Translator: Claudio Arseni <claudio.arseni@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-08-12 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,19 +28,19 @@ msgstr "Sblocca scorciatoia"
28
28
29#: qml/Components/Dialogs.qml:142
29#: qml/Components/Dialogs.qml:142
30msgid "Are you sure you would like to turn power off?"
30msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Spegnere veramente il dispositivo?"
32
32
33#: qml/Components/Dialogs.qml:144
33#: qml/Components/Dialogs.qml:144
34msgid "Power off"
34msgid "Power off"
35−msgstr ""
35+msgstr "Spegni"
36
36
37#: qml/Components/Dialogs.qml:153
37#: qml/Components/Dialogs.qml:153
38msgid "Restart"
38msgid "Restart"
39−msgstr ""
39+msgstr "Riavvia"
40
40
41#: qml/Components/Dialogs.qml:162
41#: qml/Components/Dialogs.qml:162
42msgid "Cancel"
42msgid "Cancel"
43−msgstr ""
43+msgstr "Annulla"
44
44
45#: qml/Components/EdgeDemoOverlay.qml:151
45#: qml/Components/EdgeDemoOverlay.qml:151
46msgid "Skip intro"
46msgid "Skip intro"
@@ -150,23 +150,23 @@ msgstr "Invia"
150
150
151#: qml/Dash/ScopesOverview.qml:200
151#: qml/Dash/ScopesOverview.qml:200
152msgid "Manage Dash"
152msgid "Manage Dash"
153−msgstr ""
153+msgstr "Gestione della Dash"
154
154
155#: qml/Dash/ScopesOverview.qml:405
155#: qml/Dash/ScopesOverview.qml:405
156msgid "Done"
156msgid "Done"
157−msgstr ""
157+msgstr "Fatto"
158
158
159#: qml/Dash/ScopesOverview.qml:431
159#: qml/Dash/ScopesOverview.qml:431
160msgid "Store"
160msgid "Store"
161−msgstr ""
161+msgstr "Store"
162
162
163#: qml/Dash/ScopesOverviewTab.qml:36
163#: qml/Dash/ScopesOverviewTab.qml:36
164msgid "Favorites"
164msgid "Favorites"
165−msgstr ""
165+msgstr "Preferiti"
166
166
167#: qml/Dash/ScopesOverviewTab.qml:54
167#: qml/Dash/ScopesOverviewTab.qml:54
168msgid "All"
168msgid "All"
169−msgstr ""
169+msgstr "Tutto"
170
170
171#: qml/Greeter/Greeter.qml:157
171#: qml/Greeter/Greeter.qml:157
172msgid "Swipe to unlock"
172msgid "Swipe to unlock"
@@ -234,11 +234,11 @@ msgstr "Non riuscito. Tocca per riprovare"
234
234
235#: qml/Panel/Indicators/ModemInfoItem.qml:105
235#: qml/Panel/Indicators/ModemInfoItem.qml:105
236msgid "Unlock SIM"
236msgid "Unlock SIM"
237−msgstr ""
237+msgstr "Sblocca SIM"
238
238
239#: qml/Panel/Indicators/RoamingIndication.qml:27
239#: qml/Panel/Indicators/RoamingIndication.qml:27
240msgid "Roaming"
240msgid "Roaming"
241−msgstr ""
241+msgstr "Roaming"
242
242
243#: qml/Panel/SearchIndicator.qml:27
243#: qml/Panel/SearchIndicator.qml:27
244msgid "Search"
244msgid "Search"
po/km.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
po/nb.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
po/nl.po+6−6
@@ -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-08-08 11:17+0200\n"
10"POT-Creation-Date: 2014-08-08 11:17+0200\n"
11−"PO-Revision-Date: 2014-08-12 18:13+0000\n"
11+"PO-Revision-Date: 2014-08-13 09:06+0000\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
13"Language-Team: Dutch <nl@li.org>\n"
13"Language-Team: Dutch <nl@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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,11 +28,11 @@ msgstr "Snelkoppeling losmaken"
28
28
29#: qml/Components/Dialogs.qml:142
29#: qml/Components/Dialogs.qml:142
30msgid "Are you sure you would like to turn power off?"
30msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Weet u zeker dat u het apparaat wilt uitschakelen?"
32
32
33#: qml/Components/Dialogs.qml:144
33#: qml/Components/Dialogs.qml:144
34msgid "Power off"
34msgid "Power off"
35−msgstr ""
35+msgstr "Uitschakelen"
36
36
37#: qml/Components/Dialogs.qml:153
37#: qml/Components/Dialogs.qml:153
38msgid "Restart"
38msgid "Restart"
@@ -149,7 +149,7 @@ msgstr "Snelzoeker-beheer"
149
149
150#: qml/Dash/ScopesOverview.qml:405
150#: qml/Dash/ScopesOverview.qml:405
151msgid "Done"
151msgid "Done"
152−msgstr "Gereed"
152+msgstr "Klaar"
153
153
154#: qml/Dash/ScopesOverview.qml:431
154#: qml/Dash/ScopesOverview.qml:431
155msgid "Store"
155msgid "Store"
@@ -233,7 +233,7 @@ msgstr "SIM-kaart deblokkeren"
233
233
234#: qml/Panel/Indicators/RoamingIndication.qml:27
234#: qml/Panel/Indicators/RoamingIndication.qml:27
235msgid "Roaming"
235msgid "Roaming"
236−msgstr ""
236+msgstr "Roaming"
237
237
238#: qml/Panel/SearchIndicator.qml:27
238#: qml/Panel/SearchIndicator.qml:27
239msgid "Search"
239msgid "Search"
po/pt_BR.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
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-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
18"X-Generator: Launchpad (build 17156)\n"
18"X-Generator: Launchpad (build 17156)\n"
19
19
20#: plugins/Unity/Launcher/launcheritem.cpp:43
20#: plugins/Unity/Launcher/launcheritem.cpp:43
po/unity8.pot+69−16
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-08-08 11:17+0200\n"
11+"POT-Creation-Date: 2014-08-13 14:09-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,6 +17,10 @@ msgstr ""
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
18"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
19
19
20+#: plugins/LightDM/Greeter.cpp:129
21+msgid "Password: "
22+msgstr ""
23+
20#: plugins/Unity/Launcher/launcheritem.cpp:43
24#: plugins/Unity/Launcher/launcheritem.cpp:43
21#: plugins/Unity/Launcher/launcheritem.cpp:73
25#: plugins/Unity/Launcher/launcheritem.cpp:73
22msgid "Pin shortcut"
26msgid "Pin shortcut"
@@ -88,39 +92,51 @@ msgid ""
88"phone<br><br>Tap on the screen to start"
92"phone<br><br>Tap on the screen to start"
89msgstr ""
93msgstr ""
90
94
91−#: qml/Components/Lockscreen.qml:220
95+#: qml/Components/Lockscreen.qml:161
96+msgid "Too many incorrect attempts"
97+msgstr ""
98+
99+#: qml/Components/Lockscreen.qml:163
100+msgid "Please wait"
101+msgstr ""
102+
103+#: qml/Components/Lockscreen.qml:245
92msgid "Emergency Call"
104msgid "Emergency Call"
93msgstr ""
105msgstr ""
94
106
95−#: qml/Components/Lockscreen.qml:243
107+#: qml/Components/Lockscreen.qml:268
96msgid "OK"
108msgid "OK"
97msgstr ""
109msgstr ""
98
110
99−#: qml/Components/PassphraseLockscreen.qml:62
111+#: qml/Components/PassphraseLockscreen.qml:61
100#, qt-format
112#, qt-format
101msgid "Hello %1"
113msgid "Hello %1"
102msgstr ""
114msgstr ""
103
115
104−#: qml/Components/PassphraseLockscreen.qml:62
116+#: qml/Components/PassphraseLockscreen.qml:61
105msgid "Hello"
117msgid "Hello"
106msgstr ""
118msgstr ""
107
119
108−#: qml/Components/PinLockscreen.qml:198
120+#: qml/Components/PinLockscreen.qml:197
109msgid "CANCEL"
121msgid "CANCEL"
110msgstr ""
122msgstr ""
111
123
112−#: qml/Components/PinLockscreen.qml:216
124+#: qml/Components/PinLockscreen.qml:215
113msgid "DONE"
125msgid "DONE"
114msgstr ""
126msgstr ""
115
127
116−#: qml/Dash/GenericScopeView.qml:358
128+#: qml/Dash/GenericScopeView.qml:360
117msgid "See less"
129msgid "See less"
118msgstr ""
130msgstr ""
119
131
120−#: qml/Dash/GenericScopeView.qml:358
132+#: qml/Dash/GenericScopeView.qml:360
121msgid "See all"
133msgid "See all"
122msgstr ""
134msgstr ""
123
135
136+#: qml/Dash/GenericScopeView.qml:423 qml/Panel/SearchIndicator.qml:27
137+msgid "Search"
138+msgstr ""
139+
124#: qml/Dash/Previews/PreviewActionCombo.qml:34
140#: qml/Dash/Previews/PreviewActionCombo.qml:34
125msgid "More..."
141msgid "More..."
126msgstr ""
142msgstr ""
@@ -141,15 +157,15 @@ msgstr ""
141msgid "Send"
157msgid "Send"
142msgstr ""
158msgstr ""
143
159
144−#: qml/Dash/ScopesOverview.qml:200
160+#: qml/Dash/ScopesOverview.qml:201
145msgid "Manage Dash"
161msgid "Manage Dash"
146msgstr ""
162msgstr ""
147
163
148−#: qml/Dash/ScopesOverview.qml:405
164+#: qml/Dash/ScopesOverview.qml:408
149msgid "Done"
165msgid "Done"
150msgstr ""
166msgstr ""
151
167
152−#: qml/Dash/ScopesOverview.qml:431
168+#: qml/Dash/ScopesOverview.qml:434
153msgid "Store"
169msgid "Store"
154msgstr ""
170msgstr ""
155
171
@@ -233,11 +249,48 @@ msgstr ""
233msgid "Roaming"
249msgid "Roaming"
234msgstr ""
250msgstr ""
235
251
236−#: qml/Panel/SearchIndicator.qml:27
252+#: qml/Shell.qml:267
237−msgid "Search"
253+msgid "passphrase"
254+msgstr ""
255+
256+#: qml/Shell.qml:267
257+msgid "passcode"
258+msgstr ""
259+
260+#: qml/Shell.qml:269
261+#, qt-format
262+msgid "Enter your %1"
238msgstr ""
263msgstr ""
239
264
240−#: qml/Shell.qml:256
265+#: qml/Shell.qml:270
241#, qt-format
266#, qt-format
242−msgid "Please enter %1"
267+msgid "Incorrect %1"
268+msgstr ""
269+
270+#: qml/Shell.qml:272
271+msgid "Please re-enter"
272+msgstr ""
273+
274+#: qml/Shell.qml:306
275+msgid "Sorry, incorrect passphrase."
276+msgstr ""
277+
278+#: qml/Shell.qml:307
279+msgid "Sorry, incorrect passcode."
280+msgstr ""
281+
282+#: qml/Shell.qml:308
283+msgid "This will be your last attempt."
284+msgstr ""
285+
286+#: qml/Shell.qml:310
287+msgid ""
288+"If passphrase is entered incorrectly, your phone will conduct a factory "
289+"reset and all personal data will be deleted."
290+msgstr ""
291+
292+#: qml/Shell.qml:311
293+msgid ""
294+"If passcode is entered incorrectly, your phone will conduct a factory reset "
295+"and all personal data will be deleted."
243msgstr ""
296msgstr ""
qml/Components/Lockscreen.qml+34−9
@@ -57,10 +57,15 @@ Showable {
57
57
58 onRequiredChanged: {
58 onRequiredChanged: {
59 if (required && pinPadLoader.item) {
59 if (required && pinPadLoader.item) {
60− pinPadLoader.item.clear(false);
60+ clear(false)
61 }
61 }
62 }
62 }
63
63
64+ function forceDelay(delay) {
65+ forcedDelayTimer.interval = delay
66+ forcedDelayTimer.start()
67+ }
68+
64 function reset() {
69 function reset() {
65 // This causes the loader below to destry and recreate the source
70 // This causes the loader below to destry and recreate the source
66 pinPadLoader.resetting = true;
71 pinPadLoader.resetting = true;
@@ -68,13 +73,22 @@ Showable {
68 }
73 }
69
74
70 function clear(showAnimation) {
75 function clear(showAnimation) {
76+ if (pinPadLoader.item) {
71− pinPadLoader.item.clear(showAnimation);
77+ pinPadLoader.item.clear(showAnimation);
78+ }
79+ pinPadLoader.showWrongText = showAnimation
80+ pinPadLoader.waiting = false
72 }
81 }
73
82
74 function showInfoPopup(title, text) {
83 function showInfoPopup(title, text) {
75 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
84 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
76 }
85 }
77
86
87+ Timer {
88+ id: forcedDelayTimer
89+ onTriggered: pinPadLoader.showWrongText = false
90+ }
91+
78 Rectangle {
92 Rectangle {
79 // In case background fails to load or is undefined
93 // In case background fails to load or is undefined
80 id: backgroundBackup
94 id: backgroundBackup
@@ -131,8 +145,6 @@ Showable {
131 }
145 }
132 }
146 }
133
147
134−
135−
136 Loader {
148 Loader {
137 id: pinPadLoader
149 id: pinPadLoader
138 objectName: "pinPadLoader"
150 objectName: "pinPadLoader"
@@ -143,13 +155,24 @@ Showable {
143 verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : -units.gu(4)
155 verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : -units.gu(4)
144 }
156 }
145 property bool resetting: false
157 property bool resetting: false
158+ property bool waiting: false
159+ property bool showWrongText: false
160+
161+ readonly property string forcedDelayText: i18n.tr("Too many incorrect attempts") +
162+ "\n" +
163+ i18n.tr("Please wait")
146
164
147 source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
165 source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
166+ onSourceChanged: {
167+ waiting = false
168+ showWrongText = false
169+ }
148
170
149 Connections {
171 Connections {
150 target: pinPadLoader.item
172 target: pinPadLoader.item
151
173
152 onEntered: {
174 onEntered: {
175+ pinPadLoader.waiting = true
153 root.entered(passphrase);
176 root.entered(passphrase);
154 }
177 }
155
178
@@ -171,17 +194,19 @@ Showable {
171 Binding {
194 Binding {
172 target: pinPadLoader.item
195 target: pinPadLoader.item
173 property: "placeholderText"
196 property: "placeholderText"
197+ value: forcedDelayTimer.running ? pinPadLoader.forcedDelayText :
198+ (pinPadLoader.showWrongText ? root.wrongPlaceholderText :
174− value: root.placeholderText
199+ root.placeholderText)
175 }
200 }
176 Binding {
201 Binding {
177 target: pinPadLoader.item
202 target: pinPadLoader.item
178− property: "wrongPlaceholderText"
203+ property: "username"
179− value: root.wrongPlaceholderText
204+ value: root.username
180 }
205 }
181 Binding {
206 Binding {
182 target: pinPadLoader.item
207 target: pinPadLoader.item
183− property: "username"
208+ property: "entryEnabled"
184− value: root.username
209+ value: !pinPadLoader.waiting && !forcedDelayTimer.running
185 }
210 }
186 }
211 }
187
212
qml/Components/PassphraseLockscreen.qml+3−4
@@ -23,15 +23,14 @@ Item {
23 height: highlightItem.height
23 height: highlightItem.height
24
24
25 property string placeholderText
25 property string placeholderText
26− property string wrongPlaceholderText
27 property string username: ""
26 property string username: ""
27+ property bool entryEnabled: true
28
28
29 signal entered(string passphrase)
29 signal entered(string passphrase)
30 signal cancel()
30 signal cancel()
31
31
32 function clear(playAnimation) {
32 function clear(playAnimation) {
33 pinentryField.text = "";
33 pinentryField.text = "";
34− pinentryField.enabled = true
35 if (playAnimation) {
34 if (playAnimation) {
36 wrongPasswordAnimation.start();
35 wrongPasswordAnimation.start();
37 pinentryField.forceActiveFocus();
36 pinentryField.forceActiveFocus();
@@ -76,11 +75,11 @@ Item {
76 echoMode: TextInput.Password
75 echoMode: TextInput.Password
77 opacity: 0.9
76 opacity: 0.9
78 hasClearButton: false
77 hasClearButton: false
79− placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
78+ enabled: entryEnabled
79+ placeholderText: root.placeholderText
80
80
81 onAccepted: {
81 onAccepted: {
82 if (pinentryField.text) {
82 if (pinentryField.text) {
83− pinentryField.enabled = false;
84 root.entered(pinentryField.text);
83 root.entered(pinentryField.text);
85 }
84 }
86 }
85 }
qml/Components/PinLockscreen.qml+3−4
@@ -25,7 +25,6 @@ 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
29 property int padWidth: units.gu(34)
28 property int padWidth: units.gu(34)
30 property int padHeight: units.gu(28)
29 property int padHeight: units.gu(28)
31 property int minPinLength: -1
30 property int minPinLength: -1
@@ -58,7 +57,6 @@ Column {
58 radius: "medium"
57 radius: "medium"
59 property string text: ""
58 property string text: ""
60 property string placeholderText: ""
59 property string placeholderText: ""
61− property string wrongPlaceholderText: ""
62
60
63 function appendChar(character) {
61 function appendChar(character) {
64 if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
62 if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
@@ -90,9 +88,10 @@ Column {
90 id: pinentryFieldPlaceHolder
88 id: pinentryFieldPlaceHolder
91 objectName: "pinentryFieldPlaceHolder"
89 objectName: "pinentryFieldPlaceHolder"
92 anchors.centerIn: parent
90 anchors.centerIn: parent
91+ horizontalAlignment: Text.AlignHCenter
93 color: "#f3f3e7"
92 color: "#f3f3e7"
94 opacity: 0.6
93 opacity: 0.6
95− text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
94+ text: parent.placeholderText
96 visible: pinentryFieldLabel.text.length == 0
95 visible: pinentryFieldLabel.text.length == 0
97 }
96 }
98
97
@@ -107,7 +106,7 @@ Column {
107 bottom: parent.bottom
106 bottom: parent.bottom
108 bottomMargin: units.gu(1)
107 bottomMargin: units.gu(1)
109 }
108 }
110− visible: !priv.autoConfirm
109+ visible: entryEnabled && !priv.autoConfirm
111 width: height
110 width: height
112 name: "erase"
111 name: "erase"
113 color: "#f3f3e7"
112 color: "#f3f3e7"
qml/Dash/CardHorizontalList.qmlnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+DashRenderer {
22+ id: root
23+
24+ expandedHeight: cardTool.cardHeight + units.gu(2)
25+ collapsedHeight: expandedHeight
26+ clip: true
27+
28+ ListView {
29+ id: listView
30+ anchors {
31+ fill: parent
32+ margins: units.gu(1)
33+ rightMargin: 0
34+ }
35+ rightMargin: units.gu(1)
36+ spacing: units.gu(1)
37+ model: root.model
38+ orientation: ListView.Horizontal
39+
40+ delegate: Loader {
41+ id: loader
42+ sourceComponent: cardTool.cardComponent
43+ anchors { top: parent.top; bottom: parent.bottom }
44+ width: cardTool.cardWidth
45+ onLoaded: {
46+ item.objectName = "delegate" + index;
47+ item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
48+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
49+ item.cardData = Qt.binding(function() { return model; });
50+ item.template = Qt.binding(function() { return cardTool.template; });
51+ item.components = Qt.binding(function() { return cardTool.components; });
52+ item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
53+ item.scopeStyle = root.scopeStyle;
54+ }
55+ Connections {
56+ target: loader.item
57+ onClicked: root.clicked(index, result)
58+ onPressAndHold: root.pressAndHold(index)
59+ }
60+ }
61+ }
62+}
qml/Dash/CardTool.qml+2
@@ -91,6 +91,7 @@ Item {
91 }
91 }
92 return units.gu(18.5);
92 return units.gu(18.5);
93 case "carousel":
93 case "carousel":
94+ case "horizontal-list":
94 return carouselTool.minimumTileWidth;
95 return carouselTool.minimumTileWidth;
95 case undefined:
96 case undefined:
96 case "organic-grid":
97 case "organic-grid":
@@ -111,6 +112,7 @@ Item {
111 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
112 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
112 return units.gu(18.5);
113 return units.gu(18.5);
113 case "grid":
114 case "grid":
115+ case "horizontal-list":
114 return cardLoader.item ? cardLoader.item.implicitHeight : 0
116 return cardLoader.item ? cardLoader.item.implicitHeight : 0
115 case "carousel":
117 case "carousel":
116 return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
118 return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
qml/Dash/GenericScopeView.qml+1
@@ -227,6 +227,7 @@ FocusScope {
227 switch (cardTool.categoryLayout) {
227 switch (cardTool.categoryLayout) {
228 case "carousel": return "CardCarousel.qml";
228 case "carousel": return "CardCarousel.qml";
229 case "vertical-journal": return "CardVerticalJournal.qml";
229 case "vertical-journal": return "CardVerticalJournal.qml";
230+ case "horizontal-list": return "CardHorizontalList.qml";
230 case "grid":
231 case "grid":
231 default: return "CardGrid.qml";
232 default: return "CardGrid.qml";
232 }
233 }
qml/Dash/PreviewListView.qml−8
@@ -65,14 +65,6 @@ Item {
65 maximumFlickVelocity: width * 5
65 maximumFlickVelocity: width * 5
66 cacheBuffer: 0
66 cacheBuffer: 0
67
67
68− // To be set before opening the preview
69− property string categoryId: ""
70−
71− // because the ListView is built asynchronous, setting the
72− // currentIndex directly won't work. We need to refresh it
73− // when the first preview is ready to be displayed.
74− property bool init: true
75−
76 property bool open: false
68 property bool open: false
77
69
78 onOpenChanged: {
70 onOpenChanged: {
qml/Launcher/LauncherPanel.qml+4
@@ -146,6 +146,10 @@ Rectangle {
146 delegate: FoldingLauncherDelegate {
146 delegate: FoldingLauncherDelegate {
147 id: launcherDelegate
147 id: launcherDelegate
148 objectName: "launcherDelegate" + index
148 objectName: "launcherDelegate" + index
149+ // We need the appId in the delegate in order to find
150+ // the right app when running autopilot tests for
151+ // multiple apps.
152+ readonly property string appId: model.appId
149 itemHeight: launcherListView.itemHeight
153 itemHeight: launcherListView.itemHeight
150 itemWidth: launcherListView.itemWidth
154 itemWidth: launcherListView.itemWidth
151 width: itemWidth
155 width: itemWidth
qml/Notifications/Notification.qml+3
@@ -311,6 +311,9 @@ Item {
311 onLoaded: {
311 onLoaded: {
312 notification.fullscreen = Qt.binding(function() { return fullscreen; });
312 notification.fullscreen = Qt.binding(function() { return fullscreen; });
313 }
313 }
314+ onAccepted: {
315+ notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
316+ }
314 }
317 }
315 }
318 }
316 }
319 }
qml/Notifications/NotificationMenuItemFactory.qml+5−1
@@ -30,6 +30,8 @@ Loader {
30 property int maxHeight
30 property int maxHeight
31 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
31 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
32
32
33+ signal accepted()
34+
33 property var _map: {
35 property var _map: {
34 "com.canonical.snapdecision.textfield": textfield,
36 "com.canonical.snapdecision.textfield": textfield,
35 "com.canonical.snapdecision.pinlock" : pinLock,
37 "com.canonical.snapdecision.pinlock" : pinLock,
@@ -82,6 +84,9 @@ Loader {
82 onTextChanged: {
84 onTextChanged: {
83 menuModel.changeState(menuIndex, text);
85 menuModel.changeState(menuIndex, text);
84 }
86 }
87+ onAccepted: {
88+ menuFactory.accepted()
89+ }
85 }
90 }
86
91
87 Row {
92 Row {
@@ -117,7 +122,6 @@ Loader {
117
122
118 onEntered: {
123 onEntered: {
119 menuModel.changeState(menuIndex, passphrase);
124 menuModel.changeState(menuIndex, passphrase);
120− entryEnabled = false;
121 }
125 }
122
126
123 onCancel: {
127 onCancel: {
qml/Shell.qml+53−8
@@ -19,7 +19,9 @@ import AccountsService 0.1
19import GSettings 1.0
19import GSettings 1.0
20import Unity.Application 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22+import Ubuntu.Components.Popups 1.0
22import Ubuntu.Gestures 0.1
23import Ubuntu.Gestures 0.1
24+import Ubuntu.SystemImage 0.1
23import Unity.Launcher 0.1
25import Unity.Launcher 0.1
24import Utils 0.1
26import Utils 0.1
25import LightDM 0.1 as LightDM
27import LightDM 0.1 as LightDM
@@ -53,6 +55,10 @@ Item {
53 property bool sideStageEnabled: shell.width >= units.gu(100)
55 property bool sideStageEnabled: shell.width >= units.gu(100)
54 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
56 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
55
57
58+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
59+ property int failedLoginsDelayAttempts: 7 // number of failed logins
60+ property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
61+
56 function activateApplication(appId) {
62 function activateApplication(appId) {
57 if (ApplicationManager.findApplication(appId)) {
63 if (ApplicationManager.findApplication(appId)) {
58 ApplicationManager.requestFocusApplication(appId);
64 ApplicationManager.requestFocusApplication(appId);
@@ -255,13 +261,21 @@ Item {
255
261
256 onShowPrompt: {
262 onShowPrompt: {
257 if (greeter.narrowMode) {
263 if (greeter.narrowMode) {
264+ var promptText = text.toLowerCase()
265+ if (isDefaultPrompt) {
266+ promptText = lockscreen.alphaNumeric ?
267+ i18n.tr("passphrase") : i18n.tr("passcode")
268+ }
258− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
269+ lockscreen.placeholderText = i18n.tr("Enter your %1").arg(promptText)
270+ lockscreen.wrongPlaceholderText = i18n.tr("Incorrect %1").arg(promptText) +
271+ "\n" +
272+ i18n.tr("Please re-enter")
259 lockscreen.show();
273 lockscreen.show();
260 }
274 }
261 }
275 }
262
276
263 onPromptlessChanged: {
277 onPromptlessChanged: {
264− if (LightDM.Greeter.promptless) {
278+ if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
265 lockscreen.hide()
279 lockscreen.hide()
266 } else {
280 } else {
267 lockscreen.reset();
281 lockscreen.reset();
@@ -270,13 +284,40 @@ Item {
270 }
284 }
271
285
272 onAuthenticationComplete: {
286 onAuthenticationComplete: {
287+ if (LightDM.Greeter.authenticated) {
288+ AccountsService.failedLogins = 0
289+ }
290+ // Else only penalize user for a failed login if they actually were
291+ // prompted for a password. We do this below after the promptless
292+ // early exit.
293+
273 if (LightDM.Greeter.promptless) {
294 if (LightDM.Greeter.promptless) {
274 return;
295 return;
275 }
296 }
297+
276 if (LightDM.Greeter.authenticated) {
298 if (LightDM.Greeter.authenticated) {
277 lockscreen.hide();
299 lockscreen.hide();
278 greeter.login();
300 greeter.login();
279 } else {
301 } else {
302+ AccountsService.failedLogins++
303+ if (maxFailedLogins >= 2) { // require at least a warning
304+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
305+ var title = lockscreen.alphaNumeric ?
306+ i18n.tr("Sorry, incorrect passphrase.") :
307+ i18n.tr("Sorry, incorrect passcode.")
308+ var text = i18n.tr("This will be your last attempt.") + " " +
309+ (lockscreen.alphaNumeric ?
310+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
311+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
312+ lockscreen.showInfoPopup(title, text)
313+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
314+ SystemImage.factoryReset() // Ouch!
315+ }
316+ }
317+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
318+ lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
319+ }
320+
280 lockscreen.clear(true);
321 lockscreen.clear(true);
281 if (greeter.narrowMode) {
322 if (greeter.narrowMode) {
282 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
323 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -311,8 +352,16 @@ Item {
311 }
352 }
312
353
313 property bool fullyShown: showProgress === 1.0
354 property bool fullyShown: showProgress === 1.0
355+ onFullyShownChanged: {
356+ // Wait until the greeter is completely covering lockscreen before resetting it.
357+ if (fullyShown && !LightDM.Greeter.authenticated) {
358+ lockscreen.reset();
359+ lockscreen.show();
360+ }
361+ }
362+
314 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
363 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
315− onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
364+ onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
316
365
317 Greeter {
366 Greeter {
318 id: greeter
367 id: greeter
@@ -350,10 +399,6 @@ Item {
350 if (greeter.narrowMode) {
399 if (greeter.narrowMode) {
351 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
352 }
401 }
353− if (!LightDM.Greeter.promptless) {
354− lockscreen.reset();
355− lockscreen.show();
356− }
357 greeter.fakeActiveForApp = "";
402 greeter.fakeActiveForApp = "";
358 greeter.forceActiveFocus();
403 greeter.forceActiveFocus();
359 }
404 }
@@ -403,7 +448,7 @@ Item {
403 }
448 }
404
449
405 if (LightDM.Greeter.active) {
450 if (LightDM.Greeter.active) {
406− if (!LightDM.Greeter.promptless) {
451+ if (!LightDM.Greeter.authenticated) {
407 lockscreen.show()
452 lockscreen.show()
408 }
453 }
409 greeter.hide()
454 greeter.hide()
run.sh+19−18
@@ -18,6 +18,7 @@ usage() {
18 echo " -f, --fake Force use of fake Qml modules." >&2
18 echo " -f, --fake Force use of fake Qml modules." >&2
19 echo " -p, --pinlock Use a pin protected user." >&2
19 echo " -p, --pinlock Use a pin protected user." >&2
20 echo " -k, --keylock Use a passphrase protected user." >&2
20 echo " -k, --keylock Use a passphrase protected user." >&2
21+ echo " -l, --lightdm Use the specified lightdm mock." >&2
21 echo " -g, --gdb Run through gdb." >&2
22 echo " -g, --gdb Run through gdb." >&2
22 echo " -h, --help Show this help." >&2
23 echo " -h, --help Show this help." >&2
23 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
24 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -25,16 +26,20 @@ usage() {
25 exit 1
26 exit 1
26}
27}
27
28
28−ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,nomousetouch" -o "fpkghm" -- "$@"`
29+ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,lightdm:,nomousetouch" -o "fpkl:ghm" -- "$@"`
29[ $? -ne 0 ] && usage
30[ $? -ne 0 ] && usage
30eval set -- "$ARGS"
31eval set -- "$ARGS"
31
32
32while [ $# -gt 0 ]
33while [ $# -gt 0 ]
33do
34do
34 case "$1" in
35 case "$1" in
35− -f|--fake) FAKE=true; USE_MOCKS=true;;
36+ -f|--fake) USE_MOCKS=true;;
36− -p|--pinlock) PINLOCK=true; USE_MOCKS=true;;
37+ -p|--pinlock) USE_MOCKS=true; LIGHTDM_MOCK=single-pin;;
37− -k|--keylock) KEYLOCK=true; USE_MOCKS=true;;
38+ -k|--keylock) USE_MOCKS=true; LIGHTDM_MOCK=single-passphrase;;
39+ -l|--lightdm) LIGHTDM_MOCK=$2; shift;
40+ if [ -z "$LIGHTDM_MOCK" ]; then
41+ echo "Please specify an argument to --lightdm"
42+ fi;;
38 -g|--gdb) GDB=true;;
43 -g|--gdb) GDB=true;;
39 -h|--help) usage;;
44 -h|--help) usage;;
40 -m|--nomousetouch) MOUSE_TOUCH=false;;
45 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -43,27 +48,23 @@ do
43 shift
48 shift
44done
49done
45
50
46−if $FAKE; then
51+if [ -z "$LIGHTDM_MOCK" ]; then
47− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single
52+ LIGHTDM_MOCK=single
48fi
53fi
49
54
50−if $PINLOCK; then
55+# Even without USE_MOCKS set, we want to fake our lightdm backend, because it's
51− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-pin
56+# annoying to be prompted for your password when testing. To get the same
52−fi
57+# backend used in production, pass '--lightdm=demo'.
53−
54−if $KEYLOCK; then
55− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-passphrase
58+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/$LIGHTDM_MOCK
56−fi
57
59
58if $USE_MOCKS; then
60if $USE_MOCKS; then
59 rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
61 rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
60 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
62 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
63+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics
61else
64else
62− # Still fake no-login user for convenience (it's annoying to be prompted for your password when testing)
63− # And in particular, just link our LightDM mock into the nonmirplugins folder. We don't want the rest of
65+ # Just link our LightDM mock into the nonmirplugins folder. We don't want
64− # our plugins to be used.
66+ # the rest of our plugins to be used.
65− ln -s $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
67+ ln -sf $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
66− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
67fi
68fi
68
69
69QML_PHONE_SHELL_ARGS=""
70QML_PHONE_SHELL_ARGS=""
src/Dash/CMakeLists.txt+1
@@ -1,6 +1,7 @@
1set(DASH_SRCS
1set(DASH_SRCS
2 main.cpp
2 main.cpp
3 ../MouseTouchAdaptor.cpp
3 ../MouseTouchAdaptor.cpp
4+ ../CachingNetworkManagerFactory.cpp
4)
5)
5
6
6add_executable(unity8-dash ${DASH_SRCS})
7add_executable(unity8-dash ${DASH_SRCS})
src/Dash/main.cpp+4
@@ -28,6 +28,7 @@
28
28
29#include <paths.h>
29#include <paths.h>
30#include "../MouseTouchAdaptor.h"
30#include "../MouseTouchAdaptor.h"
31+#include "../CachingNetworkManagerFactory.h"
31
32
32int main(int argc, const char *argv[])
33int main(int argc, const char *argv[])
33{
34{
@@ -84,6 +85,9 @@ int main(int argc, const char *argv[])
84 prependImportPaths(view->engine(), ::overrideImportPaths());
85 prependImportPaths(view->engine(), ::overrideImportPaths());
85 appendImportPaths(view->engine(), ::fallbackImportPaths());
86 appendImportPaths(view->engine(), ::fallbackImportPaths());
86
87
88+ CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
89+ view->engine()->setNetworkAccessManagerFactory(managerFactory);
90+
87 view->setSource(source);
91 view->setSource(source);
88 view->show();
92 view->show();
89
93
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+15
@@ -115,3 +115,18 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
115 self.launch_upstart_application(application_name)
115 self.launch_upstart_application(application_name)
116 self.assertThat(greeter.created, Eventually(Equals(False)))
116 self.assertThat(greeter.created, Eventually(Equals(False)))
117 self.assert_current_focused_application(application_name)
117 self.assert_current_focused_application(application_name)
118+
119+ def test_click_dash_icon_must_unfocus_application(self):
120+ application_name = self.launch_fake_app()
121+ self.assert_current_focused_application(application_name)
122+
123+ self.main_window.show_dash_from_launcher()
124+
125+ self.assert_current_focused_application('unity8-dash')
126+
127+ def test_click_app_icon_on_dash_must_focus_it(self):
128+ application_name = self.launch_fake_app()
129+ self.main_window.show_dash_from_launcher()
130+
131+ self.main_window.launch_application(application_name)
132+ self.assert_current_focused_application(application_name)
tests/autopilot/unity8/shell/emulators/dash.py+16−6
@@ -24,7 +24,6 @@ from unity8.shell import emulators
24
24
25from autopilot import logging as autopilot_logging
25from autopilot import logging as autopilot_logging
26from autopilot.introspection import dbus
26from autopilot.introspection import dbus
27−from testtools.matchers import MatchesAny, Equals
28from ubuntuuitoolkit import emulators as toolkit_emulators
27from ubuntuuitoolkit import emulators as toolkit_emulators
29
28
30
29
@@ -146,7 +145,8 @@ class Dash(emulators.UnityEmulatorBase):
146 current_header.globalRect.y +
145 current_header.globalRect.y +
147 current_header.height / 4)
146 current_header.height / 4)
148 self.pointing_device.click()
147 self.pointing_device.click()
149− headerContainer = current_header.select_single(objectName="headerContainer")
148+ headerContainer = current_header.select_single(
149+ objectName="headerContainer")
150 headerContainer.contentY.wait_for(0)
150 headerContainer.contentY.wait_for(0)
151 search_text_field = self._get_search_text_field()
151 search_text_field = self._get_search_text_field()
152 search_text_field.write(query)
152 search_text_field.write(query)
@@ -177,22 +177,32 @@ class GenericScopeView(emulators.UnityEmulatorBase):
177 """Open the preview of an application.
177 """Open the preview of an application.
178
178
179 :parameter category: The name of the category where the application is.
179 :parameter category: The name of the category where the application is.
180− :app_name: The name of the application.
180+ :parameter app_name: The name of the application.
181 :return: The opened preview.
181 :return: The opened preview.
182
182
183 """
183 """
184− category_element = self._get_category_element(category)
185− icon = category_element.select_single('AbstractButton', title=app_name)
186 # FIXME some categories need a long press in order to see the preview.
184 # FIXME some categories need a long press in order to see the preview.
187 # Some categories do not show previews, like recent apps.
185 # Some categories do not show previews, like recent apps.
188 # --elopio - 2014-1-14
186 # --elopio - 2014-1-14
189− self.pointing_device.click_object(icon)
187+ self.click_scope_item(category, app_name)
190 preview_list = self.wait_select_single(
188 preview_list = self.wait_select_single(
191 'PreviewListView', objectName='previewListView')
189 'PreviewListView', objectName='previewListView')
192 preview_list.x.wait_for(0)
190 preview_list.x.wait_for(0)
193 return preview_list.select_single(
191 return preview_list.select_single(
194 Preview, objectName='preview{}'.format(preview_list.currentIndex))
192 Preview, objectName='preview{}'.format(preview_list.currentIndex))
195
193
194+ @autopilot_logging.log_action(logger.debug)
195+ def click_scope_item(self, category, title):
196+ """Click an item from the scope.
197+
198+ :parameter category: The name of the category where the item is.
199+ :parameter title: The title of the item.
200+
201+ """
202+ category_element = self._get_category_element(category)
203+ icon = category_element.select_single('AbstractButton', title=title)
204+ self.pointing_device.click_object(icon)
205+
196 def _get_category_element(self, category):
206 def _get_category_element(self, category):
197 try:
207 try:
198 return self.wait_select_single(
208 return self.wait_select_single(
tests/autopilot/unity8/shell/emulators/launcher.py+38−11
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012-2013 Canonical
4+# Copyright (C) 2012, 2013, 2014 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
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
7# it under the terms of the GNU General Public License as published by
@@ -17,22 +17,49 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
18#
19
19
20−from unity8.shell.emulators import UnityEmulatorBase
20+import logging
21−from autopilot.input import Touch
22
21
22+import autopilot.logging
23
23
24+from unity8.shell import emulators
25+
26+
27+logger = logging.getLogger(__name__)
28+
29+
24−class Launcher(UnityEmulatorBase):
30+class Launcher(emulators.UnityEmulatorBase):
25
31
26 """An emulator that understands the Launcher."""
32 """An emulator that understands the Launcher."""
27
33
34+ @autopilot.logging.log_action(logger.debug)
28 def show(self):
35 def show(self):
29− """Swipes open the launcher."""
36+ """Show the launcher swiping it to the right."""
30− touch = Touch.create()
37+ if not self.shown:
38+ self._swipe_to_show_launcher()
39+ self.shown.wait_for(True)
40+ else:
41+ logger.debug('The launcher is already opened.')
31
42
43+ def _swipe_to_show_launcher(self):
32 view = self.get_root_instance().select_single('QQuickView')
44 view = self.get_root_instance().select_single('QQuickView')
45+ start_y = stop_y = view.y + view.height // 2
46+
33 start_x = view.x + 1
47 start_x = view.x + 1
34− start_y = view.y + view.height / 2
35− stop_x = start_x + self.panelWidth + 1
48+ stop_x = start_x + self.panelWidth - 1
36− stop_y = start_y
49+
37− touch.drag(start_x, start_y, stop_x, stop_y)
50+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
51+
52+ @autopilot.logging.log_action(logger.debug)
53+ def click_dash_icon(self):
38− self.shown.wait_for(True)
54+ if self.shown:
55+ dash_icon = self.select_single(
56+ 'QQuickImage', objectName='dashItem')
57+ self.pointing_device.click_object(dash_icon)
58+ else:
59+ raise emulators.UnityEmulatorException('The launcher is closed.')
60+
61+ @autopilot.logging.log_action(logger.debug)
62+ def click_application_launcher_icon(self, application_name):
63+ launcher_delegate = self.select_single(
64+ 'LauncherDelegate', appId=application_name)
65+ self.pointing_device.click_object(launcher_delegate)
tests/autopilot/unity8/shell/emulators/main_window.py+32−3
@@ -63,9 +63,6 @@ class QQuickView(emulators.UnityEmulatorBase):
63 def get_bottombar(self):
63 def get_bottombar(self):
64 return self.select_single("Bottombar")
64 return self.select_single("Bottombar")
65
65
66− def get_launcher(self):
67− return self.select_single(Launcher)
68−
69 def get_pinPadLoader(self):
66 def get_pinPadLoader(self):
70 return self.select_single(
67 return self.select_single(
71 "QQuickLoader",
68 "QQuickLoader",
@@ -113,6 +110,7 @@ class QQuickView(emulators.UnityEmulatorBase):
113 start_y = end_y = y + height // 2
110 start_y = end_y = y + height // 2
114
111
115 self.pointing_device.drag(start_x, start_y, end_x, end_y)
112 self.pointing_device.drag(start_x, start_y, end_x, end_y)
113+ self.get_current_focused_app_id().wait_for('unity8-dash')
116
114
117 def _get_shell(self):
115 def _get_shell(self):
118 return self.select_single('Shell')
116 return self.select_single('Shell')
@@ -122,6 +120,37 @@ class QQuickView(emulators.UnityEmulatorBase):
122 return self._get_shell().focusedApplicationId
120 return self._get_shell().focusedApplicationId
123
121
124 @autopilot_logging.log_action(logger.info)
122 @autopilot_logging.log_action(logger.info)
123+ def show_dash_from_launcher(self):
124+ """Open the dash clicking the dash icon on the launcher."""
125+ launcher = self.open_launcher()
126+ launcher.click_dash_icon()
127+ self.get_current_focused_app_id().wait_for('unity8-dash')
128+ launcher.shown.wait_for(False)
129+
130+ @autopilot_logging.log_action(logger.info)
131+ def open_launcher(self):
132+ launcher = self._get_launcher()
133+ launcher.show()
134+ return launcher
135+
136+ def _get_launcher(self):
137+ return self.select_single(Launcher)
138+
139+ def is_launcher_open(self):
140+ return self._get_launcher().shown
141+
142+ @autopilot_logging.log_action(logger.info)
143+ def launch_application(self, application_name):
144+ """Launch an application.
145+
146+ :parameter application_name: The name of the application to launch.
147+
148+ """
149+ launcher = self.open_launcher()
150+ launcher.click_application_launcher_icon(application_name)
151+ self.get_current_focused_app_id().wait_for(application_name)
152+ launcher.shown.wait_for(False)
153+
125 def enter_pin_code(self, code):
154 def enter_pin_code(self, code):
126 """Enter code 'code' into the single-pin lightdm pincode entry screen.
155 """Enter code 'code' into the single-pin lightdm pincode entry screen.
127
156
tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−2
@@ -184,12 +184,11 @@ class TestHud(UnityTestCase, DragMixin):
184 unity_proxy = self.launch_unity()
184 unity_proxy = self.launch_unity()
185 unlock_unity(unity_proxy)
185 unlock_unity(unity_proxy)
186 hud = self.main_window.get_hud()
186 hud = self.main_window.get_hud()
187− launcher = self.main_window.get_launcher()
188
187
189 self._launch_test_app_from_app_screen()
188 self._launch_test_app_from_app_screen()
190
189
191 hud.show()
190 hud.show()
192− launcher.show()
191+ self.main_window.open_launcher()
193
192
194 self.assertThat(hud.shown, Eventually(Equals(False)))
193 self.assertThat(hud.shown, Eventually(Equals(False)))
195
194
tests/autopilot/unity8/shell/tests/test_notifications.py+2−2
@@ -242,8 +242,8 @@ class InteractiveNotificationBase(NotificationsBase):
242 # the shell)
242 # the shell)
243 time.sleep(1)
243 time.sleep(1)
244 self.main_window.show_dash_swiping()
244 self.main_window.show_dash_swiping()
245− launcher = self.main_window.get_launcher()
246− self.assertThat(launcher.shown, Eventually(Equals(False)))
245+ self.assertThat(
246+ self.main_window.is_launcher_open, Eventually(Equals(False)))
247
247
248 # verify and interact with the triggered snap-decision notification
248 # verify and interact with the triggered snap-decision notification
249 notify_list = self._get_notifications_list()
249 notify_list = self._get_notifications_list()
tests/mocks/AccountsService/AccountsService.cpp+13−1
@@ -23,7 +23,8 @@
23AccountsService::AccountsService(QObject* parent)
23AccountsService::AccountsService(QObject* parent)
24 : QObject(parent),
24 : QObject(parent),
25 m_backgroundFile(qmlDirectory() + "graphics/phone_background.jpg"),
25 m_backgroundFile(qmlDirectory() + "graphics/phone_background.jpg"),
26− m_statsWelcomeScreen(true)
26+ m_statsWelcomeScreen(true),
27+ m_failedLogins(0)
27{
28{
28}
29}
29
30
@@ -78,3 +79,14 @@ AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() cons
78 else
79 else
79 return PasswordDisplayHint::Keyboard;
80 return PasswordDisplayHint::Keyboard;
80}
81}
82+
83+uint AccountsService::failedLogins() const
84+{
85+ return m_failedLogins;
86+}
87+
88+void AccountsService::setFailedLogins(uint failedLogins)
89+{
90+ m_failedLogins = failedLogins;
91+ failedLoginsChanged();
92+}
tests/mocks/AccountsService/AccountsService.h+8
@@ -47,6 +47,10 @@ class AccountsService: public QObject
47 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
47 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
48 READ passwordDisplayHint
48 READ passwordDisplayHint
49 NOTIFY passwordDisplayHintChanged)
49 NOTIFY passwordDisplayHintChanged)
50+ Q_PROPERTY (uint failedLogins
51+ READ failedLogins
52+ WRITE setFailedLogins
53+ NOTIFY failedLoginsChanged)
50
54
51public:
55public:
52 enum PasswordDisplayHint {
56 enum PasswordDisplayHint {
@@ -65,6 +69,8 @@ public:
65 bool statsWelcomeScreen() const;
69 bool statsWelcomeScreen() const;
66 void setStatsWelcomeScreen(bool statsWelcomeScreen);
70 void setStatsWelcomeScreen(bool statsWelcomeScreen);
67 PasswordDisplayHint passwordDisplayHint() const;
71 PasswordDisplayHint passwordDisplayHint() const;
72+ uint failedLogins() const;
73+ void setFailedLogins(uint failedLogins);
68
74
69Q_SIGNALS:
75Q_SIGNALS:
70 void userChanged();
76 void userChanged();
@@ -72,11 +78,13 @@ Q_SIGNALS:
72 void backgroundFileChanged();
78 void backgroundFileChanged();
73 void statsWelcomeScreenChanged();
79 void statsWelcomeScreenChanged();
74 void passwordDisplayHintChanged();
80 void passwordDisplayHintChanged();
81+ void failedLoginsChanged();
75
82
76private:
83private:
77 QString m_backgroundFile;
84 QString m_backgroundFile;
78 QString m_user;
85 QString m_user;
79 bool m_statsWelcomeScreen;
86 bool m_statsWelcomeScreen;
87+ uint m_failedLogins;
80};
88};
81
89
82#endif
90#endif
tests/mocks/LightDM/Greeter.cpp+11
@@ -19,6 +19,7 @@
19#include "Greeter.h"
19#include "Greeter.h"
20#include "GreeterPrivate.h"
20#include "GreeterPrivate.h"
21#include <QtCore/QCoreApplication>
21#include <QtCore/QCoreApplication>
22+#include <QTimer>
22
23
23namespace QLightDM
24namespace QLightDM
24{
25{
@@ -164,4 +165,14 @@ void Greeter::respond(const QString &response)
164 d->handleRespond(response);
165 d->handleRespond(response);
165}
166}
166
167
168+void Greeter::sendAuthenticationComplete()
169+{
170+ if (qgetenv("UNITY_TESTING").isEmpty()) {
171+ // simulate PAM's delay
172+ QTimer::singleShot(1000, this, SIGNAL(authenticationComplete()));
173+ } else {
174+ Q_EMIT authenticationComplete();
175+ }
176+}
177+
167}
178}
tests/mocks/LightDM/Greeter.h+3
@@ -96,6 +96,9 @@ Q_SIGNALS:
96 void authenticationComplete();
96 void authenticationComplete();
97 void autologinTimerExpired();
97 void autologinTimerExpired();
98
98
99+protected:
100+ void sendAuthenticationComplete();
101+
99private:
102private:
100 GreeterPrivate *d_ptr;
103 GreeterPrivate *d_ptr;
101 Q_DECLARE_PRIVATE(Greeter)
104 Q_DECLARE_PRIVATE(Greeter)
tests/mocks/LightDM/demo/CMakeLists.txt+13
@@ -9,6 +9,7 @@ set(LibLightDM_SOURCES
9 )
9 )
10
10
11add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
11add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
12+add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
12
13
13include_directories(
14include_directories(
14 ${CMAKE_CURRENT_BINARY_DIR}
15 ${CMAKE_CURRENT_BINARY_DIR}
@@ -19,5 +20,17 @@ target_link_libraries(MockLightDM-demo
19 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
20 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
20 -lpam
21 -lpam
21)
22)
23+target_link_libraries(MockLightDM-demo-shared
24+ ${LIBUSERMETRICSOUTPUT_LDFLAGS}
25+ -lpam
26+)
22
27
23qt5_use_modules(MockLightDM-demo Concurrent Gui)
28qt5_use_modules(MockLightDM-demo Concurrent Gui)
29+qt5_use_modules(MockLightDM-demo-shared Concurrent Gui)
30+
31+set_target_properties(MockLightDM-demo-shared PROPERTIES
32+ OUTPUT_NAME lightdm-qt5-2)
33+
34+install(TARGETS MockLightDM-demo-shared
35+ DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/demo
36+ )
tests/mocks/LightDM/full/GreeterPrivate.cpp+4−4
@@ -54,7 +54,7 @@ void GreeterPrivate::handleAuthenticate()
54 authenticated = true;
54 authenticated = true;
55 Q_EMIT q->authenticationComplete();
55 Q_EMIT q->authenticationComplete();
56 } else if (authenticationUser == "has-pin"){
56 } else if (authenticationUser == "has-pin"){
57− Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
57+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
58 } else if (authenticationUser == "auth-error") {
58 } else if (authenticationUser == "auth-error") {
59 authenticated = false;
59 authenticated = false;
60 Q_EMIT q->authenticationComplete();
60 Q_EMIT q->authenticationComplete();
@@ -78,11 +78,11 @@ void GreeterPrivate::handleRespond(const QString &response)
78 Q_EMIT q->showPrompt("otp", Greeter::PromptTypeQuestion);
78 Q_EMIT q->showPrompt("otp", Greeter::PromptTypeQuestion);
79 } else {
79 } else {
80 authenticated = false;
80 authenticated = false;
81− Q_EMIT q->authenticationComplete();
81+ q->sendAuthenticationComplete();
82 }
82 }
83 } else {
83 } else {
84 authenticated = (response == "otp");
84 authenticated = (response == "otp");
85− Q_EMIT q->authenticationComplete();
85+ q->sendAuthenticationComplete();
86 }
86 }
87 return;
87 return;
88 }
88 }
@@ -92,7 +92,7 @@ void GreeterPrivate::handleRespond(const QString &response)
92 } else {
92 } else {
93 authenticated = (response == "password");
93 authenticated = (response == "password");
94 }
94 }
95− Q_EMIT q->authenticationComplete();
95+ q->sendAuthenticationComplete();
96}
96}
97
97
98}
98}
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp+2−9
@@ -19,8 +19,6 @@
19#include "../Greeter.h"
19#include "../Greeter.h"
20#include "../GreeterPrivate.h"
20#include "../GreeterPrivate.h"
21
21
22−#include <QDebug>
23−
24namespace QLightDM
22namespace QLightDM
25{
23{
26
24
@@ -34,20 +32,15 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
34void GreeterPrivate::handleAuthenticate()
32void GreeterPrivate::handleAuthenticate()
35{
33{
36 Q_Q(Greeter);
34 Q_Q(Greeter);
37−
38− qDebug() << "handleAuthentication called!" << authenticationUser;
39−
40− Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
35+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
41}
36}
42
37
43void GreeterPrivate::handleRespond(const QString &response)
38void GreeterPrivate::handleRespond(const QString &response)
44{
39{
45 Q_Q(Greeter);
40 Q_Q(Greeter);
46
41
47−
48 authenticated = (response == "password");
42 authenticated = (response == "password");
49− qDebug() << "responding" << response << authenticated;
43+ q->sendAuthenticationComplete();
50− Q_EMIT q->authenticationComplete();
51}
44}
52
45
53}
46}
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+3−2
@@ -32,14 +32,15 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
32void GreeterPrivate::handleAuthenticate()
32void GreeterPrivate::handleAuthenticate()
33{
33{
34 Q_Q(Greeter);
34 Q_Q(Greeter);
35− Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
35+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
36}
36}
37
37
38void GreeterPrivate::handleRespond(const QString &response)
38void GreeterPrivate::handleRespond(const QString &response)
39{
39{
40 Q_Q(Greeter);
40 Q_Q(Greeter);
41+
41 authenticated = (response == "1234");
42 authenticated = (response == "1234");
42− Q_EMIT q->authenticationComplete();
43+ q->sendAuthenticationComplete();
43}
44}
44
45
45}
46}
tests/mocks/Ubuntu/CMakeLists.txt+1
@@ -1,4 +1,5 @@
1add_subdirectory(DownloadDaemonListener)
1add_subdirectory(DownloadDaemonListener)
2add_subdirectory(Payments)
2add_subdirectory(Payments)
3+add_subdirectory(SystemImage)
3add_subdirectory(Telephony)
4add_subdirectory(Telephony)
4add_subdirectory(Thumbnailer)
5add_subdirectory(Thumbnailer)
tests/mocks/Ubuntu/SystemImage/CMakeLists.txtnew file+10
@@ -0,0 +1,10 @@
1+set(MOCK_SYSTEMIMAGE_SOURCES
2+ plugin.cpp
3+ MockSystemImage.cpp
4+)
5+
6+add_library(MockSystemImage MODULE ${MOCK_SYSTEMIMAGE_SOURCES})
7+
8+qt5_use_modules(MockSystemImage Qml Quick Core)
9+
10+add_unity8_mock(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS MockSystemImage)
tests/mocks/Ubuntu/SystemImage/MockSystemImage.cppnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "MockSystemImage.h"
18+
19+MockSystemImage::MockSystemImage(QObject *parent)
20+ : QObject(parent)
21+{
22+}
23+
24+void MockSystemImage::factoryReset()
25+{
26+ Q_EMIT resettingDevice();
27+}
tests/mocks/Ubuntu/SystemImage/MockSystemImage.hnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCK_SYSTEMIMAGE_H
18+#define MOCK_SYSTEMIMAGE_H
19+
20+#include <QObject>
21+
22+class MockSystemImage : public QObject
23+{
24+ Q_OBJECT
25+ Q_DISABLE_COPY(MockSystemImage)
26+
27+public:
28+ explicit MockSystemImage(QObject *parent = 0);
29+
30+ Q_INVOKABLE void factoryReset();
31+
32+Q_SIGNALS:
33+ void resettingDevice(); // only for mock
34+};
35+
36+#endif // MOCK_SYSTEMIMAGE_H
tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypesnew file+18
@@ -0,0 +1,18 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Ubuntu.SystemImage 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "MockSystemImage"
12+ prototype: "QObject"
13+ exports: ["Ubuntu.SystemImage/SystemImage 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Signal { name: "resettingDevice" }
16+ Method { name: "factoryReset" }
17+ }
18+}
tests/mocks/Ubuntu/SystemImage/plugin.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "MockSystemImage.h"
19+
20+#include <QtQml>
21+
22+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
23+{
24+ Q_UNUSED(engine)
25+ Q_UNUSED(scriptEngine)
26+ return new MockSystemImage();
27+}
28+
29+void BackendPlugin::registerTypes(const char *uri)
30+{
31+ Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
32+
33+ qmlRegisterSingletonType<MockSystemImage>(uri, 0, 1, "SystemImage", service_provider);
34+}
tests/mocks/Ubuntu/SystemImage/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCK_SYSTEMIMAGE_PLUGIN_H
18+#define MOCK_SYSTEMIMAGE_PLUGIN_H
19+
20+#include <QQmlExtensionPlugin>
21+
22+class BackendPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri);
29+};
30+
31+#endif // MOCK_SYSTEMIMAGE_PLUGIN_H
tests/mocks/Ubuntu/SystemImage/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Ubuntu.SystemImage
2+plugin MockSystemImage
3+typeinfo SystemImage.qmltypes
tests/mocks/Unity/fake_previewmodel.cpp+10−1
@@ -29,6 +29,7 @@
29
29
30PreviewModel::PreviewModel(QObject* parent)
30PreviewModel::PreviewModel(QObject* parent)
31 : unity::shell::scopes::PreviewModelInterface(parent)
31 : unity::shell::scopes::PreviewModelInterface(parent)
32+ , m_loaded(true)
32{
33{
33 // we have one column by default
34 // we have one column by default
34 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
35 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
@@ -49,7 +50,7 @@ int PreviewModel::widgetColumnCount() const
49
50
50bool PreviewModel::loaded() const
51bool PreviewModel::loaded() const
51{
52{
52− return true;
53+ return m_loaded;
53}
54}
54
55
55bool PreviewModel::processingAction() const
56bool PreviewModel::processingAction() const
@@ -71,3 +72,11 @@ QVariant PreviewModel::data(const QModelIndex& index, int role) const
71 return QVariant();
72 return QVariant();
72 }
73 }
73}
74}
75+
76+void PreviewModel::setLoaded(bool loaded)
77+{
78+ if (m_loaded != loaded) {
79+ m_loaded = loaded;
80+ Q_EMIT loadedChanged();
81+ }
82+}
tests/mocks/Unity/fake_previewmodel.h+3
@@ -40,8 +40,11 @@ public:
40 bool loaded() const override;
40 bool loaded() const override;
41 bool processingAction() const override;
41 bool processingAction() const override;
42
42
43+ Q_INVOKABLE void setLoaded(bool); // Only available for testing
44+
43private:
45private:
44 QList<PreviewWidgetModel*> m_previewWidgetModels;
46 QList<PreviewWidgetModel*> m_previewWidgetModels;
47+ bool m_loaded;
45};
48};
46
49
47Q_DECLARE_METATYPE(PreviewModel*)
50Q_DECLARE_METATYPE(PreviewModel*)
tests/mocks/Unity/fake_resultsmodel.cpp+5
@@ -29,6 +29,11 @@ ResultsModel::ResultsModel(int result_count, int categoryId, QObject* parent)
29{
29{
30}
30}
31
31
32+ResultsModel::ResultsModel(QObject *parent)
33+ : ResultsModel::ResultsModel(10, 1, parent)
34+{
35+}
36+
32QString ResultsModel::categoryId() const
37QString ResultsModel::categoryId() const
33{
38{
34 return QString::number(m_categoryId);
39 return QString::number(m_categoryId);
tests/mocks/Unity/fake_resultsmodel.h+1
@@ -29,6 +29,7 @@ class ResultsModel : public unity::shell::scopes::ResultsModelInterface
29
29
30public:
30public:
31 explicit ResultsModel(int result_count, int categoryId, QObject* parent = 0);
31 explicit ResultsModel(int result_count, int categoryId, QObject* parent = 0);
32+ explicit ResultsModel(QObject* parent = 0);
32
33
33 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
34 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
34 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
35 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
tests/mocks/Unity/fake_scopesoverview.cpp+4−2
@@ -105,11 +105,12 @@ ScopesOverviewCategories::data(const QModelIndex& index, int role) const
105 }
105 }
106 case RoleComponents:
106 case RoleComponents:
107 {
107 {
108− QVariantMap map, artMap;
108+ QVariantMap map, artMap, attributesMap;
109 artMap["aspect-ratio"] = "0.5";
109 artMap["aspect-ratio"] = "0.5";
110 artMap["field"] = "art";
110 artMap["field"] = "art";
111 map["art"] = artMap;
111 map["art"] = artMap;
112 map["title"] = "HOLA";
112 map["title"] = "HOLA";
113+ map["attributes"] = attributesMap;
113 return map;
114 return map;
114 }
115 }
115 case RoleResults:
116 case RoleResults:
@@ -188,11 +189,12 @@ ScopesOverviewSearchCategories::data(const QModelIndex& index, int role) const
188 }
189 }
189 case RoleComponents:
190 case RoleComponents:
190 {
191 {
191− QVariantMap map, artMap;
192+ QVariantMap map, artMap, attributesMap;
192 artMap["aspect-ratio"] = "1";
193 artMap["aspect-ratio"] = "1";
193 artMap["field"] = "art";
194 artMap["field"] = "art";
194 map["art"] = artMap;
195 map["art"] = artMap;
195 map["title"] = "HOLA";
196 map["title"] = "HOLA";
197+ map["attributes"] = attributesMap;
196 return map;
198 return map;
197 }
199 }
198 case RoleResults:
200 case RoleResults:
tests/mocks/Unity/fake_unity_plugin.cpp+1
@@ -47,6 +47,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
47 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
47 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
48 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
48 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
49 qmlRegisterUncreatableType<ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create ResultsModel object in QML.");
49 qmlRegisterUncreatableType<ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create ResultsModel object in QML.");
50+ qmlRegisterType<ResultsModel>(uri, 0, 2, "FakeResultsModel");
50 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");
51 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");
51 qmlRegisterUncreatableType<PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
52 qmlRegisterUncreatableType<PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
52 qmlRegisterUncreatableType<PreviewStack>(uri, 0, 2, "PreviewStack", "Can't create new PreviewStack in QML. Get them from Scope instance.");
53 qmlRegisterUncreatableType<PreviewStack>(uri, 0, 2, "PreviewStack", "Can't create new PreviewStack in QML. Get them from Scope instance.");
tests/qmltests/CMakeLists.txt+1
@@ -39,6 +39,7 @@ add_qml_benchmark(Dash CardBenchmark 1000)
39add_qml_test(Dash CardTool)
39add_qml_test(Dash CardTool)
40add_qml_test(Dash GenericScopeView)
40add_qml_test(Dash GenericScopeView)
41add_qml_test(Dash PageHeader)
41add_qml_test(Dash PageHeader)
42+add_qml_test(Dash PreviewListView)
42add_qml_test(Dash/Previews Preview)
43add_qml_test(Dash/Previews Preview)
43add_qml_test(Dash/Previews PreviewActions)
44add_qml_test(Dash/Previews PreviewActions)
44add_qml_test(Dash/Previews PreviewAudioPlayback)
45add_qml_test(Dash/Previews PreviewAudioPlayback)
tests/qmltests/Dash/tst_CardTool.qml+6
@@ -163,6 +163,7 @@ Rectangle {
163 { label: "Horizontal Journal", layout: "journal" },
163 { label: "Horizontal Journal", layout: "journal" },
164 { label: "Vertical Journal", layout: "vertical-journal" },
164 { label: "Vertical Journal", layout: "vertical-journal" },
165 { label: "Carousel", layout: "carousel" },
165 { label: "Carousel", layout: "carousel" },
166+ { label: "Horizontal List", layout: "horizontal-list" },
166 ]
167 ]
167 delegate: OptionSelectorDelegate { text: modelData.label }
168 delegate: OptionSelectorDelegate { text: modelData.label }
168 onSelectedIndexChanged: {
169 onSelectedIndexChanged: {
@@ -226,6 +227,7 @@ Rectangle {
226 case "journal": layoutSelector.selectedIndex = 2; break;
227 case "journal": layoutSelector.selectedIndex = 2; break;
227 case "vertical-journal": layoutSelector.selectedIndex = 3; break;
228 case "vertical-journal": layoutSelector.selectedIndex = 3; break;
228 case "carousel": layoutSelector.selectedIndex = 4; break;
229 case "carousel": layoutSelector.selectedIndex = 4; break;
230+ case "horizontal-list": layoutSelector.selectedIndex = 5; break;
229 case "grid":
231 case "grid":
230 default: layoutSelector.selectedIndex = 0; break;
232 default: layoutSelector.selectedIndex = 0; break;
231 }
233 }
@@ -270,6 +272,10 @@ Rectangle {
270 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
272 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
271 { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
273 { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
272 { tag: "PortraitCarousel", width: units.gu(22), height: units.gu(44), viewWidth: 84, index: 10, layout_index: 4},
274 { tag: "PortraitCarousel", width: units.gu(22), height: units.gu(44), viewWidth: 84, index: 10, layout_index: 4},
275+ { tag: "SmallHorizontalList", width: units.gu(18), height: units.gu(39.5), viewWidth: 30, index: 0, layout_index: 5},
276+ { tag: "MediumHorizontalList", width: units.gu(22), height: units.gu(43.5), viewWidth: 84, index: 0, layout_index: 5},
277+ { tag: "LargeHorizontalList", width: units.gu(26), height: units.gu(47.5), viewWidth: 140, index: 0, layout_index: 5},
278+ { tag: "PortraitHorizontalList", width: units.gu(22), height: units.gu(65.5), viewWidth: 84, index: 10, layout_index: 5},
273 ]
279 ]
274 }
280 }
275
281
tests/qmltests/Dash/tst_Dash.qml+7−7
@@ -54,6 +54,7 @@ Item {
54 tryCompare(dashContentList, "count", 0);
54 tryCompare(dashContentList, "count", 0);
55 scopes.load();
55 scopes.load();
56 tryCompare(dashContentList, "currentIndex", 0);
56 tryCompare(dashContentList, "currentIndex", 0);
57+ tryCompare(dashContentList, "count", 6);
57 }
58 }
58
59
59 function get_scope_data() {
60 function get_scope_data() {
@@ -273,6 +274,8 @@ Item {
273 // Click on a temp scope in the search
274 // Click on a temp scope in the search
274 var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
275 var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
275 var cardTempScope = findChild(dashCategorysearchA, "delegate2");
276 var cardTempScope = findChild(dashCategorysearchA, "delegate2");
277+ verify(cardTempScope, "Could not find delegate2");
278+
276 waitForRendering(cardTempScope);
279 waitForRendering(cardTempScope);
277 mouseClick(cardTempScope, cardTempScope.width / 2, cardTempScope.height / 2);
280 mouseClick(cardTempScope, cardTempScope.width / 2, cardTempScope.height / 2);
278
281
@@ -313,13 +316,10 @@ Item {
313 var startY = dash.height / 2;
316 var startY = dash.height / 2;
314 var stopX = units.gu(1)
317 var stopX = units.gu(1)
315 var stopY = startY;
318 var stopY = startY;
316− var retry = 0;
317− while (dashContentList.currentIndex != 2 && retry <= 5) {
318− mouseFlick(dash, startX, startY, stopX, stopY)
319− waitForRendering(dashContentList)
319+ waitForRendering(dashContentList)
320− retry++;
320+ mouseFlick(dash, startX, startY, stopX, stopY);
321− }
321+ mouseFlick(dash, startX, startY, stopX, stopY);
322− compare(dashContentList.currentIndex, 2);
322+ compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
323 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
323 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
324 dashCommunicatorService.mockSetCurrentScope("clickscope", true, true);
324 dashCommunicatorService.mockSetCurrentScope("clickscope", true, true);
325 tryCompare(dashContentList, "currentIndex", 1)
325 tryCompare(dashContentList, "currentIndex", 1)
tests/qmltests/Dash/tst_DashContent.qml+1−1
@@ -390,7 +390,7 @@ Item {
390 var categoryListView = findChild(scope, "categoryListView");
390 var categoryListView = findChild(scope, "categoryListView");
391 waitForRendering(categoryListView);
391 waitForRendering(categoryListView);
392
392
393− compare(categoryListView.pageHeader.searchHint, "Search People");
393+ compare(categoryListView.pageHeader.item.searchHint, "Search People");
394 }
394 }
395 }
395 }
396}
396}
tests/qmltests/Dash/tst_PreviewListView.qmlnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import "../../../qml/Dash"
20+import Unity.Test 0.1 as UT
21+import Unity 0.2 as Unity
22+import Dash 0.1
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(60)
27+ height: units.gu(60)
28+ color: Theme.palette.selected.background
29+
30+ Unity.MockScope {
31+ id: mockScope
32+ }
33+
34+ Unity.FakeResultsModel {
35+ id: mockResultsModel
36+ }
37+
38+ PreviewListView {
39+ id: listView
40+ anchors.fill: parent
41+ scope: mockScope
42+ scopeStyle: ScopeStyle { }
43+ }
44+
45+ UT.UnityTestCase {
46+ id: testCase
47+ name: "PreviewListView"
48+ when: windowShown
49+
50+ property MouseArea mouseArea: findChild(listView, "processingMouseArea")
51+
52+ SignalSpy {
53+ id: clickedSpy
54+ signalName: "clicked"
55+ target: testCase.mouseArea
56+ }
57+
58+ function init() {
59+ listView.model = mockResultsModel;
60+ listView.currentIndex = 1;
61+ listView.open = true;
62+ verify(testCase.mouseArea, "Can't find the processingMouseArea object.");
63+ }
64+
65+ function cleanup() {
66+ listView.open = false;
67+ listView.model = null;
68+ clickedSpy.clear();
69+ }
70+
71+ function test_notProcessing() {
72+ expectFail("", "processingMouseArea should not receive the click.");
73+ mouseClick(listView, listView.width / 2, listView.height / 2);
74+ clickedSpy.wait();
75+ }
76+
77+ function test_processing() {
78+ verify(listView.currentItem, "currentItem is not ready yet");
79+ listView.currentItem.previewModel.setLoaded(false);
80+
81+ tryCompare(listView, "processing", true);
82+
83+ mouseClick(listView, listView.width / 2, listView.height / 2);
84+ clickedSpy.wait();
85+ }
86+ }
87+}
tests/qmltests/Greeter/tst_Lockscreen.qml−2
@@ -319,11 +319,9 @@ Rectangle {
319 if (data.animation) {
319 if (data.animation) {
320 if (data.alphanumeric) {
320 if (data.alphanumeric) {
321 tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
321 tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
322− tryCompare(inputField, "placeholderText", lockscreen.placeholderText)
323 } else {
322 } else {
324 var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
323 var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
325 tryCompare(label, "text", lockscreen.wrongPlaceholderText)
324 tryCompare(label, "text", lockscreen.wrongPlaceholderText)
326− tryCompare(label, "text", lockscreen.placeholderText)
327 }
325 }
328 }
326 }
329
327
tests/qmltests/tst_ShellWithPin.qml+56
@@ -16,8 +16,10 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19+import AccountsService 0.1
19import GSettings 1.0
20import GSettings 1.0
20import LightDM 0.1 as LightDM
21import LightDM 0.1 as LightDM
22+import Ubuntu.SystemImage 0.1
21import Unity.Application 0.1
23import Unity.Application 0.1
22import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
23import Powerd 0.1
25import Powerd 0.1
@@ -25,6 +27,7 @@ import Powerd 0.1
25import "../../qml"
27import "../../qml"
26
28
27Item {
29Item {
30+ id: root
28 width: shell.width
31 width: shell.width
29 height: shell.height
32 height: shell.height
30
33
@@ -53,6 +56,12 @@ Item {
53 signalName: "sessionStarted"
56 signalName: "sessionStarted"
54 }
57 }
55
58
59+ SignalSpy {
60+ id: resetSpy
61+ target: SystemImage
62+ signalName: "resettingDevice"
63+ }
64+
56 UT.UnityTestCase {
65 UT.UnityTestCase {
57 name: "ShellWithPin"
66 name: "ShellWithPin"
58 when: windowShown
67 when: windowShown
@@ -64,6 +73,8 @@ Item {
64
73
65 function init() {
74 function init() {
66 swipeAwayGreeter()
75 swipeAwayGreeter()
76+ shell.failedLoginsDelayAttempts = -1
77+ shell.maxFailedLogins = -1
67 }
78 }
68
79
69 function cleanup() {
80 function cleanup() {
@@ -111,6 +122,7 @@ Item {
111 }
122 }
112
123
113 function test_login() {
124 function test_login() {
125+ sessionSpy.clear()
114 tryCompare(sessionSpy, "count", 0)
126 tryCompare(sessionSpy, "count", 0)
115 enterPin("1234")
127 enterPin("1234")
116 tryCompare(sessionSpy, "count", 1)
128 tryCompare(sessionSpy, "count", 1)
@@ -175,5 +187,49 @@ Item {
175 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
187 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
176 tryCompare(lockscreen, "shown", true)
188 tryCompare(lockscreen, "shown", true)
177 }
189 }
190+
191+ function test_failedLoginsCount() {
192+ AccountsService.failedLogins = 0
193+
194+ enterPin("1111")
195+ tryCompare(AccountsService, "failedLogins", 1)
196+
197+ enterPin("1234")
198+ tryCompare(AccountsService, "failedLogins", 0)
199+ }
200+
201+ function test_wrongEntries() {
202+ shell.failedLoginsDelayAttempts = 3
203+
204+ var placeHolder = findChild(shell, "pinentryFieldPlaceHolder")
205+ tryCompare(placeHolder, "text", "Enter your passcode")
206+
207+ enterPin("1111")
208+ tryCompare(placeHolder, "text", "Incorrect passcode\nPlease re-enter")
209+
210+ enterPin("1111")
211+ tryCompare(placeHolder, "text", "Incorrect passcode\nPlease re-enter")
212+
213+ enterPin("1111")
214+ tryCompare(placeHolder, "text", "Too many incorrect attempts\nPlease wait")
215+ }
216+
217+ function test_factoryReset() {
218+ shell.maxFailedLogins = 3
219+ resetSpy.clear()
220+
221+ enterPin("1111")
222+ enterPin("1111")
223+ tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
224+
225+ var dialog = findChild(root, "infoPopup")
226+ var button = findChild(dialog, "infoPopupOkButton")
227+ mouseClick(button, units.gu(1), units.gu(1))
228+ tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
229+
230+ tryCompare(resetSpy, "count", 0)
231+ enterPin("1111")
232+ tryCompare(resetSpy, "count", 1)
233+ }
178 }
234 }
179}
235}