on xenial help sign in
Merge lp:~saviq/unity8/grey-from-palette
commitf460ff1cdf6090ea0a8f711863d2b0249b330cc1 authorMichał Sawicz <michal.sawicz@canonical.com> dateThu 14 Aug 2014, 13:35 (12 years ago) parentc0548926b15f1d
79 files changed+1093−213
cmake/modules/QmlTest.cmake+2−2 cmake/modules/autopilot.cmake+2−2 debian/changelog+47 debian/control+2−1 debian/unity8-private.install−2 plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla−6 plugins/AccountsService/AccountsService.cpp+27−1 plugins/AccountsService/AccountsService.h+9 plugins/AccountsService/CMakeLists.txt−10 plugins/AccountsService/com.canonical.unity.AccountsService.policy−24 plugins/AccountsService/com.canonical.unity.AccountsService.xml+28−5 plugins/LightDM/Greeter.cpp+4−1 plugins/LightDM/Greeter.h+1−1 plugins/Ubuntu/CMakeLists.txt+1 plugins/Ubuntu/SystemImage/CMakeLists.txt+10 plugins/Ubuntu/SystemImage/SystemImage.cpp+34 plugins/Ubuntu/SystemImage/SystemImage.h+38 plugins/Ubuntu/SystemImage/SystemImage.qmltypes+17 plugins/Ubuntu/SystemImage/plugin.cpp+34 plugins/Ubuntu/SystemImage/plugin.h+31 plugins/Ubuntu/SystemImage/qmldir+3 po/en_AU.po+1−1 po/fi.po+1−1 po/gl.po+13−13 po/it.po+13−13 po/km.po+1−1 po/nb.po+1−1 po/nl.po+6−6 po/pt_BR.po+1−1 po/unity8.pot+69−16 qml/Components/Lockscreen.qml+34−9 qml/Components/PassphraseLockscreen.qml+3−4 qml/Components/PinLockscreen.qml+3−4 qml/Dash/CardHorizontalList.qml+62 qml/Dash/CardTool.qml+2 qml/Dash/GenericScopeView.qml+1 qml/Dash/PreviewListView.qml−8 qml/Launcher/LauncherPanel.qml+4 qml/Notifications/Notification.qml+3 qml/Notifications/NotificationMenuItemFactory.qml+5−1 qml/Shell.qml+53−8 run.sh+19−18 src/Dash/CMakeLists.txt+1 src/Dash/main.cpp+4 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+15 tests/autopilot/unity8/shell/emulators/dash.py+16−6 tests/autopilot/unity8/shell/emulators/launcher.py+38−11 tests/autopilot/unity8/shell/emulators/main_window.py+32−3 tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−2 tests/autopilot/unity8/shell/tests/test_notifications.py+2−2 tests/mocks/AccountsService/AccountsService.cpp+13−1 tests/mocks/AccountsService/AccountsService.h+8 tests/mocks/LightDM/Greeter.cpp+11 tests/mocks/LightDM/Greeter.h+3 tests/mocks/LightDM/demo/CMakeLists.txt+13 tests/mocks/LightDM/full/GreeterPrivate.cpp+4−4 tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp+2−9 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+3−2 tests/mocks/Ubuntu/CMakeLists.txt+1 tests/mocks/Ubuntu/SystemImage/CMakeLists.txt+10 tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp+27 tests/mocks/Ubuntu/SystemImage/MockSystemImage.h+36 tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypes+18 tests/mocks/Ubuntu/SystemImage/plugin.cpp+34 tests/mocks/Ubuntu/SystemImage/plugin.h+31 tests/mocks/Ubuntu/SystemImage/qmldir+3 tests/mocks/Unity/fake_previewmodel.cpp+10−1 tests/mocks/Unity/fake_previewmodel.h+3 tests/mocks/Unity/fake_resultsmodel.cpp+5 tests/mocks/Unity/fake_resultsmodel.h+1 tests/mocks/Unity/fake_scopesoverview.cpp+4−2 tests/mocks/Unity/fake_unity_plugin.cpp+1 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Dash/tst_CardTool.qml+6 tests/qmltests/Dash/tst_Dash.qml+7−7 tests/qmltests/Dash/tst_DashContent.qml+1−1 tests/qmltests/Dash/tst_PreviewListView.qml+87 tests/qmltests/Greeter/tst_Lockscreen.qml−2 tests/qmltests/tst_ShellWithPin.qml+56
cmake/modules/QmlTest.cmake+2−2
@@ -103,7 +103,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
103103 endif()
104104
105105 set(qmltest_command
106− env ${qmltest_ENVIRONMENT}
106+ env ${qmltest_ENVIRONMENT} UNITY_TESTING=1
107107 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
108108 ${qmltestrunner_imports}
109109 ${ITERATIONS_STRING}
@@ -117,7 +117,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
117117 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
118118 endif()
119119 set(qmltest_xvfb_command
120− env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH}
120+ env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH} UNITY_TESTING=1
121121 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
122122 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
123123 ${qmltestrunner_imports}
cmake/modules/autopilot.cmake+2−2
@@ -2,7 +2,7 @@ add_custom_target(autopilot)
22
33function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
44 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}
66 WORKING_DIRECTORY ${WORKING_DIR}
77 DEPENDS fake_install
88 )
@@ -14,7 +14,7 @@ function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
1414 add_dependencies(autopilot autopilot-${TEST_NAME})
1515
1616 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}
1818 WORKING_DIRECTORY ${WORKING_DIR}
1919 DEPENDS fake_install
2020 )
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+
148unity8 (8.00+14.10.20140811-0ubuntu1) utopic; urgency=low
249
350 [ Andrea Cimitan ]
debian/control+2−1
@@ -165,7 +165,8 @@ Package: unity8-private
165165Architecture: any
166166Multi-Arch: same
167167Pre-Depends: ${misc:Pre-Depends},
168−Depends: gsettings-ubuntu-schemas,
168+Depends: accountsservice-ubuntu-schemas,
169+ gsettings-ubuntu-schemas,
169170 libhardware2,
170171 libunity-core-6.0-9,
171172 pay-service,
debian/unity8-private.install−2
@@ -10,5 +10,3 @@ usr/lib/*/unity8/qml/Unity
1010usr/lib/*/unity8/qml/Utils
1111usr/share/accountsservice/interfaces
1212usr/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)
2727 m_user(qgetenv("USER")),
2828 m_demoEdges(false),
2929 m_statsWelcomeScreen(false),
30− m_passwordDisplayHint(Keyboard)
30+ m_passwordDisplayHint(Keyboard),
31+ m_failedLogins(0)
3132{
3233 connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
3334 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
@@ -49,6 +50,7 @@ void AccountsService::setUser(const QString &user)
4950 updateBackgroundFile();
5051 updateStatsWelcomeScreen();
5152 updatePasswordDisplayHint();
53+ updateFailedLogins();
5254}
5355
5456bool AccountsService::demoEdges() const
@@ -113,6 +115,26 @@ void AccountsService::updatePasswordDisplayHint()
113115 }
114116}
115117
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+
116138void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
117139{
118140 if (m_user != user) {
@@ -123,6 +145,10 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
123145 if (changed.contains("demo-edges")) {
124146 updateDemoEdges();
125147 }
148+ } else if (interface == "com.canonical.unity.AccountsService.Private") {
149+ if (changed.contains("FailedLogins")) {
150+ updateFailedLogins();
151+ }
126152 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
127153 if (changed.contains("StatsWelcomeScreen")) {
128154 updateStatsWelcomeScreen();
plugins/AccountsService/AccountsService.h+9
@@ -45,6 +45,10 @@ class AccountsService: public QObject
4545 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
4646 READ passwordDisplayHint
4747 NOTIFY passwordDisplayHintChanged)
48+ Q_PROPERTY (uint failedLogins
49+ READ failedLogins
50+ WRITE setFailedLogins
51+ NOTIFY failedLoginsChanged)
4852
4953public:
5054 enum PasswordDisplayHint {
@@ -61,6 +65,8 @@ public:
6165 QString backgroundFile() const;
6266 bool statsWelcomeScreen() const;
6367 PasswordDisplayHint passwordDisplayHint() const;
68+ uint failedLogins() const;
69+ void setFailedLogins(uint failedLogins);
6470
6571Q_SIGNALS:
6672 void userChanged();
@@ -68,6 +74,7 @@ Q_SIGNALS:
6874 void backgroundFileChanged();
6975 void statsWelcomeScreenChanged();
7076 void passwordDisplayHintChanged();
77+ void failedLoginsChanged();
7178
7279private Q_SLOTS:
7380 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
@@ -78,6 +85,7 @@ private:
7885 void updateBackgroundFile();
7986 void updateStatsWelcomeScreen();
8087 void updatePasswordDisplayHint();
88+ void updateFailedLogins();
8189
8290 AccountsServiceDBusAdaptor *m_service;
8391 QString m_user;
@@ -85,6 +93,7 @@ private:
8593 QString m_backgroundFile;
8694 bool m_statsWelcomeScreen;
8795 PasswordDisplayHint m_passwordDisplayHint;
96+ uint m_failedLogins;
8897};
8998
9099#endif
plugins/AccountsService/CMakeLists.txt−10
@@ -10,8 +10,6 @@ qt5_use_modules(AccountsService-qml DBus Qml)
1010
1111add_unity8_plugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
1212
13−set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
14−set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
1513set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
1614set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
1715
@@ -24,11 +22,3 @@ install(CODE "
2422execute_process(COMMAND mkdir -p \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
2523execute_process(COMMAND ln -sf ../../dbus-1/interfaces/com.canonical.unity.AccountsService.xml \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
2624 ")
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 @@
33
44 <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
55
6− <annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
7− value="com.canonical.unity.AccountsService.ModifyOwnUser"/>
8−
96 <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
10− value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
7+ value="com.ubuntu.AccountsService.GreeterReadAny"/>
118
129 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
13− value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
10+ value="com.ubuntu.AccountsService.GreeterChangeAny"/>
1411
12+ <!-- Should have been named DemoEdges, sorry folks. -mterry -->
1513 <property name="demo-edges" type="b" access="readwrite">
1614 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
1715 </property>
1816
17+ <!-- Should have been named LauncherItems, sorry folks. -mterry -->
1918 <property name="launcher-items" type="aa{sv}" access="readwrite">
2019 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
2120 </property>
2221
2322 </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>
2447</node>
plugins/LightDM/Greeter.cpp+4−1
@@ -17,6 +17,7 @@
1717 */
1818
1919#include "Greeter.h"
20+#include <libintl.h>
2021#include <QLightDM/Greeter>
2122
2223class GreeterPrivate
@@ -125,13 +126,15 @@ void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptTyp
125126 Q_D(Greeter);
126127 d->wasPrompted = true;
127128
129+ bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
130+
128131 // Strip prompt of any colons at the end
129132 QString trimmedText = text.trimmed();
130133 if (trimmedText.endsWith(":") || trimmedText.endsWith(":")) {
131134 trimmedText.chop(1);
132135 }
133136
134− Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret);
137+ Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);
135138}
136139
137140void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
plugins/LightDM/Greeter.h+1−1
@@ -55,7 +55,7 @@ public Q_SLOTS:
5555
5656Q_SIGNALS:
5757 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);
5959 void authenticationComplete();
6060 void authenticationUserChanged(const QString &user);
6161 void isActiveChanged();
plugins/Ubuntu/CMakeLists.txt+1
@@ -1,3 +1,4 @@
11add_subdirectory(Gestures)
22add_subdirectory(DownloadDaemonListener)
33add_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 ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/fi.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/gl.po+13−13
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-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"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-12 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,19 +28,19 @@ msgstr "Eliminar o atallo"
2828
2929#: qml/Components/Dialogs.qml:142
3030msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Está certo de apagar o aparato?"
3232
3333#: qml/Components/Dialogs.qml:144
3434msgid "Power off"
35−msgstr ""
35+msgstr "Apagar"
3636
3737#: qml/Components/Dialogs.qml:153
3838msgid "Restart"
39−msgstr ""
39+msgstr "Reiniciar"
4040
4141#: qml/Components/Dialogs.qml:162
4242msgid "Cancel"
43−msgstr ""
43+msgstr "Cancelar"
4444
4545#: qml/Components/EdgeDemoOverlay.qml:151
4646msgid "Skip intro"
@@ -147,23 +147,23 @@ msgstr "Enviar"
147147
148148#: qml/Dash/ScopesOverview.qml:200
149149msgid "Manage Dash"
150−msgstr ""
150+msgstr "Xestionar o Panel"
151151
152152#: qml/Dash/ScopesOverview.qml:405
153153msgid "Done"
154−msgstr ""
154+msgstr "Feito"
155155
156156#: qml/Dash/ScopesOverview.qml:431
157157msgid "Store"
158−msgstr ""
158+msgstr "Tenda"
159159
160160#: qml/Dash/ScopesOverviewTab.qml:36
161161msgid "Favorites"
162−msgstr ""
162+msgstr "Favoritos"
163163
164164#: qml/Dash/ScopesOverviewTab.qml:54
165165msgid "All"
166−msgstr ""
166+msgstr "Todos"
167167
168168#: qml/Greeter/Greeter.qml:157
169169msgid "Swipe to unlock"
@@ -231,11 +231,11 @@ msgstr "Fallou, toque para reintentar"
231231
232232#: qml/Panel/Indicators/ModemInfoItem.qml:105
233233msgid "Unlock SIM"
234−msgstr ""
234+msgstr "Desbloquear a SIM"
235235
236236#: qml/Panel/Indicators/RoamingIndication.qml:27
237237msgid "Roaming"
238−msgstr ""
238+msgstr "Itinerancia"
239239
240240#: qml/Panel/SearchIndicator.qml:27
241241msgid "Search"
po/it.po+13−13
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-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"
1212"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-12 06:44+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,19 +28,19 @@ msgstr "Sblocca scorciatoia"
2828
2929#: qml/Components/Dialogs.qml:142
3030msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Spegnere veramente il dispositivo?"
3232
3333#: qml/Components/Dialogs.qml:144
3434msgid "Power off"
35−msgstr ""
35+msgstr "Spegni"
3636
3737#: qml/Components/Dialogs.qml:153
3838msgid "Restart"
39−msgstr ""
39+msgstr "Riavvia"
4040
4141#: qml/Components/Dialogs.qml:162
4242msgid "Cancel"
43−msgstr ""
43+msgstr "Annulla"
4444
4545#: qml/Components/EdgeDemoOverlay.qml:151
4646msgid "Skip intro"
@@ -150,23 +150,23 @@ msgstr "Invia"
150150
151151#: qml/Dash/ScopesOverview.qml:200
152152msgid "Manage Dash"
153−msgstr ""
153+msgstr "Gestione della Dash"
154154
155155#: qml/Dash/ScopesOverview.qml:405
156156msgid "Done"
157−msgstr ""
157+msgstr "Fatto"
158158
159159#: qml/Dash/ScopesOverview.qml:431
160160msgid "Store"
161−msgstr ""
161+msgstr "Store"
162162
163163#: qml/Dash/ScopesOverviewTab.qml:36
164164msgid "Favorites"
165−msgstr ""
165+msgstr "Preferiti"
166166
167167#: qml/Dash/ScopesOverviewTab.qml:54
168168msgid "All"
169−msgstr ""
169+msgstr "Tutto"
170170
171171#: qml/Greeter/Greeter.qml:157
172172msgid "Swipe to unlock"
@@ -234,11 +234,11 @@ msgstr "Non riuscito. Tocca per riprovare"
234234
235235#: qml/Panel/Indicators/ModemInfoItem.qml:105
236236msgid "Unlock SIM"
237−msgstr ""
237+msgstr "Sblocca SIM"
238238
239239#: qml/Panel/Indicators/RoamingIndication.qml:27
240240msgid "Roaming"
241−msgstr ""
241+msgstr "Roaming"
242242
243243#: qml/Panel/SearchIndicator.qml:27
244244msgid "Search"
po/km.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/nb.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/nl.po+6−6
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-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"
1212"Last-Translator: rob <linuxned@gmail.com>\n"
1313"Language-Team: Dutch <nl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:37+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -28,11 +28,11 @@ msgstr "Snelkoppeling losmaken"
2828
2929#: qml/Components/Dialogs.qml:142
3030msgid "Are you sure you would like to turn power off?"
31−msgstr ""
31+msgstr "Weet u zeker dat u het apparaat wilt uitschakelen?"
3232
3333#: qml/Components/Dialogs.qml:144
3434msgid "Power off"
35−msgstr ""
35+msgstr "Uitschakelen"
3636
3737#: qml/Components/Dialogs.qml:153
3838msgid "Restart"
@@ -149,7 +149,7 @@ msgstr "Snelzoeker-beheer"
149149
150150#: qml/Dash/ScopesOverview.qml:405
151151msgid "Done"
152−msgstr "Gereed"
152+msgstr "Klaar"
153153
154154#: qml/Dash/ScopesOverview.qml:431
155155msgid "Store"
@@ -233,7 +233,7 @@ msgstr "SIM-kaart deblokkeren"
233233
234234#: qml/Panel/Indicators/RoamingIndication.qml:27
235235msgid "Roaming"
236−msgstr ""
236+msgstr "Roaming"
237237
238238#: qml/Panel/SearchIndicator.qml:27
239239msgid "Search"
po/pt_BR.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-13 06:56+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-14 07:38+0000\n"
1818"X-Generator: Launchpad (build 17156)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
po/unity8.pot+69−16
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"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"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,6 +17,10 @@ msgstr ""
1717"Content-Type: text/plain; charset=UTF-8\n"
1818"Content-Transfer-Encoding: 8bit\n"
1919
20+#: plugins/LightDM/Greeter.cpp:129
21+msgid "Password: "
22+msgstr ""
23+
2024#: plugins/Unity/Launcher/launcheritem.cpp:43
2125#: plugins/Unity/Launcher/launcheritem.cpp:73
2226msgid "Pin shortcut"
@@ -88,39 +92,51 @@ msgid ""
8892"phone<br><br>Tap on the screen to start"
8993msgstr ""
9094
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
92104msgid "Emergency Call"
93105msgstr ""
94106
95−#: qml/Components/Lockscreen.qml:243
107+#: qml/Components/Lockscreen.qml:268
96108msgid "OK"
97109msgstr ""
98110
99−#: qml/Components/PassphraseLockscreen.qml:62
111+#: qml/Components/PassphraseLockscreen.qml:61
100112#, qt-format
101113msgid "Hello %1"
102114msgstr ""
103115
104−#: qml/Components/PassphraseLockscreen.qml:62
116+#: qml/Components/PassphraseLockscreen.qml:61
105117msgid "Hello"
106118msgstr ""
107119
108−#: qml/Components/PinLockscreen.qml:198
120+#: qml/Components/PinLockscreen.qml:197
109121msgid "CANCEL"
110122msgstr ""
111123
112−#: qml/Components/PinLockscreen.qml:216
124+#: qml/Components/PinLockscreen.qml:215
113125msgid "DONE"
114126msgstr ""
115127
116−#: qml/Dash/GenericScopeView.qml:358
128+#: qml/Dash/GenericScopeView.qml:360
117129msgid "See less"
118130msgstr ""
119131
120−#: qml/Dash/GenericScopeView.qml:358
132+#: qml/Dash/GenericScopeView.qml:360
121133msgid "See all"
122134msgstr ""
123135
136+#: qml/Dash/GenericScopeView.qml:423 qml/Panel/SearchIndicator.qml:27
137+msgid "Search"
138+msgstr ""
139+
124140#: qml/Dash/Previews/PreviewActionCombo.qml:34
125141msgid "More..."
126142msgstr ""
@@ -141,15 +157,15 @@ msgstr ""
141157msgid "Send"
142158msgstr ""
143159
144−#: qml/Dash/ScopesOverview.qml:200
160+#: qml/Dash/ScopesOverview.qml:201
145161msgid "Manage Dash"
146162msgstr ""
147163
148−#: qml/Dash/ScopesOverview.qml:405
164+#: qml/Dash/ScopesOverview.qml:408
149165msgid "Done"
150166msgstr ""
151167
152−#: qml/Dash/ScopesOverview.qml:431
168+#: qml/Dash/ScopesOverview.qml:434
153169msgid "Store"
154170msgstr ""
155171
@@ -233,11 +249,48 @@ msgstr ""
233249msgid "Roaming"
234250msgstr ""
235251
236−#: qml/Panel/SearchIndicator.qml:27
237−msgid "Search"
252+#: qml/Shell.qml:267
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"
238263msgstr ""
239264
240−#: qml/Shell.qml:256
265+#: qml/Shell.qml:270
241266#, 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."
243296msgstr ""
qml/Components/Lockscreen.qml+34−9
@@ -57,10 +57,15 @@ Showable {
5757
5858 onRequiredChanged: {
5959 if (required && pinPadLoader.item) {
60− pinPadLoader.item.clear(false);
60+ clear(false)
6161 }
6262 }
6363
64+ function forceDelay(delay) {
65+ forcedDelayTimer.interval = delay
66+ forcedDelayTimer.start()
67+ }
68+
6469 function reset() {
6570 // This causes the loader below to destry and recreate the source
6671 pinPadLoader.resetting = true;
@@ -68,13 +73,22 @@ Showable {
6873 }
6974
7075 function clear(showAnimation) {
71− pinPadLoader.item.clear(showAnimation);
76+ if (pinPadLoader.item) {
77+ pinPadLoader.item.clear(showAnimation);
78+ }
79+ pinPadLoader.showWrongText = showAnimation
80+ pinPadLoader.waiting = false
7281 }
7382
7483 function showInfoPopup(title, text) {
7584 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
7685 }
7786
87+ Timer {
88+ id: forcedDelayTimer
89+ onTriggered: pinPadLoader.showWrongText = false
90+ }
91+
7892 Rectangle {
7993 // In case background fails to load or is undefined
8094 id: backgroundBackup
@@ -131,8 +145,6 @@ Showable {
131145 }
132146 }
133147
134−
135−
136148 Loader {
137149 id: pinPadLoader
138150 objectName: "pinPadLoader"
@@ -143,13 +155,24 @@ Showable {
143155 verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : -units.gu(4)
144156 }
145157 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")
146164
147165 source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
166+ onSourceChanged: {
167+ waiting = false
168+ showWrongText = false
169+ }
148170
149171 Connections {
150172 target: pinPadLoader.item
151173
152174 onEntered: {
175+ pinPadLoader.waiting = true
153176 root.entered(passphrase);
154177 }
155178
@@ -171,17 +194,19 @@ Showable {
171194 Binding {
172195 target: pinPadLoader.item
173196 property: "placeholderText"
174− value: root.placeholderText
197+ value: forcedDelayTimer.running ? pinPadLoader.forcedDelayText :
198+ (pinPadLoader.showWrongText ? root.wrongPlaceholderText :
199+ root.placeholderText)
175200 }
176201 Binding {
177202 target: pinPadLoader.item
178− property: "wrongPlaceholderText"
179− value: root.wrongPlaceholderText
203+ property: "username"
204+ value: root.username
180205 }
181206 Binding {
182207 target: pinPadLoader.item
183− property: "username"
184− value: root.username
208+ property: "entryEnabled"
209+ value: !pinPadLoader.waiting && !forcedDelayTimer.running
185210 }
186211 }
187212
qml/Components/PassphraseLockscreen.qml+3−4
@@ -23,15 +23,14 @@ Item {
2323 height: highlightItem.height
2424
2525 property string placeholderText
26− property string wrongPlaceholderText
2726 property string username: ""
27+ property bool entryEnabled: true
2828
2929 signal entered(string passphrase)
3030 signal cancel()
3131
3232 function clear(playAnimation) {
3333 pinentryField.text = "";
34− pinentryField.enabled = true
3534 if (playAnimation) {
3635 wrongPasswordAnimation.start();
3736 pinentryField.forceActiveFocus();
@@ -76,11 +75,11 @@ Item {
7675 echoMode: TextInput.Password
7776 opacity: 0.9
7877 hasClearButton: false
79− placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
78+ enabled: entryEnabled
79+ placeholderText: root.placeholderText
8080
8181 onAccepted: {
8282 if (pinentryField.text) {
83− pinentryField.enabled = false;
8483 root.entered(pinentryField.text);
8584 }
8685 }
qml/Components/PinLockscreen.qml+3−4
@@ -25,7 +25,6 @@ Column {
2525 spacing: units.gu(3.5)
2626
2727 property alias placeholderText: pinentryField.placeholderText
28− property alias wrongPlaceholderText: pinentryField.wrongPlaceholderText
2928 property int padWidth: units.gu(34)
3029 property int padHeight: units.gu(28)
3130 property int minPinLength: -1
@@ -58,7 +57,6 @@ Column {
5857 radius: "medium"
5958 property string text: ""
6059 property string placeholderText: ""
61− property string wrongPlaceholderText: ""
6260
6361 function appendChar(character) {
6462 if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
@@ -90,9 +88,10 @@ Column {
9088 id: pinentryFieldPlaceHolder
9189 objectName: "pinentryFieldPlaceHolder"
9290 anchors.centerIn: parent
91+ horizontalAlignment: Text.AlignHCenter
9392 color: "#f3f3e7"
9493 opacity: 0.6
95− text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
94+ text: parent.placeholderText
9695 visible: pinentryFieldLabel.text.length == 0
9796 }
9897
@@ -107,7 +106,7 @@ Column {
107106 bottom: parent.bottom
108107 bottomMargin: units.gu(1)
109108 }
110− visible: !priv.autoConfirm
109+ visible: entryEnabled && !priv.autoConfirm
111110 width: height
112111 name: "erase"
113112 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 {
9191 }
9292 return units.gu(18.5);
9393 case "carousel":
94+ case "horizontal-list":
9495 return carouselTool.minimumTileWidth;
9596 case undefined:
9697 case "organic-grid":
@@ -111,6 +112,7 @@ Item {
111112 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
112113 return units.gu(18.5);
113114 case "grid":
115+ case "horizontal-list":
114116 return cardLoader.item ? cardLoader.item.implicitHeight : 0
115117 case "carousel":
116118 return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
qml/Dash/GenericScopeView.qml+1
@@ -227,6 +227,7 @@ FocusScope {
227227 switch (cardTool.categoryLayout) {
228228 case "carousel": return "CardCarousel.qml";
229229 case "vertical-journal": return "CardVerticalJournal.qml";
230+ case "horizontal-list": return "CardHorizontalList.qml";
230231 case "grid":
231232 default: return "CardGrid.qml";
232233 }
qml/Dash/PreviewListView.qml−8
@@ -65,14 +65,6 @@ Item {
6565 maximumFlickVelocity: width * 5
6666 cacheBuffer: 0
6767
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−
7668 property bool open: false
7769
7870 onOpenChanged: {
qml/Launcher/LauncherPanel.qml+4
@@ -146,6 +146,10 @@ Rectangle {
146146 delegate: FoldingLauncherDelegate {
147147 id: launcherDelegate
148148 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
149153 itemHeight: launcherListView.itemHeight
150154 itemWidth: launcherListView.itemWidth
151155 width: itemWidth
qml/Notifications/Notification.qml+3
@@ -311,6 +311,9 @@ Item {
311311 onLoaded: {
312312 notification.fullscreen = Qt.binding(function() { return fullscreen; });
313313 }
314+ onAccepted: {
315+ notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
316+ }
314317 }
315318 }
316319 }
qml/Notifications/NotificationMenuItemFactory.qml+5−1
@@ -30,6 +30,8 @@ Loader {
3030 property int maxHeight
3131 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
3232
33+ signal accepted()
34+
3335 property var _map: {
3436 "com.canonical.snapdecision.textfield": textfield,
3537 "com.canonical.snapdecision.pinlock" : pinLock,
@@ -82,6 +84,9 @@ Loader {
8284 onTextChanged: {
8385 menuModel.changeState(menuIndex, text);
8486 }
87+ onAccepted: {
88+ menuFactory.accepted()
89+ }
8590 }
8691
8792 Row {
@@ -117,7 +122,6 @@ Loader {
117122
118123 onEntered: {
119124 menuModel.changeState(menuIndex, passphrase);
120− entryEnabled = false;
121125 }
122126
123127 onCancel: {
qml/Shell.qml+53−8
@@ -19,7 +19,9 @@ import AccountsService 0.1
1919import GSettings 1.0
2020import Unity.Application 0.1
2121import Ubuntu.Components 0.1
22+import Ubuntu.Components.Popups 1.0
2223import Ubuntu.Gestures 0.1
24+import Ubuntu.SystemImage 0.1
2325import Unity.Launcher 0.1
2426import Utils 0.1
2527import LightDM 0.1 as LightDM
@@ -53,6 +55,10 @@ Item {
5355 property bool sideStageEnabled: shell.width >= units.gu(100)
5456 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
5557
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+
5662 function activateApplication(appId) {
5763 if (ApplicationManager.findApplication(appId)) {
5864 ApplicationManager.requestFocusApplication(appId);
@@ -255,13 +261,21 @@ Item {
255261
256262 onShowPrompt: {
257263 if (greeter.narrowMode) {
258− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
264+ var promptText = text.toLowerCase()
265+ if (isDefaultPrompt) {
266+ promptText = lockscreen.alphaNumeric ?
267+ i18n.tr("passphrase") : i18n.tr("passcode")
268+ }
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")
259273 lockscreen.show();
260274 }
261275 }
262276
263277 onPromptlessChanged: {
264− if (LightDM.Greeter.promptless) {
278+ if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
265279 lockscreen.hide()
266280 } else {
267281 lockscreen.reset();
@@ -270,13 +284,40 @@ Item {
270284 }
271285
272286 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+
273294 if (LightDM.Greeter.promptless) {
274295 return;
275296 }
297+
276298 if (LightDM.Greeter.authenticated) {
277299 lockscreen.hide();
278300 greeter.login();
279301 } 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+
280321 lockscreen.clear(true);
281322 if (greeter.narrowMode) {
282323 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -311,8 +352,16 @@ Item {
311352 }
312353
313354 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+
314363 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()
316365
317366 Greeter {
318367 id: greeter
@@ -350,10 +399,6 @@ Item {
350399 if (greeter.narrowMode) {
351400 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
352401 }
353− if (!LightDM.Greeter.promptless) {
354− lockscreen.reset();
355− lockscreen.show();
356− }
357402 greeter.fakeActiveForApp = "";
358403 greeter.forceActiveFocus();
359404 }
@@ -403,7 +448,7 @@ Item {
403448 }
404449
405450 if (LightDM.Greeter.active) {
406− if (!LightDM.Greeter.promptless) {
451+ if (!LightDM.Greeter.authenticated) {
407452 lockscreen.show()
408453 }
409454 greeter.hide()
run.sh+19−18
@@ -18,6 +18,7 @@ usage() {
1818 echo " -f, --fake Force use of fake Qml modules." >&2
1919 echo " -p, --pinlock Use a pin protected user." >&2
2020 echo " -k, --keylock Use a passphrase protected user." >&2
21+ echo " -l, --lightdm Use the specified lightdm mock." >&2
2122 echo " -g, --gdb Run through gdb." >&2
2223 echo " -h, --help Show this help." >&2
2324 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -25,16 +26,20 @@ usage() {
2526 exit 1
2627}
2728
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" -- "$@"`
2930[ $? -ne 0 ] && usage
3031eval set -- "$ARGS"
3132
3233while [ $# -gt 0 ]
3334do
3435 case "$1" in
35− -f|--fake) FAKE=true; USE_MOCKS=true;;
36− -p|--pinlock) PINLOCK=true; USE_MOCKS=true;;
37− -k|--keylock) KEYLOCK=true; USE_MOCKS=true;;
36+ -f|--fake) USE_MOCKS=true;;
37+ -p|--pinlock) USE_MOCKS=true; LIGHTDM_MOCK=single-pin;;
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;;
3843 -g|--gdb) GDB=true;;
3944 -h|--help) usage;;
4045 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -43,27 +48,23 @@ do
4348 shift
4449done
4550
46−if $FAKE; then
47− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single
51+if [ -z "$LIGHTDM_MOCK" ]; then
52+ LIGHTDM_MOCK=single
4853fi
4954
50−if $PINLOCK; then
51− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-pin
52−fi
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
56−fi
55+# Even without USE_MOCKS set, we want to fake our lightdm backend, because it's
56+# annoying to be prompted for your password when testing. To get the same
57+# backend used in production, pass '--lightdm=demo'.
58+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/$LIGHTDM_MOCK
5759
5860if $USE_MOCKS; then
5961 rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
6062 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
6164else
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
64− # our plugins to be used.
65− ln -s $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
66− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
65+ # Just link our LightDM mock into the nonmirplugins folder. We don't want
66+ # the rest of our plugins to be used.
67+ ln -sf $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
6768fi
6869
6970QML_PHONE_SHELL_ARGS=""
src/Dash/CMakeLists.txt+1
@@ -1,6 +1,7 @@
11set(DASH_SRCS
22 main.cpp
33 ../MouseTouchAdaptor.cpp
4+ ../CachingNetworkManagerFactory.cpp
45)
56
67add_executable(unity8-dash ${DASH_SRCS})
src/Dash/main.cpp+4
@@ -28,6 +28,7 @@
2828
2929#include <paths.h>
3030#include "../MouseTouchAdaptor.h"
31+#include "../CachingNetworkManagerFactory.h"
3132
3233int main(int argc, const char *argv[])
3334{
@@ -84,6 +85,9 @@ int main(int argc, const char *argv[])
8485 prependImportPaths(view->engine(), ::overrideImportPaths());
8586 appendImportPaths(view->engine(), ::fallbackImportPaths());
8687
88+ CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
89+ view->engine()->setNetworkAccessManagerFactory(managerFactory);
90+
8791 view->setSource(source);
8892 view->show();
8993
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+15
@@ -115,3 +115,18 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
115115 self.launch_upstart_application(application_name)
116116 self.assertThat(greeter.created, Eventually(Equals(False)))
117117 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
2424
2525from autopilot import logging as autopilot_logging
2626from autopilot.introspection import dbus
27−from testtools.matchers import MatchesAny, Equals
2827from ubuntuuitoolkit import emulators as toolkit_emulators
2928
3029
@@ -146,7 +145,8 @@ class Dash(emulators.UnityEmulatorBase):
146145 current_header.globalRect.y +
147146 current_header.height / 4)
148147 self.pointing_device.click()
149− headerContainer = current_header.select_single(objectName="headerContainer")
148+ headerContainer = current_header.select_single(
149+ objectName="headerContainer")
150150 headerContainer.contentY.wait_for(0)
151151 search_text_field = self._get_search_text_field()
152152 search_text_field.write(query)
@@ -177,22 +177,32 @@ class GenericScopeView(emulators.UnityEmulatorBase):
177177 """Open the preview of an application.
178178
179179 :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.
181181 :return: The opened preview.
182182
183183 """
184− category_element = self._get_category_element(category)
185− icon = category_element.select_single('AbstractButton', title=app_name)
186184 # FIXME some categories need a long press in order to see the preview.
187185 # Some categories do not show previews, like recent apps.
188186 # --elopio - 2014-1-14
189− self.pointing_device.click_object(icon)
187+ self.click_scope_item(category, app_name)
190188 preview_list = self.wait_select_single(
191189 'PreviewListView', objectName='previewListView')
192190 preview_list.x.wait_for(0)
193191 return preview_list.select_single(
194192 Preview, objectName='preview{}'.format(preview_list.currentIndex))
195193
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+
196206 def _get_category_element(self, category):
197207 try:
198208 return self.wait_select_single(
tests/autopilot/unity8/shell/emulators/launcher.py+38−11
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012-2013 Canonical
4+# Copyright (C) 2012, 2013, 2014 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -17,22 +17,49 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−from unity8.shell.emulators import UnityEmulatorBase
21−from autopilot.input import Touch
20+import logging
2221
22+import autopilot.logging
2323
24−class Launcher(UnityEmulatorBase):
24+from unity8.shell import emulators
25+
26+
27+logger = logging.getLogger(__name__)
28+
29+
30+class Launcher(emulators.UnityEmulatorBase):
2531
2632 """An emulator that understands the Launcher."""
2733
34+ @autopilot.logging.log_action(logger.debug)
2835 def show(self):
29− """Swipes open the launcher."""
30− touch = Touch.create()
36+ """Show the launcher swiping it to the right."""
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.')
3142
43+ def _swipe_to_show_launcher(self):
3244 view = self.get_root_instance().select_single('QQuickView')
45+ start_y = stop_y = view.y + view.height // 2
46+
3347 start_x = view.x + 1
34− start_y = view.y + view.height / 2
35− stop_x = start_x + self.panelWidth + 1
36− stop_y = start_y
37− touch.drag(start_x, start_y, stop_x, stop_y)
38− self.shown.wait_for(True)
48+ stop_x = start_x + self.panelWidth - 1
49+
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):
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):
6363 def get_bottombar(self):
6464 return self.select_single("Bottombar")
6565
66− def get_launcher(self):
67− return self.select_single(Launcher)
68−
6966 def get_pinPadLoader(self):
7067 return self.select_single(
7168 "QQuickLoader",
@@ -113,6 +110,7 @@ class QQuickView(emulators.UnityEmulatorBase):
113110 start_y = end_y = y + height // 2
114111
115112 self.pointing_device.drag(start_x, start_y, end_x, end_y)
113+ self.get_current_focused_app_id().wait_for('unity8-dash')
116114
117115 def _get_shell(self):
118116 return self.select_single('Shell')
@@ -122,6 +120,37 @@ class QQuickView(emulators.UnityEmulatorBase):
122120 return self._get_shell().focusedApplicationId
123121
124122 @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+
125154 def enter_pin_code(self, code):
126155 """Enter code 'code' into the single-pin lightdm pincode entry screen.
127156
tests/autopilot/unity8/shell/tests/disabled_test_hud.py+1−2
@@ -184,12 +184,11 @@ class TestHud(UnityTestCase, DragMixin):
184184 unity_proxy = self.launch_unity()
185185 unlock_unity(unity_proxy)
186186 hud = self.main_window.get_hud()
187− launcher = self.main_window.get_launcher()
188187
189188 self._launch_test_app_from_app_screen()
190189
191190 hud.show()
192− launcher.show()
191+ self.main_window.open_launcher()
193192
194193 self.assertThat(hud.shown, Eventually(Equals(False)))
195194
tests/autopilot/unity8/shell/tests/test_notifications.py+2−2
@@ -242,8 +242,8 @@ class InteractiveNotificationBase(NotificationsBase):
242242 # the shell)
243243 time.sleep(1)
244244 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)))
247247
248248 # verify and interact with the triggered snap-decision notification
249249 notify_list = self._get_notifications_list()
tests/mocks/AccountsService/AccountsService.cpp+13−1
@@ -23,7 +23,8 @@
2323AccountsService::AccountsService(QObject* parent)
2424 : QObject(parent),
2525 m_backgroundFile(qmlDirectory() + "graphics/phone_background.jpg"),
26− m_statsWelcomeScreen(true)
26+ m_statsWelcomeScreen(true),
27+ m_failedLogins(0)
2728{
2829}
2930
@@ -78,3 +79,14 @@ AccountsService::PasswordDisplayHint AccountsService::passwordDisplayHint() cons
7879 else
7980 return PasswordDisplayHint::Keyboard;
8081}
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
4747 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
4848 READ passwordDisplayHint
4949 NOTIFY passwordDisplayHintChanged)
50+ Q_PROPERTY (uint failedLogins
51+ READ failedLogins
52+ WRITE setFailedLogins
53+ NOTIFY failedLoginsChanged)
5054
5155public:
5256 enum PasswordDisplayHint {
@@ -65,6 +69,8 @@ public:
6569 bool statsWelcomeScreen() const;
6670 void setStatsWelcomeScreen(bool statsWelcomeScreen);
6771 PasswordDisplayHint passwordDisplayHint() const;
72+ uint failedLogins() const;
73+ void setFailedLogins(uint failedLogins);
6874
6975Q_SIGNALS:
7076 void userChanged();
@@ -72,11 +78,13 @@ Q_SIGNALS:
7278 void backgroundFileChanged();
7379 void statsWelcomeScreenChanged();
7480 void passwordDisplayHintChanged();
81+ void failedLoginsChanged();
7582
7683private:
7784 QString m_backgroundFile;
7885 QString m_user;
7986 bool m_statsWelcomeScreen;
87+ uint m_failedLogins;
8088};
8189
8290#endif
tests/mocks/LightDM/Greeter.cpp+11
@@ -19,6 +19,7 @@
1919#include "Greeter.h"
2020#include "GreeterPrivate.h"
2121#include <QtCore/QCoreApplication>
22+#include <QTimer>
2223
2324namespace QLightDM
2425{
@@ -164,4 +165,14 @@ void Greeter::respond(const QString &response)
164165 d->handleRespond(response);
165166}
166167
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+
167178}
tests/mocks/LightDM/Greeter.h+3
@@ -96,6 +96,9 @@ Q_SIGNALS:
9696 void authenticationComplete();
9797 void autologinTimerExpired();
9898
99+protected:
100+ void sendAuthenticationComplete();
101+
99102private:
100103 GreeterPrivate *d_ptr;
101104 Q_DECLARE_PRIVATE(Greeter)
tests/mocks/LightDM/demo/CMakeLists.txt+13
@@ -9,6 +9,7 @@ set(LibLightDM_SOURCES
99 )
1010
1111add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
12+add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
1213
1314include_directories(
1415 ${CMAKE_CURRENT_BINARY_DIR}
@@ -19,5 +20,17 @@ target_link_libraries(MockLightDM-demo
1920 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
2021 -lpam
2122)
23+target_link_libraries(MockLightDM-demo-shared
24+ ${LIBUSERMETRICSOUTPUT_LDFLAGS}
25+ -lpam
26+)
2227
2328qt5_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()
5454 authenticated = true;
5555 Q_EMIT q->authenticationComplete();
5656 } else if (authenticationUser == "has-pin"){
57− Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
57+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
5858 } else if (authenticationUser == "auth-error") {
5959 authenticated = false;
6060 Q_EMIT q->authenticationComplete();
@@ -78,11 +78,11 @@ void GreeterPrivate::handleRespond(const QString &response)
7878 Q_EMIT q->showPrompt("otp", Greeter::PromptTypeQuestion);
7979 } else {
8080 authenticated = false;
81− Q_EMIT q->authenticationComplete();
81+ q->sendAuthenticationComplete();
8282 }
8383 } else {
8484 authenticated = (response == "otp");
85− Q_EMIT q->authenticationComplete();
85+ q->sendAuthenticationComplete();
8686 }
8787 return;
8888 }
@@ -92,7 +92,7 @@ void GreeterPrivate::handleRespond(const QString &response)
9292 } else {
9393 authenticated = (response == "password");
9494 }
95− Q_EMIT q->authenticationComplete();
95+ q->sendAuthenticationComplete();
9696}
9797
9898}
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp+2−9
@@ -19,8 +19,6 @@
1919#include "../Greeter.h"
2020#include "../GreeterPrivate.h"
2121
22−#include <QDebug>
23−
2422namespace QLightDM
2523{
2624
@@ -34,20 +32,15 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
3432void GreeterPrivate::handleAuthenticate()
3533{
3634 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);
4136}
4237
4338void GreeterPrivate::handleRespond(const QString &response)
4439{
4540 Q_Q(Greeter);
4641
47−
4842 authenticated = (response == "password");
49− qDebug() << "responding" << response << authenticated;
50− Q_EMIT q->authenticationComplete();
43+ q->sendAuthenticationComplete();
5144}
5245
5346}
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+3−2
@@ -32,14 +32,15 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
3232void GreeterPrivate::handleAuthenticate()
3333{
3434 Q_Q(Greeter);
35− Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
35+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
3636}
3737
3838void GreeterPrivate::handleRespond(const QString &response)
3939{
4040 Q_Q(Greeter);
41+
4142 authenticated = (response == "1234");
42− Q_EMIT q->authenticationComplete();
43+ q->sendAuthenticationComplete();
4344}
4445
4546}
tests/mocks/Ubuntu/CMakeLists.txt+1
@@ -1,4 +1,5 @@
11add_subdirectory(DownloadDaemonListener)
22add_subdirectory(Payments)
3+add_subdirectory(SystemImage)
34add_subdirectory(Telephony)
45add_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 @@
2929
3030PreviewModel::PreviewModel(QObject* parent)
3131 : unity::shell::scopes::PreviewModelInterface(parent)
32+ , m_loaded(true)
3233{
3334 // we have one column by default
3435 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
@@ -49,7 +50,7 @@ int PreviewModel::widgetColumnCount() const
4950
5051bool PreviewModel::loaded() const
5152{
52− return true;
53+ return m_loaded;
5354}
5455
5556bool PreviewModel::processingAction() const
@@ -71,3 +72,11 @@ QVariant PreviewModel::data(const QModelIndex& index, int role) const
7172 return QVariant();
7273 }
7374}
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:
4040 bool loaded() const override;
4141 bool processingAction() const override;
4242
43+ Q_INVOKABLE void setLoaded(bool); // Only available for testing
44+
4345private:
4446 QList<PreviewWidgetModel*> m_previewWidgetModels;
47+ bool m_loaded;
4548};
4649
4750Q_DECLARE_METATYPE(PreviewModel*)
tests/mocks/Unity/fake_resultsmodel.cpp+5
@@ -29,6 +29,11 @@ ResultsModel::ResultsModel(int result_count, int categoryId, QObject* parent)
2929{
3030}
3131
32+ResultsModel::ResultsModel(QObject *parent)
33+ : ResultsModel::ResultsModel(10, 1, parent)
34+{
35+}
36+
3237QString ResultsModel::categoryId() const
3338{
3439 return QString::number(m_categoryId);
tests/mocks/Unity/fake_resultsmodel.h+1
@@ -29,6 +29,7 @@ class ResultsModel : public unity::shell::scopes::ResultsModelInterface
2929
3030public:
3131 explicit ResultsModel(int result_count, int categoryId, QObject* parent = 0);
32+ explicit ResultsModel(QObject* parent = 0);
3233
3334 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
3435 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
105105 }
106106 case RoleComponents:
107107 {
108− QVariantMap map, artMap;
108+ QVariantMap map, artMap, attributesMap;
109109 artMap["aspect-ratio"] = "0.5";
110110 artMap["field"] = "art";
111111 map["art"] = artMap;
112112 map["title"] = "HOLA";
113+ map["attributes"] = attributesMap;
113114 return map;
114115 }
115116 case RoleResults:
@@ -188,11 +189,12 @@ ScopesOverviewSearchCategories::data(const QModelIndex& index, int role) const
188189 }
189190 case RoleComponents:
190191 {
191− QVariantMap map, artMap;
192+ QVariantMap map, artMap, attributesMap;
192193 artMap["aspect-ratio"] = "1";
193194 artMap["field"] = "art";
194195 map["art"] = artMap;
195196 map["title"] = "HOLA";
197+ map["attributes"] = attributesMap;
196198 return map;
197199 }
198200 case RoleResults:
tests/mocks/Unity/fake_unity_plugin.cpp+1
@@ -47,6 +47,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
4747 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
4848 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
4949 qmlRegisterUncreatableType<ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create ResultsModel object in QML.");
50+ qmlRegisterType<ResultsModel>(uri, 0, 2, "FakeResultsModel");
5051 qmlRegisterType<PreviewModel>(uri, 0, 2, "FakePreviewModel");
5152 qmlRegisterUncreatableType<PreviewWidgetModel>(uri, 0, 2, "PreviewWidgetModel", "Can't create new PreviewWidgetModel in QML. Get them from PreviewModel instance.");
5253 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)
3939add_qml_test(Dash CardTool)
4040add_qml_test(Dash GenericScopeView)
4141add_qml_test(Dash PageHeader)
42+add_qml_test(Dash PreviewListView)
4243add_qml_test(Dash/Previews Preview)
4344add_qml_test(Dash/Previews PreviewActions)
4445add_qml_test(Dash/Previews PreviewAudioPlayback)
tests/qmltests/Dash/tst_CardTool.qml+6
@@ -163,6 +163,7 @@ Rectangle {
163163 { label: "Horizontal Journal", layout: "journal" },
164164 { label: "Vertical Journal", layout: "vertical-journal" },
165165 { label: "Carousel", layout: "carousel" },
166+ { label: "Horizontal List", layout: "horizontal-list" },
166167 ]
167168 delegate: OptionSelectorDelegate { text: modelData.label }
168169 onSelectedIndexChanged: {
@@ -226,6 +227,7 @@ Rectangle {
226227 case "journal": layoutSelector.selectedIndex = 2; break;
227228 case "vertical-journal": layoutSelector.selectedIndex = 3; break;
228229 case "carousel": layoutSelector.selectedIndex = 4; break;
230+ case "horizontal-list": layoutSelector.selectedIndex = 5; break;
229231 case "grid":
230232 default: layoutSelector.selectedIndex = 0; break;
231233 }
@@ -270,6 +272,10 @@ Rectangle {
270272 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
271273 { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
272274 { 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},
273279 ]
274280 }
275281
tests/qmltests/Dash/tst_Dash.qml+7−7
@@ -54,6 +54,7 @@ Item {
5454 tryCompare(dashContentList, "count", 0);
5555 scopes.load();
5656 tryCompare(dashContentList, "currentIndex", 0);
57+ tryCompare(dashContentList, "count", 6);
5758 }
5859
5960 function get_scope_data() {
@@ -273,6 +274,8 @@ Item {
273274 // Click on a temp scope in the search
274275 var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
275276 var cardTempScope = findChild(dashCategorysearchA, "delegate2");
277+ verify(cardTempScope, "Could not find delegate2");
278+
276279 waitForRendering(cardTempScope);
277280 mouseClick(cardTempScope, cardTempScope.width / 2, cardTempScope.height / 2);
278281
@@ -313,13 +316,10 @@ Item {
313316 var startY = dash.height / 2;
314317 var stopX = units.gu(1)
315318 var stopY = startY;
316− var retry = 0;
317− while (dashContentList.currentIndex != 2 && retry <= 5) {
318− mouseFlick(dash, startX, startY, stopX, stopY)
319− waitForRendering(dashContentList)
320− retry++;
321− }
322− compare(dashContentList.currentIndex, 2);
319+ waitForRendering(dashContentList)
320+ mouseFlick(dash, startX, startY, stopX, stopY);
321+ mouseFlick(dash, startX, startY, stopX, stopY);
322+ compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
323323 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
324324 dashCommunicatorService.mockSetCurrentScope("clickscope", true, true);
325325 tryCompare(dashContentList, "currentIndex", 1)
tests/qmltests/Dash/tst_DashContent.qml+1−1
@@ -390,7 +390,7 @@ Item {
390390 var categoryListView = findChild(scope, "categoryListView");
391391 waitForRendering(categoryListView);
392392
393− compare(categoryListView.pageHeader.searchHint, "Search People");
393+ compare(categoryListView.pageHeader.item.searchHint, "Search People");
394394 }
395395 }
396396}
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 {
319319 if (data.animation) {
320320 if (data.alphanumeric) {
321321 tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
322− tryCompare(inputField, "placeholderText", lockscreen.placeholderText)
323322 } else {
324323 var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
325324 tryCompare(label, "text", lockscreen.wrongPlaceholderText)
326− tryCompare(label, "text", lockscreen.placeholderText)
327325 }
328326 }
329327
tests/qmltests/tst_ShellWithPin.qml+56
@@ -16,8 +16,10 @@
1616
1717import QtQuick 2.0
1818import QtTest 1.0
19+import AccountsService 0.1
1920import GSettings 1.0
2021import LightDM 0.1 as LightDM
22+import Ubuntu.SystemImage 0.1
2123import Unity.Application 0.1
2224import Unity.Test 0.1 as UT
2325import Powerd 0.1
@@ -25,6 +27,7 @@ import Powerd 0.1
2527import "../../qml"
2628
2729Item {
30+ id: root
2831 width: shell.width
2932 height: shell.height
3033
@@ -53,6 +56,12 @@ Item {
5356 signalName: "sessionStarted"
5457 }
5558
59+ SignalSpy {
60+ id: resetSpy
61+ target: SystemImage
62+ signalName: "resettingDevice"
63+ }
64+
5665 UT.UnityTestCase {
5766 name: "ShellWithPin"
5867 when: windowShown
@@ -64,6 +73,8 @@ Item {
6473
6574 function init() {
6675 swipeAwayGreeter()
76+ shell.failedLoginsDelayAttempts = -1
77+ shell.maxFailedLogins = -1
6778 }
6879
6980 function cleanup() {
@@ -111,6 +122,7 @@ Item {
111122 }
112123
113124 function test_login() {
125+ sessionSpy.clear()
114126 tryCompare(sessionSpy, "count", 0)
115127 enterPin("1234")
116128 tryCompare(sessionSpy, "count", 1)
@@ -175,5 +187,49 @@ Item {
175187 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
176188 tryCompare(lockscreen, "shown", true)
177189 }
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+ }
178234 }
179235}